summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AconfigFlags.bp15
-rw-r--r--Android.bp1
-rw-r--r--Ravenwood.bp2
-rw-r--r--api/ApiDocs.bp8
-rw-r--r--api/StubLibraries.bp8
-rw-r--r--api/javadoc-lint-baseline146
-rw-r--r--core/api/current.txt20
-rw-r--r--core/api/system-current.txt22
-rw-r--r--core/api/test-current.txt7
-rw-r--r--core/java/android/app/LocaleConfig.java14
-rw-r--r--core/java/android/app/admin/SystemUpdatePolicy.java2
-rw-r--r--core/java/android/app/usage/flags.aconfig2
-rw-r--r--core/java/android/companion/virtual/sensor/VirtualSensor.java5
-rw-r--r--core/java/android/content/ContextParams.java5
-rw-r--r--core/java/android/content/Intent.java2
-rw-r--r--core/java/android/content/pm/ArchivedActivityParcel.aidl3
-rw-r--r--core/java/android/content/pm/flags.aconfig8
-rw-r--r--core/java/android/content/res/flags.aconfig10
-rw-r--r--core/java/android/hardware/SensorManager.java79
-rw-r--r--core/java/android/hardware/SystemSensorManager.java51
-rw-r--r--core/java/android/hardware/face/FaceManager.java11
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintManager.java11
-rw-r--r--core/java/android/hardware/input/InputDeviceSensorManager.java2
-rw-r--r--core/java/android/os/VibrationAttributes.java111
-rw-r--r--core/java/android/os/Vibrator.java10
-rw-r--r--core/java/android/os/flags.aconfig2
-rw-r--r--core/java/android/os/vibrator/VibrationConfig.java12
-rw-r--r--core/java/android/os/vibrator/flags.aconfig7
-rw-r--r--core/java/android/permission/flags.aconfig7
-rw-r--r--core/java/android/provider/Settings.java8
-rw-r--r--core/java/android/service/voice/AbstractDetector.java6
-rw-r--r--core/java/android/service/voice/VoiceInteractionService.java21
-rw-r--r--core/java/android/speech/SpeechRecognizer.java14
-rw-r--r--core/java/android/util/FeatureFlagUtils.java8
-rw-r--r--core/java/android/view/AttachedSurfaceControl.java6
-rw-r--r--core/java/android/view/ContentRecordingSession.java111
-rw-r--r--core/java/android/view/Display.java1
-rw-r--r--core/java/android/view/SurfaceControlRegistry.java5
-rw-r--r--core/java/android/view/View.java48
-rw-r--r--core/java/android/view/ViewRootImpl.java182
-rw-r--r--core/java/android/view/contentcapture/MainContentCaptureSession.java19
-rw-r--r--core/java/android/view/contentprotection/ContentProtectionEventProcessor.java154
-rw-r--r--core/java/android/view/contentprotection/ContentProtectionUtils.java28
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java7
-rw-r--r--core/java/android/view/inputmethod/flags.aconfig8
-rw-r--r--core/java/android/widget/TextView.java3
-rw-r--r--core/java/android/window/TaskFragmentOperation.java24
-rw-r--r--core/java/android/window/TransitionInfo.java15
-rw-r--r--core/java/android/window/TransitionRequestInfo.java48
-rw-r--r--core/java/android/window/flags/windowing_frontend.aconfig16
-rw-r--r--core/java/com/android/internal/policy/WearGestureInterceptionDetector.java16
-rw-r--r--core/jni/android_hardware_SensorManager.cpp17
-rw-r--r--core/proto/android/service/package.proto3
-rw-r--r--core/res/Android.bp1
-rw-r--r--core/res/AndroidManifest.xml4
-rw-r--r--core/res/res/values/config.xml15
-rw-r--r--core/res/res/values/symbols.xml4
-rw-r--r--core/res/res/xml/sms_short_codes.xml9
-rw-r--r--core/tests/coretests/Android.bp2
-rw-r--r--core/tests/coretests/src/android/hardware/face/FaceManagerTest.java19
-rw-r--r--core/tests/coretests/src/android/hardware/fingerprint/FingerprintManagerTest.java19
-rw-r--r--core/tests/coretests/src/android/view/ViewRootImplTest.java136
-rw-r--r--core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java22
-rw-r--r--core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java496
-rw-r--r--core/tests/coretests/src/android/view/contentprotection/ContentProtectionUtilsTest.java68
-rw-r--r--core/tests/coretests/src/android/widget/HorizontalScrollViewFunctionalTest.java43
-rw-r--r--graphics/java/android/graphics/text/LineBreakConfig.java7
-rw-r--r--keystore/java/android/security/keystore/KeyGenParameterSpec.java36
-rw-r--r--libs/WindowManager/Shell/Android.bp1
-rw-r--r--libs/WindowManager/Shell/res/values-television/config.xml6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java7
-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/dagger/pip/OWNERS1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java29
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java133
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java24
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java26
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java91
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt99
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt96
-rw-r--r--media/java/android/media/IMediaRouterService.aidl3
-rw-r--r--media/java/android/media/MediaRouter2.java4
-rw-r--r--media/java/android/media/MediaRouter2Manager.java2
-rw-r--r--media/java/android/media/RingtoneV1.java43
-rw-r--r--media/java/android/media/midi/MidiUmpDeviceService.java12
-rw-r--r--media/java/android/media/projection/IMediaProjectionManager.aidl2
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RingtoneTest.java (renamed from media/tests/ringtone/src/com/android/media/RingtoneBuilderTest.java)285
-rw-r--r--media/tests/ringtone/Android.bp15
-rw-r--r--media/tests/ringtone/OWNERS3
-rw-r--r--media/tests/ringtone/src/com/android/media/testing/MediaPlayerTestHelper.java75
-rw-r--r--media/tests/ringtone/src/com/android/media/testing/RingtoneInjectablesTrackingTestRule.java225
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt2
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/ListPreferencePageProvider.kt120
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMainPageProvider.kt (renamed from packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMain.kt)1
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt12
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsOpacity.kt5
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt2
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt2
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsDialog.kt46
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt6
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ListPreference.kt138
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/MainSwitchPreference.kt2
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt15
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt15
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/dialog/SettingsDialogTest.kt57
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ListPreferenceTest.kt177
-rw-r--r--packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowColorDisplayManager.java46
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java1
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java1
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java26
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java22
-rw-r--r--packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java49
-rw-r--r--packages/SoundPicker/Android.bp36
-rw-r--r--packages/SoundPicker/AndroidManifest.xml29
-rw-r--r--packages/SoundPicker/res/layout/add_new_sound_item.xml32
-rw-r--r--packages/SoundPicker/res/layout/radio_with_work_badge.xml20
-rw-r--r--packages/SoundPicker/res/values/strings.xml4
-rw-r--r--packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java812
-rw-r--r--packages/SoundPicker2/Android.bp46
-rw-r--r--packages/SoundPicker2/AndroidManifest.xml43
-rw-r--r--packages/SoundPicker2/OWNERS2
-rw-r--r--packages/SoundPicker2/res/drawable/ic_add.xml24
-rw-r--r--packages/SoundPicker2/res/drawable/ic_add_padded.xml22
-rw-r--r--packages/SoundPicker2/res/layout-watch/add_new_sound_item.xml36
-rw-r--r--packages/SoundPicker2/res/layout-watch/radio_with_work_badge.xml47
-rw-r--r--packages/SoundPicker2/res/layout/activity_ringtone_picker.xml (renamed from packages/SoundPicker/res/layout/activity_ringtone_picker.xml)0
-rw-r--r--packages/SoundPicker2/res/layout/add_new_sound_item.xml49
-rw-r--r--packages/SoundPicker2/res/layout/fragment_ringtone_picker.xml (renamed from packages/SoundPicker/res/layout/fragment_ringtone_picker.xml)0
-rw-r--r--packages/SoundPicker2/res/layout/fragment_tabbed_dialog.xml (renamed from packages/SoundPicker/res/layout/fragment_tabbed_dialog.xml)0
-rw-r--r--packages/SoundPicker2/res/layout/radio_with_work_badge.xml50
-rw-r--r--packages/SoundPicker2/res/raw/default_alarm_alert.ogg0
-rw-r--r--packages/SoundPicker2/res/raw/default_notification_sound.ogg0
-rw-r--r--packages/SoundPicker2/res/raw/default_ringtone.ogg0
-rw-r--r--packages/SoundPicker2/res/values/config.xml25
-rw-r--r--packages/SoundPicker2/res/values/strings.xml47
-rw-r--r--packages/SoundPicker2/res/values/styles.xml22
-rw-r--r--packages/SoundPicker2/src/com/android/soundpicker/BasePickerFragment.java (renamed from packages/SoundPicker/src/com/android/soundpicker/BasePickerFragment.java)0
-rw-r--r--packages/SoundPicker2/src/com/android/soundpicker/CheckedListItem.java67
-rw-r--r--packages/SoundPicker2/src/com/android/soundpicker/ListeningExecutorServiceFactory.java (renamed from packages/SoundPicker/src/com/android/soundpicker/ListeningExecutorServiceFactory.java)0
-rw-r--r--packages/SoundPicker2/src/com/android/soundpicker/LocalizedCursor.java (renamed from packages/SoundPicker/src/com/android/soundpicker/LocalizedCursor.java)0
-rw-r--r--packages/SoundPicker2/src/com/android/soundpicker/RingtoneFactory.java (renamed from packages/SoundPicker/src/com/android/soundpicker/RingtoneFactory.java)0
-rw-r--r--packages/SoundPicker2/src/com/android/soundpicker/RingtoneListHandler.java (renamed from packages/SoundPicker/src/com/android/soundpicker/RingtoneListHandler.java)0
-rw-r--r--packages/SoundPicker2/src/com/android/soundpicker/RingtoneListViewAdapter.java (renamed from packages/SoundPicker/src/com/android/soundpicker/RingtoneListViewAdapter.java)0
-rw-r--r--packages/SoundPicker2/src/com/android/soundpicker/RingtoneManagerFactory.java (renamed from packages/SoundPicker/src/com/android/soundpicker/RingtoneManagerFactory.java)0
-rw-r--r--packages/SoundPicker2/src/com/android/soundpicker/RingtoneOverlayService.java113
-rw-r--r--packages/SoundPicker2/src/com/android/soundpicker/RingtonePickerActivity.java218
-rw-r--r--packages/SoundPicker2/src/com/android/soundpicker/RingtonePickerApplication.java (renamed from packages/SoundPicker/src/com/android/soundpicker/RingtonePickerApplication.java)0
-rw-r--r--packages/SoundPicker2/src/com/android/soundpicker/RingtonePickerViewModel.java (renamed from packages/SoundPicker/src/com/android/soundpicker/RingtonePickerViewModel.java)0
-rw-r--r--packages/SoundPicker2/src/com/android/soundpicker/RingtoneReceiver.java36
-rw-r--r--packages/SoundPicker2/src/com/android/soundpicker/SoundPickerFragment.java (renamed from packages/SoundPicker/src/com/android/soundpicker/SoundPickerFragment.java)0
-rw-r--r--packages/SoundPicker2/src/com/android/soundpicker/TabbedDialogFragment.java (renamed from packages/SoundPicker/src/com/android/soundpicker/TabbedDialogFragment.java)0
-rw-r--r--packages/SoundPicker2/src/com/android/soundpicker/VibrationPickerFragment.java (renamed from packages/SoundPicker/src/com/android/soundpicker/VibrationPickerFragment.java)0
-rw-r--r--packages/SoundPicker2/src/com/android/soundpicker/ViewPagerAdapter.java (renamed from packages/SoundPicker/src/com/android/soundpicker/ViewPagerAdapter.java)0
-rw-r--r--packages/SoundPicker2/tests/Android.bp (renamed from packages/SoundPicker/tests/Android.bp)4
-rw-r--r--packages/SoundPicker2/tests/AndroidManifest.xml (renamed from packages/SoundPicker/tests/AndroidManifest.xml)0
-rw-r--r--packages/SoundPicker2/tests/src/com/android/soundpicker/RingtoneListHandlerTest.java (renamed from packages/SoundPicker/tests/src/com/android/soundpicker/RingtoneListHandlerTest.java)0
-rw-r--r--packages/SoundPicker2/tests/src/com/android/soundpicker/RingtonePickerViewModelTest.java (renamed from packages/SoundPicker/tests/src/com/android/soundpicker/RingtonePickerViewModelTest.java)0
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig7
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt62
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt28
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt46
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt8
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt2
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/grid/Grids.kt5
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt58
-rw-r--r--packages/SystemUI/res/values/config.xml3
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java19
-rw-r--r--packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt31
-rw-r--r--packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java26
-rw-r--r--packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java17
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java13
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt8
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java24
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java24
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java18
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java10
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java14
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java82
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java8
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java11
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java99
-rw-r--r--packages/SystemUI/src/com/android/keyguard/LockIconView.java14
-rw-r--r--packages/SystemUI/src/com/android/keyguard/LockIconViewController.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/LatencyTester.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistManager.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt55
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetMetadata.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/shared/CommunalContentSize.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepository.kt72
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt133
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java73
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt74
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BiometricUnlockInteractor.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DozeInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt49
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissBinder.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLogger.kt50
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java77
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt101
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java271
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java131
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java86
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLib.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.kt49
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt (renamed from packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt)19
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModel.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/Events.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt338
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt10
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java182
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/utils/FlagUtils.java35
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt44
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt195
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt97
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java31
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt78
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt43
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java34
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/MediaArtworkProcessorTest.kt109
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java87
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java150
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLibTest.kt85
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt50
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt)10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/volume/EventsTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java60
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java11
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt10
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt55
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt14
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/util/sensors/FakeSensorManager.java2
-rw-r--r--services/accessibility/accessibility.aconfig7
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java10
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java160
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java14
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java4
-rw-r--r--services/core/Android.bp19
-rw-r--r--services/core/java/android/content/pm/PackageManagerInternal.java5
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueueImpl.java5
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueueModernImpl.java4
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java3
-rw-r--r--services/core/java/com/android/server/am/ProcessProviderRecord.java2
-rw-r--r--services/core/java/com/android/server/am/ProcessStateRecord.java2
-rw-r--r--services/core/java/com/android/server/am/SettingsToPropertiesMapper.java8
-rw-r--r--services/core/java/com/android/server/am/flags.aconfig2
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java6
-rw-r--r--services/core/java/com/android/server/audio/MusicFxHelper.java3
-rw-r--r--services/core/java/com/android/server/clipboard/ClipboardService.java4
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java5
-rw-r--r--services/core/java/com/android/server/display/SmallAreaDetectionController.java73
-rw-r--r--services/core/java/com/android/server/display/feature/DisplayManagerFlags.java8
-rw-r--r--services/core/java/com/android/server/display/feature/display_flags.aconfig9
-rw-r--r--services/core/java/com/android/server/display/mode/DisplayModeDirector.java4
-rw-r--r--services/core/java/com/android/server/media/MediaRouterService.java3
-rw-r--r--services/core/java/com/android/server/media/projection/MediaProjectionSessionIdGenerator.java91
-rw-r--r--services/core/java/com/android/server/media/projection/MediaProjectionTimestampStore.java109
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java32
-rw-r--r--services/core/java/com/android/server/pm/PackageArchiver.java18
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerInternalBase.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java16
-rw-r--r--services/core/java/com/android/server/pm/PackageSetting.java3
-rw-r--r--services/core/java/com/android/server/pm/Settings.java40
-rw-r--r--services/core/java/com/android/server/pm/pkg/ArchiveState.java32
-rw-r--r--services/core/java/com/android/server/power/Android.bp6
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java12
-rw-r--r--services/core/java/com/android/server/power/stats/flags.aconfig4
-rw-r--r--services/core/java/com/android/server/stats/OWNERS5
-rw-r--r--services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java95
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationSettings.java43
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorManagerService.java10
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java70
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java2
-rw-r--r--services/core/java/com/android/server/wm/BackNavigationController.java4
-rw-r--r--services/core/java/com/android/server/wm/Dimmer.java47
-rw-r--r--services/core/java/com/android/server/wm/InsetsSourceProvider.java44
-rw-r--r--services/core/java/com/android/server/wm/InsetsStateController.java6
-rw-r--r--services/core/java/com/android/server/wm/LegacyDimmer.java19
-rw-r--r--services/core/java/com/android/server/wm/OWNERS1
-rw-r--r--services/core/java/com/android/server/wm/SmoothDimmer.java40
-rw-r--r--services/core/java/com/android/server/wm/Task.java28
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java33
-rw-r--r--services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java5
-rw-r--r--services/core/java/com/android/server/wm/Transition.java4
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java13
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java4
-rw-r--r--services/core/java/com/android/server/wm/WallpaperWindowToken.java3
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerFlags.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java44
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java110
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java19
-rw-r--r--services/core/java/com/android/server/wm/WindowToken.java1
-rw-r--r--services/core/jni/Android.bp4
-rw-r--r--services/core/jni/com_android_server_display_SmallAreaDetectionController.cpp24
-rw-r--r--services/core/jni/tvinput/JTvInputHal.cpp55
-rw-r--r--services/core/jni/tvinput/JTvInputHal.h3
-rw-r--r--services/java/com/android/server/SystemServer.java8
-rw-r--r--services/midi/Android.bp3
-rw-r--r--services/midi/java/com/android/server/midi/MidiService.java8
-rw-r--r--services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java21
-rw-r--r--services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageUserStateTest.java16
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java4
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java89
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java7
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/display/SmallAreaDetectionControllerTest.java52
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java70
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java43
-rw-r--r--services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionSessionIdGeneratorTest.java103
-rw-r--r--services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionTimestampStoreTest.java104
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java68
-rw-r--r--services/tests/vibrator/Android.bp1
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java145
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java67
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java12
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java22
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DimmerTests.java76
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java136
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java67
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java101
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java39
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java22
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java19
-rw-r--r--telephony/java/android/telephony/BarringInfo.java18
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java10
-rw-r--r--telephony/java/android/telephony/satellite/INtnSignalStrengthCallback.aidl31
-rw-r--r--telephony/java/android/telephony/satellite/NtnSignalStrength.aidl19
-rw-r--r--telephony/java/android/telephony/satellite/NtnSignalStrength.java149
-rw-r--r--telephony/java/android/telephony/satellite/NtnSignalStrengthCallback.java39
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteManager.java171
-rw-r--r--telephony/java/android/telephony/satellite/stub/INtnSignalStrengthConsumer.aidl28
-rw-r--r--telephony/java/android/telephony/satellite/stub/ISatellite.aidl41
-rw-r--r--telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl7
-rw-r--r--telephony/java/android/telephony/satellite/stub/NtnSignalStrength.aidl30
-rw-r--r--telephony/java/android/telephony/satellite/stub/NtnSignalStrengthLevel.aidl29
-rw-r--r--telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java55
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl39
-rw-r--r--telephony/java/com/android/internal/telephony/RILConstants.java49
-rw-r--r--tools/aapt2/integration-tests/AutoVersionTest/Android.bp1
-rw-r--r--tools/aapt2/integration-tests/BasicTest/Android.bp1
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/App/Android.bp2
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.bp1
-rw-r--r--tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.bp1
-rw-r--r--tools/aapt2/integration-tests/SymlinkTest/Android.bp1
-rw-r--r--tools/hoststubgen/hoststubgen/Android.bp51
-rwxr-xr-xtools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh4
-rw-r--r--wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java53
467 files changed, 10315 insertions, 4103 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 36430b6a215d..6d74a840525b 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -16,6 +16,7 @@ aconfig_srcjars = [
":android.app.usage.flags-aconfig-java{.generated_srcjars}",
":android.companion.flags-aconfig-java{.generated_srcjars}",
":android.content.pm.flags-aconfig-java{.generated_srcjars}",
+ ":android.content.res.flags-aconfig-java{.generated_srcjars}",
":android.hardware.radio.flags-aconfig-java{.generated_srcjars}",
":android.nfc.flags-aconfig-java{.generated_srcjars}",
":android.os.flags-aconfig-java{.generated_srcjars}",
@@ -47,6 +48,7 @@ aconfig_srcjars = [
":android.credentials.flags-aconfig-java{.generated_srcjars}",
":android.view.contentprotection.flags-aconfig-java{.generated_srcjars}",
":android.service.voice.flags-aconfig-java{.generated_srcjars}",
+ ":aconfig_midi_flags_java_lib{.generated_srcjars}",
":android.service.autofill.flags-aconfig-java{.generated_srcjars}",
":com.android.net.flags-aconfig-java{.generated_srcjars}",
]
@@ -305,6 +307,19 @@ java_aconfig_library {
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+// Resources
+aconfig_declarations {
+ name: "android.content.res.flags-aconfig",
+ package: "android.content.res",
+ srcs: ["core/java/android/content/res/*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.content.res.flags-aconfig-java",
+ aconfig_declarations: "android.content.res.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// Media BetterTogether
aconfig_declarations {
name: "com.android.media.flags.bettertogether-aconfig",
diff --git a/Android.bp b/Android.bp
index a507465aa419..0c199a634725 100644
--- a/Android.bp
+++ b/Android.bp
@@ -227,7 +227,6 @@ java_library {
"android.hardware.radio.messaging-V3-java",
"android.hardware.radio.modem-V3-java",
"android.hardware.radio.network-V3-java",
- "android.hardware.radio.satellite-V1-java",
"android.hardware.radio.sim-V3-java",
"android.hardware.radio.voice-V3-java",
"android.hardware.thermal-V1.0-java-constants",
diff --git a/Ravenwood.bp b/Ravenwood.bp
index 9218cc9bc3f8..da02298b7415 100644
--- a/Ravenwood.bp
+++ b/Ravenwood.bp
@@ -59,6 +59,7 @@ java_genrule_host {
// Extract the impl jar from "framework-minus-apex.ravenwood-base" for subsequent build rules.
java_genrule_host {
name: "framework-minus-apex.ravenwood",
+ defaults: ["hoststubgen-for-prototype-only-genrule"],
cmd: "cp $(in) $(out)",
srcs: [
":framework-minus-apex.ravenwood-base{ravenwood.jar}",
@@ -66,5 +67,4 @@ java_genrule_host {
out: [
"framework-minus-apex.ravenwood.jar",
],
- visibility: ["//visibility:public"],
}
diff --git a/api/ApiDocs.bp b/api/ApiDocs.bp
index 7142eb5bef53..e1621008cc33 100644
--- a/api/ApiDocs.bp
+++ b/api/ApiDocs.bp
@@ -131,13 +131,7 @@ droidstubs {
defaults: ["framework-doc-stubs-sources-default"],
args: metalava_framework_docs_args +
" --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) ",
- api_levels_annotations_enabled: true,
- api_levels_annotations_dirs: [
- "sdk-dir",
- "api-versions-jars-dir",
- ],
- api_levels_sdk_type: "system",
- extensions_info_file: ":sdk-extensions-info",
+ api_levels_module: "api_versions_system",
}
/////////////////////////////////////////////////////////////////////
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index fa4bc0f98fa5..7e41660cf1a2 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -695,6 +695,7 @@ java_api_library {
"api-stubs-docs-non-updatable.api.contribution",
],
visibility: ["//visibility:public"],
+ enable_validation: false,
}
java_api_library {
@@ -710,6 +711,7 @@ java_api_library {
"system-api-stubs-docs-non-updatable.api.contribution",
],
visibility: ["//visibility:public"],
+ enable_validation: false,
}
java_api_library {
@@ -727,6 +729,7 @@ java_api_library {
"test-api-stubs-docs-non-updatable.api.contribution",
],
visibility: ["//visibility:public"],
+ enable_validation: false,
}
java_api_library {
@@ -742,6 +745,7 @@ java_api_library {
"api-stubs-docs-non-updatable.api.contribution",
"system-api-stubs-docs-non-updatable.api.contribution",
],
+ enable_validation: false,
}
java_api_library {
@@ -761,6 +765,7 @@ java_api_library {
"module-lib-api-stubs-docs-non-updatable.api.contribution",
],
visibility: ["//visibility:public"],
+ enable_validation: false,
}
java_api_library {
@@ -774,6 +779,7 @@ java_api_library {
"stub-annotations",
],
visibility: ["//visibility:public"],
+ enable_validation: false,
}
java_api_library {
@@ -798,6 +804,7 @@ java_api_library {
visibility: [
"//visibility:private",
],
+ enable_validation: false,
}
java_api_library {
@@ -814,6 +821,7 @@ java_api_library {
"android_module_lib_stubs_current.from-text",
],
visibility: ["//visibility:public"],
+ enable_validation: false,
}
////////////////////////////////////////////////////////////////////////
diff --git a/api/javadoc-lint-baseline b/api/javadoc-lint-baseline
index d9e72b83e46e..29a8dfa96a57 100644
--- a/api/javadoc-lint-baseline
+++ b/api/javadoc-lint-baseline
@@ -1,58 +1,4 @@
-android/adservices/ondevicepersonalization/DownloadCompletedInput.java:22: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onDownloadCompleted() IsolatedWorker#onDownloadCompleted()" in android.adservices.ondevicepersonalization.DownloadCompletedInput [101]
-android/adservices/ondevicepersonalization/DownloadCompletedOutput.java:21: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onDownloadCompleted() IsolatedWorker#onDownloadCompleted()" in android.adservices.ondevicepersonalization.DownloadCompletedOutput [101]
-android/adservices/ondevicepersonalization/EventLogRecord.java:13: lint: Unresolved link/see tag "RequestRecordRecord" in android.adservices.ondevicepersonalization.EventLogRecord [101]
-android/adservices/ondevicepersonalization/EventUrlProvider.java:43: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onEvent IsolatedWorker#onEvent" in android.adservices.ondevicepersonalization.EventUrlProvider [101]
-android/adservices/ondevicepersonalization/ExecuteInput.java:22: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.ExecuteInput [101]
-android/adservices/ondevicepersonalization/ExecuteOutput.java:20: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.ExecuteOutput [101]
-android/adservices/ondevicepersonalization/ExecuteOutput.java:20: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.OnDevicePersonalizationManager#execute() OnDevicePersonalizationManager#execute()" in android.adservices.ondevicepersonalization.ExecuteOutput [101]
-android/adservices/ondevicepersonalization/ExecuteOutput.java:31: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.ExecuteOutput [101]
-android/adservices/ondevicepersonalization/ExecuteOutput.java:93: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.ExecuteOutput.Builder [101]
-android/adservices/ondevicepersonalization/IsolatedService.java:18: lint: Unresolved link/see tag "SurfaceView" in android.adservices.ondevicepersonalization.IsolatedService [101]
-android/adservices/ondevicepersonalization/IsolatedService.java:18: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.OnDevicePersonalizationManager#execute" in android.adservices.ondevicepersonalization.IsolatedService [101]
-android/adservices/ondevicepersonalization/IsolatedService.java:119: lint: Unresolved link/see tag "IsolatedCmputationCallback#onWebViewEvent()" in android.adservices.ondevicepersonalization.IsolatedService [101]
-android/adservices/ondevicepersonalization/IsolatedService.java:119: lint: Unresolved link/see tag "IsolatedCmputationCallback#onEvent()" in android.adservices.ondevicepersonalization.IsolatedService [101]
-android/adservices/ondevicepersonalization/IsolatedService.java:119: lint: Unresolved link/see tag "WebView" in android.adservices.ondevicepersonalization.IsolatedService [101]
-android/adservices/ondevicepersonalization/IsolatedWorker.java:9: lint: Unresolved link/see tag "RunTimeException" in android.adservices.ondevicepersonalization.IsolatedWorker [101]
-android/adservices/ondevicepersonalization/IsolatedWorker.java:24: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.OnDevicePersonalizationManager#execute" in android.adservices.ondevicepersonalization.IsolatedWorker [101]
-android/adservices/ondevicepersonalization/IsolatedWorker.java:57: lint: Unresolved link/see tag "#onExecute()" in android.adservices.ondevicepersonalization.IsolatedWorker [101]
-android/adservices/ondevicepersonalization/IsolatedWorker.java:74: lint: Unresolved link/see tag "#onRender()" in android.adservices.ondevicepersonalization.IsolatedWorker [101]
-android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:-11: lint: Unresolved link/see tag "requestSurfacePackage" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
-android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:11: lint: Unresolved link/see tag "SurfaceView" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
-android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:11: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedService#onExecute() IsolatedService#onExecute()" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
-android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:19: lint: Unresolved link/see tag "SurfaceView" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
-android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:54: lint: Unresolved link/see tag "SurfaceView" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
-android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:54: lint: Unresolved link/see tag "SurfaceView#getHostToken()" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
-android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:54: lint: Unresolved link/see tag "execute" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
-android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:60: lint: Unresolved link/see tag "#execute()" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
-android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:60: lint: Unresolved link/see tag "SurfacePackage" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
-android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:60: lint: Unresolved link/see tag "SurfaceView" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
-android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:60: lint: Unresolved link/see tag "View" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
-android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:64: lint: Unresolved link/see tag "SurfacePackage" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
-android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:69: lint: Unresolved link/see tag "SurfacePackage" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
-android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:70: lint: Unresolved link/see tag "SurfacePackage" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
-android/adservices/ondevicepersonalization/RenderInput.java:21: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onRender() IsolatedWorker#onRender()" in android.adservices.ondevicepersonalization.RenderInput [101]
-android/adservices/ondevicepersonalization/RenderInput.java:53: lint: Unresolved link/see tag "onExecute" in android.adservices.ondevicepersonalization.RenderInput [101]
-android/adservices/ondevicepersonalization/RenderOutput.java:20: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.RenderOutput [101]
-android/adservices/ondevicepersonalization/RenderOutput.java:20: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.OnDevicePersonalizationManager#requestSurfacePackage() OnDevicePersonalizationManager#requestSurfacePackage()" in android.adservices.ondevicepersonalization.RenderOutput [101]
-android/adservices/ondevicepersonalization/RenderOutput.java:31: lint: Unresolved link/see tag "getTemplateId()" in android.adservices.ondevicepersonalization.RenderOutput [101]
-android/adservices/ondevicepersonalization/RenderOutput.java:31: lint: Unresolved link/see tag "getTemplateParams" in android.adservices.ondevicepersonalization.RenderOutput [101]
-android/adservices/ondevicepersonalization/RenderOutput.java:41: lint: Unresolved link/see tag "getContent()" in android.adservices.ondevicepersonalization.RenderOutput [101]
-android/adservices/ondevicepersonalization/RenderOutput.java:52: lint: Unresolved link/see tag "getTemplateId()" in android.adservices.ondevicepersonalization.RenderOutput [101]
-android/adservices/ondevicepersonalization/RenderOutput.java:102: lint: Unresolved link/see tag "getTemplateId()" in android.adservices.ondevicepersonalization.RenderOutput.Builder [101]
-android/adservices/ondevicepersonalization/RenderOutput.java:102: lint: Unresolved link/see tag "getTemplateParams" in android.adservices.ondevicepersonalization.RenderOutput.Builder [101]
-android/adservices/ondevicepersonalization/RenderOutput.java:114: lint: Unresolved link/see tag "getContent()" in android.adservices.ondevicepersonalization.RenderOutput.Builder [101]
-android/adservices/ondevicepersonalization/RenderOutput.java:127: lint: Unresolved link/see tag "getTemplateId()" in android.adservices.ondevicepersonalization.RenderOutput.Builder [101]
-android/adservices/ondevicepersonalization/RenderingConfig.java:20: lint: Unresolved link/see tag "View" in android.adservices.ondevicepersonalization.RenderingConfig [101]
-android/adservices/ondevicepersonalization/RenderingConfig.java:20: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.RenderingConfig [101]
-android/adservices/ondevicepersonalization/RenderingConfig.java:20: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onRender() IsolatedWorker#onRender()" in android.adservices.ondevicepersonalization.RenderingConfig [101]
-android/adservices/ondevicepersonalization/RenderingConfig.java:33: lint: Unresolved link/see tag "IsolatedSurface#getRemoteData" in android.adservices.ondevicepersonalization.RenderingConfig [101]
-android/adservices/ondevicepersonalization/RenderingConfig.java:85: lint: Unresolved link/see tag "IsolatedSurface#getRemoteData" in android.adservices.ondevicepersonalization.RenderingConfig.Builder [101]
-android/adservices/ondevicepersonalization/RequestLogRecord.java:19: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.RequestLogRecord [101]
-android/adservices/ondevicepersonalization/SurfacePackageToken.java:20: lint: Unresolved link/see tag "SurfaceView" in android.adservices.ondevicepersonalization.SurfacePackageToken [101]
-android/adservices/ondevicepersonalization/WebViewEventInput.java:21: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onWebViewEvent() IsolatedWorker#onWebViewEvent()" in android.adservices.ondevicepersonalization.WebViewEventInput [101]
-android/adservices/ondevicepersonalization/WebViewEventInput.java:30: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.WebViewEventInput [101]
-android/adservices/ondevicepersonalization/WebViewEventInput.java:41: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.EventUrlProvider#createEventTrackingUrlWithResponse() EventUrlProvider#createEventTrackingUrlWithResponse()" in android.adservices.ondevicepersonalization.WebViewEventInput [101]
-android/adservices/ondevicepersonalization/WebViewEventOutput.java:21: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onWebViewEvent() IsolatedWorker#onWebViewEvent()" in android.adservices.ondevicepersonalization.WebViewEventOutput [101]
+// b/305195721
android/app/admin/DevicePolicyManager.java:2670: lint: Unresolved link/see tag "android.os.UserManager#DISALLOW_CAMERA UserManager#DISALLOW_CAMERA" in android.app.admin.DevicePolicyManager [101]
android/app/admin/DevicePolicyManager.java:7257: lint: Unresolved link/see tag "android.app.admin.DevicePolicyIdentifiers#USB_DATA_SIGNALING_POLICY DevicePolicyIdentifiers#USB_DATA_SIGNALING_POLICY" in android.app.admin.DevicePolicyManager [101]
android/app/admin/DevicePolicyManager.java:7425: lint: Unresolved link/see tag "ACTION_DEVICE_FINANCING_STATE_CHANGED" in android.app.admin.DevicePolicyManager [101]
@@ -61,6 +7,8 @@ android/app/admin/DevicePolicyManager.java:7428: lint: Unresolved link/see tag "
android/app/admin/DevicePolicyManager.java:8860: lint: Unresolved link/see tag "android.app.admin.DevicePolicyResources.Drawables DevicePolicyResources.Drawables" in android.app.admin.DevicePolicyManager [101]
android/app/admin/DevicePolicyManager.java:8860: lint: Unresolved link/see tag "android.app.admin.DevicePolicyResources.Strings DevicePolicyResources.Strings" in android.app.admin.DevicePolicyManager [101]
android/app/admin/DevicePolicyResourcesManager.java:179: lint: Unresolved link/see tag "android.app.admin.DevicePolicyResources.Strings DevicePolicyResources.Strings" in android.app.admin.DevicePolicyResourcesManager [101]
+
+// b/303477132
android/app/appsearch/AppSearchSchema.java:402: lint: Unresolved link/see tag "#getIndexableNestedProperties()" in android.app.appsearch.AppSearchSchema.DocumentPropertyConfig.Builder [101]
android/app/appsearch/AppSearchSession.java:55: lint: Unresolved link/see tag "Features#LIST_FILTER_QUERY_LANGUAGE" in android.app.appsearch.AppSearchSession [101]
android/app/appsearch/AppSearchSession.java:55: lint: Unresolved link/see tag "Features#NUMERIC_SEARCH" in android.app.appsearch.AppSearchSession [101]
@@ -73,6 +21,8 @@ android/app/appsearch/SearchSpec.java:244: lint: Unresolved link/see tag "Featur
android/app/appsearch/SearchSpec.java:913: lint: Unresolved link/see tag "Features#NUMERIC_SEARCH" in android.app.appsearch.SearchSpec.Builder [101]
android/app/appsearch/SearchSpec.java:925: lint: Unresolved link/see tag "Features#VERBATIM_SEARCH" in android.app.appsearch.SearchSpec.Builder [101]
android/app/appsearch/SearchSpec.java:929: lint: Unresolved link/see tag "Features#LIST_FILTER_QUERY_LANGUAGE" in android.app.appsearch.SearchSpec.Builder [101]
+
+// b/303582215
android/hardware/camera2/CameraCharacteristics.java:2169: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#stream-use-case-capability-additional-guaranteed-configurations guideline" in android.hardware.camera2.CameraCharacteristics [101]
android/hardware/camera2/CameraCharacteristics.java:2344: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#concurrent-stream-guaranteed-configurations guideline" in android.hardware.camera2.CameraCharacteristics [101]
android/hardware/camera2/CameraCharacteristics.java:2344: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#concurrent-stream-guaranteed-configurations tables" in android.hardware.camera2.CameraCharacteristics [101]
@@ -98,77 +48,37 @@ android/hardware/camera2/CaptureRequest.java:704: lint: Unresolved link/see tag
android/hardware/camera2/CaptureRequest.java:1501: lint: Unresolved link/see tag "SessionConfiguration#setSessionParameters" in android.hardware.camera2.CaptureRequest [101]
android/hardware/camera2/CaptureResult.java:923: lint: Unresolved link/see tag "SessionConfiguration#setSessionParameters" in android.hardware.camera2.CaptureResult [101]
android/hardware/camera2/CaptureResult.java:2337: lint: Unresolved link/see tag "SessionConfiguration#setSessionParameters" in android.hardware.camera2.CaptureResult [101]
-android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_ALARM AttributeSdkUsage#USAGE_ALARM" in android.media.AudioAttributes.Builder [101]
-android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_ASSISTANCE_ACCESSIBILITY AttributeSdkUsage#USAGE_ASSISTANCE_ACCESSIBILITY" in android.media.AudioAttributes.Builder [101]
-android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_ASSISTANCE_NAVIGATION_GUIDANCE AttributeSdkUsage#USAGE_ASSISTANCE_NAVIGATION_GUIDANCE" in android.media.AudioAttributes.Builder [101]
-android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_ASSISTANCE_SONIFICATION AttributeSdkUsage#USAGE_ASSISTANCE_SONIFICATION" in android.media.AudioAttributes.Builder [101]
-android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_ASSISTANT AttributeSdkUsage#USAGE_ASSISTANT" in android.media.AudioAttributes.Builder [101]
-android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_GAME AttributeSdkUsage#USAGE_GAME" in android.media.AudioAttributes.Builder [101]
-android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_MEDIA AttributeSdkUsage#USAGE_MEDIA" in android.media.AudioAttributes.Builder [101]
-android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_NOTIFICATION_EVENT AttributeSdkUsage#USAGE_NOTIFICATION_EVENT" in android.media.AudioAttributes.Builder [101]
-android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_NOTIFICATION_RINGTONE AttributeSdkUsage#USAGE_NOTIFICATION_RINGTONE" in android.media.AudioAttributes.Builder [101]
-android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_UNKNOWN AttributeSdkUsage#USAGE_UNKNOWN" in android.media.AudioAttributes.Builder [101]
-android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_VOICE_COMMUNICATION AttributeSdkUsage#USAGE_VOICE_COMMUNICATION" in android.media.AudioAttributes.Builder [101]
-android/media/AudioAttributes.java:443: lint: Unresolved link/see tag "android.media.AudioAttributes.AttributeSdkUsage#USAGE_VOICE_COMMUNICATION_SIGNALLING AttributeSdkUsage#USAGE_VOICE_COMMUNICATION_SIGNALLING" in android.media.AudioAttributes.Builder [101]
-android/media/AudioFormat.java:963: lint: Unresolved link/see tag "android.media.AudioSystem#OUT_CHANNEL_COUNT_MAX AudioSystem#OUT_CHANNEL_COUNT_MAX" in android.media.AudioFormat.Builder [101]
-android/media/AudioManager.java:275: lint: Unresolved link/see tag "android.media.audiopolicy.AudioVolumeGroup" in android.media.AudioManager [101]
-android/media/AudioManager.java:287: lint: Unresolved link/see tag "android.media.audiopolicy.AudioVolumeGroup" in android.media.AudioManager [101]
-android/media/AudioManager.java:311: lint: Unresolved link/see tag "android.media.audiopolicy.AudioVolumeGroup" in android.media.AudioManager [101]
-android/media/AudioManager.java:313: lint: Unresolved link/see tag "android.media.audiopolicy.AudioVolumeGroup" in android.media.AudioManager [101]
-android/media/AudioMetadata.java:118: lint: Unresolved link/see tag "android.media.AudioPresentation.ContentClassifier One of {@link android.media.AudioPresentation#CONTENT_UNKNOWN AudioPresentation#CONTENT_UNKNOWN}, {@link android.media.AudioPresentation#CONTENT_MAIN AudioPresentation#CONTENT_MAIN}, {@link android.media.AudioPresentation#CONTENT_MUSIC_AND_EFFECTS AudioPresentation#CONTENT_MUSIC_AND_EFFECTS}, {@link android.media.AudioPresentation#CONTENT_VISUALLY_IMPAIRED AudioPresentation#CONTENT_VISUALLY_IMPAIRED}, {@link android.media.AudioPresentation#CONTENT_HEARING_IMPAIRED AudioPresentation#CONTENT_HEARING_IMPAIRED}, {@link android.media.AudioPresentation#CONTENT_DIALOG AudioPresentation#CONTENT_DIALOG}, {@link android.media.AudioPresentation#CONTENT_COMMENTARY AudioPresentation#CONTENT_COMMENTARY}, {@link android.media.AudioPresentation#CONTENT_EMERGENCY AudioPresentation#CONTENT_EMERGENCY}, {@link android.media.AudioPresentation#CONTENT_VOICEOVER AudioPresentation#CONTENT_VOICEOVER}." in android.media.AudioMetadata.Format [101]
-android/media/tv/SectionRequest.java:44: lint: Unresolved link/see tag "android.media.tv.BroadcastInfoRequest.RequestOption BroadcastInfoRequest.RequestOption" in android.media.tv.SectionRequest [101]
-android/media/tv/SectionResponse.java:39: lint: Unresolved link/see tag "android.media.tv.BroadcastInfoRequest.RequestOption BroadcastInfoRequest.RequestOption" in android.media.tv.SectionResponse [101]
-android/media/tv/TableRequest.java:48: lint: Unresolved link/see tag "android.media.tv.BroadcastInfoRequest.RequestOption BroadcastInfoRequest.RequestOption" in android.media.tv.TableRequest [101]
-android/media/tv/TableResponse.java:82: lint: Unresolved link/see tag "android.media.tv.BroadcastInfoRequest.RequestOption BroadcastInfoRequest.RequestOption" in android.media.tv.TableResponse [101]
-android/net/EthernetNetworkSpecifier.java:21: lint: Unresolved link/see tag "android.net.EthernetManager" in android.net.EthernetNetworkSpecifier [101]
-android/net/eap/EapSessionConfig.java:120: lint: Unresolved link/see tag "android.telephony.Annotation.UiccAppType UiccAppType" in android.net.eap.EapSessionConfig.Builder [101]
-android/net/eap/EapSessionConfig.java:135: lint: Unresolved link/see tag "android.telephony.Annotation.UiccAppType UiccAppType" in android.net.eap.EapSessionConfig.Builder [101]
-android/net/eap/EapSessionConfig.java:148: lint: Unresolved link/see tag "android.telephony.Annotation.UiccAppType UiccAppType" in android.net.eap.EapSessionConfig.Builder [101]
-android/net/eap/EapSessionConfig.java:161: lint: Unresolved link/see tag "android.telephony.Annotation.UiccAppType UiccAppType" in android.net.eap.EapSessionConfig.Builder [101]
-android/net/eap/EapSessionConfig.java:288: lint: Unresolved link/see tag "android.telephony.Annotation.UiccAppType UiccAppType" in android.net.eap.EapSessionConfig.EapAkaConfig [101]
-android/net/eap/EapSessionConfig.java:390: lint: Unresolved link/see tag "android.telephony.Annotation.UiccAppType UiccAppType" in android.net.eap.EapSessionConfig.EapAkaPrimeConfig [101]
-android/net/eap/EapSessionConfig.java:587: lint: Unresolved link/see tag "android.telephony.Annotation.UiccAppType UiccAppType" in android.net.eap.EapSessionConfig.EapSimConfig [101]
-android/net/wifi/MloLink.java:32: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_24_GHZ WifiScanner#WIFI_BAND_24_GHZ" in android.net.wifi.MloLink [101]
-android/net/wifi/MloLink.java:32: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_5_GHZ WifiScanner#WIFI_BAND_5_GHZ" in android.net.wifi.MloLink [101]
-android/net/wifi/MloLink.java:32: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_6_GHZ WifiScanner#WIFI_BAND_6_GHZ" in android.net.wifi.MloLink [101]
-android/net/wifi/MloLink.java:32: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_UNSPECIFIED WifiScanner#WIFI_BAND_UNSPECIFIED" in android.net.wifi.MloLink [101]
-android/net/wifi/SoftApConfiguration.java:9: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder SoftApConfiguration.Builder" in android.net.wifi.SoftApConfiguration [101]
-android/net/wifi/SoftApConfiguration.java:66: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setSsid(java.lang.String) Builder#setSsid(String)" in android.net.wifi.SoftApConfiguration [101]
-android/net/wifi/SoftApConfiguration.java:85: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setWifiSsid(android.net.wifi.WifiSsid) Builder#setWifiSsid(WifiSsid)" in android.net.wifi.SoftApConfiguration [101]
-android/net/wifi/SoftApConfiguration.java:96: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setBssid(android.net.MacAddress) Builder#setBssid(MacAddress)" in android.net.wifi.SoftApConfiguration [101]
-android/net/wifi/SoftApConfiguration.java:107: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setPassphrase(java.lang.String,int) Builder#setPassphrase(String, int)" in android.net.wifi.SoftApConfiguration [101]
-android/net/wifi/SoftApConfiguration.java:118: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setHiddenSsid(boolean) Builder#setHiddenSsid(boolean)" in android.net.wifi.SoftApConfiguration [101]
-android/net/wifi/WifiManager.java:764: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setBands(int[]) SoftApConfiguration.Builder#setBands(int[])" in android.net.wifi.WifiManager [101]
-android/net/wifi/WifiManager.java:764: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setChannels(android.util.SparseIntArray) SoftApConfiguration.Builder#setChannels(android.util.SparseIntArray)" in android.net.wifi.WifiManager [101]
-android/net/wifi/WifiManager.java:779: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setBands(int[]) SoftApConfiguration.Builder#setBands(int[])" in android.net.wifi.WifiManager [101]
-android/net/wifi/WifiManager.java:779: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setChannels(android.util.SparseIntArray) SoftApConfiguration.Builder#setChannels(android.util.SparseIntArray)" in android.net.wifi.WifiManager [101]
-android/net/wifi/WifiManager.java:2466: lint: Unresolved link/see tag "TelephonyManager#hasCarrierPrivileges()." in android.net.wifi.WifiManager [101]
-android/net/wifi/aware/PublishConfig.java:50: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_24_GHZ WifiScanner#WIFI_BAND_24_GHZ" in android.net.wifi.aware.PublishConfig [101]
-android/net/wifi/aware/PublishConfig.java:50: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_5_GHZ WifiScanner#WIFI_BAND_5_GHZ" in android.net.wifi.aware.PublishConfig [101]
-android/net/wifi/aware/PublishConfig.java:249: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_24_GHZ WifiScanner#WIFI_BAND_24_GHZ" in android.net.wifi.aware.PublishConfig.Builder [101]
-android/net/wifi/aware/PublishConfig.java:249: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_5_GHZ WifiScanner#WIFI_BAND_5_GHZ" in android.net.wifi.aware.PublishConfig.Builder [101]
-android/net/wifi/aware/SubscribeConfig.java:51: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_24_GHZ WifiScanner#WIFI_BAND_24_GHZ" in android.net.wifi.aware.SubscribeConfig [101]
-android/net/wifi/aware/SubscribeConfig.java:51: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_5_GHZ WifiScanner#WIFI_BAND_5_GHZ" in android.net.wifi.aware.SubscribeConfig [101]
-android/net/wifi/aware/SubscribeConfig.java:276: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_24_GHZ WifiScanner#WIFI_BAND_24_GHZ" in android.net.wifi.aware.SubscribeConfig.Builder [101]
-android/net/wifi/aware/SubscribeConfig.java:276: lint: Unresolved link/see tag "android.net.wifi.WifiScanner#WIFI_BAND_5_GHZ WifiScanner#WIFI_BAND_5_GHZ" in android.net.wifi.aware.SubscribeConfig.Builder [101]
-android/net/wifi/SoftApConfiguration.java:173: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setShutdownTimeoutMillis(long)" in android.net.wifi.SoftApConfiguration [101]
-android/os/UserManager.java:2384: lint: Unresolved link/see tag "android.annotation.UserHandleAware @UserHandleAware" in android.os.UserManager [101]
+// These are javadoc errors for @ChangeId constants, which are problematic to generate documentation
+// for. They're not necessarily errors in the docs themselves but are also a limitation in the tool.
+// Regardless, the docs currently generated for them is not good, but it is also not used directly
+// in production at the moment.
+// The main limitation is that all references must be fully qualified in order to resolve properly
+// (aside from the normal limitatinos of only being able to @link public APIs).
+// See the CompatInfo.java source file in doclava for more information.
+android/net/wifi/SoftApConfiguration.java:171: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setShutdownTimeoutMillis(long)" in android.net.wifi.SoftApConfiguration [101]
android/os/UserManager.java:2384: lint: Unresolved link/see tag "android.annotation.UserHandleAware#enabledSinceTargetSdkVersion" in android.os.UserManager [101]
-android/service/voice/AlwaysOnHotwordDetector.java:269: lint: Unresolved link/see tag "#initialize( PersistableBundle, SharedMemory, SoundTrigger.ModuleProperties)" in android.service.voice.AlwaysOnHotwordDetector [101]
-android/service/voice/AlwaysOnHotwordDetector.java:269: lint: Unresolved link/see tag "STATE_HARDWARE_UNAVAILABLE" in android.service.voice.AlwaysOnHotwordDetector [101]
-android/service/voice/AlwaysOnHotwordDetector.java:278: lint: Unresolved link/see tag "#STATE_ERROR" in android [101]
+android/os/UserManager.java:2384: lint: Unresolved link/see tag "android.annotation.UserHandleAware @UserHandleAware" in android.os.UserManager [101]
+android/service/voice/AlwaysOnHotwordDetector.java:269: lint: Unresolved link/see tag "#initialize( PersistableBundle, SharedMemory, SoundTrigger.ModuleProperties)" in android [101]
+android/service/voice/AlwaysOnHotwordDetector.java:269: lint: Unresolved link/see tag "STATE_HARDWARE_UNAVAILABLE" in android [101]
+android/service/voice/AlwaysOnHotwordDetector.java:278: lint: Unresolved link/see tag "android.service.voice.AlwaysOnHotwordDetector.Callback#onFailure" in android [101]
+android/service/voice/AlwaysOnHotwordDetector.java:278: lint: Unresolved link/see tag "android.service.voice.AlwaysOnHotwordDetector.Callback#onUnknownFailure" in android [101]
+android/service/voice/AlwaysOnHotwordDetector.java:278: lint: Unresolved link/see tag "android.service.voice.AlwaysOnHotwordDetector#STATE_ERROR" in android [101]
android/service/voice/AlwaysOnHotwordDetector.java:278: lint: Unresolved link/see tag "Callback#onFailure" in android [101]
android/service/voice/AlwaysOnHotwordDetector.java:278: lint: Unresolved link/see tag "Callback#onUnknownFailure" in android [101]
-com/android/internal/policy/PhoneWindow.java:172: lint: Unresolved link/see tag "Build.VERSION_CODES#VANILLA_ICE_CREAM" in com.android.internal.policy.PhoneWindow [101]
-
+android/service/voice/AlwaysOnHotwordDetector.java:278: lint: Unresolved link/see tag "#STATE_ERROR" in android [101]
+com/android/internal/policy/PhoneWindow.java:172: lint: Unresolved link/see tag "Build.VERSION_CODES#VANILLA_ICE_CREAM" in android [101]
com/android/server/broadcastradio/aidl/ConversionUtils.java:70: lint: Unresolved link/see tag "IdentifierType#DAB_SID_EXT" in android [101]
com/android/server/broadcastradio/aidl/ConversionUtils.java:70: lint: Unresolved link/see tag "ProgramSelector#IDENTIFIER_TYPE_DAB_DMB_SID_EXT" in android [101]
com/android/server/broadcastradio/aidl/ConversionUtils.java:70: lint: Unresolved link/see tag "RadioTuner" in android [101]
+com/android/server/broadcastradio/aidl/ConversionUtils.java:72: lint: Unresolved link/see tag "com.android.server.broadcastradio.aidl.IdentifierType#DAB_SID_EXT" in android [101]
+com/android/server/broadcastradio/aidl/ConversionUtils.java:72: lint: Unresolved link/see tag "com.android.server.broadcastradio.aidl.ProgramSelector#IDENTIFIER_TYPE_DAB_DMB_SID_EXT" in android [101]
+com/android/server/broadcastradio/aidl/ConversionUtils.java:72: lint: Unresolved link/see tag "com.android.server.broadcastradio.aidl.RadioTuner" in android [101]
+com/android/server/devicepolicy/DevicePolicyManagerService.java:861: lint: Unresolved link/see tag "android.security.IKeyChainService#setGrant" in android [101]
com/android/server/pm/PackageInstallerSession.java:313: lint: Unresolved link/see tag "Build.VERSION_CODES#S API 31" in android [101]
com/android/server/pm/PackageInstallerSession.java:313: lint: Unresolved link/see tag "PackageInstaller.SessionParams#setRequireUserAction" in android [101]
-com/android/server/pm/PackageInstallerSession.java:327: lint: Unresolved link/see tag "#requestUserPreapproval(PreapprovalDetails, IntentSender)" in android [101]
com/android/server/pm/PackageInstallerSession.java:327: lint: Unresolved link/see tag "Build.VERSION_CODES#UPSIDE_DOWN_CAKE API 34" in android [101]
com/android/server/pm/PackageInstallerSession.java:327: lint: Unresolved link/see tag "PackageInstaller.SessionParams#setRequestUpdateOwnership(boolean)" in android [101]
-com/android/server/pm/PackageInstallerSession.java:358: lint: Unresolved link/see tag "IntentSender" in android [101]
-com/android/server/devicepolicy/DevicePolicyManagerService.java:860: lint: Unresolved link/see tag "android.security.IKeyChainService#setGrant" in android [101]
+com/android/server/pm/PackageInstallerSession.java:327: lint: Unresolved link/see tag "#requestUserPreapproval(PreapprovalDetails, IntentSender)" in android [101]
+com/android/server/pm/PackageInstallerSession.java:330: lint: Unresolved link/see tag "com.android.android.server.pm#requestUserPreapproval(PreapprovalDetails, IntentSender)" in android [101]
+com/android/server/pm/PackageInstallerSession.java:358: lint: Unresolved link/see tag "IntentSender" in android [101] \ No newline at end of file
diff --git a/core/api/current.txt b/core/api/current.txt
index 69a3cb35fa61..66aeb0f7cbaf 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -10571,7 +10571,7 @@ package android.content {
public final class ContextParams {
method @Nullable public String getAttributionTag();
method @Nullable public android.content.AttributionSource getNextAttributionSource();
- method @NonNull public boolean shouldRegisterAttributionSource();
+ method @FlaggedApi("android.permission.flags.should_register_attribution_source") @NonNull public boolean shouldRegisterAttributionSource();
}
public static final class ContextParams.Builder {
@@ -10580,7 +10580,7 @@ package android.content {
method @NonNull public android.content.ContextParams build();
method @NonNull public android.content.ContextParams.Builder setAttributionTag(@Nullable String);
method @NonNull public android.content.ContextParams.Builder setNextAttributionSource(@Nullable android.content.AttributionSource);
- method @NonNull public android.content.ContextParams.Builder setShouldRegisterAttributionSource(boolean);
+ method @FlaggedApi("android.permission.flags.should_register_attribution_source") @NonNull public android.content.ContextParams.Builder setShouldRegisterAttributionSource(boolean);
}
public class ContextWrapper extends android.content.Context {
@@ -25787,15 +25787,15 @@ package android.media.midi {
method public abstract void onDisconnect(android.media.midi.MidiReceiver);
}
- public abstract class MidiUmpDeviceService extends android.app.Service {
+ @FlaggedApi("com.android.media.midi.flags.virtual_ump") public abstract class MidiUmpDeviceService extends android.app.Service {
ctor public MidiUmpDeviceService();
- method @Nullable public final android.media.midi.MidiDeviceInfo getDeviceInfo();
- method @NonNull public final java.util.List<android.media.midi.MidiReceiver> getOutputPortReceivers();
- method @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
- method public void onClose();
- method public void onDeviceStatusChanged(@NonNull android.media.midi.MidiDeviceStatus);
- method @NonNull public abstract java.util.List<android.media.midi.MidiReceiver> onGetInputPortReceivers();
- field public static final String SERVICE_INTERFACE = "android.media.midi.MidiUmpDeviceService";
+ method @FlaggedApi("com.android.media.midi.flags.virtual_ump") @Nullable public final android.media.midi.MidiDeviceInfo getDeviceInfo();
+ method @FlaggedApi("com.android.media.midi.flags.virtual_ump") @NonNull public final java.util.List<android.media.midi.MidiReceiver> getOutputPortReceivers();
+ method @FlaggedApi("com.android.media.midi.flags.virtual_ump") @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
+ method @FlaggedApi("com.android.media.midi.flags.virtual_ump") public void onClose();
+ method @FlaggedApi("com.android.media.midi.flags.virtual_ump") public void onDeviceStatusChanged(@NonNull android.media.midi.MidiDeviceStatus);
+ method @FlaggedApi("com.android.media.midi.flags.virtual_ump") @NonNull public abstract java.util.List<android.media.midi.MidiReceiver> onGetInputPortReceivers();
+ field @FlaggedApi("com.android.media.midi.flags.virtual_ump") public static final String SERVICE_INTERFACE = "android.media.midi.MidiUmpDeviceService";
}
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index c72d09d66494..5d1f6dc3e675 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -312,7 +312,7 @@ package android {
field public static final String REMOVE_DRM_CERTIFICATES = "android.permission.REMOVE_DRM_CERTIFICATES";
field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
field public static final String RENOUNCE_PERMISSIONS = "android.permission.RENOUNCE_PERMISSIONS";
- field @FlaggedApi("backstage_power.report_usage_stats_permission") public static final String REPORT_USAGE_STATS = "android.permission.REPORT_USAGE_STATS";
+ field @FlaggedApi("android.app.usage.report_usage_stats_permission") public static final String REPORT_USAGE_STATS = "android.permission.REPORT_USAGE_STATS";
field @Deprecated public static final String REQUEST_NETWORK_SCORES = "android.permission.REQUEST_NETWORK_SCORES";
field public static final String REQUEST_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE";
field public static final String RESET_PASSWORD = "android.permission.RESET_PASSWORD";
@@ -16741,6 +16741,23 @@ package android.telephony.satellite {
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.AntennaPosition> CREATOR;
}
+ @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public final class NtnSignalStrength implements android.os.Parcelable {
+ ctor @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public NtnSignalStrength(@Nullable android.telephony.satellite.NtnSignalStrength);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int describeContents();
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int getLevel();
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.NtnSignalStrength> CREATOR;
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int NTN_SIGNAL_STRENGTH_GOOD = 3; // 0x3
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int NTN_SIGNAL_STRENGTH_GREAT = 4; // 0x4
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int NTN_SIGNAL_STRENGTH_MODERATE = 2; // 0x2
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int NTN_SIGNAL_STRENGTH_NONE = 0; // 0x0
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int NTN_SIGNAL_STRENGTH_POOR = 1; // 0x1
+ }
+
+ @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface NtnSignalStrengthCallback {
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onNtnSignalStrengthChanged(@NonNull android.telephony.satellite.NtnSignalStrength);
+ }
+
@FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public final class PointingInfo implements android.os.Parcelable {
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int describeContents();
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public float getSatelliteAzimuthDegrees();
@@ -16776,6 +16793,7 @@ package android.telephony.satellite {
method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public java.util.Set<java.lang.Integer> getSatelliteAttachRestrictionReasonsForCarrier(int);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void pollPendingSatelliteDatagrams(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void provisionSatelliteService(@NonNull String, @NonNull byte[], @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForNtnSignalStrengthChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.NtnSignalStrengthCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteDatagram(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteDatagramCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteModemStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteStateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteProvisionStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteProvisionStateCallback);
@@ -16786,6 +16804,7 @@ package android.telephony.satellite {
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteEnabled(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteProvisioned(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void requestIsSatelliteSupported(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestNtnSignalStrength(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.satellite.NtnSignalStrength,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteAttachEnabledForCarrier(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteCapabilities(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.satellite.SatelliteCapabilities,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteEnabled(boolean, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
@@ -16794,6 +16813,7 @@ package android.telephony.satellite {
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void setDeviceAlignedWithSatellite(boolean);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void startSatelliteTransmissionUpdates(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull android.telephony.satellite.SatelliteTransmissionUpdateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void stopSatelliteTransmissionUpdates(@NonNull android.telephony.satellite.SatelliteTransmissionUpdateCallback, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForNtnSignalStrengthChanged(@NonNull android.telephony.satellite.NtnSignalStrengthCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteDatagram(@NonNull android.telephony.satellite.SatelliteDatagramCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteModemStateChanged(@NonNull android.telephony.satellite.SatelliteStateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteProvisionStateChanged(@NonNull android.telephony.satellite.SatelliteProvisionStateCallback);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 8cde0cfcdea0..df556a8f3aeb 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -3185,7 +3185,6 @@ package android.telephony {
field public static final int HAL_SERVICE_MESSAGING = 2; // 0x2
field public static final int HAL_SERVICE_MODEM = 3; // 0x3
field public static final int HAL_SERVICE_NETWORK = 4; // 0x4
- field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int HAL_SERVICE_SATELLITE = 8; // 0x8
field public static final int HAL_SERVICE_SIM = 5; // 0x5
field public static final int HAL_SERVICE_VOICE = 6; // 0x6
field public static final android.util.Pair HAL_VERSION_UNKNOWN;
@@ -3798,14 +3797,14 @@ package android.view.inputmethod {
public final class InputMethodManager {
method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public void addVirtualStylusIdForTestSession();
method public int getDisplayId();
- method @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public java.util.List<android.view.inputmethod.InputMethodInfo> getEnabledInputMethodListAsUser(@NonNull android.os.UserHandle);
- method @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public java.util.List<android.view.inputmethod.InputMethodSubtype> getEnabledInputMethodSubtypeListAsUser(@NonNull String, boolean, @NonNull android.os.UserHandle);
+ method @FlaggedApi("android.view.inputmethod.imm_userhandle_hostsidetests") @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public java.util.List<android.view.inputmethod.InputMethodInfo> getEnabledInputMethodListAsUser(@NonNull android.os.UserHandle);
+ method @FlaggedApi("android.view.inputmethod.imm_userhandle_hostsidetests") @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public java.util.List<android.view.inputmethod.InputMethodSubtype> getEnabledInputMethodSubtypeListAsUser(@NonNull String, boolean, @NonNull android.os.UserHandle);
method @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public java.util.List<android.view.inputmethod.InputMethodInfo> getInputMethodListAsUser(int);
method public boolean hasActiveInputConnection(@Nullable android.view.View);
method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean hasPendingImeVisibilityRequests();
method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean isCurrentRootView(@NonNull android.view.View);
method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean isInputMethodPickerShown();
- method @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public boolean isStylusHandwritingAvailableAsUser(@NonNull android.os.UserHandle);
+ method @FlaggedApi("android.view.inputmethod.imm_userhandle_hostsidetests") @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public boolean isStylusHandwritingAvailableAsUser(@NonNull android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public void setStylusWindowIdleTimeoutForTest(long);
field public static final long CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING = 214016041L; // 0xcc1a029L
}
diff --git a/core/java/android/app/LocaleConfig.java b/core/java/android/app/LocaleConfig.java
index 0857c9655e8d..1fdc51687433 100644
--- a/core/java/android/app/LocaleConfig.java
+++ b/core/java/android/app/LocaleConfig.java
@@ -20,7 +20,6 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
@@ -133,15 +132,14 @@ public class LocaleConfig implements Parcelable {
return;
}
}
- int resId = 0;
Resources res = context.getResources();
+ //Get the resource id
+ int resId = context.getApplicationInfo().getLocaleConfigRes();
+ if (resId == 0) {
+ mStatus = STATUS_NOT_SPECIFIED;
+ return;
+ }
try {
- //Get the resource id
- resId = new ApplicationInfo(context.getApplicationInfo()).getLocaleConfigRes();
- if (resId == 0) {
- mStatus = STATUS_NOT_SPECIFIED;
- return;
- }
//Get the parser to read XML data
XmlResourceParser parser = res.getXml(resId);
parseLocaleConfig(parser, res);
diff --git a/core/java/android/app/admin/SystemUpdatePolicy.java b/core/java/android/app/admin/SystemUpdatePolicy.java
index c7602982bee7..113a6dd0ca48 100644
--- a/core/java/android/app/admin/SystemUpdatePolicy.java
+++ b/core/java/android/app/admin/SystemUpdatePolicy.java
@@ -125,7 +125,7 @@ public final class SystemUpdatePolicy implements Parcelable {
*
* <p>The system limits each update to one 30-day postponement. The period begins when the
* system first postpones the update and setting new {@code TYPE_POSTPONE} policies won’t extend
- * the period. If, after 30 days the update isn’t installed (through policy changes), the system
+ * the period. If, after 30 days the update isn't installed (through policy changes), the system
* prompts the user to install the update.
*
* <p><strong>Note</strong>: Device manufacturers or carriers might choose to exempt important
diff --git a/core/java/android/app/usage/flags.aconfig b/core/java/android/app/usage/flags.aconfig
index d1f90676a15f..f401a7607364 100644
--- a/core/java/android/app/usage/flags.aconfig
+++ b/core/java/android/app/usage/flags.aconfig
@@ -2,7 +2,7 @@ package: "android.app.usage"
flag {
name: "user_interaction_type_api"
- namespace: "power_optimization"
+ namespace: "backstage_power"
description: "Feature flag for user interaction event report/query API"
bug: "296061232"
}
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensor.java b/core/java/android/companion/virtual/sensor/VirtualSensor.java
index eaa17925b14b..14c799763a96 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensor.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensor.java
@@ -115,6 +115,11 @@ public final class VirtualSensor implements Parcelable {
parcel.writeStrongBinder(mToken);
}
+ @Override
+ public String toString() {
+ return "VirtualSensor{ mType=" + mType + ", mName='" + mName + "' }";
+ }
+
/**
* Send a sensor event to the system.
*/
diff --git a/core/java/android/content/ContextParams.java b/core/java/android/content/ContextParams.java
index 988a9c0ab9b3..b844d358a523 100644
--- a/core/java/android/content/ContextParams.java
+++ b/core/java/android/content/ContextParams.java
@@ -16,7 +16,10 @@
package android.content;
+import static android.permission.flags.Flags.FLAG_SHOULD_REGISTER_ATTRIBUTION_SOURCE;
+
import android.Manifest;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -102,6 +105,7 @@ public final class ContextParams {
* registered.
*/
@NonNull
+ @FlaggedApi(FLAG_SHOULD_REGISTER_ATTRIBUTION_SOURCE)
public boolean shouldRegisterAttributionSource() {
return mShouldRegisterAttributionSource;
}
@@ -179,6 +183,7 @@ public final class ContextParams {
* created should be registered.
*/
@NonNull
+ @FlaggedApi(FLAG_SHOULD_REGISTER_ATTRIBUTION_SOURCE)
public Builder setShouldRegisterAttributionSource(boolean shouldRegister) {
mShouldRegisterAttributionSource = shouldRegister;
return this;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index d2b23083ab64..b765562ab587 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3929,6 +3929,8 @@ public class Intent implements Parcelable, Cloneable {
* {@link #ACTION_BOOT_COMPLETED} is sent. This is sent as a foreground
* broadcast, since it is part of a visible user interaction; be as quick
* as possible when handling it.
+ *
+ * <p><b>Note:</b> This broadcast is not sent to the system user.
*/
public static final String ACTION_USER_INITIALIZE =
"android.intent.action.USER_INITIALIZE";
diff --git a/core/java/android/content/pm/ArchivedActivityParcel.aidl b/core/java/android/content/pm/ArchivedActivityParcel.aidl
index 7ab7ed1cc5df..74953fff40d8 100644
--- a/core/java/android/content/pm/ArchivedActivityParcel.aidl
+++ b/core/java/android/content/pm/ArchivedActivityParcel.aidl
@@ -16,9 +16,12 @@
package android.content.pm;
+import android.content.ComponentName;
+
/** @hide */
parcelable ArchivedActivityParcel {
String title;
+ ComponentName originalComponentName;
// PNG compressed bitmaps.
byte[] iconBitmap;
byte[] monochromeIconBitmap;
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index b2cc070228b7..db12728cfb98 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -50,3 +50,11 @@ flag {
description: "Feature flag to enable the features that rely on new ART Service APIs that are in the VIC version of the ART module."
bug: "304741685"
}
+
+flag {
+ name: "sdk_lib_independence"
+ namespace: "package_manager_service"
+ description: "Feature flag to keep app working even if its declared sdk-library dependency is unavailable."
+ bug: "295827951"
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/content/res/flags.aconfig b/core/java/android/content/res/flags.aconfig
new file mode 100644
index 000000000000..0c2c0f494257
--- /dev/null
+++ b/core/java/android/content/res/flags.aconfig
@@ -0,0 +1,10 @@
+package: "android.content.res"
+
+flag {
+ name: "default_locale"
+ namespace: "resource_manager"
+ description: "Feature flag for default locale in LocaleConfig"
+ bug: "117306409"
+ # fixed_read_only or device wont boot because of permission issues accessing flags during boot
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index f033f9740b3d..bcf447b4267d 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -16,6 +16,7 @@
package android.hardware;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.SystemService;
@@ -27,6 +28,8 @@ import android.os.MemoryFile;
import android.util.Log;
import android.util.SparseArray;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -1809,6 +1812,41 @@ public abstract class SensorManager {
protected abstract boolean cancelTriggerSensorImpl(TriggerEventListener listener,
Sensor sensor, boolean disable);
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({DATA_INJECTION, REPLAY_DATA_INJECTION, HAL_BYPASS_REPLAY_DATA_INJECTION})
+ public @interface DataInjectionMode {}
+ /**
+ * This mode is only used for testing purposes. Not all HALs support this mode. In this mode,
+ * the HAL ignores the sensor data provided by physical sensors and accepts the data that is
+ * injected from the SensorService as if it were the real sensor data. This mode is primarily
+ * used for testing various algorithms like vendor provided SensorFusion, Step Counter and
+ * Step Detector etc. Typically, in this mode, there is a client app which injects
+ * sensor data into the HAL. Normal apps can register and unregister for any sensor
+ * that supports injection. Registering to sensors that do not support injection will
+ * give an error.
+ * This is the default data injection mode.
+ * @hide
+ */
+ public static final int DATA_INJECTION = 1;
+ /**
+ * Mostly equivalent to DATA_INJECTION with the difference being that the injected data is
+ * delivered to all requesting apps rather than just the package allowed to inject data.
+ * This mode is only allowed to be used on development builds.
+ * @hide
+ */
+ public static final int REPLAY_DATA_INJECTION = 3;
+ /**
+ * Like REPLAY_DATA_INJECTION but injected data is not sent into the HAL. It is stored in a
+ * buffer in the platform and played back to all requesting apps.
+ * This is useful for playing back sensor data to test platform components without
+ * relying on the HAL to support data injection.
+ * @hide
+ */
+ public static final int HAL_BYPASS_REPLAY_DATA_INJECTION = 4;
+
/**
* For testing purposes only. Not for third party applications.
@@ -1833,13 +1871,47 @@ public abstract class SensorManager {
*/
@SystemApi
public boolean initDataInjection(boolean enable) {
- return initDataInjectionImpl(enable);
+ return initDataInjectionImpl(enable, DATA_INJECTION);
+ }
+
+ /**
+ * For testing purposes only. Not for third party applications.
+ *
+ * Initialize data injection mode and create a client for data injection. SensorService should
+ * already be operating in one of DATA_INJECTION, REPLAY_DATA_INJECTION or
+ * HAL_BYPASS_REPLAY_DATA_INJECTION modes for this call succeed. To set SensorService in
+ * a Data Injection mode, use one of:
+ *
+ * <ul>
+ * <li>adb shell dumpsys sensorservice data_injection</li>
+ * <li>adb shell dumpsys sensorservice replay_data_injection package_name</li>
+ * <li>adb shell dumpsys sensorservice hal_bypass_replay_data_injection package_name</li>
+ * </ul>
+ *
+ * Typically this is done using a host side test. This mode is expected to be used
+ * only for testing purposes. See {@link DataInjectionMode} for details of each data injection
+ * mode. Once this method succeeds, the test can call
+ * {@link #injectSensorData(Sensor, float[], int, long)} to inject sensor data into the HAL.
+ * To put SensorService back into normal mode, use "adb shell dumpsys sensorservice enable"
+ *
+ * @param enable True to initialize a client in a data injection mode.
+ * False to clean up the native resources.
+ *
+ * @param mode One of DATA_INJECTION, REPLAY_DATA_INJECTION or HAL_BYPASS_DATA_INJECTION.
+ * See {@link DataInjectionMode} for details.
+ *
+ * @return true if the HAL supports data injection and false
+ * otherwise.
+ * @hide
+ */
+ public boolean initDataInjection(boolean enable, @DataInjectionMode int mode) {
+ return initDataInjectionImpl(enable, mode);
}
/**
* @hide
*/
- protected abstract boolean initDataInjectionImpl(boolean enable);
+ protected abstract boolean initDataInjectionImpl(boolean enable, @DataInjectionMode int mode);
/**
* For testing purposes only. Not for third party applications.
@@ -1871,9 +1943,6 @@ public abstract class SensorManager {
if (sensor == null) {
throw new IllegalArgumentException("sensor cannot be null");
}
- if (!sensor.isDataInjectionSupported()) {
- throw new IllegalArgumentException("sensor does not support data injection");
- }
if (values == null) {
throw new IllegalArgumentException("sensor data cannot be null");
}
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index dfd380233662..40e03dbb8b34 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -90,6 +90,8 @@ public class SystemSensorManager extends SensorManager {
private static native void nativeGetRuntimeSensors(
long nativeInstance, int deviceId, List<Sensor> list);
private static native boolean nativeIsDataInjectionEnabled(long nativeInstance);
+ private static native boolean nativeIsReplayDataInjectionEnabled(long nativeInstance);
+ private static native boolean nativeIsHalBypassReplayDataInjectionEnabled(long nativeInstance);
private static native int nativeCreateDirectChannel(
long nativeInstance, int deviceId, long size, int channelType, int fd,
@@ -384,20 +386,41 @@ public class SystemSensorManager extends SensorManager {
}
}
- protected boolean initDataInjectionImpl(boolean enable) {
+ protected boolean initDataInjectionImpl(boolean enable, @DataInjectionMode int mode) {
synchronized (sLock) {
+ boolean isDataInjectionModeEnabled = false;
if (enable) {
- boolean isDataInjectionModeEnabled = nativeIsDataInjectionEnabled(mNativeInstance);
+ switch (mode) {
+ case DATA_INJECTION:
+ isDataInjectionModeEnabled = nativeIsDataInjectionEnabled(mNativeInstance);
+ break;
+ case REPLAY_DATA_INJECTION:
+ isDataInjectionModeEnabled = nativeIsReplayDataInjectionEnabled(
+ mNativeInstance);
+ break;
+ case HAL_BYPASS_REPLAY_DATA_INJECTION:
+ isDataInjectionModeEnabled = nativeIsHalBypassReplayDataInjectionEnabled(
+ mNativeInstance);
+ break;
+ default:
+ break;
+ }
// The HAL does not support injection OR SensorService hasn't been set in DI mode.
if (!isDataInjectionModeEnabled) {
- Log.e(TAG, "Data Injection mode not enabled");
+ Log.e(TAG, "The correct Data Injection mode has not been enabled");
return false;
}
+ if (sInjectEventQueue != null && sInjectEventQueue.getDataInjectionMode() != mode) {
+ // The inject event queue has been initialized for a different type of DI
+ // close it and create a new one
+ sInjectEventQueue.dispose();
+ sInjectEventQueue = null;
+ }
// Initialize a client for data_injection.
if (sInjectEventQueue == null) {
try {
sInjectEventQueue = new InjectEventQueue(
- mMainLooper, this, mContext.getPackageName());
+ mMainLooper, this, mode, mContext.getPackageName());
} catch (RuntimeException e) {
Log.e(TAG, "Cannot create InjectEventQueue: " + e);
}
@@ -421,6 +444,12 @@ public class SystemSensorManager extends SensorManager {
Log.e(TAG, "Data injection mode not activated before calling injectSensorData");
return false;
}
+ if (sInjectEventQueue.getDataInjectionMode() != HAL_BYPASS_REPLAY_DATA_INJECTION
+ && !sensor.isDataInjectionSupported()) {
+ // DI mode and Replay DI mode require support from the sensor HAL
+ // HAL Bypass mode doesn't require this.
+ throw new IllegalArgumentException("sensor does not support data injection");
+ }
int ret = sInjectEventQueue.injectSensorData(sensor.getHandle(), values, accuracy,
timestamp);
// If there are any errors in data injection clean up the native resources.
@@ -825,6 +854,8 @@ public class SystemSensorManager extends SensorManager {
protected static final int OPERATING_MODE_NORMAL = 0;
protected static final int OPERATING_MODE_DATA_INJECTION = 1;
+ protected static final int OPERATING_MODE_REPLAY_DATA_INJECTION = 3;
+ protected static final int OPERATING_MODE_HAL_BYPASS_REPLAY_DATA_INJECTION = 4;
BaseEventQueue(Looper looper, SystemSensorManager manager, int mode, String packageName) {
if (packageName == null) packageName = "";
@@ -1134,8 +1165,12 @@ public class SystemSensorManager extends SensorManager {
}
final class InjectEventQueue extends BaseEventQueue {
- public InjectEventQueue(Looper looper, SystemSensorManager manager, String packageName) {
- super(looper, manager, OPERATING_MODE_DATA_INJECTION, packageName);
+
+ private int mMode;
+ public InjectEventQueue(Looper looper, SystemSensorManager manager,
+ @DataInjectionMode int mode, String packageName) {
+ super(looper, manager, mode, packageName);
+ mMode = mode;
}
int injectSensorData(int handle, float[] values, int accuracy, long timestamp) {
@@ -1161,6 +1196,10 @@ public class SystemSensorManager extends SensorManager {
protected void removeSensorEvent(Sensor sensor) {
}
+
+ int getDataInjectionMode() {
+ return mMode;
+ }
}
protected boolean setOperationParameterImpl(SensorAdditionalInfo parameter) {
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 02212968cdb0..02304b5ba4f3 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -1074,6 +1074,14 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
*/
public interface FaceDetectionCallback {
void onFaceDetected(int sensorId, int userId, boolean isStrongBiometric);
+
+ /**
+ * An error has occurred with face detection.
+ *
+ * This callback signifies that this operation has been completed, and
+ * no more callbacks should be expected.
+ */
+ default void onDetectionError(int errorMsgId) {}
}
/**
@@ -1373,6 +1381,9 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
} else if (mRemovalCallback != null) {
mRemovalCallback.onRemovalError(mRemovalFace, clientErrMsgId,
getErrorString(mContext, errMsgId, vendorCode));
+ } else if (mFaceDetectionCallback != null) {
+ mFaceDetectionCallback.onDetectionError(errMsgId);
+ mFaceDetectionCallback = null;
}
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 5bfda70f03de..935157a48a45 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -462,6 +462,14 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
* Invoked when a fingerprint has been detected.
*/
void onFingerprintDetected(int sensorId, int userId, boolean isStrongBiometric);
+
+ /**
+ * An error has occurred with fingerprint detection.
+ *
+ * This callback signifies that this operation has been completed, and
+ * no more callbacks should be expected.
+ */
+ default void onDetectionError(int errorMsgId) {}
}
/**
@@ -1484,6 +1492,9 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
? mRemoveTracker.mSingleFingerprint : null;
mRemovalCallback.onRemovalError(fp, clientErrMsgId,
getErrorString(mContext, errMsgId, vendorCode));
+ } else if (mFingerprintDetectionCallback != null) {
+ mFingerprintDetectionCallback.onDetectionError(errMsgId);
+ mFingerprintDetectionCallback = null;
}
}
diff --git a/core/java/android/hardware/input/InputDeviceSensorManager.java b/core/java/android/hardware/input/InputDeviceSensorManager.java
index aa55e543dd48..05024ea95eda 100644
--- a/core/java/android/hardware/input/InputDeviceSensorManager.java
+++ b/core/java/android/hardware/input/InputDeviceSensorManager.java
@@ -644,7 +644,7 @@ public class InputDeviceSensorManager implements InputManager.InputDeviceListene
}
@Override
- protected boolean initDataInjectionImpl(boolean enable) {
+ protected boolean initDataInjectionImpl(boolean enable, int mode) {
return false;
}
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
index 98f9dffaae13..5078dc351f6f 100644
--- a/core/java/android/os/VibrationAttributes.java
+++ b/core/java/android/os/VibrationAttributes.java
@@ -140,6 +140,31 @@ public final class VibrationAttributes implements Parcelable {
*/
public static final int USAGE_MEDIA = 0x10 | USAGE_CLASS_MEDIA;
+ /** @hide */
+ @IntDef(prefix = { "CATEGORY_" }, value = {
+ CATEGORY_UNKNOWN,
+ CATEGORY_KEYBOARD,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Category {}
+
+ /**
+ * Category value when the vibration category is unknown.
+ *
+ * @hide
+ */
+ public static final int CATEGORY_UNKNOWN = 0x0;
+
+ /**
+ * Category value for keyboard vibrations.
+ *
+ * <p>Most typical keyboard vibrations are haptic feedback for virtual keyboard key
+ * press/release, for example.
+ *
+ * @hide
+ */
+ public static final int CATEGORY_KEYBOARD = 1;
+
/**
* @hide
*/
@@ -147,7 +172,8 @@ public final class VibrationAttributes implements Parcelable {
FLAG_BYPASS_INTERRUPTION_POLICY,
FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF,
FLAG_INVALIDATE_SETTINGS_CACHE,
- FLAG_PIPELINED_EFFECT
+ FLAG_PIPELINED_EFFECT,
+ FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE
})
@Retention(RetentionPolicy.SOURCE)
public @interface Flag{}
@@ -167,6 +193,8 @@ public final class VibrationAttributes implements Parcelable {
* {@link android.view.HapticFeedbackConstants#FLAG_IGNORE_GLOBAL_SETTING} and
* {@link AudioAttributes#FLAG_BYPASS_MUTE}.
*
+ * <p>Only privileged apps can ignore user settings, and this flag will be ignored otherwise.
+ *
* @hide
*/
public static final int FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF = 1 << 1;
@@ -199,12 +227,31 @@ public final class VibrationAttributes implements Parcelable {
public static final int FLAG_PIPELINED_EFFECT = 1 << 3;
/**
+ * Flag requesting that this vibration effect to be played without applying the user
+ * intensity setting to scale the vibration.
+ *
+ * <p>The user setting is still applied to enable/disable the vibration, but the vibration
+ * effect strength will not be scaled based on the enabled setting value.
+ *
+ * <p>This is intended to be used on scenarios where the system needs to enforce a specific
+ * strength for the vibration effect, regardless of the user preference. Only privileged apps
+ * can ignore user settings, and this flag will be ignored otherwise.
+ *
+ * <p>If you need to bypass the user setting when it's disabling vibrations then this also
+ * needs the flag {@link #FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF} to be set.
+ *
+ * @hide
+ */
+ public static final int FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE = 1 << 4;
+
+ /**
* All flags supported by vibrator service, update it when adding new flag.
* @hide
*/
public static final int FLAG_ALL_SUPPORTED =
FLAG_BYPASS_INTERRUPTION_POLICY | FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF
- | FLAG_INVALIDATE_SETTINGS_CACHE | FLAG_PIPELINED_EFFECT;
+ | FLAG_INVALIDATE_SETTINGS_CACHE | FLAG_PIPELINED_EFFECT
+ | FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE;
/** Creates a new {@link VibrationAttributes} instance with given usage. */
public static @NonNull VibrationAttributes createForUsage(@Usage int usage) {
@@ -214,12 +261,14 @@ public final class VibrationAttributes implements Parcelable {
private final int mUsage;
private final int mFlags;
private final int mOriginalAudioUsage;
+ private final int mCategory;
private VibrationAttributes(@Usage int usage, @AudioAttributes.AttributeUsage int audioUsage,
- @Flag int flags) {
+ @Flag int flags, @Category int category) {
mUsage = usage;
mOriginalAudioUsage = audioUsage;
mFlags = flags & FLAG_ALL_SUPPORTED;
+ mCategory = category;
}
/**
@@ -248,6 +297,20 @@ public final class VibrationAttributes implements Parcelable {
}
/**
+ * Return the vibration category.
+ *
+ * <p>Vibration categories describe the source of the vibration, and it can be combined with
+ * the vibration usage to best match to a user setting, e.g. a vibration with usage touch and
+ * category keyboard can be used to control keyboard haptic feedback independently.
+ *
+ * @hide
+ */
+ @Category
+ public int getCategory() {
+ return mCategory;
+ }
+
+ /**
* Check whether a flag is set
* @return true if a flag is set and false otherwise
*/
@@ -298,12 +361,14 @@ public final class VibrationAttributes implements Parcelable {
dest.writeInt(mUsage);
dest.writeInt(mOriginalAudioUsage);
dest.writeInt(mFlags);
+ dest.writeInt(mCategory);
}
private VibrationAttributes(Parcel src) {
mUsage = src.readInt();
mOriginalAudioUsage = src.readInt();
mFlags = src.readInt();
+ mCategory = src.readInt();
}
public static final @NonNull Parcelable.Creator<VibrationAttributes>
@@ -326,12 +391,12 @@ public final class VibrationAttributes implements Parcelable {
}
VibrationAttributes rhs = (VibrationAttributes) o;
return mUsage == rhs.mUsage && mOriginalAudioUsage == rhs.mOriginalAudioUsage
- && mFlags == rhs.mFlags;
+ && mFlags == rhs.mFlags && mCategory == rhs.mCategory;
}
@Override
public int hashCode() {
- return Objects.hash(mUsage, mOriginalAudioUsage, mFlags);
+ return Objects.hash(mUsage, mOriginalAudioUsage, mFlags, mCategory);
}
@Override
@@ -340,6 +405,7 @@ public final class VibrationAttributes implements Parcelable {
+ "mUsage=" + usageToString()
+ ", mAudioUsage= " + AudioAttributes.usageToString(mOriginalAudioUsage)
+ ", mFlags=" + mFlags
+ + ", mCategory=" + categoryToString()
+ '}';
}
@@ -376,6 +442,23 @@ public final class VibrationAttributes implements Parcelable {
}
}
+ /** @hide */
+ public String categoryToString() {
+ return categoryToString(mCategory);
+ }
+
+ /** @hide */
+ public static String categoryToString(@Category int category) {
+ switch (category) {
+ case CATEGORY_UNKNOWN:
+ return "UNKNOWN";
+ case CATEGORY_KEYBOARD:
+ return "KEYBOARD";
+ default:
+ return "unknown category " + category;
+ }
+ }
+
/**
* Builder class for {@link VibrationAttributes} objects.
* By default, all information is set to UNKNOWN.
@@ -384,6 +467,7 @@ public final class VibrationAttributes implements Parcelable {
private int mUsage = USAGE_UNKNOWN;
private int mOriginalAudioUsage = AudioAttributes.USAGE_UNKNOWN;
private int mFlags = 0x0;
+ private int mCategory = CATEGORY_UNKNOWN;
/**
* Constructs a new Builder with the defaults.
@@ -399,6 +483,7 @@ public final class VibrationAttributes implements Parcelable {
mUsage = vib.mUsage;
mOriginalAudioUsage = vib.mOriginalAudioUsage;
mFlags = vib.mFlags;
+ mCategory = vib.mCategory;
}
}
@@ -464,7 +549,8 @@ public final class VibrationAttributes implements Parcelable {
* @return a new {@link VibrationAttributes} object
*/
public @NonNull VibrationAttributes build() {
- VibrationAttributes ans = new VibrationAttributes(mUsage, mOriginalAudioUsage, mFlags);
+ VibrationAttributes ans = new VibrationAttributes(
+ mUsage, mOriginalAudioUsage, mFlags, mCategory);
return ans;
}
@@ -480,6 +566,19 @@ public final class VibrationAttributes implements Parcelable {
}
/**
+ * Sets the attribute describing the category of the corresponding vibration.
+ *
+ * @param category The category for the vibration
+ * @return the same Builder instance.
+ *
+ * @hide
+ */
+ public @NonNull Builder setCategory(@Category int category) {
+ mCategory = category;
+ return this;
+ }
+
+ /**
* Sets only the flags specified in the bitmask, leaving the other supported flag values
* unchanged in the builder.
*
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 99c9925d9cb7..2fc24142acf2 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -184,6 +184,16 @@ public abstract class Vibrator {
}
/**
+ * Whether the keyboard vibration is enabled by default.
+ *
+ * @return {@code true} if the keyboard vibration is default enabled, {@code false} otherwise.
+ * @hide
+ */
+ public boolean isDefaultKeyboardVibrationEnabled() {
+ return getConfig().isDefaultKeyboardVibrationEnabled();
+ }
+
+ /**
* Return the ID of this vibrator.
*
* @return A non-negative integer representing the id of the vibrator controlled by this
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 7fceda4a4393..b7f2e065bac8 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -16,7 +16,7 @@ flag {
flag {
name: "remove_app_profiler_pss_collection"
- namespace: "power_optimization"
+ namespace: "backstage_power"
description: "Replaces background PSS collection in AppProfiler with RSS"
bug: "297542292"
}
diff --git a/core/java/android/os/vibrator/VibrationConfig.java b/core/java/android/os/vibrator/VibrationConfig.java
index bde334a6edc5..92e49676cbae 100644
--- a/core/java/android/os/vibrator/VibrationConfig.java
+++ b/core/java/android/os/vibrator/VibrationConfig.java
@@ -65,6 +65,8 @@ public class VibrationConfig {
@VibrationIntensity
private final int mDefaultRingVibrationIntensity;
+ private final boolean mDefaultKeyboardVibrationEnabled;
+
/** @hide */
public VibrationConfig(@Nullable Resources resources) {
mHapticChannelMaxVibrationAmplitude = loadFloat(resources,
@@ -76,6 +78,8 @@ public class VibrationConfig {
mIgnoreVibrationsOnWirelessCharger = loadBoolean(resources,
com.android.internal.R.bool.config_ignoreVibrationsOnWirelessCharger, false);
+ mDefaultKeyboardVibrationEnabled = loadBoolean(resources,
+ com.android.internal.R.bool.config_defaultKeyboardVibrationEnabled, true);
mDefaultAlarmVibrationIntensity = loadDefaultIntensity(resources,
com.android.internal.R.integer.config_defaultAlarmVibrationIntensity);
@@ -157,6 +161,14 @@ public class VibrationConfig {
return mIgnoreVibrationsOnWirelessCharger;
}
+ /**
+ * Whether keyboard vibration settings is enabled by default.
+ * @hide
+ */
+ public boolean isDefaultKeyboardVibrationEnabled() {
+ return mDefaultKeyboardVibrationEnabled;
+ }
+
/** Get the default vibration intensity for given usage. */
@VibrationIntensity
public int getDefaultVibrationIntensity(@VibrationAttributes.Usage int usage) {
diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig
index 66ad12c7559a..69d86a6604ad 100644
--- a/core/java/android/os/vibrator/flags.aconfig
+++ b/core/java/android/os/vibrator/flags.aconfig
@@ -37,3 +37,10 @@ flag {
is_fixed_read_only: true
bug: "291128479"
}
+
+flag {
+ namespace: "haptics"
+ name: "keyboard_category_enabled"
+ description: "Enables the independent keyboard vibration settings feature"
+ bug: "289107579"
+}
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index d60d4c6372fe..3f06a91f6e5b 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -28,3 +28,10 @@ flag {
description: "enable AttributionSource.setNextAttributionSource"
bug: "304478648"
}
+
+flag {
+ name: "should_register_attribution_source"
+ namespace: "permissions"
+ description: "enable the shouldRegisterAttributionSource API"
+ bug: "305057691"
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b34e09f2c97a..f0906b1e3c06 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5129,6 +5129,14 @@ public final class Settings {
"hardware_haptic_feedback_intensity";
/**
+ * Whether keyboard vibration feedback is enabled. The value is boolean (1 or 0).
+ *
+ * @hide
+ */
+ @Readable
+ public static final String KEYBOARD_VIBRATION_ENABLED = "keyboard_vibration_enabled";
+
+ /**
* Ringer volume. This is used internally, changing this value will not
* change the volume. See AudioManager.
*
diff --git a/core/java/android/service/voice/AbstractDetector.java b/core/java/android/service/voice/AbstractDetector.java
index 7af7fe6108e6..db97d4f52643 100644
--- a/core/java/android/service/voice/AbstractDetector.java
+++ b/core/java/android/service/voice/AbstractDetector.java
@@ -199,8 +199,12 @@ abstract class AbstractDetector implements HotwordDetector {
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
+ Consumer<AbstractDetector> onDestroyListener;
synchronized (mLock) {
- mOnDestroyListener.accept(this);
+ onDestroyListener = mOnDestroyListener;
+ }
+ if (onDestroyListener != null) {
+ onDestroyListener.accept(this);
}
}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 3f41c56ac7f1..d2806217a276 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -520,7 +520,7 @@ public class VoiceInteractionService extends Service {
@NonNull String keyphrase, @SuppressLint("UseIcu") @NonNull Locale locale,
@NonNull @CallbackExecutor Executor executor,
@NonNull AlwaysOnHotwordDetector.Callback callback) {
- // TODO (b/269080850): Resolve AndroidFrameworkRequiresPermission lint warning
+ // TODO(b/269080850): Resolve AndroidFrameworkRequiresPermission lint warning
Objects.requireNonNull(keyphrase);
Objects.requireNonNull(locale);
@@ -546,6 +546,10 @@ public class VoiceInteractionService extends Service {
@NonNull SoundTrigger.ModuleProperties moduleProperties,
@NonNull @CallbackExecutor Executor executor,
@NonNull AlwaysOnHotwordDetector.Callback callback) {
+ // TODO(b/305787465): Remove the MANAGE_HOTWORD_DETECTION permission enforcement on the
+ // {@link #createAlwaysOnHotwordDetectorForTest(String, Locale,
+ // SoundTrigger.ModuleProperties, AlwaysOnHotwordDetector.Callback)} and replace with the
+ // permission RECEIVE_SANDBOX_TRIGGER_AUDIO when it is fully launched.
Objects.requireNonNull(keyphrase);
Objects.requireNonNull(locale);
@@ -612,6 +616,11 @@ public class VoiceInteractionService extends Service {
@Nullable PersistableBundle options,
@Nullable SharedMemory sharedMemory,
@SuppressLint("MissingNullability") AlwaysOnHotwordDetector.Callback callback) {
+ // TODO(b/305787465): Remove the MANAGE_HOTWORD_DETECTION permission enforcement on the
+ // {@link #createAlwaysOnHotwordDetector(String, Locale, PersistableBundle, SharedMemory,
+ // AlwaysOnHotwordDetector.Callback)} and replace with the permission
+ // RECEIVE_SANDBOX_TRIGGER_AUDIO when it is fully launched.
+
return createAlwaysOnHotwordDetectorInternal(keyphrase, locale,
/* supportHotwordDetectionService= */ true, options, sharedMemory,
/* modulProperties */ null, /* executor= */ null, callback);
@@ -663,7 +672,11 @@ public class VoiceInteractionService extends Service {
@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory,
@NonNull @CallbackExecutor Executor executor,
@NonNull AlwaysOnHotwordDetector.Callback callback) {
- // TODO (b/269080850): Resolve AndroidFrameworkRequiresPermission lint warning
+ // TODO(b/269080850): Resolve AndroidFrameworkRequiresPermission lint warning
+ // TODO(b/305787465): Remove the MANAGE_HOTWORD_DETECTION permission enforcement on the
+ // {@link #createAlwaysOnHotwordDetector(String, Locale, PersistableBundle, SharedMemory,
+ // Executor, AlwaysOnHotwordDetector.Callback)} and replace with the permission
+ // RECEIVE_SANDBOX_TRIGGER_AUDIO when it is fully launched.
Objects.requireNonNull(keyphrase);
Objects.requireNonNull(locale);
@@ -690,6 +703,10 @@ public class VoiceInteractionService extends Service {
@NonNull SoundTrigger.ModuleProperties moduleProperties,
@NonNull @CallbackExecutor Executor executor,
@NonNull AlwaysOnHotwordDetector.Callback callback) {
+ // TODO(b/305787465): Remove the MANAGE_HOTWORD_DETECTION permission enforcement on the
+ // {@link #createAlwaysOnHotwordDetectorForTest(String, Locale, PersistableBundle,
+ // SharedMemory, SoundTrigger.ModuleProperties, Executor, AlwaysOnHotwordDetector.Callback)}
+ // and replace with the permission RECEIVE_SANDBOX_TRIGGER_AUDIO when it is fully launched.
Objects.requireNonNull(keyphrase);
Objects.requireNonNull(locale);
diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java
index 3ed13bbeeaad..35834fd3886f 100644
--- a/core/java/android/speech/SpeechRecognizer.java
+++ b/core/java/android/speech/SpeechRecognizer.java
@@ -767,7 +767,7 @@ public class SpeechRecognizer {
try {
mService.startListening(recognizerIntent, mListener, mContext.getAttributionSource());
if (DBG) Log.d(TAG, "service start listening command succeeded");
- } catch (final RemoteException e) {
+ } catch (final Exception e) {
Log.e(TAG, "startListening() failed", e);
mListener.onError(ERROR_CLIENT);
}
@@ -781,7 +781,7 @@ public class SpeechRecognizer {
try {
mService.stopListening(mListener);
if (DBG) Log.d(TAG, "service stop listening command succeeded");
- } catch (final RemoteException e) {
+ } catch (final Exception e) {
Log.e(TAG, "stopListening() failed", e);
mListener.onError(ERROR_CLIENT);
}
@@ -795,7 +795,7 @@ public class SpeechRecognizer {
try {
mService.cancel(mListener, /*isShutdown*/ false);
if (DBG) Log.d(TAG, "service cancel command succeeded");
- } catch (final RemoteException e) {
+ } catch (final Exception e) {
Log.e(TAG, "cancel() failed", e);
mListener.onError(ERROR_CLIENT);
}
@@ -830,7 +830,7 @@ public class SpeechRecognizer {
mContext.getAttributionSource(),
new InternalSupportCallback(callbackExecutor, recognitionSupportCallback));
if (DBG) Log.d(TAG, "service support command succeeded");
- } catch (final RemoteException e) {
+ } catch (final Exception e) {
Log.e(TAG, "checkRecognitionSupport() failed", e);
callbackExecutor.execute(() -> recognitionSupportCallback.onError(ERROR_CLIENT));
}
@@ -850,7 +850,7 @@ public class SpeechRecognizer {
mService.triggerModelDownload(
recognizerIntent, mContext.getAttributionSource(), null);
if (DBG) Log.d(TAG, "triggerModelDownload() without a listener");
- } catch (final RemoteException e) {
+ } catch (final Exception e) {
Log.e(TAG, "triggerModelDownload() without a listener failed", e);
mListener.onError(ERROR_CLIENT);
}
@@ -862,7 +862,7 @@ public class SpeechRecognizer {
recognizerIntent, mContext.getAttributionSource(),
new InternalModelDownloadListener(callbackExecutor, modelDownloadListener));
if (DBG) Log.d(TAG, "triggerModelDownload() with a listener");
- } catch (final RemoteException e) {
+ } catch (final Exception e) {
Log.e(TAG, "triggerModelDownload() with a listener failed", e);
callbackExecutor.execute(() -> modelDownloadListener.onError(ERROR_CLIENT));
}
@@ -889,7 +889,7 @@ public class SpeechRecognizer {
if (mService != null) {
try {
mService.cancel(mListener, /*isShutdown*/ true);
- } catch (final RemoteException e) {
+ } catch (final Exception e) {
// Not important
}
}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 2906d86f803d..766e924c994e 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -200,6 +200,12 @@ public class FeatureFlagUtils {
public static final String SETTINGS_REMOTE_DEVICE_CREDENTIAL_VALIDATION =
"settings_remote_device_credential_validation";
+ /**
+ * Flag to enable/disable to start treating any calls to "suspend" an app as "quarantine".
+ * @hide
+ */
+ public static final String SETTINGS_TREAT_PAUSE_AS_QUARANTINE =
+ "settings_treat_pause_as_quarantine";
private static final Map<String, String> DEFAULT_FLAGS;
@@ -247,6 +253,7 @@ public class FeatureFlagUtils {
DEFAULT_FLAGS.put(SETTINGS_BIOMETRICS2_FINGERPRINT_SETTINGS, "false");
// TODO: b/298454866 Replace with Trunk Stable Feature Flag
DEFAULT_FLAGS.put(SETTINGS_REMOTEAUTH_ENROLLMENT_SETTINGS, "false");
+ DEFAULT_FLAGS.put(SETTINGS_TREAT_PAUSE_AS_QUARANTINE, "false");
}
private static final Set<String> PERSISTENT_FLAGS;
@@ -264,6 +271,7 @@ public class FeatureFlagUtils {
PERSISTENT_FLAGS.add(SETTINGS_ENABLE_SPA);
PERSISTENT_FLAGS.add(SETTINGS_ENABLE_SPA_PHASE2);
PERSISTENT_FLAGS.add(SETTINGS_PREFER_ACCESSIBILITY_MENU_IN_SYSTEM);
+ PERSISTENT_FLAGS.add(SETTINGS_TREAT_PAUSE_AS_QUARANTINE);
}
/**
diff --git a/core/java/android/view/AttachedSurfaceControl.java b/core/java/android/view/AttachedSurfaceControl.java
index 1ed5d3fb6268..71d382e19e2b 100644
--- a/core/java/android/view/AttachedSurfaceControl.java
+++ b/core/java/android/view/AttachedSurfaceControl.java
@@ -92,6 +92,12 @@ public interface AttachedSurfaceControl {
* SurfaceView Surface, the buffer producer will already have access to the transform hint and
* no additional work is needed.
*
+ * If the root surface is not available, the API will return {@code BUFFER_TRANSFORM_IDENTITY}.
+ * The caller should register a listener to listen for any changes. @see
+ * {@link #addOnBufferTransformHintChangedListener(OnBufferTransformHintChangedListener)}.
+ * Warning: Calling this API in Android 14 (API Level 34) or earlier will crash if the root
+ * surface is not available.
+ *
* @see HardwareBuffer
*/
default @SurfaceControl.BufferTransform int getBufferTransformHint() {
diff --git a/core/java/android/view/ContentRecordingSession.java b/core/java/android/view/ContentRecordingSession.java
index a89f79540c65..dc41b70d1683 100644
--- a/core/java/android/view/ContentRecordingSession.java
+++ b/core/java/android/view/ContentRecordingSession.java
@@ -52,6 +52,12 @@ public final class ContentRecordingSession implements Parcelable {
*/
public static final int RECORD_CONTENT_TASK = 1;
+ /** Full screen sharing (app is not selected). */
+ public static final int TARGET_UID_FULL_SCREEN = -1;
+
+ /** Can't report (e.g. side loaded app). */
+ public static final int TARGET_UID_UNKNOWN = -2;
+
/**
* Unique logical identifier of the {@link android.hardware.display.VirtualDisplay} that has
* recorded content rendered to its surface.
@@ -89,27 +95,36 @@ public final class ContentRecordingSession implements Parcelable {
*/
private boolean mWaitingForConsent = false;
+ /** UID of the package that is captured if selected. */
+ private int mTargetUid = TARGET_UID_UNKNOWN;
+
/**
* Default instance, with recording the display.
*/
private ContentRecordingSession() {
}
- /**
- * Returns an instance initialized for recording the indicated display.
- */
+ /** Returns an instance initialized for recording the indicated display. */
public static ContentRecordingSession createDisplaySession(int displayToMirror) {
- return new ContentRecordingSession().setDisplayToRecord(displayToMirror)
- .setContentToRecord(RECORD_CONTENT_DISPLAY);
+ return new ContentRecordingSession()
+ .setDisplayToRecord(displayToMirror)
+ .setContentToRecord(RECORD_CONTENT_DISPLAY)
+ .setTargetUid(TARGET_UID_FULL_SCREEN);
}
- /**
- * Returns an instance initialized for task recording.
- */
+ /** Returns an instance initialized for task recording. */
public static ContentRecordingSession createTaskSession(
@NonNull IBinder taskWindowContainerToken) {
- return new ContentRecordingSession().setContentToRecord(RECORD_CONTENT_TASK)
- .setTokenToRecord(taskWindowContainerToken);
+ return createTaskSession(taskWindowContainerToken, TARGET_UID_UNKNOWN);
+ }
+
+ /** Returns an instance initialized for task recording. */
+ public static ContentRecordingSession createTaskSession(
+ @NonNull IBinder taskWindowContainerToken, int targetUid) {
+ return new ContentRecordingSession()
+ .setContentToRecord(RECORD_CONTENT_TASK)
+ .setTokenToRecord(taskWindowContainerToken)
+ .setTargetUid(targetUid);
}
/**
@@ -175,13 +190,33 @@ public final class ContentRecordingSession implements Parcelable {
}
}
+ @IntDef(prefix = "TARGET_UID_", value = {
+ TARGET_UID_FULL_SCREEN,
+ TARGET_UID_UNKNOWN
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @DataClass.Generated.Member
+ public @interface TargetUid {}
+
+ @DataClass.Generated.Member
+ public static String targetUidToString(@TargetUid int value) {
+ switch (value) {
+ case TARGET_UID_FULL_SCREEN:
+ return "TARGET_UID_FULL_SCREEN";
+ case TARGET_UID_UNKNOWN:
+ return "TARGET_UID_UNKNOWN";
+ default: return Integer.toHexString(value);
+ }
+ }
+
@DataClass.Generated.Member
/* package-private */ ContentRecordingSession(
int virtualDisplayId,
@RecordContent int contentToRecord,
int displayToRecord,
@Nullable IBinder tokenToRecord,
- boolean waitingForConsent) {
+ boolean waitingForConsent,
+ int targetUid) {
this.mVirtualDisplayId = virtualDisplayId;
this.mContentToRecord = contentToRecord;
@@ -196,6 +231,7 @@ public final class ContentRecordingSession implements Parcelable {
this.mDisplayToRecord = displayToRecord;
this.mTokenToRecord = tokenToRecord;
this.mWaitingForConsent = waitingForConsent;
+ this.mTargetUid = targetUid;
// onConstructed(); // You can define this method to get a callback
}
@@ -251,6 +287,14 @@ public final class ContentRecordingSession implements Parcelable {
}
/**
+ * UID of the package that is captured if selected.
+ */
+ @DataClass.Generated.Member
+ public int getTargetUid() {
+ return mTargetUid;
+ }
+
+ /**
* Unique logical identifier of the {@link android.hardware.display.VirtualDisplay} that has
* recorded content rendered to its surface.
*/
@@ -314,6 +358,15 @@ public final class ContentRecordingSession implements Parcelable {
return this;
}
+ /**
+ * UID of the package that is captured if selected.
+ */
+ @DataClass.Generated.Member
+ public @NonNull ContentRecordingSession setTargetUid( int value) {
+ mTargetUid = value;
+ return this;
+ }
+
@Override
@DataClass.Generated.Member
public String toString() {
@@ -325,7 +378,8 @@ public final class ContentRecordingSession implements Parcelable {
"contentToRecord = " + recordContentToString(mContentToRecord) + ", " +
"displayToRecord = " + mDisplayToRecord + ", " +
"tokenToRecord = " + mTokenToRecord + ", " +
- "waitingForConsent = " + mWaitingForConsent +
+ "waitingForConsent = " + mWaitingForConsent + ", " +
+ "targetUid = " + mTargetUid +
" }";
}
@@ -346,7 +400,8 @@ public final class ContentRecordingSession implements Parcelable {
&& mContentToRecord == that.mContentToRecord
&& mDisplayToRecord == that.mDisplayToRecord
&& java.util.Objects.equals(mTokenToRecord, that.mTokenToRecord)
- && mWaitingForConsent == that.mWaitingForConsent;
+ && mWaitingForConsent == that.mWaitingForConsent
+ && mTargetUid == that.mTargetUid;
}
@Override
@@ -361,6 +416,7 @@ public final class ContentRecordingSession implements Parcelable {
_hash = 31 * _hash + mDisplayToRecord;
_hash = 31 * _hash + java.util.Objects.hashCode(mTokenToRecord);
_hash = 31 * _hash + Boolean.hashCode(mWaitingForConsent);
+ _hash = 31 * _hash + mTargetUid;
return _hash;
}
@@ -378,6 +434,7 @@ public final class ContentRecordingSession implements Parcelable {
dest.writeInt(mContentToRecord);
dest.writeInt(mDisplayToRecord);
if (mTokenToRecord != null) dest.writeStrongBinder(mTokenToRecord);
+ dest.writeInt(mTargetUid);
}
@Override
@@ -397,6 +454,7 @@ public final class ContentRecordingSession implements Parcelable {
int contentToRecord = in.readInt();
int displayToRecord = in.readInt();
IBinder tokenToRecord = (flg & 0x8) == 0 ? null : (IBinder) in.readStrongBinder();
+ int targetUid = in.readInt();
this.mVirtualDisplayId = virtualDisplayId;
this.mContentToRecord = contentToRecord;
@@ -412,6 +470,7 @@ public final class ContentRecordingSession implements Parcelable {
this.mDisplayToRecord = displayToRecord;
this.mTokenToRecord = tokenToRecord;
this.mWaitingForConsent = waitingForConsent;
+ this.mTargetUid = targetUid;
// onConstructed(); // You can define this method to get a callback
}
@@ -442,6 +501,7 @@ public final class ContentRecordingSession implements Parcelable {
private int mDisplayToRecord;
private @Nullable IBinder mTokenToRecord;
private boolean mWaitingForConsent;
+ private int mTargetUid;
private long mBuilderFieldsSet = 0L;
@@ -513,10 +573,21 @@ public final class ContentRecordingSession implements Parcelable {
return this;
}
+ /**
+ * UID of the package that is captured if selected.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setTargetUid(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x20;
+ mTargetUid = value;
+ return this;
+ }
+
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull ContentRecordingSession build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x20; // Mark builder used
+ mBuilderFieldsSet |= 0x40; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
mVirtualDisplayId = INVALID_DISPLAY;
@@ -533,17 +604,21 @@ public final class ContentRecordingSession implements Parcelable {
if ((mBuilderFieldsSet & 0x10) == 0) {
mWaitingForConsent = false;
}
+ if ((mBuilderFieldsSet & 0x20) == 0) {
+ mTargetUid = TARGET_UID_UNKNOWN;
+ }
ContentRecordingSession o = new ContentRecordingSession(
mVirtualDisplayId,
mContentToRecord,
mDisplayToRecord,
mTokenToRecord,
- mWaitingForConsent);
+ mWaitingForConsent,
+ mTargetUid);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x20) != 0) {
+ if ((mBuilderFieldsSet & 0x40) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -551,10 +626,10 @@ public final class ContentRecordingSession implements Parcelable {
}
@DataClass.Generated(
- time = 1683628463074L,
+ time = 1697456140720L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/view/ContentRecordingSession.java",
- inputSignatures = "public static final int RECORD_CONTENT_DISPLAY\npublic static final int RECORD_CONTENT_TASK\nprivate int mVirtualDisplayId\nprivate @android.view.ContentRecordingSession.RecordContent int mContentToRecord\nprivate int mDisplayToRecord\nprivate @android.annotation.Nullable android.os.IBinder mTokenToRecord\nprivate boolean mWaitingForConsent\npublic static android.view.ContentRecordingSession createDisplaySession(int)\npublic static android.view.ContentRecordingSession createTaskSession(android.os.IBinder)\npublic static boolean isValid(android.view.ContentRecordingSession)\npublic static boolean isProjectionOnSameDisplay(android.view.ContentRecordingSession,android.view.ContentRecordingSession)\nclass ContentRecordingSession extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genToString=true, genSetters=true, genEqualsHashCode=true)")
+ inputSignatures = "public static final int RECORD_CONTENT_DISPLAY\npublic static final int RECORD_CONTENT_TASK\npublic static final int TARGET_UID_FULL_SCREEN\npublic static final int TARGET_UID_UNKNOWN\nprivate int mVirtualDisplayId\nprivate @android.view.ContentRecordingSession.RecordContent int mContentToRecord\nprivate int mDisplayToRecord\nprivate @android.annotation.Nullable android.os.IBinder mTokenToRecord\nprivate boolean mWaitingForConsent\nprivate int mTargetUid\npublic static android.view.ContentRecordingSession createDisplaySession(int)\npublic static android.view.ContentRecordingSession createTaskSession(android.os.IBinder)\npublic static android.view.ContentRecordingSession createTaskSession(android.os.IBinder,int)\npublic static boolean isValid(android.view.ContentRecordingSession)\npublic static boolean isProjectionOnSameDisplay(android.view.ContentRecordingSession,android.view.ContentRecordingSession)\nclass ContentRecordingSession extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genToString=true, genSetters=true, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 506945501609..17c7fcc3a7bb 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -2656,6 +2656,7 @@ public final class Display {
if (displayId == getDisplayId()) {
float newRatio = getHdrSdrRatio();
if (newRatio != mLastReportedRatio) {
+ mLastReportedRatio = newRatio;
mListener.accept(Display.this);
}
}
diff --git a/core/java/android/view/SurfaceControlRegistry.java b/core/java/android/view/SurfaceControlRegistry.java
index 67ac811287cb..0f35048cac67 100644
--- a/core/java/android/view/SurfaceControlRegistry.java
+++ b/core/java/android/view/SurfaceControlRegistry.java
@@ -78,6 +78,11 @@ public class SurfaceControlRegistry {
for (int i = 0; i < size; i++) {
final Map.Entry<SurfaceControl, Long> entry = entries.get(i);
final SurfaceControl sc = entry.getKey();
+ if (sc == null) {
+ // Just skip if the key has since been removed from the weak hash map
+ continue;
+ }
+
final long timeRegistered = entry.getValue();
pw.print(" ");
pw.print(sc.getName());
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index e9d0e4c557c6..49b16c7697c9 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -19,6 +19,8 @@ package android.view;
import static android.content.res.Resources.ID_NULL;
import static android.os.Trace.TRACE_TAG_APP;
import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP;
+import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
+import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_BOUNDS;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW;
@@ -27,6 +29,7 @@ import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ER
import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH;
import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH_ERROR_CODE;
import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API;
+import static android.view.flags.Flags.toolkitSetFrameRate;
import static android.view.flags.Flags.viewVelocityApi;
import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS;
@@ -114,6 +117,7 @@ import android.sysprop.DisplayProperties;
import android.text.InputType;
import android.text.TextUtils;
import android.util.AttributeSet;
+import android.util.DisplayMetrics;
import android.util.FloatProperty;
import android.util.LayoutDirection;
import android.util.Log;
@@ -5509,6 +5513,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private ViewTranslationResponse mViewTranslationResponse;
/**
+ * A threshold value to determine the frame rate category of the View based on the size.
+ */
+ private static final float FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD = 0.07f;
+
+ /**
* Simple constructor to use when creating a view from code.
*
* @param context The Context the view is running in, through which it can
@@ -20183,6 +20192,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
return;
}
+ // For VRR to vote the preferred frame rate
+ votePreferredFrameRate();
+
// Reset content capture caches
mPrivateFlags4 &= ~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK;
mContentCaptureSessionCached = false;
@@ -20285,6 +20297,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
protected void damageInParent() {
if (mParent != null && mAttachInfo != null) {
+ // For VRR to vote the preferred frame rate
+ votePreferredFrameRate();
mParent.onDescendantInvalidated(this, this);
}
}
@@ -32981,6 +32995,40 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
return null;
}
+ private float getSizePercentage() {
+ if (mResources == null || getAlpha() == 0 || getVisibility() != VISIBLE) {
+ return 0;
+ }
+
+ DisplayMetrics displayMetrics = mResources.getDisplayMetrics();
+ int screenSize = displayMetrics.widthPixels
+ * displayMetrics.heightPixels;
+ int viewSize = getWidth() * getHeight();
+
+ if (screenSize == 0 || viewSize == 0) {
+ return 0f;
+ }
+ return (float) viewSize / screenSize;
+ }
+
+ private int calculateFrameRateCategory() {
+ float sizePercentage = getSizePercentage();
+
+ if (sizePercentage <= FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD) {
+ return FRAME_RATE_CATEGORY_LOW;
+ } else {
+ return FRAME_RATE_CATEGORY_NORMAL;
+ }
+ }
+
+ private void votePreferredFrameRate() {
+ // use toolkitSetFrameRate flag to gate the change
+ ViewRootImpl viewRootImpl = getViewRootImpl();
+ if (toolkitSetFrameRate() && viewRootImpl != null && getSizePercentage() > 0) {
+ viewRootImpl.votePreferredFrameRateCategory(calculateFrameRateCategory());
+ }
+ }
+
/**
* Set the current velocity of the View, we only track positive value.
* We will use the velocity information to adjust the frame rate when applicable.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 939278314df4..52b7cb18259f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -24,6 +24,8 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.InputDevice.SOURCE_CLASS_NONE;
import static android.view.InsetsSource.ID_IME;
+import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
+import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
import static android.view.View.PFLAG_DRAW_ANIMATION;
import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
@@ -74,7 +76,10 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_E
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
@@ -87,6 +92,7 @@ import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
import static android.view.accessibility.Flags.forceInvertColor;
import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_FOCUS_CONTROLLER;
import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INSETS_CONTROLLER;
+import static android.view.flags.Flags.toolkitSetFrameRate;
import android.Manifest;
import android.accessibilityservice.AccessibilityService;
@@ -732,6 +738,7 @@ public final class ViewRootImpl implements ViewParent,
private SurfaceControl mBoundsLayer;
private final SurfaceSession mSurfaceSession = new SurfaceSession();
private final Transaction mTransaction = new Transaction();
+ private final Transaction mFrameRateTransaction = new Transaction();
@UnsupportedAppUsage
boolean mAdded;
@@ -955,6 +962,34 @@ public final class ViewRootImpl implements ViewParent,
private AccessibilityWindowAttributes mAccessibilityWindowAttributes;
+ /*
+ * for Variable Refresh Rate project
+ */
+
+ // The preferred frame rate category of the view that
+ // could be updated on a frame-by-frame basis.
+ private int mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
+ // The preferred frame rate category of the last frame that
+ // could be used to lower frame rate after touch boost
+ private int mLastPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
+ // The preferred frame rate of the view that is mainly used for
+ // touch boosting, view velocity handling, and TextureView.
+ private float mPreferredFrameRate = 0;
+ // Used to check if there were any view invalidations in
+ // the previous time frame (FRAME_RATE_IDLENESS_REEVALUATE_TIME).
+ private boolean mHasInvalidation = false;
+ // Used to check if it is in the touch boosting period.
+ private boolean mIsFrameRateBoosting = false;
+ // Used to check if there is a message in the message queue
+ // for idleness handling.
+ private boolean mHasIdledMessage = false;
+ // time for touch boost period.
+ private static final int FRAME_RATE_TOUCH_BOOST_TIME = 1500;
+ // time for checking idle status periodically.
+ private static final int FRAME_RATE_IDLENESS_CHECK_TIME_MILLIS = 500;
+ // time for revaluating the idle status before lowering the frame rate.
+ private static final int FRAME_RATE_IDLENESS_REEVALUATE_TIME = 500;
+
/**
* A temporary object used so relayoutWindow can return the latest SyncSeqId
* system. The SyncSeqId system was designed to work without synchronous relayout
@@ -3918,6 +3953,12 @@ public final class ViewRootImpl implements ViewParent,
mWmsRequestSyncGroupState = WMS_SYNC_NONE;
}
}
+
+ // For the variable refresh rate project.
+ setPreferredFrameRate(mPreferredFrameRate);
+ setPreferredFrameRateCategory(mPreferredFrameRateCategory);
+ mLastPreferredFrameRateCategory = mPreferredFrameRateCategory;
+ mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
}
private void createSyncIfNeeded() {
@@ -5970,6 +6011,8 @@ public final class ViewRootImpl implements ViewParent,
private static final int MSG_REPORT_KEEP_CLEAR_RECTS = 36;
private static final int MSG_PAUSED_FOR_SYNC_TIMEOUT = 37;
private static final int MSG_DECOR_VIEW_GESTURE_INTERCEPTION = 38;
+ private static final int MSG_TOUCH_BOOST_TIMEOUT = 39;
+ private static final int MSG_CHECK_INVALIDATION_IDLE = 40;
final class ViewRootHandler extends Handler {
@Override
@@ -6265,6 +6308,32 @@ public final class ViewRootImpl implements ViewParent,
mNumPausedForSync = 0;
scheduleTraversals();
break;
+ case MSG_TOUCH_BOOST_TIMEOUT:
+ /**
+ * Lower the frame rate after the boosting period (FRAME_RATE_TOUCH_BOOST_TIME).
+ */
+ mIsFrameRateBoosting = false;
+ setPreferredFrameRateCategory(Math.max(mPreferredFrameRateCategory,
+ mLastPreferredFrameRateCategory));
+ break;
+ case MSG_CHECK_INVALIDATION_IDLE:
+ if (!mHasInvalidation && !mIsFrameRateBoosting) {
+ mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
+ setPreferredFrameRateCategory(mPreferredFrameRateCategory);
+ mHasIdledMessage = false;
+ } else {
+ /**
+ * If there is no invalidation within a certain period,
+ * we consider the display is idled.
+ * We then set the frame rate catetogry to NO_PREFERENCE.
+ * Note that SurfaceFlinger also has a mechanism to lower the refresh rate
+ * if there is no updates of the buffer.
+ */
+ mHasInvalidation = false;
+ mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE,
+ FRAME_RATE_IDLENESS_REEVALUATE_TIME);
+ }
+ break;
}
}
}
@@ -7207,6 +7276,7 @@ public final class ViewRootImpl implements ViewParent,
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
+ final int action = event.getAction();
boolean handled = mHandwritingInitiator.onTouchEvent(event);
if (handled) {
// If handwriting is started, toolkit doesn't receive ACTION_UP.
@@ -7227,6 +7297,22 @@ public final class ViewRootImpl implements ViewParent,
scheduleConsumeBatchedInputImmediately();
}
}
+
+ // For the variable refresh rate project
+ if (handled && shouldTouchBoost(action, mWindowAttributes.type)) {
+ // set the frame rate to the maximum value.
+ mIsFrameRateBoosting = true;
+ setPreferredFrameRateCategory(mPreferredFrameRateCategory);
+ }
+ /**
+ * We want to lower the refresh rate when MotionEvent.ACTION_UP,
+ * MotionEvent.ACTION_CANCEL is detected.
+ * Not using ACTION_MOVE to avoid checking and sending messages too frequently.
+ */
+ if (mIsFrameRateBoosting && (action == MotionEvent.ACTION_UP
+ || action == MotionEvent.ACTION_CANCEL)) {
+ sendDelayedEmptyMessage(MSG_TOUCH_BOOST_TIMEOUT, FRAME_RATE_TOUCH_BOOST_TIME);
+ }
return handled ? FINISH_HANDLED : FORWARD;
}
@@ -11441,7 +11527,11 @@ public final class ViewRootImpl implements ViewParent,
@Override
public @SurfaceControl.BufferTransform int getBufferTransformHint() {
- return mSurfaceControl.getTransformHint();
+ if (mSurfaceControl.isValid()) {
+ return mSurfaceControl.getTransformHint();
+ } else {
+ return SurfaceControl.BUFFER_TRANSFORM_IDENTITY;
+ }
}
@Override
@@ -11810,4 +11900,94 @@ public final class ViewRootImpl implements ViewParent,
@NonNull Consumer<Boolean> listener) {
t.clearTrustedPresentationCallback(getSurfaceControl());
}
+
+ private void setPreferredFrameRateCategory(int preferredFrameRateCategory) {
+ if (!shouldSetFrameRateCategory()) {
+ return;
+ }
+
+ int frameRateCategory = mIsFrameRateBoosting
+ ? FRAME_RATE_CATEGORY_HIGH : preferredFrameRateCategory;
+
+ try {
+ mFrameRateTransaction.setFrameRateCategory(mSurfaceControl,
+ frameRateCategory, false).apply();
+ } catch (Exception e) {
+ Log.e(mTag, "Unable to set frame rate category", e);
+ }
+
+ if (mPreferredFrameRateCategory != FRAME_RATE_CATEGORY_NO_PREFERENCE && !mHasIdledMessage) {
+ // Check where the display is idled periodically.
+ // If so, set the frame rate category to NO_PREFERENCE
+ mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE,
+ FRAME_RATE_IDLENESS_CHECK_TIME_MILLIS);
+ mHasIdledMessage = true;
+ }
+ }
+
+ private void setPreferredFrameRate(float preferredFrameRate) {
+ if (!shouldSetFrameRate()) {
+ return;
+ }
+
+ try {
+ mFrameRateTransaction.setFrameRate(mSurfaceControl,
+ preferredFrameRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT).apply();
+ } catch (Exception e) {
+ Log.e(mTag, "Unable to set frame rate", e);
+ }
+ }
+
+ private void sendDelayedEmptyMessage(int message, int delayedTime) {
+ mHandler.removeMessages(message);
+
+ mHandler.sendEmptyMessageDelayed(message, delayedTime);
+ }
+
+ private boolean shouldSetFrameRateCategory() {
+ // use toolkitSetFrameRate flag to gate the change
+ return mSurface.isValid() && toolkitSetFrameRate();
+ }
+
+ private boolean shouldSetFrameRate() {
+ // use toolkitSetFrameRate flag to gate the change
+ return mPreferredFrameRate > 0 && toolkitSetFrameRate();
+ }
+
+ private boolean shouldTouchBoost(int motionEventAction, int windowType) {
+ boolean desiredAction = motionEventAction == MotionEvent.ACTION_DOWN
+ || motionEventAction == MotionEvent.ACTION_MOVE
+ || motionEventAction == MotionEvent.ACTION_UP;
+ boolean desiredType = windowType == TYPE_BASE_APPLICATION || windowType == TYPE_APPLICATION
+ || windowType == TYPE_APPLICATION_STARTING || windowType == TYPE_DRAWN_APPLICATION;
+ // use toolkitSetFrameRate flag to gate the change
+ return desiredAction && desiredType && toolkitSetFrameRate();
+ }
+
+ /**
+ * Allow Views to vote for the preferred frame rate category
+ *
+ * @param frameRateCategory the preferred frame rate category of a View
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
+ public void votePreferredFrameRateCategory(int frameRateCategory) {
+ mPreferredFrameRateCategory = Math.max(mPreferredFrameRateCategory, frameRateCategory);
+ mHasInvalidation = true;
+ }
+
+ /**
+ * Get the value of mPreferredFrameRateCategory
+ */
+ @VisibleForTesting
+ public int getPreferredFrameRateCategory() {
+ return mPreferredFrameRateCategory;
+ }
+
+ /**
+ * Get the value of mPreferredFrameRate
+ */
+ @VisibleForTesting
+ public float getPreferredFrameRate() {
+ return mPreferredFrameRate;
+ }
}
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 2241fd5dc37a..b44d6a496058 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -317,16 +317,14 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
}
}
- // Should not be possible for mComponentName to be null here but check anyway
- if (mManager.mOptions.contentProtectionOptions.enableReceiver
- && mManager.getContentProtectionEventBuffer() != null
- && mComponentName != null) {
+ if (isContentProtectionEnabled()) {
mContentProtectionEventProcessor =
new ContentProtectionEventProcessor(
mManager.getContentProtectionEventBuffer(),
mHandler,
mSystemServerInterface,
- mComponentName.getPackageName());
+ mComponentName.getPackageName(),
+ mManager.mOptions.contentProtectionOptions);
} else {
mContentProtectionEventProcessor = null;
}
@@ -956,4 +954,15 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
private boolean isContentCaptureReceiverEnabled() {
return mManager.mOptions.enableReceiver;
}
+
+ @UiThread
+ private boolean isContentProtectionEnabled() {
+ // Should not be possible for mComponentName to be null here but check anyway
+ // Should not be possible for groups to be empty if receiver is enabled but check anyway
+ return mManager.mOptions.contentProtectionOptions.enableReceiver
+ && mManager.getContentProtectionEventBuffer() != null
+ && mComponentName != null
+ && (!mManager.mOptions.contentProtectionOptions.requiredGroups.isEmpty()
+ || !mManager.mOptions.contentProtectionOptions.optionalGroups.isEmpty());
+ }
}
diff --git a/core/java/android/view/contentprotection/ContentProtectionEventProcessor.java b/core/java/android/view/contentprotection/ContentProtectionEventProcessor.java
index b44abf3eb04d..aaf90bd00535 100644
--- a/core/java/android/view/contentprotection/ContentProtectionEventProcessor.java
+++ b/core/java/android/view/contentprotection/ContentProtectionEventProcessor.java
@@ -19,9 +19,9 @@ package android.view.contentprotection;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UiThread;
+import android.content.ContentCaptureOptions;
import android.content.pm.ParceledListSlice;
import android.os.Handler;
-import android.text.InputType;
import android.util.Log;
import android.view.contentcapture.ContentCaptureEvent;
import android.view.contentcapture.IContentCaptureManager;
@@ -33,10 +33,10 @@ import com.android.internal.util.RingBuffer;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
+import java.util.Collection;
import java.util.List;
import java.util.Set;
+import java.util.stream.Stream;
/**
* Main entry point for processing {@link ContentCaptureEvent} for the content protection flow.
@@ -47,33 +47,13 @@ public class ContentProtectionEventProcessor {
private static final String TAG = "ContentProtectionEventProcessor";
- private static final List<Integer> PASSWORD_FIELD_INPUT_TYPES =
- Collections.unmodifiableList(
- Arrays.asList(
- InputType.TYPE_NUMBER_VARIATION_PASSWORD,
- InputType.TYPE_TEXT_VARIATION_PASSWORD,
- InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD,
- InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD));
-
- private static final List<String> PASSWORD_TEXTS =
- Collections.unmodifiableList(
- Arrays.asList("password", "pass word", "code", "pin", "credential"));
-
- private static final List<String> ADDITIONAL_SUSPICIOUS_TEXTS =
- Collections.unmodifiableList(
- Arrays.asList("user", "mail", "phone", "number", "login", "log in", "sign in"));
-
private static final Duration MIN_DURATION_BETWEEN_FLUSHING = Duration.ofSeconds(3);
- private static final String ANDROID_CLASS_NAME_PREFIX = "android.";
-
private static final Set<Integer> EVENT_TYPES_TO_STORE =
- Collections.unmodifiableSet(
- new HashSet<>(
- Arrays.asList(
- ContentCaptureEvent.TYPE_VIEW_APPEARED,
- ContentCaptureEvent.TYPE_VIEW_DISAPPEARED,
- ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED)));
+ Set.of(
+ ContentCaptureEvent.TYPE_VIEW_APPEARED,
+ ContentCaptureEvent.TYPE_VIEW_DISAPPEARED,
+ ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED);
private static final int RESET_LOGIN_TOTAL_EVENTS_TO_PROCESS = 150;
@@ -85,11 +65,7 @@ public class ContentProtectionEventProcessor {
@NonNull private final String mPackageName;
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
- public boolean mPasswordFieldDetected = false;
-
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
- public boolean mSuspiciousTextDetected = false;
+ @NonNull private final ContentCaptureOptions.ContentProtectionOptions mOptions;
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
@Nullable
@@ -97,15 +73,32 @@ public class ContentProtectionEventProcessor {
private int mResetLoginRemainingEventsToProcess;
+ private boolean mAnyGroupFound = false;
+
+ // Ordered by priority
+ private final List<SearchGroup> mGroupsRequired;
+
+ // Ordered by priority
+ private final List<SearchGroup> mGroupsOptional;
+
+ // Ordered by priority
+ private final List<SearchGroup> mGroupsAll;
+
public ContentProtectionEventProcessor(
@NonNull RingBuffer<ContentCaptureEvent> eventBuffer,
@NonNull Handler handler,
@NonNull IContentCaptureManager contentCaptureManager,
- @NonNull String packageName) {
+ @NonNull String packageName,
+ @NonNull ContentCaptureOptions.ContentProtectionOptions options) {
mEventBuffer = eventBuffer;
mHandler = handler;
mContentCaptureManager = contentCaptureManager;
mPackageName = packageName;
+ mOptions = options;
+ mGroupsRequired = options.requiredGroups.stream().map(SearchGroup::new).toList();
+ mGroupsOptional = options.optionalGroups.stream().map(SearchGroup::new).toList();
+ mGroupsAll =
+ Stream.of(mGroupsRequired, mGroupsOptional).flatMap(Collection::stream).toList();
}
/** Main entry point for {@link ContentCaptureEvent} processing. */
@@ -130,9 +123,31 @@ public class ContentProtectionEventProcessor {
@UiThread
private void processViewAppearedEvent(@NonNull ContentCaptureEvent event) {
- mPasswordFieldDetected |= isPasswordField(event);
- mSuspiciousTextDetected |= isSuspiciousText(event);
- if (mPasswordFieldDetected && mSuspiciousTextDetected) {
+ ViewNode viewNode = event.getViewNode();
+ String eventText = ContentProtectionUtils.getEventTextLower(event);
+ String viewNodeText = ContentProtectionUtils.getViewNodeTextLower(viewNode);
+ String hintText = ContentProtectionUtils.getHintTextLower(viewNode);
+
+ mGroupsAll.stream()
+ .filter(group -> !group.mFound)
+ .filter(
+ group ->
+ group.matches(eventText)
+ || group.matches(viewNodeText)
+ || group.matches(hintText))
+ .findFirst()
+ .ifPresent(
+ group -> {
+ group.mFound = true;
+ mAnyGroupFound = true;
+ });
+
+ boolean loginDetected =
+ mGroupsRequired.stream().allMatch(group -> group.mFound)
+ && mGroupsOptional.stream().filter(group -> group.mFound).count()
+ >= mOptions.optionalGroupsThreshold;
+
+ if (loginDetected) {
loginDetected();
} else {
maybeResetLoginFlags();
@@ -150,14 +165,13 @@ public class ContentProtectionEventProcessor {
@UiThread
private void resetLoginFlags() {
- mPasswordFieldDetected = false;
- mSuspiciousTextDetected = false;
- mResetLoginRemainingEventsToProcess = 0;
+ mGroupsAll.forEach(group -> group.mFound = false);
+ mAnyGroupFound = false;
}
@UiThread
private void maybeResetLoginFlags() {
- if (mPasswordFieldDetected || mSuspiciousTextDetected) {
+ if (mAnyGroupFound) {
if (mResetLoginRemainingEventsToProcess <= 0) {
mResetLoginRemainingEventsToProcess = RESET_LOGIN_TOTAL_EVENTS_TO_PROCESS;
} else {
@@ -194,61 +208,21 @@ public class ContentProtectionEventProcessor {
}
}
- private boolean isPasswordField(@NonNull ContentCaptureEvent event) {
- return isPasswordField(event.getViewNode());
- }
-
- private boolean isPasswordField(@Nullable ViewNode viewNode) {
- if (viewNode == null) {
- return false;
- }
- return isAndroidPasswordField(viewNode) || isWebViewPasswordField(viewNode);
- }
-
- private boolean isAndroidPasswordField(@NonNull ViewNode viewNode) {
- if (!isAndroidViewNode(viewNode)) {
- return false;
- }
- int inputType = viewNode.getInputType();
- return PASSWORD_FIELD_INPUT_TYPES.stream()
- .anyMatch(passwordInputType -> (inputType & passwordInputType) != 0);
- }
+ private static final class SearchGroup {
- private boolean isWebViewPasswordField(@NonNull ViewNode viewNode) {
- if (viewNode.getClassName() != null) {
- return false;
- }
- return isPasswordText(ContentProtectionUtils.getViewNodeText(viewNode));
- }
+ @NonNull private final List<String> mSearchStrings;
- private boolean isAndroidViewNode(@NonNull ViewNode viewNode) {
- String className = viewNode.getClassName();
- return className != null && className.startsWith(ANDROID_CLASS_NAME_PREFIX);
- }
-
- private boolean isSuspiciousText(@NonNull ContentCaptureEvent event) {
- return isSuspiciousText(ContentProtectionUtils.getEventText(event))
- || isSuspiciousText(ContentProtectionUtils.getViewNodeText(event));
- }
+ public boolean mFound = false;
- private boolean isSuspiciousText(@Nullable String text) {
- if (text == null) {
- return false;
+ SearchGroup(@NonNull List<String> searchStrings) {
+ mSearchStrings = searchStrings;
}
- if (isPasswordText(text)) {
- return true;
- }
- String lowerCaseText = text.toLowerCase();
- return ADDITIONAL_SUSPICIOUS_TEXTS.stream()
- .anyMatch(suspiciousText -> lowerCaseText.contains(suspiciousText));
- }
- private boolean isPasswordText(@Nullable String text) {
- if (text == null) {
- return false;
+ public boolean matches(@Nullable String text) {
+ if (text == null) {
+ return false;
+ }
+ return mSearchStrings.stream().anyMatch(text::contains);
}
- String lowerCaseText = text.toLowerCase();
- return PASSWORD_TEXTS.stream()
- .anyMatch(passwordText -> lowerCaseText.contains(passwordText));
}
}
diff --git a/core/java/android/view/contentprotection/ContentProtectionUtils.java b/core/java/android/view/contentprotection/ContentProtectionUtils.java
index 9abf6f10d05d..1ecac7f188ab 100644
--- a/core/java/android/view/contentprotection/ContentProtectionUtils.java
+++ b/core/java/android/view/contentprotection/ContentProtectionUtils.java
@@ -28,33 +28,39 @@ import android.view.contentcapture.ViewNode;
*/
public final class ContentProtectionUtils {
- /** Returns the text extracted directly from the {@link ContentCaptureEvent}, if set. */
+ /** Returns the lowercase text extracted from the {@link ContentCaptureEvent}, if set. */
@Nullable
- public static String getEventText(@NonNull ContentCaptureEvent event) {
+ public static String getEventTextLower(@NonNull ContentCaptureEvent event) {
CharSequence text = event.getText();
if (text == null) {
return null;
}
- return text.toString();
+ return text.toString().toLowerCase();
}
- /** Returns the text extracted from the event's {@link ViewNode}, if set. */
+ /** Returns the lowercase text extracted from the {@link ViewNode}, if set. */
@Nullable
- public static String getViewNodeText(@NonNull ContentCaptureEvent event) {
- ViewNode viewNode = event.getViewNode();
+ public static String getViewNodeTextLower(@Nullable ViewNode viewNode) {
if (viewNode == null) {
return null;
}
- return getViewNodeText(viewNode);
+ CharSequence text = viewNode.getText();
+ if (text == null) {
+ return null;
+ }
+ return text.toString().toLowerCase();
}
- /** Returns the text extracted directly from the {@link ViewNode}, if set. */
+ /** Returns the lowercase hint text extracted from the {@link ViewNode}, if set. */
@Nullable
- public static String getViewNodeText(@NonNull ViewNode viewNode) {
- CharSequence text = viewNode.getText();
+ public static String getHintTextLower(@Nullable ViewNode viewNode) {
+ if (viewNode == null) {
+ return null;
+ }
+ String text = viewNode.getHint();
if (text == null) {
return null;
}
- return text.toString();
+ return text.toLowerCase();
}
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 8159af3ddd4a..eeab005771f5 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -39,6 +39,7 @@ import android.Manifest;
import android.annotation.DisplayContext;
import android.annotation.DrawableRes;
import android.annotation.DurationMillisLong;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -1512,6 +1513,7 @@ public final class InputMethodManager {
* Returns {@code true} if currently selected IME supports Stylus handwriting & is enabled.
* If the method returns {@code false}, {@link #startStylusHandwriting(View)} shouldn't be
* called and Stylus touch should continue as normal touch input.
+ *
* @see #startStylusHandwriting(View)
*/
public boolean isStylusHandwritingAvailable() {
@@ -1535,6 +1537,7 @@ public final class InputMethodManager {
@NonNull
@RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
@TestApi
+ @FlaggedApi(Flags.FLAG_IMM_USERHANDLE_HOSTSIDETESTS)
@SuppressLint("UserHandle")
public boolean isStylusHandwritingAvailableAsUser(@NonNull UserHandle user) {
final Context fallbackContext = ActivityThread.currentApplication();
@@ -1655,6 +1658,7 @@ public final class InputMethodManager {
@NonNull
@RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
@TestApi
+ @FlaggedApi(Flags.FLAG_IMM_USERHANDLE_HOSTSIDETESTS)
@SuppressLint("UserHandle")
public List<InputMethodInfo> getEnabledInputMethodListAsUser(@NonNull UserHandle user) {
return IInputMethodManagerGlobalInvoker.getEnabledInputMethodList(user.getIdentifier());
@@ -1690,12 +1694,13 @@ public final class InputMethodManager {
* {@link Manifest.permission#INTERACT_ACROSS_USERS_FULL} is required if this is
* different from the calling process user ID.
* @return {@link List} of {@link InputMethodSubtype}.
- * @see #getEnabledInputMethodListAsUser(int)
+ * @see #getEnabledInputMethodListAsUser(UserHandle)
* @hide
*/
@NonNull
@RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
@TestApi
+ @FlaggedApi(Flags.FLAG_IMM_USERHANDLE_HOSTSIDETESTS)
@SuppressLint("UserHandle")
public List<InputMethodSubtype> getEnabledInputMethodSubtypeListAsUser(
@NonNull String imeId, boolean allowsImplicitlyEnabledSubtypes,
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index c14b5104242a..1e8718ce42c0 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -14,4 +14,12 @@ flag {
description: "Feature flag for adding EditorInfo#mStylusHandwritingEnabled"
bug: "293898187"
is_fixed_read_only: true
+}
+
+flag {
+ name: "imm_userhandle_hostsidetests"
+ namespace: "input_method"
+ description: "Feature flag for replacing UserIdInt with UserHandle in some helper IMM functions"
+ bug: "301713309"
+ is_fixed_read_only: true
} \ No newline at end of file
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index e8281eac5928..17c82b63c443 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -15555,6 +15555,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private void ensureIterableTextForAccessibilitySelectable() {
if (!(mText instanceof Spannable)) {
setText(mText, BufferType.SPANNABLE);
+ if (getLayout() == null) {
+ assumeLayout();
+ }
}
}
diff --git a/core/java/android/window/TaskFragmentOperation.java b/core/java/android/window/TaskFragmentOperation.java
index 43fa0be6c1b7..4e0f9a51c0a0 100644
--- a/core/java/android/window/TaskFragmentOperation.java
+++ b/core/java/android/window/TaskFragmentOperation.java
@@ -88,6 +88,26 @@ public final class TaskFragmentOperation implements Parcelable {
*/
public static final int OP_TYPE_SET_ISOLATED_NAVIGATION = 11;
+ /**
+ * Reorders the TaskFragment to be the bottom-most in the Task. Note that this op will bring the
+ * TaskFragment to the bottom of the Task below all the other Activities and TaskFragments.
+ *
+ * This is only allowed for system organizers. See
+ * {@link com.android.server.wm.TaskFragmentOrganizerController#registerOrganizer(
+ * ITaskFragmentOrganizer, boolean)}
+ */
+ public static final int OP_TYPE_REORDER_TO_BOTTOM_OF_TASK = 12;
+
+ /**
+ * Reorders the TaskFragment to be the top-most in the Task. Note that this op will bring the
+ * TaskFragment to the top of the Task above all the other Activities and TaskFragments.
+ *
+ * This is only allowed for system organizers. See
+ * {@link com.android.server.wm.TaskFragmentOrganizerController#registerOrganizer(
+ * ITaskFragmentOrganizer, boolean)}
+ */
+ public static final int OP_TYPE_REORDER_TO_TOP_OF_TASK = 13;
+
@IntDef(prefix = { "OP_TYPE_" }, value = {
OP_TYPE_UNKNOWN,
OP_TYPE_CREATE_TASK_FRAGMENT,
@@ -101,7 +121,9 @@ public final class TaskFragmentOperation implements Parcelable {
OP_TYPE_SET_ANIMATION_PARAMS,
OP_TYPE_SET_RELATIVE_BOUNDS,
OP_TYPE_REORDER_TO_FRONT,
- OP_TYPE_SET_ISOLATED_NAVIGATION
+ OP_TYPE_SET_ISOLATED_NAVIGATION,
+ OP_TYPE_REORDER_TO_BOTTOM_OF_TASK,
+ OP_TYPE_REORDER_TO_TOP_OF_TASK,
})
@Retention(RetentionPolicy.SOURCE)
public @interface OperationType {}
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 61f340a856c4..1a2d202be934 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -402,6 +402,18 @@ public final class TransitionInfo implements Parcelable {
@Override
public String toString() {
+ return toString("");
+ }
+
+ /**
+ * Returns a string representation of this transition info.
+ * @hide
+ */
+ public String toString(@NonNull String prefix) {
+ final boolean shouldPrettyPrint = !prefix.isEmpty() && !mChanges.isEmpty();
+ final String innerPrefix = shouldPrettyPrint ? prefix + " " : "";
+ final String changesLineStart = shouldPrettyPrint ? "\n" + prefix : "";
+ final String perChangeLineStart = shouldPrettyPrint ? "\n" + innerPrefix : "";
StringBuilder sb = new StringBuilder();
sb.append("{id=").append(mDebugId).append(" t=").append(transitTypeToString(mType))
.append(" f=0x").append(Integer.toHexString(mFlags)).append(" trk=").append(mTrack)
@@ -413,12 +425,15 @@ public final class TransitionInfo implements Parcelable {
sb.append(mRoots.get(i).mDisplayId).append("@").append(mRoots.get(i).mOffset);
}
sb.append("] c=[");
+ sb.append(perChangeLineStart);
for (int i = 0; i < mChanges.size(); ++i) {
if (i > 0) {
sb.append(',');
+ sb.append(perChangeLineStart);
}
sb.append(mChanges.get(i));
}
+ sb.append(changesLineStart);
sb.append("]}");
return sb.toString();
}
diff --git a/core/java/android/window/TransitionRequestInfo.java b/core/java/android/window/TransitionRequestInfo.java
index 932608a3b57b..bd54e14bc996 100644
--- a/core/java/android/window/TransitionRequestInfo.java
+++ b/core/java/android/window/TransitionRequestInfo.java
@@ -62,13 +62,16 @@ public final class TransitionRequestInfo implements Parcelable {
/** The transition flags known at the time of the request. These may not be complete. */
private final int mFlags;
+ /** This is only a BEST-EFFORT id used for log correlation. DO NOT USE for any real work! */
+ private final int mDebugId;
+
/** constructor override */
public TransitionRequestInfo(
@WindowManager.TransitionType int type,
@Nullable ActivityManager.RunningTaskInfo triggerTask,
@Nullable RemoteTransition remoteTransition) {
this(type, triggerTask, null /* pipTask */,
- remoteTransition, null /* displayChange */, 0 /* flags */);
+ remoteTransition, null /* displayChange */, 0 /* flags */, -1 /* debugId */);
}
/** constructor override */
@@ -78,16 +81,29 @@ public final class TransitionRequestInfo implements Parcelable {
@Nullable RemoteTransition remoteTransition,
int flags) {
this(type, triggerTask, null /* pipTask */,
- remoteTransition, null /* displayChange */, flags);
+ remoteTransition, null /* displayChange */, flags, -1 /* debugId */);
}
+ /** constructor override */
public TransitionRequestInfo(
@WindowManager.TransitionType int type,
@Nullable ActivityManager.RunningTaskInfo triggerTask,
@Nullable RemoteTransition remoteTransition,
@Nullable TransitionRequestInfo.DisplayChange displayChange,
int flags) {
- this(type, triggerTask, null /* pipTask */, remoteTransition, displayChange, flags);
+ this(type, triggerTask, null /* pipTask */, remoteTransition, displayChange, flags,
+ -1 /* debugId */);
+ }
+
+ /** constructor override */
+ public TransitionRequestInfo(
+ @WindowManager.TransitionType int type,
+ @Nullable ActivityManager.RunningTaskInfo triggerTask,
+ @Nullable ActivityManager.RunningTaskInfo pipTask,
+ @Nullable RemoteTransition remoteTransition,
+ @Nullable TransitionRequestInfo.DisplayChange displayChange,
+ int flags) {
+ this(type, triggerTask, pipTask, remoteTransition, displayChange, flags, -1 /* debugId */);
}
/** @hide */
@@ -270,7 +286,7 @@ public final class TransitionRequestInfo implements Parcelable {
};
@DataClass.Generated(
- time = 1695667226050L,
+ time = 1697564781403L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/window/TransitionRequestInfo.java",
inputSignatures = "private final int mDisplayId\nprivate @android.annotation.Nullable android.graphics.Rect mStartAbsBounds\nprivate @android.annotation.Nullable android.graphics.Rect mEndAbsBounds\nprivate int mStartRotation\nprivate int mEndRotation\nprivate boolean mPhysicalDisplayChanged\nclass DisplayChange extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genBuilder=false, genConstructor=false)")
@@ -318,6 +334,8 @@ public final class TransitionRequestInfo implements Parcelable {
* (if size is changing).
* @param flags
* The transition flags known at the time of the request. These may not be complete.
+ * @param debugId
+ * This is only a BEST-EFFORT id used for log correlation. DO NOT USE for any real work!
*/
@DataClass.Generated.Member
public TransitionRequestInfo(
@@ -326,7 +344,8 @@ public final class TransitionRequestInfo implements Parcelable {
@Nullable ActivityManager.RunningTaskInfo pipTask,
@Nullable RemoteTransition remoteTransition,
@Nullable TransitionRequestInfo.DisplayChange displayChange,
- int flags) {
+ int flags,
+ int debugId) {
this.mType = type;
com.android.internal.util.AnnotationValidations.validate(
WindowManager.TransitionType.class, null, mType);
@@ -335,6 +354,7 @@ public final class TransitionRequestInfo implements Parcelable {
this.mRemoteTransition = remoteTransition;
this.mDisplayChange = displayChange;
this.mFlags = flags;
+ this.mDebugId = debugId;
// onConstructed(); // You can define this method to get a callback
}
@@ -392,6 +412,14 @@ public final class TransitionRequestInfo implements Parcelable {
}
/**
+ * This is only a BEST-EFFORT id used for log correlation. DO NOT USE for any real work!
+ */
+ @DataClass.Generated.Member
+ public int getDebugId() {
+ return mDebugId;
+ }
+
+ /**
* If non-null, the task containing the activity whose lifecycle change (start or
* finish) has caused this transition to occur.
*/
@@ -443,7 +471,8 @@ public final class TransitionRequestInfo implements Parcelable {
"pipTask = " + mPipTask + ", " +
"remoteTransition = " + mRemoteTransition + ", " +
"displayChange = " + mDisplayChange + ", " +
- "flags = " + mFlags +
+ "flags = " + mFlags + ", " +
+ "debugId = " + mDebugId +
" }";
}
@@ -465,6 +494,7 @@ public final class TransitionRequestInfo implements Parcelable {
if (mRemoteTransition != null) dest.writeTypedObject(mRemoteTransition, flags);
if (mDisplayChange != null) dest.writeTypedObject(mDisplayChange, flags);
dest.writeInt(mFlags);
+ dest.writeInt(mDebugId);
}
@Override
@@ -485,6 +515,7 @@ public final class TransitionRequestInfo implements Parcelable {
RemoteTransition remoteTransition = (flg & 0x8) == 0 ? null : (RemoteTransition) in.readTypedObject(RemoteTransition.CREATOR);
TransitionRequestInfo.DisplayChange displayChange = (flg & 0x10) == 0 ? null : (TransitionRequestInfo.DisplayChange) in.readTypedObject(TransitionRequestInfo.DisplayChange.CREATOR);
int flags = in.readInt();
+ int debugId = in.readInt();
this.mType = type;
com.android.internal.util.AnnotationValidations.validate(
@@ -494,6 +525,7 @@ public final class TransitionRequestInfo implements Parcelable {
this.mRemoteTransition = remoteTransition;
this.mDisplayChange = displayChange;
this.mFlags = flags;
+ this.mDebugId = debugId;
// onConstructed(); // You can define this method to get a callback
}
@@ -513,10 +545,10 @@ public final class TransitionRequestInfo implements Parcelable {
};
@DataClass.Generated(
- time = 1695667226088L,
+ time = 1697564781438L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/window/TransitionRequestInfo.java",
- inputSignatures = "private final @android.view.WindowManager.TransitionType int mType\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mTriggerTask\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mPipTask\nprivate @android.annotation.Nullable android.window.RemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.window.TransitionRequestInfo.DisplayChange mDisplayChange\nprivate final int mFlags\n java.lang.String typeToString()\nclass TransitionRequestInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
+ inputSignatures = "private final @android.view.WindowManager.TransitionType int mType\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mTriggerTask\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mPipTask\nprivate @android.annotation.Nullable android.window.RemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.window.TransitionRequestInfo.DisplayChange mDisplayChange\nprivate final int mFlags\nprivate final int mDebugId\n java.lang.String typeToString()\nclass TransitionRequestInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 392aa1b13e0e..73778900399c 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -20,4 +20,20 @@ flag {
description: "Refactor dim to fix flickers"
bug: "281632483,295291019"
is_fixed_read_only: true
+}
+
+flag {
+ name: "transit_ready_tracking"
+ namespace: "windowing_frontend"
+ description: "Enable accurate transition readiness tracking"
+ bug: "294925498"
+}
+
+
+flag {
+ name: "wallpaper_offset_async"
+ namespace: "windowing_frontend"
+ description: "Do not synchronise the wallpaper offset"
+ bug: "293248754"
+ is_fixed_read_only: true
} \ No newline at end of file
diff --git a/core/java/com/android/internal/policy/WearGestureInterceptionDetector.java b/core/java/com/android/internal/policy/WearGestureInterceptionDetector.java
index 6fd50180e78b..504928cd4347 100644
--- a/core/java/com/android/internal/policy/WearGestureInterceptionDetector.java
+++ b/core/java/com/android/internal/policy/WearGestureInterceptionDetector.java
@@ -74,16 +74,15 @@ public class WearGestureInterceptionDetector {
return windowSwipeToDismiss;
}
- private boolean isPointerIndexValid(MotionEvent ev) {
+ private int getIndexForValidPointer(MotionEvent ev) {
int pointerIndex = ev.findPointerIndex(mActivePointerId);
if (pointerIndex == -1) {
if (DEBUG) {
Log.e(TAG, "Invalid pointer index: ignoring.");
}
mDiscardIntercept = true;
- return false;
}
- return true;
+ return pointerIndex;
}
private void updateSwiping(MotionEvent ev) {
@@ -98,7 +97,7 @@ public class WearGestureInterceptionDetector {
}
}
- private void updateDiscardIntercept(MotionEvent ev) {
+ private void updateDiscardIntercept(MotionEvent ev, int pointerIndex) {
if (!mSwiping) {
// Don't look at canScroll until we have passed the touch slop
return;
@@ -107,8 +106,8 @@ public class WearGestureInterceptionDetector {
return;
}
final boolean checkLeft = mDownX < ev.getRawX();
- final float x = ev.getX(mActivePointerId);
- final float y = ev.getY(mActivePointerId);
+ final float x = ev.getX(pointerIndex);
+ final float y = ev.getY(pointerIndex);
if (canScroll(mInstalledDecorView, false, checkLeft, x, y)) {
mDiscardIntercept = true;
}
@@ -152,11 +151,12 @@ public class WearGestureInterceptionDetector {
if (mDiscardIntercept) {
break;
}
- if (!isPointerIndexValid(ev)) {
+ final int pointerIndex = getIndexForValidPointer(ev);
+ if (pointerIndex == -1) {
break;
}
updateSwiping(ev);
- updateDiscardIntercept(ev);
+ updateDiscardIntercept(ev, pointerIndex);
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index deb138fda867..9c883d18a9ae 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -265,6 +265,18 @@ static jboolean nativeIsDataInjectionEnabled(JNIEnv *_env, jclass _this, jlong s
return mgr->isDataInjectionEnabled();
}
+static jboolean nativeIsReplayDataInjectionEnabled(JNIEnv *_env, jclass _this,
+ jlong sensorManager) {
+ SensorManager *mgr = reinterpret_cast<SensorManager *>(sensorManager);
+ return mgr->isReplayDataInjectionEnabled();
+}
+
+static jboolean nativeIsHalBypassReplayDataInjectionEnabled(JNIEnv *_env, jclass _this,
+ jlong sensorManager) {
+ SensorManager *mgr = reinterpret_cast<SensorManager *>(sensorManager);
+ return mgr->isHalBypassReplayDataInjectionEnabled();
+}
+
static jint nativeCreateDirectChannel(JNIEnv *_env, jclass _this, jlong sensorManager,
jint deviceId, jlong size, jint channelType, jint fd,
jobject hardwareBufferObj) {
@@ -533,6 +545,11 @@ static const JNINativeMethod gSystemSensorManagerMethods[] = {
{"nativeIsDataInjectionEnabled", "(J)Z", (void *)nativeIsDataInjectionEnabled},
+ {"nativeIsReplayDataInjectionEnabled", "(J)Z", (void *)nativeIsReplayDataInjectionEnabled},
+
+ {"nativeIsHalBypassReplayDataInjectionEnabled", "(J)Z",
+ (void *)nativeIsHalBypassReplayDataInjectionEnabled},
+
{"nativeCreateDirectChannel", "(JIJIILandroid/hardware/HardwareBuffer;)I",
(void *)nativeCreateDirectChannel},
diff --git a/core/proto/android/service/package.proto b/core/proto/android/service/package.proto
index eb14db070f9f..068f4dd07ccb 100644
--- a/core/proto/android/service/package.proto
+++ b/core/proto/android/service/package.proto
@@ -115,6 +115,9 @@ message PackageProto {
// Only set if the app defined a monochrome icon.
optional string monochrome_icon_bitmap_path = 3;
+
+ // The component name of the original activity (pre-archival).
+ optional string original_component_name = 4;
}
/** Information about main activities. */
diff --git a/core/res/Android.bp b/core/res/Android.bp
index b71995f899c5..4e686b7ad80c 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -104,6 +104,7 @@ genrule {
android_app {
name: "framework-res",
+ use_resource_processor: false,
sdk_version: "core_platform",
certificate: "platform",
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index adf93f6fd1b2..6daa5b934284 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -6124,7 +6124,7 @@
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
<!-- @SystemApi @hide
- @FlaggedApi("backstage_power.report_usage_stats_permission")
+ @FlaggedApi("android.app.usage.report_usage_stats_permission")
Allows trusted system components to report events to UsageStatsManager -->
<permission android:name="android.permission.REPORT_USAGE_STATS"
android:protectionLevel="signature|module" />
@@ -7144,7 +7144,7 @@
{@link ActivityOptions#makeRemoteAnimation}
@hide <p>Not for use by third-party applications. -->
<permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|recents" />
<!-- Allows an application to watch changes and/or active state of app ops.
@hide <p>Not for use by third-party applications. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b211ac2fd316..5a1f2d1ae680 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3988,6 +3988,13 @@
limit is unknown. -->
<item name="config_hapticChannelMaxVibrationAmplitude" format="float" type="dimen">0</item>
+ <!-- The fixed keyboard vibration strength in [0,1], or -1 to indicate the strength not fixed
+ and should depend on the touch feedback intensity user setting -->
+ <item name="config_keyboardHapticFeedbackFixedAmplitude" format="float" type="dimen">-1</item>
+
+ <!-- The default value for keyboard vibration toggle in settings. -->
+ <bool name="config_defaultKeyboardVibrationEnabled">true</bool>
+
<!-- If the device should still vibrate even in low power mode, for certain priority vibrations
(e.g. accessibility, alarms). This is mainly for Wear devices that don't have speakers. -->
<bool name="config_allowPriorityVibrationsInLowPowerMode">false</bool>
@@ -5541,6 +5548,14 @@
<!-- Add packages here -->
</string-array>
+ <!-- Enable pause wallpaper rendering upon state change such as app launch -->
+ <bool name="config_pauseWallpaperRenderWhenStateChangeEnabled">false</bool>
+
+ <!-- The list of packages to pause wallpaper rendering upon state change such as app launch -->
+ <string-array name="pause_wallpaper_render_when_state_change" translatable="false">
+ <!-- Add packages here -->
+ </string-array>
+
<!-- Whether or not to hide the navigation bar when the soft keyboard is visible in order to
create additional screen real estate outside beyond the keyboard. Note that the user needs
to have a confirmed way to dismiss the keyboard when desired. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 76744ea67589..8e1c09ede581 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2053,6 +2053,8 @@
<java-symbol type="integer" name="config_previousVibrationsDumpAggregationTimeMillisLimit" />
<java-symbol type="integer" name="config_defaultVibrationAmplitude" />
<java-symbol type="dimen" name="config_hapticChannelMaxVibrationAmplitude" />
+ <java-symbol type="dimen" name="config_keyboardHapticFeedbackFixedAmplitude" />
+ <java-symbol type="bool" name="config_defaultKeyboardVibrationEnabled" />
<java-symbol type="integer" name="config_vibrationWaveformRampStepDuration" />
<java-symbol type="bool" name="config_ignoreVibrationsOnWirelessCharger" />
<java-symbol type="integer" name="config_vibrationWaveformRampDownDuration" />
@@ -4306,6 +4308,8 @@
<java-symbol type="string" name="config_factoryResetPackage" />
<java-symbol type="array" name="config_highRefreshRateBlacklist" />
<java-symbol type="array" name="config_forceSlowJpegModeList" />
+ <java-symbol type="array" name="pause_wallpaper_render_when_state_change" />
+ <java-symbol type="bool" name="config_pauseWallpaperRenderWhenStateChangeEnabled" />
<java-symbol type="array" name="config_smallAreaDetectionAllowlist" />
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index af8c69ea1441..3a2e50aa06e8 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -54,6 +54,9 @@
<!-- Azerbaijan: 4-5 digits, known premium codes listed -->
<shortcode country="az" pattern="\\d{4,5}" premium="330[12]|87744|901[234]|93(?:94|101)|9426|9525" />
+ <!-- Bangladesh: 1-5 digits (standard system default, not country specific) -->
+ <shortcode country="bd" pattern="\\d{1,5}" free="16672" />
+
<!-- Belgium: 4 digits, plus EU: http://www.mobileweb.be/en/mobileweb/sms-numberplan.asp -->
<shortcode country="be" premium="\\d{4}" free="8\\d{3}|116\\d{3}" />
@@ -145,7 +148,7 @@
<shortcode country="in" pattern="\\d{1,5}" free="59336|53969" />
<!-- Indonesia: 1-5 digits (standard system default, not country specific) -->
- <shortcode country="id" pattern="\\d{1,5}" free="99477|6006|46645|363" />
+ <shortcode country="id" pattern="\\d{1,5}" free="99477|6006|46645|363|93457" />
<!-- Ireland: 5 digits, 5xxxx (50xxx=free, 5[12]xxx=standard), plus EU:
http://www.comreg.ie/_fileupload/publications/ComReg1117.pdf -->
@@ -190,7 +193,7 @@
<shortcode country="mk" pattern="\\d{1,6}" free="129005|122" />
<!-- Mexico: 4-5 digits (not confirmed), known premium codes listed -->
- <shortcode country="mx" pattern="\\d{4,5}" premium="53035|7766" free="26259|46645|50025|50052|5050|76551|88778|9963|91101" />
+ <shortcode country="mx" pattern="\\d{4,5}" premium="53035|7766" free="26259|46645|50025|50052|5050|76551|88778|9963|91101|45453" />
<!-- Malaysia: 5 digits: http://www.skmm.gov.my/attachment/Consumer_Regulation/Mobile_Content_Services_FAQs.pdf -->
<shortcode country="my" pattern="\\d{5}" premium="32298|33776" free="22099|28288|66668" />
@@ -205,7 +208,7 @@
<shortcode country="no" pattern="\\d{4,5}" premium="2201|222[67]" free="2171" />
<!-- New Zealand: 3-4 digits, known premium codes listed -->
- <shortcode country="nz" pattern="\\d{3,4}" premium="3903|8995|4679" free="1737|176|2141|3067|3068|3110|4006|4053|4061|4062|4202|4300|4334|4412|4575|5626|8006|8681" />
+ <shortcode country="nz" pattern="\\d{3,4}" premium="3903|8995|4679" free="1737|176|2141|3067|3068|3110|3876|4006|4053|4061|4062|4202|4300|4334|4412|4575|5626|8006|8681" />
<!-- Peru: 4-5 digits (not confirmed), known premium codes listed -->
<shortcode country="pe" pattern="\\d{4,5}" free="9963|40778" />
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 2993a0e63228..445ddf52bf1c 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -65,6 +65,7 @@ android_test {
"device-time-shell-utils",
"testables",
"com.android.text.flags-aconfig-java",
+ "flag-junit",
],
libs: [
@@ -75,6 +76,7 @@ android_test {
"framework",
"ext",
"framework-res",
+ "android.view.flags-aconfig-java",
],
jni_libs: [
"libpowermanagertest_jni",
diff --git a/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java b/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java
index 07dec5d9e222..b843ad75ac0f 100644
--- a/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java
+++ b/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java
@@ -79,6 +79,8 @@ public class FaceManagerTest {
private FaceManager.AuthenticationCallback mAuthCallback;
@Mock
private FaceManager.EnrollmentCallback mEnrollmentCallback;
+ @Mock
+ private FaceManager.FaceDetectionCallback mFaceDetectionCallback;
@Captor
private ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback> mCaptor;
@@ -191,6 +193,23 @@ public class FaceManagerTest {
any(), anyString(), any(), any(), anyBoolean());
}
+ @Test
+ public void detectClient_onError() throws RemoteException {
+ ArgumentCaptor<IFaceServiceReceiver> argumentCaptor =
+ ArgumentCaptor.forClass(IFaceServiceReceiver.class);
+
+ CancellationSignal cancellationSignal = new CancellationSignal();
+ mFaceManager.detectFace(cancellationSignal, mFaceDetectionCallback,
+ new FaceAuthenticateOptions.Builder().build());
+
+ verify(mService).detectFace(any(), argumentCaptor.capture(), any());
+
+ argumentCaptor.getValue().onError(5 /* error */, 0 /* vendorCode */);
+ mLooper.dispatchAll();
+
+ verify(mFaceDetectionCallback).onDetectionError(anyInt());
+ }
+
private void initializeProperties() throws RemoteException {
verify(mService).addAuthenticatorsRegisteredCallback(mCaptor.capture());
diff --git a/core/tests/coretests/src/android/hardware/fingerprint/FingerprintManagerTest.java b/core/tests/coretests/src/android/hardware/fingerprint/FingerprintManagerTest.java
index 625e2e3723a7..70313b8c9ea7 100644
--- a/core/tests/coretests/src/android/hardware/fingerprint/FingerprintManagerTest.java
+++ b/core/tests/coretests/src/android/hardware/fingerprint/FingerprintManagerTest.java
@@ -74,6 +74,8 @@ public class FingerprintManagerTest {
private FingerprintManager.AuthenticationCallback mAuthCallback;
@Mock
private FingerprintManager.EnrollmentCallback mEnrollCallback;
+ @Mock
+ private FingerprintManager.FingerprintDetectionCallback mFingerprintDetectionCallback;
@Captor
private ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mCaptor;
@@ -166,4 +168,21 @@ public class FingerprintManagerTest {
anyString());
verify(mService, never()).enroll(any(), any(), anyInt(), any(), anyString(), anyInt());
}
+
+ @Test
+ public void detectClient_onError() throws RemoteException {
+ ArgumentCaptor<IFingerprintServiceReceiver> argumentCaptor =
+ ArgumentCaptor.forClass(IFingerprintServiceReceiver.class);
+
+ mFingerprintManager.detectFingerprint(new CancellationSignal(),
+ mFingerprintDetectionCallback,
+ new FingerprintAuthenticateOptions.Builder().build());
+
+ verify(mService).detectFingerprint(any(), argumentCaptor.capture(), any());
+
+ argumentCaptor.getValue().onError(5 /* error */, 0 /* vendorCode */);
+ mLooper.dispatchAll();
+
+ verify(mFingerprintDetectionCallback).onDetectionError(anyInt());
+ }
}
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 6a9fc04230f8..1a38decae604 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -17,6 +17,11 @@
package android.view;
import static android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR;
+import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE;
+import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
+import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
+import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
+import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
@@ -48,8 +53,12 @@ import android.hardware.display.DisplayManagerGlobal;
import android.os.Binder;
import android.os.SystemProperties;
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.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.view.WindowInsets.Side;
import android.view.WindowInsets.Type;
@@ -97,6 +106,10 @@ public class ViewRootImplTest {
// state after the test completes.
private static boolean sOriginalTouchMode;
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
@BeforeClass
public static void setUpClass() {
sContext = sInstrumentation.getTargetContext();
@@ -427,6 +440,129 @@ public class ViewRootImplTest {
assertThat(result).isFalse();
}
+ /**
+ * Test the default values are properly set
+ */
+ @UiThreadTest
+ @Test
+ @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE)
+ public void votePreferredFrameRate_getDefaultValues() {
+ ViewRootImpl viewRootImpl = new ViewRootImpl(sContext,
+ sContext.getDisplayNoVerify());
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
+ FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ assertEquals(viewRootImpl.getPreferredFrameRate(), 0, 0.1);
+ }
+
+ /**
+ * Test the value of the frame rate cateogry based on the visibility of a view
+ * Invsible: FRAME_RATE_CATEGORY_NO_PREFERENCE
+ * Visible: FRAME_RATE_CATEGORY_NORMAL
+ */
+ @Test
+ @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE)
+ public void votePreferredFrameRate_voteFrameRateCategory_visibility() {
+ View view = new View(sContext);
+ attachViewToWindow(view);
+ ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ sInstrumentation.runOnMainSync(() -> {
+ view.setVisibility(View.INVISIBLE);
+ view.invalidate();
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
+ FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ });
+
+ sInstrumentation.runOnMainSync(() -> {
+ view.setVisibility(View.VISIBLE);
+ view.invalidate();
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
+ FRAME_RATE_CATEGORY_NORMAL);
+ });
+ }
+
+ /**
+ * Test the value of the frame rate cateogry based on the size of a view.
+ * The current threshold value is 7% of the screen size
+ * <7%: FRAME_RATE_CATEGORY_LOW
+ */
+ @Test
+ @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE)
+ public void votePreferredFrameRate_voteFrameRateCategory_smallSize() {
+ View view = new View(sContext);
+ WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
+ wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
+ wmlp.width = 1;
+ wmlp.height = 1;
+
+ sInstrumentation.runOnMainSync(() -> {
+ WindowManager wm = sContext.getSystemService(WindowManager.class);
+ wm.addView(view, wmlp);
+ });
+ sInstrumentation.waitForIdleSync();
+
+ ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ sInstrumentation.runOnMainSync(() -> {
+ view.invalidate();
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_LOW);
+ });
+ }
+
+ /**
+ * Test the value of the frame rate cateogry based on the size of a view.
+ * The current threshold value is 7% of the screen size
+ * >=7% : FRAME_RATE_CATEGORY_NORMAL
+ */
+ @Test
+ @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE)
+ public void votePreferredFrameRate_voteFrameRateCategory_normalSize() {
+ View view = new View(sContext);
+ WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
+ wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
+
+ sInstrumentation.runOnMainSync(() -> {
+ WindowManager wm = sContext.getSystemService(WindowManager.class);
+ Display display = wm.getDefaultDisplay();
+ DisplayMetrics metrics = new DisplayMetrics();
+ display.getMetrics(metrics);
+ wmlp.width = (int) (metrics.widthPixels * 0.9);
+ wmlp.height = (int) (metrics.heightPixels * 0.9);
+ wm.addView(view, wmlp);
+ });
+ sInstrumentation.waitForIdleSync();
+
+ ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ sInstrumentation.runOnMainSync(() -> {
+ view.invalidate();
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NORMAL);
+ });
+ }
+
+ /**
+ * Test how values of the frame rate cateogry are aggregated.
+ * It should take the max value among all of the voted categories per frame.
+ */
+ @Test
+ @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE)
+ public void votePreferredFrameRate_voteFrameRateCategory_aggregate() {
+ View view = new View(sContext);
+ attachViewToWindow(view);
+ sInstrumentation.runOnMainSync(() -> {
+ ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
+ FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_LOW);
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_LOW);
+ viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_NORMAL);
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NORMAL);
+ viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_HIGH);
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
+ viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_NORMAL);
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
+ viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_LOW);
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
+ });
+ }
+
@Test
public void forceInvertOffDarkThemeOff_forceDarkModeDisabled() {
mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);
diff --git a/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
index e76d266c614c..d47d7891d0e4 100644
--- a/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
@@ -115,6 +115,26 @@ public class MainContentCaptureSessionTest {
new ContentCaptureOptions.ContentProtectionOptions(
/* enableReceiver= */ true,
-BUFFER_SIZE,
+ /* requiredGroups= */ List.of(List.of("a")),
+ /* optionalGroups= */ Collections.emptyList(),
+ /* optionalGroupsThreshold= */ 0));
+ MainContentCaptureSession session = createSession(options);
+ session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
+
+ session.onSessionStarted(/* resultCode= */ 0, /* binder= */ null);
+
+ assertThat(session.mContentProtectionEventProcessor).isNull();
+ verifyZeroInteractions(mMockContentProtectionEventProcessor);
+ }
+
+ @Test
+ public void onSessionStarted_contentProtectionNoGroups_processorNotCreated() {
+ ContentCaptureOptions options =
+ createOptions(
+ /* enableContentCaptureReceiver= */ true,
+ new ContentCaptureOptions.ContentProtectionOptions(
+ /* enableReceiver= */ true,
+ BUFFER_SIZE,
/* requiredGroups= */ Collections.emptyList(),
/* optionalGroups= */ Collections.emptyList(),
/* optionalGroupsThreshold= */ 0));
@@ -320,7 +340,7 @@ public class MainContentCaptureSessionTest {
new ContentCaptureOptions.ContentProtectionOptions(
enableContentProtectionReceiver,
BUFFER_SIZE,
- /* requiredGroups= */ Collections.emptyList(),
+ /* requiredGroups= */ List.of(List.of("a")),
/* optionalGroups= */ Collections.emptyList(),
/* optionalGroupsThreshold= */ 0));
}
diff --git a/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java b/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java
index 39a2e0e048e8..ba0dbf454ad2 100644
--- a/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java
+++ b/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java
@@ -29,12 +29,13 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.ContentCaptureOptions;
import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.os.Handler;
-import android.os.Looper;
-import android.text.InputType;
+import android.os.test.TestLooper;
import android.view.View;
import android.view.contentcapture.ContentCaptureEvent;
import android.view.contentcapture.IContentCaptureManager;
@@ -57,6 +58,7 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import org.mockito.verification.VerificationMode;
import java.time.Instant;
import java.util.ArrayList;
@@ -75,13 +77,25 @@ public class ContentProtectionEventProcessorTest {
private static final String PACKAGE_NAME = "com.test.package.name";
- private static final String ANDROID_CLASS_NAME = "android.test.some.class.name";
+ private static final String TEXT_REQUIRED1 = "TEXT REQUIRED1 TEXT";
- private static final String PASSWORD_TEXT = "ENTER PASSWORD HERE";
+ private static final String TEXT_REQUIRED2 = "TEXT REQUIRED2 TEXT";
- private static final String SUSPICIOUS_TEXT = "PLEASE SIGN IN";
+ private static final String TEXT_OPTIONAL1 = "TEXT OPTIONAL1 TEXT";
- private static final String SAFE_TEXT = "SAFE TEXT";
+ private static final String TEXT_OPTIONAL2 = "TEXT OPTIONAL2 TEXT";
+
+ private static final String TEXT_CONTAINS_OPTIONAL3 = "TEXTOPTIONAL3TEXT";
+
+ private static final String TEXT_SHARED = "TEXT SHARED TEXT";
+
+ private static final String TEXT_SAFE = "TEXT SAFE TEXT";
+
+ private static final List<List<String>> REQUIRED_GROUPS =
+ List.of(List.of("required1", "missing"), List.of("required2", "shared"));
+
+ private static final List<List<String>> OPTIONAL_GROUPS =
+ List.of(List.of("optional1"), List.of("optional2", "optional3"), List.of("shared"));
private static final ContentCaptureEvent PROCESS_EVENT = createProcessEvent();
@@ -91,7 +105,17 @@ public class ContentProtectionEventProcessorTest {
private static final Set<Integer> EVENT_TYPES_TO_STORE =
ImmutableSet.of(TYPE_VIEW_APPEARED, TYPE_VIEW_DISAPPEARED, TYPE_VIEW_TEXT_CHANGED);
- private static final int RESET_LOGIN_TOTAL_EVENTS_TO_PROCESS = 150;
+ private static final int BUFFER_SIZE = 150;
+
+ private static final int OPTIONAL_GROUPS_THRESHOLD = 1;
+
+ private static final ContentCaptureOptions.ContentProtectionOptions OPTIONS =
+ new ContentCaptureOptions.ContentProtectionOptions(
+ /* enableReceiver= */ true,
+ BUFFER_SIZE,
+ REQUIRED_GROUPS,
+ OPTIONAL_GROUPS,
+ OPTIONAL_GROUPS_THRESHOLD);
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@@ -101,16 +125,19 @@ public class ContentProtectionEventProcessorTest {
private final Context mContext = ApplicationProvider.getApplicationContext();
- private ContentProtectionEventProcessor mContentProtectionEventProcessor;
+ private final TestLooper mTestLooper = new TestLooper();
+
+ @NonNull private ContentProtectionEventProcessor mContentProtectionEventProcessor;
@Before
public void setup() {
mContentProtectionEventProcessor =
new ContentProtectionEventProcessor(
mMockEventBuffer,
- new Handler(Looper.getMainLooper()),
+ new Handler(mTestLooper.getLooper()),
mMockContentCaptureManager,
- PACKAGE_NAME);
+ PACKAGE_NAME,
+ OPTIONS);
}
@Test
@@ -156,347 +183,224 @@ public class ContentProtectionEventProcessorTest {
}
@Test
- public void processEvent_loginDetected_inspectsOnlyTypeViewAppeared() {
- mContentProtectionEventProcessor.mPasswordFieldDetected = true;
- mContentProtectionEventProcessor.mSuspiciousTextDetected = true;
-
- for (int type = -100; type <= 100; type++) {
- if (type == TYPE_VIEW_APPEARED) {
- continue;
- }
-
- mContentProtectionEventProcessor.processEvent(createEvent(type));
-
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isTrue();
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue();
- }
-
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
- }
-
- @Test
- public void processEvent_loginDetected() throws Exception {
+ public void processEvent_loginDetected_true_eventText() throws Exception {
when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);
- mContentProtectionEventProcessor.mPasswordFieldDetected = true;
- mContentProtectionEventProcessor.mSuspiciousTextDetected = true;
- mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
-
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
- verify(mMockEventBuffer).clear();
- verify(mMockEventBuffer).toArray();
- assertOnLoginDetected();
- }
-
- @Test
- public void processEvent_loginDetected_passwordFieldNotDetected() {
- mContentProtectionEventProcessor.mSuspiciousTextDetected = true;
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
-
- mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
-
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
- }
-
- @Test
- public void processEvent_loginDetected_suspiciousTextNotDetected() {
- mContentProtectionEventProcessor.mPasswordFieldDetected = true;
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
-
- mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
-
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isTrue();
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
- }
-
- @Test
- public void processEvent_loginDetected_withoutViewNode() {
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
-
- mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
+ mContentProtectionEventProcessor.processEvent(
+ createProcessEvent(
+ /* eventText= */ TEXT_REQUIRED1,
+ /* viewNodeText= */ null,
+ /* hintText= */ null));
+ mContentProtectionEventProcessor.processEvent(
+ createProcessEvent(
+ /* eventText= */ TEXT_REQUIRED2,
+ /* viewNodeText= */ null,
+ /* hintText= */ null));
+ mContentProtectionEventProcessor.processEvent(
+ createProcessEvent(
+ /* eventText= */ TEXT_OPTIONAL1,
+ /* viewNodeText= */ null,
+ /* hintText= */ null));
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
+ assertLoginDetected();
}
@Test
- public void processEvent_loginDetected_belowResetLimit() throws Exception {
+ public void processEvent_loginDetected_true_viewNodeText() throws Exception {
when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);
- mContentProtectionEventProcessor.mSuspiciousTextDetected = true;
- ContentCaptureEvent event =
- createAndroidPasswordFieldEvent(
- ANDROID_CLASS_NAME, InputType.TYPE_TEXT_VARIATION_PASSWORD);
-
- for (int i = 0; i < RESET_LOGIN_TOTAL_EVENTS_TO_PROCESS; i++) {
- mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
- }
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
-
- mContentProtectionEventProcessor.processEvent(event);
+ mContentProtectionEventProcessor.processEvent(
+ createProcessEvent(
+ /* eventText= */ null,
+ /* viewNodeText= */ TEXT_REQUIRED1,
+ /* hintText= */ null));
+ mContentProtectionEventProcessor.processEvent(
+ createProcessEvent(
+ /* eventText= */ null,
+ /* viewNodeText= */ TEXT_REQUIRED2,
+ /* hintText= */ null));
+ mContentProtectionEventProcessor.processEvent(
+ createProcessEvent(
+ /* eventText= */ null,
+ /* viewNodeText= */ TEXT_OPTIONAL1,
+ /* hintText= */ null));
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
- verify(mMockEventBuffer).clear();
- verify(mMockEventBuffer).toArray();
- assertOnLoginDetected();
+ assertLoginDetected();
}
@Test
- public void processEvent_loginDetected_aboveResetLimit() throws Exception {
- mContentProtectionEventProcessor.mSuspiciousTextDetected = true;
- ContentCaptureEvent event =
- createAndroidPasswordFieldEvent(
- ANDROID_CLASS_NAME, InputType.TYPE_TEXT_VARIATION_PASSWORD);
-
- for (int i = 0; i < RESET_LOGIN_TOTAL_EVENTS_TO_PROCESS + 1; i++) {
- mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
- }
-
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
+ public void processEvent_loginDetected_true_hintText() throws Exception {
+ when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);
- mContentProtectionEventProcessor.processEvent(event);
+ mContentProtectionEventProcessor.processEvent(
+ createProcessEvent(
+ /* eventText= */ null,
+ /* viewNodeText= */ null,
+ /* hintText= */ TEXT_REQUIRED1));
+ mContentProtectionEventProcessor.processEvent(
+ createProcessEvent(
+ /* eventText= */ null,
+ /* viewNodeText= */ null,
+ /* hintText= */ TEXT_REQUIRED2));
+ mContentProtectionEventProcessor.processEvent(
+ createProcessEvent(
+ /* eventText= */ null,
+ /* viewNodeText= */ null,
+ /* hintText= */ TEXT_OPTIONAL1));
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isTrue();
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
+ assertLoginDetected();
}
@Test
- public void processEvent_multipleLoginsDetected_belowFlushThreshold() throws Exception {
+ public void processEvent_loginDetected_true_differentOptionalGroup() throws Exception {
when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);
- mContentProtectionEventProcessor.mPasswordFieldDetected = true;
- mContentProtectionEventProcessor.mSuspiciousTextDetected = true;
- mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
-
- mContentProtectionEventProcessor.mPasswordFieldDetected = true;
- mContentProtectionEventProcessor.mSuspiciousTextDetected = true;
- mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED2));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_OPTIONAL2));
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
- verify(mMockEventBuffer).clear();
- verify(mMockEventBuffer).toArray();
- assertOnLoginDetected();
+ assertLoginDetected();
}
@Test
- public void processEvent_multipleLoginsDetected_aboveFlushThreshold() throws Exception {
+ public void processEvent_loginDetected_true_usesContains() throws Exception {
when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);
- mContentProtectionEventProcessor.mPasswordFieldDetected = true;
- mContentProtectionEventProcessor.mSuspiciousTextDetected = true;
- mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
-
- mContentProtectionEventProcessor.mLastFlushTime = Instant.now().minusSeconds(5);
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED2));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_CONTAINS_OPTIONAL3));
- mContentProtectionEventProcessor.mPasswordFieldDetected = true;
- mContentProtectionEventProcessor.mSuspiciousTextDetected = true;
- mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
-
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
- verify(mMockEventBuffer, times(2)).clear();
- verify(mMockEventBuffer, times(2)).toArray();
- assertOnLoginDetected(PROCESS_EVENT, /* times= */ 2);
+ assertLoginDetected();
}
@Test
- public void isPasswordField_android() {
- ContentCaptureEvent event =
- createAndroidPasswordFieldEvent(
- ANDROID_CLASS_NAME, InputType.TYPE_TEXT_VARIATION_PASSWORD);
-
- mContentProtectionEventProcessor.processEvent(event);
+ public void processEvent_loginDetected_false_missingRequiredGroups() {
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_OPTIONAL1));
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isTrue();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
+ assertLoginNotDetected();
}
@Test
- public void isPasswordField_android_withoutClassName() {
- ContentCaptureEvent event =
- createAndroidPasswordFieldEvent(
- /* className= */ null, InputType.TYPE_TEXT_VARIATION_PASSWORD);
-
- mContentProtectionEventProcessor.processEvent(event);
+ public void processEvent_loginDetected_false_missingOptionalGroups() {
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED2));
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
+ assertLoginNotDetected();
}
@Test
- public void isPasswordField_android_wrongClassName() {
- ContentCaptureEvent event =
- createAndroidPasswordFieldEvent(
- "wrong.prefix" + ANDROID_CLASS_NAME,
- InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ public void processEvent_loginDetected_false_safeText() {
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED2));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_SAFE));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_SAFE));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_SAFE));
- mContentProtectionEventProcessor.processEvent(event);
-
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
+ assertLoginNotDetected();
}
@Test
- public void isPasswordField_android_wrongInputType() {
- ContentCaptureEvent event =
- createAndroidPasswordFieldEvent(
- ANDROID_CLASS_NAME, InputType.TYPE_TEXT_VARIATION_NORMAL);
+ public void processEvent_loginDetected_false_sharedTextOnce() {
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_SHARED));
- mContentProtectionEventProcessor.processEvent(event);
-
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
+ assertLoginNotDetected();
}
@Test
- public void isPasswordField_webView() throws Exception {
- ContentCaptureEvent event =
- createWebViewPasswordFieldEvent(
- /* className= */ null, /* eventText= */ null, PASSWORD_TEXT);
- when(mMockEventBuffer.toArray()).thenReturn(new ContentCaptureEvent[] {event});
+ public void processEvent_loginDetected_true_sharedTextMultiple() throws Exception {
+ when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);
- mContentProtectionEventProcessor.processEvent(event);
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_SHARED));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_SHARED));
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
- verify(mMockEventBuffer).clear();
- verify(mMockEventBuffer).toArray();
- assertOnLoginDetected(event, /* times= */ 1);
+ assertLoginDetected();
}
@Test
- public void isPasswordField_webView_withClassName() {
- ContentCaptureEvent event =
- createWebViewPasswordFieldEvent(
- /* className= */ "any.class.name", /* eventText= */ null, PASSWORD_TEXT);
+ public void processEvent_loginDetected_false_inspectsOnlyTypeViewAppeared() {
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED2));
- mContentProtectionEventProcessor.processEvent(event);
+ for (int type = -100; type <= 100; type++) {
+ if (type == TYPE_VIEW_APPEARED) {
+ continue;
+ }
+ ContentCaptureEvent event = createEvent(type);
+ event.setText(TEXT_OPTIONAL1);
+ mContentProtectionEventProcessor.processEvent(event);
+ }
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
+ assertLoginNotDetected();
}
@Test
- public void isPasswordField_webView_withSafeViewNodeText() {
- ContentCaptureEvent event =
- createWebViewPasswordFieldEvent(
- /* className= */ null, /* eventText= */ null, SAFE_TEXT);
+ public void processEvent_loginDetected_true_belowResetLimit() throws Exception {
+ when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);
- mContentProtectionEventProcessor.processEvent(event);
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED2));
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
- }
+ for (int i = 0; i < BUFFER_SIZE - 2; i++) {
+ mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
+ }
- @Test
- public void isPasswordField_webView_withEventText() {
- ContentCaptureEvent event =
- createWebViewPasswordFieldEvent(/* className= */ null, PASSWORD_TEXT, SAFE_TEXT);
+ assertLoginNotDetected();
- mContentProtectionEventProcessor.processEvent(event);
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_OPTIONAL1));
- assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
+ assertLoginDetected();
}
@Test
- public void isSuspiciousText_withSafeText() {
- ContentCaptureEvent event = createSuspiciousTextEvent(SAFE_TEXT, SAFE_TEXT);
+ public void processEvent_loginDetected_false_aboveResetLimit() {
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED2));
- mContentProtectionEventProcessor.processEvent(event);
-
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
- }
+ for (int i = 0; i < BUFFER_SIZE - 1; i++) {
+ mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
+ }
- @Test
- public void isSuspiciousText_eventText_suspiciousText() {
- ContentCaptureEvent event = createSuspiciousTextEvent(SUSPICIOUS_TEXT, SAFE_TEXT);
+ assertLoginNotDetected();
- mContentProtectionEventProcessor.processEvent(event);
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_OPTIONAL1));
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
+ assertLoginNotDetected();
}
@Test
- public void isSuspiciousText_viewNodeText_suspiciousText() {
- ContentCaptureEvent event = createSuspiciousTextEvent(SAFE_TEXT, SUSPICIOUS_TEXT);
+ public void processEvent_multipleLoginsDetected_belowFlushThreshold() throws Exception {
+ when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);
- mContentProtectionEventProcessor.processEvent(event);
+ for (int i = 0; i < 2; i++) {
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED2));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_OPTIONAL1));
+ mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
+ }
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
+ assertLoginDetected();
}
@Test
- public void isSuspiciousText_eventText_passwordText() {
- ContentCaptureEvent event = createSuspiciousTextEvent(PASSWORD_TEXT, SAFE_TEXT);
-
- mContentProtectionEventProcessor.processEvent(event);
+ public void processEvent_multipleLoginsDetected_aboveFlushThreshold() throws Exception {
+ when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS);
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
- }
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED2));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_OPTIONAL1));
+ mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
- @Test
- public void isSuspiciousText_viewNodeText_passwordText() {
- // Specify the class to differ from {@link isPasswordField_webView} test in this version
- ContentCaptureEvent event =
- createProcessEvent(
- "test.class.not.a.web.view", /* inputType= */ 0, SAFE_TEXT, PASSWORD_TEXT);
+ mContentProtectionEventProcessor.mLastFlushTime = Instant.now().minusSeconds(5);
- mContentProtectionEventProcessor.processEvent(event);
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED1));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_REQUIRED2));
+ mContentProtectionEventProcessor.processEvent(createProcessEvent(TEXT_OPTIONAL1));
+ mContentProtectionEventProcessor.processEvent(PROCESS_EVENT);
- assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue();
- verify(mMockEventBuffer, never()).clear();
- verify(mMockEventBuffer, never()).toArray();
- verifyZeroInteractions(mMockContentCaptureManager);
+ assertLoginDetected(times(2));
}
private static ContentCaptureEvent createEvent(int type) {
@@ -511,20 +415,20 @@ public class ContentProtectionEventProcessorTest {
return createEvent(TYPE_VIEW_APPEARED);
}
+ private ContentCaptureEvent createProcessEvent(@Nullable String eventText) {
+ return createProcessEvent(eventText, /* viewNodeText= */ null, /* hintText= */ null);
+ }
+
private ContentCaptureEvent createProcessEvent(
- @Nullable String className,
- int inputType,
- @Nullable String eventText,
- @Nullable String viewNodeText) {
+ @Nullable String eventText, @Nullable String viewNodeText, @Nullable String hintText) {
View view = new View(mContext);
ViewStructureImpl viewStructure = new ViewStructureImpl(view);
- if (className != null) {
- viewStructure.setClassName(className);
- }
if (viewNodeText != null) {
viewStructure.setText(viewNodeText);
}
- viewStructure.setInputType(inputType);
+ if (hintText != null) {
+ viewStructure.setHint(hintText);
+ }
ContentCaptureEvent event = createProcessEvent();
event.setViewNode(viewStructure.getNode());
@@ -535,34 +439,28 @@ public class ContentProtectionEventProcessorTest {
return event;
}
- private ContentCaptureEvent createAndroidPasswordFieldEvent(
- @Nullable String className, int inputType) {
- return createProcessEvent(
- className, inputType, /* eventText= */ null, /* viewNodeText= */ null);
- }
-
- private ContentCaptureEvent createWebViewPasswordFieldEvent(
- @Nullable String className, @Nullable String eventText, @Nullable String viewNodeText) {
- return createProcessEvent(className, /* inputType= */ 0, eventText, viewNodeText);
+ private void assertLoginNotDetected() {
+ mTestLooper.dispatchAll();
+ verify(mMockEventBuffer, never()).clear();
+ verify(mMockEventBuffer, never()).toArray();
+ verifyZeroInteractions(mMockContentCaptureManager);
}
- private ContentCaptureEvent createSuspiciousTextEvent(
- @Nullable String eventText, @Nullable String viewNodeText) {
- return createProcessEvent(
- /* className= */ null, /* inputType= */ 0, eventText, viewNodeText);
+ private void assertLoginDetected() throws Exception {
+ assertLoginDetected(times(1));
}
- private void assertOnLoginDetected() throws Exception {
- assertOnLoginDetected(PROCESS_EVENT, /* times= */ 1);
- }
+ private void assertLoginDetected(@NonNull VerificationMode verificationMode) throws Exception {
+ mTestLooper.dispatchAll();
+ verify(mMockEventBuffer, verificationMode).clear();
+ verify(mMockEventBuffer, verificationMode).toArray();
- private void assertOnLoginDetected(ContentCaptureEvent event, int times) throws Exception {
ArgumentCaptor<ParceledListSlice> captor = ArgumentCaptor.forClass(ParceledListSlice.class);
- verify(mMockContentCaptureManager, times(times)).onLoginDetected(captor.capture());
+ verify(mMockContentCaptureManager, verificationMode).onLoginDetected(captor.capture());
assertThat(captor.getValue()).isNotNull();
List<ContentCaptureEvent> actual = captor.getValue().getList();
assertThat(actual).isNotNull();
- assertThat(actual).containsExactly(event);
+ assertThat(actual).containsExactly(PROCESS_EVENT);
}
}
diff --git a/core/tests/coretests/src/android/view/contentprotection/ContentProtectionUtilsTest.java b/core/tests/coretests/src/android/view/contentprotection/ContentProtectionUtilsTest.java
index 1459799adee5..fbe478e31888 100644
--- a/core/tests/coretests/src/android/view/contentprotection/ContentProtectionUtilsTest.java
+++ b/core/tests/coretests/src/android/view/contentprotection/ContentProtectionUtilsTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 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.
@@ -44,68 +44,74 @@ public class ContentProtectionUtilsTest {
private static final String TEXT = "TEST_TEXT";
- private static final ContentCaptureEvent EVENT = createEvent();
-
- private static final ViewNode VIEW_NODE = new ViewNode();
-
- private static final ViewNode VIEW_NODE_WITH_TEXT = createViewNodeWithText();
+ private static final String TEXT_LOWER = TEXT.toLowerCase();
@Test
- public void event_getEventText_null() {
- String actual = ContentProtectionUtils.getEventText(EVENT);
+ public void getEventTextLower_null() {
+ String actual = ContentProtectionUtils.getEventTextLower(createEvent());
assertThat(actual).isNull();
}
@Test
- public void event_getEventText_notNull() {
- ContentCaptureEvent event = createEvent();
- event.setText(TEXT);
-
- String actual = ContentProtectionUtils.getEventText(event);
+ public void getEventTextLower_notNull() {
+ String actual = ContentProtectionUtils.getEventTextLower(createEventWithText());
- assertThat(actual).isEqualTo(TEXT);
+ assertThat(actual).isEqualTo(TEXT_LOWER);
}
@Test
- public void event_getViewNodeText_null() {
- String actual = ContentProtectionUtils.getViewNodeText(EVENT);
+ public void getViewNodeTextLower_null() {
+ String actual = ContentProtectionUtils.getViewNodeTextLower(new ViewNode());
assertThat(actual).isNull();
}
@Test
- public void event_getViewNodeText_notNull() {
- ContentCaptureEvent event = createEvent();
- event.setViewNode(VIEW_NODE_WITH_TEXT);
-
- String actual = ContentProtectionUtils.getViewNodeText(event);
+ public void getViewNodeTextLower_notNull() {
+ String actual = ContentProtectionUtils.getViewNodeTextLower(createViewNodeWithText());
- assertThat(actual).isEqualTo(TEXT);
+ assertThat(actual).isEqualTo(TEXT_LOWER);
}
@Test
- public void viewNode_getViewNodeText_null() {
- String actual = ContentProtectionUtils.getViewNodeText(VIEW_NODE);
+ public void getHintTextLower_null() {
+ String actual = ContentProtectionUtils.getHintTextLower(new ViewNode());
assertThat(actual).isNull();
}
@Test
- public void viewNode_getViewNodeText_notNull() {
- String actual = ContentProtectionUtils.getViewNodeText(VIEW_NODE_WITH_TEXT);
+ public void getHintTextLower_notNull() {
+ String actual = ContentProtectionUtils.getHintTextLower(createViewNodeWithHint());
- assertThat(actual).isEqualTo(TEXT);
+ assertThat(actual).isEqualTo(TEXT_LOWER);
}
private static ContentCaptureEvent createEvent() {
return new ContentCaptureEvent(/* sessionId= */ 123, TYPE_SESSION_STARTED);
}
- private static ViewNode createViewNodeWithText() {
+ private static ContentCaptureEvent createEventWithText() {
+ ContentCaptureEvent event = createEvent();
+ event.setText(TEXT);
+ return event;
+ }
+
+ private static ViewStructureImpl createViewStructureImpl() {
View view = new View(ApplicationProvider.getApplicationContext());
- ViewStructureImpl viewStructure = new ViewStructureImpl(view);
- viewStructure.setText(TEXT);
- return viewStructure.getNode();
+ return new ViewStructureImpl(view);
+ }
+
+ private static ViewNode createViewNodeWithText() {
+ ViewStructureImpl viewStructureImpl = createViewStructureImpl();
+ viewStructureImpl.setText(TEXT);
+ return viewStructureImpl.getNode();
+ }
+
+ private static ViewNode createViewNodeWithHint() {
+ ViewStructureImpl viewStructureImpl = createViewStructureImpl();
+ viewStructureImpl.setHint(TEXT);
+ return viewStructureImpl.getNode();
}
}
diff --git a/core/tests/coretests/src/android/widget/HorizontalScrollViewFunctionalTest.java b/core/tests/coretests/src/android/widget/HorizontalScrollViewFunctionalTest.java
index 86f26e59e370..df212ebe1744 100644
--- a/core/tests/coretests/src/android/widget/HorizontalScrollViewFunctionalTest.java
+++ b/core/tests/coretests/src/android/widget/HorizontalScrollViewFunctionalTest.java
@@ -17,7 +17,9 @@
package android.widget;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import android.content.Context;
import android.platform.test.annotations.Presubmit;
import android.util.PollingCheck;
@@ -32,6 +34,9 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
@RunWith(AndroidJUnit4.class)
@MediumTest
@Presubmit
@@ -49,23 +54,43 @@ public class HorizontalScrollViewFunctionalTest {
}
@Test
- public void testScrollAfterFlingTop() {
- mHorizontalScrollView.scrollTo(100, 0);
- mHorizontalScrollView.fling(-10000);
- PollingCheck.waitFor(() -> mHorizontalScrollView.mEdgeGlowLeft.getDistance() > 0);
- PollingCheck.waitFor(() -> mHorizontalScrollView.mEdgeGlowLeft.getDistance() == 0f);
+ public void testScrollAfterFlingLeft() throws Throwable {
+ WatchedEdgeEffect edgeEffect = new WatchedEdgeEffect(mActivity);
+ mHorizontalScrollView.mEdgeGlowLeft = edgeEffect;
+ mActivityRule.runOnUiThread(() -> mHorizontalScrollView.scrollTo(100, 0));
+ mActivityRule.runOnUiThread(() -> mHorizontalScrollView.fling(-10000));
+ assertTrue(edgeEffect.onAbsorbLatch.await(1, TimeUnit.SECONDS));
+ mActivityRule.runOnUiThread(() -> {}); // let the absorb takes effect -- least one frame
+ PollingCheck.waitFor(() -> edgeEffect.getDistance() == 0f);
assertEquals(0, mHorizontalScrollView.getScrollX());
}
@Test
- public void testScrollAfterFlingBottom() {
+ public void testScrollAfterFlingRight() throws Throwable {
+ WatchedEdgeEffect edgeEffect = new WatchedEdgeEffect(mActivity);
+ mHorizontalScrollView.mEdgeGlowRight = edgeEffect;
int childWidth = mHorizontalScrollView.getChildAt(0).getWidth();
int maxScroll = childWidth - mHorizontalScrollView.getWidth();
- mHorizontalScrollView.scrollTo(maxScroll - 100, 0);
- mHorizontalScrollView.fling(10000);
- PollingCheck.waitFor(() -> mHorizontalScrollView.mEdgeGlowRight.getDistance() > 0);
+ mActivityRule.runOnUiThread(() -> mHorizontalScrollView.scrollTo(maxScroll - 100, 0));
+ mActivityRule.runOnUiThread(() -> mHorizontalScrollView.fling(10000));
+ assertTrue(edgeEffect.onAbsorbLatch.await(1, TimeUnit.SECONDS));
+ mActivityRule.runOnUiThread(() -> {}); // let the absorb takes effect -- at least one frame
PollingCheck.waitFor(() -> mHorizontalScrollView.mEdgeGlowRight.getDistance() == 0f);
assertEquals(maxScroll, mHorizontalScrollView.getScrollX());
}
+
+ static class WatchedEdgeEffect extends EdgeEffect {
+ public CountDownLatch onAbsorbLatch = new CountDownLatch(1);
+
+ WatchedEdgeEffect(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onAbsorb(int velocity) {
+ super.onAbsorb(velocity);
+ onAbsorbLatch.countDown();
+ }
+ }
}
diff --git a/graphics/java/android/graphics/text/LineBreakConfig.java b/graphics/java/android/graphics/text/LineBreakConfig.java
index f5e5803d4796..dc1773bd7290 100644
--- a/graphics/java/android/graphics/text/LineBreakConfig.java
+++ b/graphics/java/android/graphics/text/LineBreakConfig.java
@@ -39,12 +39,14 @@ public final class LineBreakConfig {
/**
* No hyphenation preference is specified.
*
+ * <p>
* This is a special value of hyphenation preference indicating no hyphenation preference is
* specified. When overriding a {@link LineBreakConfig} with another {@link LineBreakConfig}
* with {@link Builder#merge(LineBreakConfig)} function, the hyphenation preference of
* overridden config will be kept if the hyphenation preference of overriding config is
* {@link #HYPHENATION_UNSPECIFIED}.
*
+ * <p>
* <pre>
* val override = LineBreakConfig.Builder()
* .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE)
@@ -57,6 +59,7 @@ public final class LineBreakConfig {
* // LINE_BREAK_WORD_STYLE_PHRASE for line break word style.
* </pre>
*
+ * <p>
* This value is resolved to {@link #HYPHENATION_ENABLED} if this value is used for text
* layout/rendering.
*/
@@ -89,6 +92,7 @@ public final class LineBreakConfig {
/**
* No line break style is specified.
*
+ * <p>
* This is a special value of line break style indicating no style value is specified.
* When overriding a {@link LineBreakConfig} with another {@link LineBreakConfig} with
* {@link Builder#merge(LineBreakConfig)} function, the line break style of overridden config
@@ -107,6 +111,7 @@ public final class LineBreakConfig {
* // LINE_BREAK_WORD_STYLE_PHRASE for line break word style.
* </pre>
*
+ * <p>
* This value is resolved to {@link #LINE_BREAK_STYLE_NONE} if this value is used for text
* layout/rendering.
*/
@@ -270,6 +275,8 @@ public final class LineBreakConfig {
/**
* Resets this builder to the given config state.
*
+ * @param config a config value used for resetting. {@code null} is allowed. If {@code null}
+ * is passed, all configs are reset to unspecified.
* @return This {@code Builder}.
* @hide
*/
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 96c257b304a0..1ba41b106f56 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -75,16 +75,18 @@ import javax.security.auth.x500.X500Principal;
* {@link java.security.interfaces.ECPublicKey} or {@link java.security.interfaces.RSAPublicKey}
* interfaces.
*
- * <p>For asymmetric key pairs, a self-signed X.509 certificate will be also generated and stored in
- * the Android Keystore. This is because the {@link java.security.KeyStore} abstraction does not
- * support storing key pairs without a certificate. The subject, serial number, and validity dates
- * of the certificate can be customized in this spec. The self-signed certificate may be replaced at
- * a later time by a certificate signed by a Certificate Authority (CA).
+ * <p>For asymmetric key pairs, a X.509 certificate will be also generated and stored in the Android
+ * Keystore. This is because the {@link java.security.KeyStore} abstraction does not support storing
+ * key pairs without a certificate. The subject, serial number, and validity dates of the
+ * certificate can be customized in this spec. The certificate may be replaced at a later time by a
+ * certificate signed by a Certificate Authority (CA).
*
- * <p>NOTE: If a private key is not authorized to sign the self-signed certificate, then the
- * certificate will be created with an invalid signature which will not verify. Such a certificate
- * is still useful because it provides access to the public key. To generate a valid signature for
- * the certificate the key needs to be authorized for all of the following:
+ * <p>NOTE: If attestation is not requested using {@link Builder#setAttestationChallenge(byte[])},
+ * generated certificate may be self-signed. If a private key is not authorized to sign the
+ * certificate, then the certificate will be created with an invalid signature which will not
+ * verify. Such a certificate is still useful because it provides access to the public key. To
+ * generate a valid signature for the certificate the key needs to be authorized for all of the
+ * following:
* <ul>
* <li>{@link KeyProperties#PURPOSE_SIGN},</li>
* <li>operation without requiring the user to be authenticated (see
@@ -989,12 +991,6 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
* @param purposes set of purposes (e.g., encrypt, decrypt, sign) for which the key can be
* used. Attempts to use the key for any other purpose will be rejected.
*
- * <p>If the set of purposes for which the key can be used does not contain
- * {@link KeyProperties#PURPOSE_SIGN}, the self-signed certificate generated by
- * {@link KeyPairGenerator} of {@code AndroidKeyStore} provider will contain an
- * invalid signature. This is OK if the certificate is only used for obtaining the
- * public key from Android KeyStore.
- *
* <p>See {@link KeyProperties}.{@code PURPOSE} flags.
*/
public Builder(@NonNull String keystoreAlias, @KeyProperties.PurposeEnum int purposes) {
@@ -1140,7 +1136,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
}
/**
- * Sets the subject used for the self-signed certificate of the generated key pair.
+ * Sets the subject used for the certificate of the generated key pair.
*
* <p>By default, the subject is {@code CN=fake}.
*/
@@ -1154,7 +1150,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
}
/**
- * Sets the serial number used for the self-signed certificate of the generated key pair.
+ * Sets the serial number used for the certificate of the generated key pair.
*
* <p>By default, the serial number is {@code 1}.
*/
@@ -1168,8 +1164,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
}
/**
- * Sets the start of the validity period for the self-signed certificate of the generated
- * key pair.
+ * Sets the start of the validity period for the certificate of the generated key pair.
*
* <p>By default, this date is {@code Jan 1 1970}.
*/
@@ -1183,8 +1178,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
}
/**
- * Sets the end of the validity period for the self-signed certificate of the generated key
- * pair.
+ * Sets the end of the validity period for the certificate of the generated key pair.
*
* <p>By default, this date is {@code Jan 1 2048}.
*/
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index c72a42cce2bd..187964947b94 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -151,6 +151,7 @@ android_library {
static_libs: [
"androidx.appcompat_appcompat",
"androidx.core_core-animation",
+ "androidx.core_core-ktx",
"androidx.arch.core_core-runtime",
"androidx-constraintlayout_constraintlayout",
"androidx.dynamicanimation_dynamicanimation",
diff --git a/libs/WindowManager/Shell/res/values-television/config.xml b/libs/WindowManager/Shell/res/values-television/config.xml
index da8abde8407f..8d2e28b9492f 100644
--- a/libs/WindowManager/Shell/res/values-television/config.xml
+++ b/libs/WindowManager/Shell/res/values-television/config.xml
@@ -45,13 +45,13 @@
<integer name="config_pipForceCloseDelay">5000</integer>
<!-- Animation duration when exit starting window: fade out icon -->
- <integer name="starting_window_app_reveal_icon_fade_out_duration">500</integer>
+ <integer name="starting_window_app_reveal_icon_fade_out_duration">200</integer>
<!-- Animation delay when exit starting window: reveal app -->
- <integer name="starting_window_app_reveal_anim_delay">0</integer>
+ <integer name="starting_window_app_reveal_anim_delay">200</integer>
<!-- Animation duration when exit starting window: reveal app -->
- <integer name="starting_window_app_reveal_anim_duration">500</integer>
+ <integer name="starting_window_app_reveal_anim_duration">300</integer>
<!-- Default animation type when hiding the starting window. The possible values are:
- 0 for radial vanish + slide up
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 2241c343a208..ac5ba51ec139 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1784,13 +1784,14 @@ public class BubbleStackView extends FrameLayout
mStackOnLeftOrWillBe = mPositioner.isStackOnLeft(startPosition);
mStackAnimationController.setStackPosition(startPosition);
mExpandedAnimationController.setCollapsePoint(startPosition);
- // Set the translation x so that this bubble will animate in from the same side they
- // expand / collapse on.
- bubble.getIconView().setTranslationX(startPosition.x);
} else if (firstBubble) {
mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide();
}
+ // Set the view translation x so that this bubble will animate in from the same side they
+ // expand / collapse on.
+ bubble.getIconView().setTranslationX(mStackAnimationController.getStackPosition().x);
+
mBubbleContainer.addView(bubble.getIconView(), 0,
new FrameLayout.LayoutParams(mPositioner.getBubbleSize(),
mPositioner.getBubbleSize()));
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 c51af46accdb..ea7b2e92fefb 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
@@ -605,6 +605,7 @@ public abstract class WMShellBaseModule {
@Provides
static Transitions provideTransitions(Context context,
ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
ShellController shellController,
ShellTaskOrganizer organizer,
TransactionPool pool,
@@ -612,14 +613,13 @@ public abstract class WMShellBaseModule {
@ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler,
@ShellAnimationThread ShellExecutor animExecutor,
- ShellCommandHandler shellCommandHandler,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
if (!context.getResources().getBoolean(R.bool.config_registerShellTransitionsOnInit)) {
// TODO(b/238217847): Force override shell init if registration is disabled
shellInit = new ShellInit(mainExecutor);
}
- return new Transitions(context, shellInit, shellController, organizer, pool,
- displayController, mainExecutor, mainHandler, animExecutor, shellCommandHandler,
+ return new Transitions(context, shellInit, shellCommandHandler, shellController, organizer,
+ pool, displayController, mainExecutor, mainHandler, animExecutor,
rootTaskDisplayAreaOrganizer);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/OWNERS
new file mode 100644
index 000000000000..74a29ddad073
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/OWNERS
@@ -0,0 +1 @@
+hwwang@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
index 8a6403705c1c..1898ea737729 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
@@ -17,19 +17,28 @@
package com.android.wm.shell.dagger.pip;
import android.annotation.NonNull;
+import android.content.Context;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.dagger.WMShellBaseModule;
import com.android.wm.shell.dagger.WMSingleton;
+import com.android.wm.shell.pip2.phone.PipController;
import com.android.wm.shell.pip2.phone.PipTransition;
+import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import dagger.Module;
import dagger.Provides;
+import java.util.Optional;
+
/**
* Provides dependencies from {@link com.android.wm.shell.pip2}, this implementation is meant to be
* the successor of its sibling {@link Pip1Module}.
@@ -42,8 +51,26 @@ public abstract class Pip2Module {
@NonNull ShellTaskOrganizer shellTaskOrganizer,
@NonNull Transitions transitions,
PipBoundsState pipBoundsState,
- PipBoundsAlgorithm pipBoundsAlgorithm) {
+ PipBoundsAlgorithm pipBoundsAlgorithm,
+ Optional<PipController> pipController) {
return new PipTransition(shellInit, shellTaskOrganizer, transitions, pipBoundsState, null,
pipBoundsAlgorithm);
}
+
+ @WMSingleton
+ @Provides
+ static Optional<PipController> providePipController(Context context,
+ ShellInit shellInit,
+ ShellController shellController,
+ DisplayController displayController,
+ DisplayInsetsController displayInsetsController,
+ PipDisplayLayoutState pipDisplayLayoutState) {
+ if (!PipUtils.isPip2ExperimentEnabled()) {
+ return Optional.empty();
+ } else {
+ return Optional.ofNullable(PipController.create(
+ context, shellInit, shellController, displayController, displayInsetsController,
+ pipDisplayLayoutState));
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
new file mode 100644
index 000000000000..186cb615f4ec
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
@@ -0,0 +1,133 @@
+/*
+ * 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.wm.shell.pip2.phone;
+
+import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.view.InsetsState;
+
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
+import com.android.wm.shell.common.pip.PipUtils;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.sysui.ConfigurationChangeListener;
+import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
+
+/**
+ * Manages the picture-in-picture (PIP) UI and states for Phones.
+ */
+public class PipController implements ConfigurationChangeListener,
+ DisplayController.OnDisplaysChangedListener {
+ private static final String TAG = PipController.class.getSimpleName();
+
+ private Context mContext;
+ private ShellController mShellController;
+ private DisplayController mDisplayController;
+ private DisplayInsetsController mDisplayInsetsController;
+ private PipDisplayLayoutState mPipDisplayLayoutState;
+
+ private PipController(Context context,
+ ShellInit shellInit,
+ ShellController shellController,
+ DisplayController displayController,
+ DisplayInsetsController displayInsetsController,
+ PipDisplayLayoutState pipDisplayLayoutState) {
+ mContext = context;
+ mShellController = shellController;
+ mDisplayController = displayController;
+ mDisplayInsetsController = displayInsetsController;
+ mPipDisplayLayoutState = pipDisplayLayoutState;
+
+ if (PipUtils.isPip2ExperimentEnabled()) {
+ shellInit.addInitCallback(this::onInit, this);
+ }
+ }
+
+ private void onInit() {
+ // Ensure that we have the display info in case we get calls to update the bounds before the
+ // listener calls back
+ mPipDisplayLayoutState.setDisplayId(mContext.getDisplayId());
+ DisplayLayout layout = new DisplayLayout(mContext, mContext.getDisplay());
+ mPipDisplayLayoutState.setDisplayLayout(layout);
+
+ mShellController.addConfigurationChangeListener(this);
+ mDisplayController.addDisplayWindowListener(this);
+ mDisplayInsetsController.addInsetsChangedListener(mPipDisplayLayoutState.getDisplayId(),
+ new DisplayInsetsController.OnInsetsChangedListener() {
+ @Override
+ public void insetsChanged(InsetsState insetsState) {
+ onDisplayChanged(mDisplayController
+ .getDisplayLayout(mPipDisplayLayoutState.getDisplayId()));
+ }
+ });
+ }
+
+ /**
+ * Instantiates {@link PipController}, returns {@code null} if the feature not supported.
+ */
+ public static PipController create(Context context,
+ ShellInit shellInit,
+ ShellController shellController,
+ DisplayController displayController,
+ DisplayInsetsController displayInsetsController,
+ PipDisplayLayoutState pipDisplayLayoutState) {
+ if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
+ ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: Device doesn't support Pip feature", TAG);
+ return null;
+ }
+ return new PipController(context, shellInit, shellController, displayController,
+ displayInsetsController, pipDisplayLayoutState);
+ }
+
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfiguration) {
+ mPipDisplayLayoutState.onConfigurationChanged();
+ }
+
+ @Override
+ public void onThemeChanged() {
+ onDisplayChanged(new DisplayLayout(mContext, mContext.getDisplay()));
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ if (displayId != mPipDisplayLayoutState.getDisplayId()) {
+ return;
+ }
+ onDisplayChanged(mDisplayController.getDisplayLayout(displayId));
+ }
+
+ @Override
+ public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
+ if (displayId != mPipDisplayLayoutState.getDisplayId()) {
+ return;
+ }
+ onDisplayChanged(mDisplayController.getDisplayLayout(displayId));
+ }
+
+ private void onDisplayChanged(DisplayLayout layout) {
+ mPipDisplayLayoutState.setDisplayLayout(layout);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index d31476c63890..d277eef761e9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -925,19 +925,8 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
if (toHome) wct.reorder(mRecentsTask, true /* toTop */);
else wct.restoreTransientOrder(mRecentsTask);
}
- if (!toHome
- // If a recents gesture starts on the 3p launcher, then the 3p launcher is the
- // live tile (pausing app). If the gesture is "cancelled" we need to return to
- // 3p launcher instead of "task-switching" away from it.
- && (!mWillFinishToHome || mPausingSeparateHome)
- && mPausingTasks != null && mState == STATE_NORMAL) {
- if (mPausingSeparateHome) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
- " returning to 3p home");
- } else {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
- " returning to app");
- }
+ if (!toHome && !mWillFinishToHome && mPausingTasks != null && mState == STATE_NORMAL) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, " returning to app");
// The gesture is returning to the pausing-task(s) rather than continuing with
// recents, so end the transition by moving the app back to the top (and also
// re-showing it's task).
@@ -969,6 +958,15 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
wct.restoreTransientOrder(mRecentsTask);
}
} else {
+ if (mPausingSeparateHome) {
+ if (mOpeningTasks.isEmpty()) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ " recents occluded 3p home");
+ } else {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ " switch task by recents on 3p home");
+ }
+ }
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, " normal finish");
// The general case: committing to recents, going home, or switching tasks.
for (int i = 0; i < mOpeningTasks.size(); ++i) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 68ca2313f709..7a4834cb5adb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -23,6 +23,7 @@ import static android.app.ComponentOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVIT
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
import static android.content.res.Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
@@ -395,6 +396,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return mMainStage.isActive();
}
+ /** @return whether this transition-request has the launch-adjacent flag. */
+ public boolean requestHasLaunchAdjacentFlag(TransitionRequestInfo request) {
+ final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
+ return triggerTask != null && triggerTask.baseIntent != null
+ && (triggerTask.baseIntent.getFlags() & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0;
+ }
+
/** @return whether the transition-request implies entering pip from split. */
public boolean requestImpliesSplitToPip(TransitionRequestInfo request) {
if (!isSplitActive() || !mMixedHandler.requestHasPipEnter(request)) {
@@ -2459,10 +2467,20 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP);
}
- // When split in the background, it should be only opening/dismissing transition and
- // would keep out not empty. Prevent intercepting all transitions for split screen when
- // it is in the background and not identify to handle it.
- return (!out.isEmpty() || isSplitScreenVisible()) ? out : null;
+ if (!out.isEmpty()) {
+ // One of the cases above handled it
+ return out;
+ } else if (isSplitScreenVisible()) {
+ // If split is visible, only defer handling this transition if it's launching
+ // adjacent while there is already a split pair -- this may trigger PIP and
+ // that should be handled by the mixed handler.
+ final boolean deferTransition = requestHasLaunchAdjacentFlag(request)
+ && mMainStage.getChildCount() != 0 && mSideStage.getChildCount() != 0;
+ return !deferTransition ? out : null;
+ }
+ // Don't intercept the transition if we are not handling it as a part of one of the
+ // cases above and it is not already visible
+ return null;
} else {
if (isOpening && getStageOfTask(triggerTask) != null) {
// One task is appearing into split, prepare to enter split screen.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
index 8b050e524038..b1fc16ddf19b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
@@ -63,7 +63,7 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler {
@NonNull Transitions.TransitionFinishCallback finishCallback) {
if (mTransition != transition) return false;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Using registered One-shot remote"
- + " transition %s for #%d.", mRemote, info.getDebugId());
+ + " transition %s for (#%d).", mRemote, info.getDebugId());
final IBinder.DeathRecipient remoteDied = () -> {
Log.e(Transitions.TAG, "Remote transition died, finishing");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index 592b22a47bc4..ca2c3b4fbff1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -126,7 +126,7 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
}
}
}
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Delegate animation for #%d to %s",
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Delegate animation for (#%d) to %s",
info.getDebugId(), pendingRemote);
if (pendingRemote == null) return false;
@@ -241,7 +241,7 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
if (remote == null) return null;
mRequestedRemotes.put(transition, remote);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "RemoteTransition directly requested"
- + " for %s: %s", transition, remote);
+ + " for (#%d) %s: %s", request.getDebugId(), transition, remote);
return new WindowContainerTransaction();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 0d9a9e9f07ff..576bba96044c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -179,6 +179,7 @@ public class Transitions implements RemoteCallable<Transitions>,
private final DefaultTransitionHandler mDefaultTransitionHandler;
private final RemoteTransitionHandler mRemoteTransitionHandler;
private final DisplayController mDisplayController;
+ private final ShellCommandHandler mShellCommandHandler;
private final ShellController mShellController;
private final ShellTransitionImpl mImpl = new ShellTransitionImpl();
private final SleepHandler mSleepHandler = new SleepHandler();
@@ -188,9 +189,6 @@ public class Transitions implements RemoteCallable<Transitions>,
/** List of possible handlers. Ordered by specificity (eg. tapped back to front). */
private final ArrayList<TransitionHandler> mHandlers = new ArrayList<>();
- @Nullable
- private final ShellCommandHandler mShellCommandHandler;
-
private final ArrayList<TransitionObserver> mObservers = new ArrayList<>();
/** List of {@link Runnable} instances to run when the last active transition has finished. */
@@ -237,7 +235,7 @@ public class Transitions implements RemoteCallable<Transitions>,
@Override
public String toString() {
if (mInfo != null && mInfo.getDebugId() >= 0) {
- return "(#" + mInfo.getDebugId() + ")" + mToken + "@" + getTrack();
+ return "(#" + mInfo.getDebugId() + ") " + mToken + "@" + getTrack();
}
return mToken.toString() + "@" + getTrack();
}
@@ -275,13 +273,14 @@ public class Transitions implements RemoteCallable<Transitions>,
@NonNull ShellExecutor mainExecutor,
@NonNull Handler mainHandler,
@NonNull ShellExecutor animExecutor) {
- this(context, shellInit, shellController, organizer, pool, displayController, mainExecutor,
- mainHandler, animExecutor, null,
+ this(context, shellInit, new ShellCommandHandler(), shellController, organizer, pool,
+ displayController, mainExecutor, mainHandler, animExecutor,
new RootTaskDisplayAreaOrganizer(mainExecutor, context, shellInit));
}
public Transitions(@NonNull Context context,
@NonNull ShellInit shellInit,
+ @Nullable ShellCommandHandler shellCommandHandler,
@NonNull ShellController shellController,
@NonNull WindowOrganizer organizer,
@NonNull TransactionPool pool,
@@ -289,7 +288,6 @@ public class Transitions implements RemoteCallable<Transitions>,
@NonNull ShellExecutor mainExecutor,
@NonNull Handler mainHandler,
@NonNull ShellExecutor animExecutor,
- @Nullable ShellCommandHandler shellCommandHandler,
@NonNull RootTaskDisplayAreaOrganizer rootTDAOrganizer) {
mOrganizer = organizer;
mContext = context;
@@ -300,13 +298,13 @@ public class Transitions implements RemoteCallable<Transitions>,
mDefaultTransitionHandler = new DefaultTransitionHandler(context, shellInit,
displayController, pool, mainExecutor, mainHandler, animExecutor, rootTDAOrganizer);
mRemoteTransitionHandler = new RemoteTransitionHandler(mMainExecutor);
+ mShellCommandHandler = shellCommandHandler;
mShellController = shellController;
// The very last handler (0 in the list) should be the default one.
mHandlers.add(mDefaultTransitionHandler);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Default");
// Next lowest priority is remote transitions.
mHandlers.add(mRemoteTransitionHandler);
- mShellCommandHandler = shellCommandHandler;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Remote");
shellInit.addInitCallback(this::onInit, this);
}
@@ -339,9 +337,8 @@ public class Transitions implements RemoteCallable<Transitions>,
TransitionMetrics.getInstance();
}
- if (mShellCommandHandler != null) {
- mShellCommandHandler.addCommandCallback("transitions", this, this);
- }
+ mShellCommandHandler.addCommandCallback("transitions", this, this);
+ mShellCommandHandler.addDumpCallback(this::dump, this);
}
public boolean isRegistered() {
@@ -655,8 +652,8 @@ public class Transitions implements RemoteCallable<Transitions>,
void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
info.setUnreleasedWarningCallSiteForAllSurfaces("Transitions.onTransitionReady");
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady %s: %s",
- transitionToken, info);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady (#%d) %s: %s",
+ info.getDebugId(), transitionToken, info);
final int activeIdx = findByToken(mPendingTransitions, transitionToken);
if (activeIdx < 0) {
throw new IllegalStateException("Got transitionReady for non-pending transition "
@@ -1073,8 +1070,8 @@ public class Transitions implements RemoteCallable<Transitions>,
void requestStartTransition(@NonNull IBinder transitionToken,
@Nullable TransitionRequestInfo request) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition requested: %s %s",
- transitionToken, request);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition requested (#%d): %s %s",
+ request.getDebugId(), transitionToken, request);
if (isTransitionKnown(transitionToken)) {
throw new RuntimeException("Transition already started " + transitionToken);
}
@@ -1475,4 +1472,68 @@ public class Transitions implements RemoteCallable<Transitions>,
pw.println(prefix + "tracing");
mTracer.printShellCommandHelp(pw, prefix + " ");
}
+
+ private void dump(@NonNull PrintWriter pw, String prefix) {
+ pw.println(prefix + TAG);
+
+ final String innerPrefix = prefix + " ";
+ pw.println(prefix + "Handlers:");
+ for (TransitionHandler handler : mHandlers) {
+ pw.print(innerPrefix);
+ pw.print(handler.getClass().getSimpleName());
+ pw.println(" (" + Integer.toHexString(System.identityHashCode(handler)) + ")");
+ }
+
+ pw.println(prefix + "Observers:");
+ for (TransitionObserver observer : mObservers) {
+ pw.print(innerPrefix);
+ pw.println(observer.getClass().getSimpleName());
+ }
+
+ pw.println(prefix + "Pending Transitions:");
+ for (ActiveTransition transition : mPendingTransitions) {
+ pw.print(innerPrefix + "token=");
+ pw.println(transition.mToken);
+ pw.print(innerPrefix + "id=");
+ pw.println(transition.mInfo != null
+ ? transition.mInfo.getDebugId()
+ : -1);
+ pw.print(innerPrefix + "handler=");
+ pw.println(transition.mHandler != null
+ ? transition.mHandler.getClass().getSimpleName()
+ : null);
+ }
+ if (mPendingTransitions.isEmpty()) {
+ pw.println(innerPrefix + "none");
+ }
+
+ pw.println(prefix + "Ready-during-sync Transitions:");
+ for (ActiveTransition transition : mReadyDuringSync) {
+ pw.print(innerPrefix + "token=");
+ pw.println(transition.mToken);
+ pw.print(innerPrefix + "id=");
+ pw.println(transition.mInfo != null
+ ? transition.mInfo.getDebugId()
+ : -1);
+ pw.print(innerPrefix + "handler=");
+ pw.println(transition.mHandler != null
+ ? transition.mHandler.getClass().getSimpleName()
+ : null);
+ }
+ if (mReadyDuringSync.isEmpty()) {
+ pw.println(innerPrefix + "none");
+ }
+
+ pw.println(prefix + "Tracks:");
+ for (int i = 0; i < mTracks.size(); i++) {
+ final ActiveTransition transition = mTracks.get(i).mActiveTransition;
+ pw.println(innerPrefix + "Track #" + i);
+ pw.print(innerPrefix + "active=");
+ pw.println(transition);
+ if (transition != null) {
+ pw.print(innerPrefix + "hander=");
+ pw.println(transition.mHandler);
+ }
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
index 389db62ef9d2..dadd264596fb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.windowdecor;
import android.graphics.PointF;
import android.graphics.Rect;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.window.WindowContainerTransaction;
@@ -45,6 +46,7 @@ class FluidResizeTaskPositioner implements DragPositioningCallback {
private final int mDisallowedAreaForEndBoundsHeight;
private boolean mHasDragResized;
private int mCtrlType;
+ @Surface.Rotation private int mRotation;
FluidResizeTaskPositioner(ShellTaskOrganizer taskOrganizer, WindowDecoration windowDecoration,
DisplayController displayController, int disallowedAreaForEndBoundsHeight) {
@@ -78,7 +80,10 @@ class FluidResizeTaskPositioner implements DragPositioningCallback {
mTaskOrganizer.applyTransaction(wct);
}
mRepositionTaskBounds.set(mTaskBoundsAtDragStart);
- if (mStableBounds.isEmpty()) {
+ int rotation = mWindowDecoration
+ .mTaskInfo.configuration.windowConfiguration.getDisplayRotation();
+ if (mStableBounds.isEmpty() || mRotation != rotation) {
+ mRotation = rotation;
mDisplayController.getDisplayLayout(mWindowDecoration.mDisplay.getDisplayId())
.getStableBounds(mStableBounds);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
index 303954a2d9fb..852c037baad5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
@@ -21,6 +21,7 @@ import static android.view.WindowManager.TRANSIT_CHANGE;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.IBinder;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
@@ -58,6 +59,7 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback,
private final int mDisallowedAreaForEndBoundsHeight;
private final Supplier<SurfaceControl.Transaction> mTransactionSupplier;
private int mCtrlType;
+ @Surface.Rotation private int mRotation;
public VeiledResizeTaskPositioner(ShellTaskOrganizer taskOrganizer,
DesktopModeWindowDecoration windowDecoration, DisplayController displayController,
@@ -98,7 +100,10 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback,
}
mDragStartListener.onDragStart(mDesktopWindowDecoration.mTaskInfo.taskId);
mRepositionTaskBounds.set(mTaskBoundsAtDragStart);
- if (mStableBounds.isEmpty()) {
+ int rotation = mDesktopWindowDecoration
+ .mTaskInfo.configuration.windowConfiguration.getDisplayRotation();
+ if (mStableBounds.isEmpty() || mRotation != rotation) {
+ mRotation = rotation;
mDisplayController.getDisplayLayout(mDesktopWindowDecoration.mDisplay.getDisplayId())
.getStableBounds(mStableBounds);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 0548a8e751cc..d0e647b30a96 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -297,7 +297,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
}
// Task surface itself
- float shadowRadius = loadDimension(resources, params.mShadowRadiusId);
+ float shadowRadius;
final Point taskPosition = mTaskInfo.positionInParent;
if (isFullscreen) {
// Setting the task crop to the width/height stops input events from being sent to
@@ -308,9 +308,12 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
// drag-resized by the window decoration.
startT.setWindowCrop(mTaskSurface, null);
finishT.setWindowCrop(mTaskSurface, null);
+ // Shadow is not needed for fullscreen tasks
+ shadowRadius = 0;
} else {
startT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight);
finishT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight);
+ shadowRadius = loadDimension(resources, params.mShadowRadiusId);
}
startT.setShadowRadius(mTaskSurface, shadowRadius)
.show(mTaskSurface);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
index c0c4498e3ebf..add78b2ee8b3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
@@ -6,6 +6,9 @@ import android.graphics.Rect
import android.os.IBinder
import android.testing.AndroidTestingRunner
import android.view.Display
+import android.view.Surface
+import android.view.Surface.ROTATION_270
+import android.view.Surface.ROTATION_90
import android.view.SurfaceControl
import android.window.WindowContainerToken
import android.window.WindowContainerTransaction
@@ -24,6 +27,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
+import org.mockito.Mockito
import org.mockito.Mockito.any
import org.mockito.Mockito.argThat
import org.mockito.Mockito.never
@@ -76,7 +80,15 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
whenever(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout)
whenever(mockDisplayLayout.densityDpi()).thenReturn(DENSITY_DPI)
whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { i ->
- (i.arguments.first() as Rect).set(STABLE_BOUNDS)
+ if (mockWindowDecoration.mTaskInfo.configuration.windowConfiguration
+ .displayRotation == ROTATION_90 ||
+ mockWindowDecoration.mTaskInfo.configuration.windowConfiguration
+ .displayRotation == ROTATION_270
+ ) {
+ (i.arguments.first() as Rect).set(STABLE_BOUNDS_LANDSCAPE)
+ } else {
+ (i.arguments.first() as Rect).set(STABLE_BOUNDS_PORTRAIT)
+ }
}
`when`(mockDisplayLayout.stableInsets()).thenReturn(STABLE_INSETS)
`when`(mockTransactionFactory.get()).thenReturn(mockTransaction)
@@ -89,6 +101,7 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
defaultMinSize = DEFAULT_MIN
displayId = DISPLAY_ID
configuration.windowConfiguration.setBounds(STARTING_BOUNDS)
+ configuration.windowConfiguration.displayRotation = ROTATION_90
}
mockWindowDecoration.mDisplay = mockDisplay
whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID }
@@ -623,7 +636,7 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
)
val newX = STARTING_BOUNDS.left.toFloat()
- val newY = STABLE_BOUNDS.top.toFloat() - 5
+ val newY = STABLE_BOUNDS_LANDSCAPE.top.toFloat() - 5
taskPositioner.onDragPositioningMove(
newX,
newY
@@ -641,9 +654,81 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
token == taskBinder &&
(change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
change.configuration.windowConfiguration.bounds.top ==
- STABLE_BOUNDS.top
+ STABLE_BOUNDS_LANDSCAPE.top
+ }
+ })
+ }
+
+ @Test
+ fun testDragResize_drag_updatesStableBoundsOnRotate() {
+ // Test landscape stable bounds
+ performDrag(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat(),
+ STARTING_BOUNDS.right.toFloat() + 2000, STARTING_BOUNDS.bottom.toFloat() + 2000,
+ CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM)
+ val rectAfterDrag = Rect(STARTING_BOUNDS)
+ rectAfterDrag.right += 2000
+ // First drag; we should fetch stable bounds.
+ verify(mockDisplayLayout, Mockito.times(1)).getStableBounds(any())
+ verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
+ change.configuration.windowConfiguration.bounds == rectAfterDrag
+ }
+ })
+ // Drag back to starting bounds.
+ performDrag(
+ STARTING_BOUNDS.right.toFloat() + 2000, STARTING_BOUNDS.bottom.toFloat(),
+ STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat(),
+ CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM)
+
+ // Display did not rotate; we should use previous stable bounds
+ verify(mockDisplayLayout, Mockito.times(1)).getStableBounds(any())
+
+ // Rotate the screen to portrait
+ mockWindowDecoration.mTaskInfo.apply {
+ configuration.windowConfiguration.displayRotation = Surface.ROTATION_0
+ }
+ // Test portrait stable bounds
+ performDrag(
+ STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat(),
+ STARTING_BOUNDS.right.toFloat() + 2000, STARTING_BOUNDS.bottom.toFloat() + 2000,
+ CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM)
+ rectAfterDrag.right -= 2000
+ rectAfterDrag.bottom += 2000
+
+ verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
+ change.configuration.windowConfiguration.bounds == rectAfterDrag
}
})
+ // Display has rotated; we expect a new stable bounds.
+ verify(mockDisplayLayout, Mockito.times(2)).getStableBounds(any())
+ }
+
+ private fun performDrag(
+ startX: Float,
+ startY: Float,
+ endX: Float,
+ endY: Float,
+ ctrlType: Int
+ ) {
+ taskPositioner.onDragPositioningStart(
+ ctrlType,
+ startX,
+ startY
+ )
+ taskPositioner.onDragPositioningMove(
+ endX,
+ endY
+ )
+
+ taskPositioner.onDragPositioningEnd(
+ endX,
+ endY
+ )
}
companion object {
@@ -664,11 +749,17 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT,
DISPLAY_BOUNDS.right,
DISPLAY_BOUNDS.bottom)
- private val STABLE_BOUNDS = Rect(
+ private val STABLE_BOUNDS_LANDSCAPE = Rect(
DISPLAY_BOUNDS.left,
DISPLAY_BOUNDS.top + CAPTION_HEIGHT,
DISPLAY_BOUNDS.right,
DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT
)
+ private val STABLE_BOUNDS_PORTRAIT = Rect(
+ DISPLAY_BOUNDS.top,
+ DISPLAY_BOUNDS.left + CAPTION_HEIGHT,
+ DISPLAY_BOUNDS.bottom,
+ DISPLAY_BOUNDS.right - NAVBAR_HEIGHT
+ )
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
index 8913453aa578..a70ebf14324a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
@@ -21,6 +21,9 @@ import android.graphics.Rect
import android.os.IBinder
import android.testing.AndroidTestingRunner
import android.view.Display
+import android.view.Surface.ROTATION_0
+import android.view.Surface.ROTATION_270
+import android.view.Surface.ROTATION_90
import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_CHANGE
import android.window.WindowContainerToken
@@ -30,6 +33,7 @@ import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM
import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT
import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP
import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_UNDEFINED
@@ -93,10 +97,17 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
whenever(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout)
whenever(mockDisplayLayout.densityDpi()).thenReturn(DENSITY_DPI)
whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { i ->
- (i.arguments.first() as Rect).set(STABLE_BOUNDS)
+ if (mockDesktopWindowDecoration.mTaskInfo.configuration.windowConfiguration
+ .displayRotation == ROTATION_90 ||
+ mockDesktopWindowDecoration.mTaskInfo.configuration.windowConfiguration
+ .displayRotation == ROTATION_270
+ ) {
+ (i.arguments.first() as Rect).set(STABLE_BOUNDS_LANDSCAPE)
+ } else {
+ (i.arguments.first() as Rect).set(STABLE_BOUNDS_PORTRAIT)
+ }
}
`when`(mockTransactionFactory.get()).thenReturn(mockTransaction)
-
mockDesktopWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply {
taskId = TASK_ID
token = taskToken
@@ -105,6 +116,7 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
defaultMinSize = DEFAULT_MIN
displayId = DISPLAY_ID
configuration.windowConfiguration.setBounds(STARTING_BOUNDS)
+ configuration.windowConfiguration.displayRotation = ROTATION_90
}
mockDesktopWindowDecoration.mDisplay = mockDisplay
whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID }
@@ -343,7 +355,7 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
)
val newX = STARTING_BOUNDS.left.toFloat()
- val newY = STABLE_BOUNDS.top.toFloat() - 5
+ val newY = STABLE_BOUNDS_LANDSCAPE.top.toFloat() - 5
taskPositioner.onDragPositioningMove(
newX,
newY
@@ -361,11 +373,79 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
token == taskBinder &&
(change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
change.configuration.windowConfiguration.bounds.top ==
- STABLE_BOUNDS.top
+ STABLE_BOUNDS_LANDSCAPE.top
}
})
}
+ @Test
+ fun testDragResize_drag_updatesStableBoundsOnRotate() {
+ // Test landscape stable bounds
+ performDrag(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat(),
+ STARTING_BOUNDS.right.toFloat() + 2000, STARTING_BOUNDS.bottom.toFloat() + 2000,
+ CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM)
+ val rectAfterDrag = Rect(STARTING_BOUNDS)
+ rectAfterDrag.right += 2000
+ // First drag; we should fetch stable bounds.
+ verify(mockDisplayLayout, times(1)).getStableBounds(any())
+ verify(mockTransitions).startTransition(eq(TRANSIT_CHANGE), argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
+ change.configuration.windowConfiguration.bounds == rectAfterDrag}},
+ eq(taskPositioner))
+ // Drag back to starting bounds.
+ performDrag(STARTING_BOUNDS.right.toFloat() + 2000, STARTING_BOUNDS.bottom.toFloat(),
+ STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat(),
+ CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM)
+
+ // Display did not rotate; we should use previous stable bounds
+ verify(mockDisplayLayout, times(1)).getStableBounds(any())
+
+ // Rotate the screen to portrait
+ mockDesktopWindowDecoration.mTaskInfo.apply {
+ configuration.windowConfiguration.displayRotation = ROTATION_0
+ }
+ // Test portrait stable bounds
+ performDrag(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat(),
+ STARTING_BOUNDS.right.toFloat() + 2000, STARTING_BOUNDS.bottom.toFloat() + 2000,
+ CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM)
+ rectAfterDrag.right -= 2000
+ rectAfterDrag.bottom += 2000
+
+ verify(mockTransitions).startTransition(eq(TRANSIT_CHANGE), argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
+ change.configuration.windowConfiguration.bounds == rectAfterDrag}},
+ eq(taskPositioner))
+ // Display has rotated; we expect a new stable bounds.
+ verify(mockDisplayLayout, times(2)).getStableBounds(any())
+ }
+
+ private fun performDrag(
+ startX: Float,
+ startY: Float,
+ endX: Float,
+ endY: Float,
+ ctrlType: Int
+ ) {
+ taskPositioner.onDragPositioningStart(
+ ctrlType,
+ startX,
+ startY
+ )
+ taskPositioner.onDragPositioningMove(
+ endX,
+ endY
+ )
+
+ taskPositioner.onDragPositioningEnd(
+ endX,
+ endY
+ )
+ }
+
companion object {
private const val TASK_ID = 5
private const val MIN_WIDTH = 10
@@ -378,11 +458,17 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
private const val DISALLOWED_AREA_FOR_END_BOUNDS_HEIGHT = 10
private val DISPLAY_BOUNDS = Rect(0, 0, 2400, 1600)
private val STARTING_BOUNDS = Rect(100, 100, 200, 200)
- private val STABLE_BOUNDS = Rect(
+ private val STABLE_BOUNDS_LANDSCAPE = Rect(
DISPLAY_BOUNDS.left,
DISPLAY_BOUNDS.top + CAPTION_HEIGHT,
DISPLAY_BOUNDS.right,
DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT
)
+ private val STABLE_BOUNDS_PORTRAIT = Rect(
+ DISPLAY_BOUNDS.top,
+ DISPLAY_BOUNDS.left + CAPTION_HEIGHT,
+ DISPLAY_BOUNDS.bottom,
+ DISPLAY_BOUNDS.right - NAVBAR_HEIGHT
+ )
}
}
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index 7eb0c76fbf4e..4a5b4f2de20f 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -74,8 +74,7 @@ interface IMediaRouterService {
// Methods for MediaRouter2Manager
List<RoutingSessionInfo> getRemoteSessions(IMediaRouter2Manager manager);
- RoutingSessionInfo getSystemSessionInfoForPackage(
- IMediaRouter2Manager manager, String packageName);
+ RoutingSessionInfo getSystemSessionInfoForPackage(String packageName);
void registerManager(IMediaRouter2Manager manager, String packageName);
void unregisterManager(IMediaRouter2Manager manager);
void setRouteVolumeWithManager(IMediaRouter2Manager manager, int requestId,
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 21690904fe42..159427bc2796 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -2059,9 +2059,7 @@ public final class MediaRouter2 {
public RoutingSessionInfo getSystemSessionInfo() {
RoutingSessionInfo result;
try {
- result =
- mMediaRouterService.getSystemSessionInfoForPackage(
- mClient, mClientPackageName);
+ result = mMediaRouterService.getSystemSessionInfoForPackage(mClientPackageName);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 3abfc6297b0b..830708cb38b2 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -377,7 +377,7 @@ public final class MediaRouter2Manager {
@Nullable
public RoutingSessionInfo getSystemRoutingSession(@Nullable String packageName) {
try {
- return mMediaRouterService.getSystemSessionInfoForPackage(mClient, packageName);
+ return mMediaRouterService.getSystemSessionInfoForPackage(packageName);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
diff --git a/media/java/android/media/RingtoneV1.java b/media/java/android/media/RingtoneV1.java
index b761afaeaa67..3c54d4a0d166 100644
--- a/media/java/android/media/RingtoneV1.java
+++ b/media/java/android/media/RingtoneV1.java
@@ -16,14 +16,15 @@
package android.media;
-import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources.NotFoundException;
import android.media.audiofx.HapticGenerator;
import android.net.Uri;
import android.os.Binder;
+import android.os.Build;
import android.os.RemoteException;
import android.os.Trace;
import android.os.VibrationEffect;
@@ -61,7 +62,6 @@ class RingtoneV1 implements Ringtone.ApiInterface {
private final Context mContext;
private final AudioManager mAudioManager;
- private final Ringtone.Injectables mInjectables;
private VolumeShaper.Configuration mVolumeShaperConfig;
private VolumeShaper mVolumeShaper;
@@ -74,10 +74,12 @@ class RingtoneV1 implements Ringtone.ApiInterface {
private final IRingtonePlayer mRemotePlayer;
private final Binder mRemoteToken;
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private MediaPlayer mLocalPlayer;
private final MyOnCompletionListener mCompletionListener = new MyOnCompletionListener();
private HapticGenerator mHapticGenerator;
+ @UnsupportedAppUsage
private Uri mUri;
private String mTitle;
@@ -92,15 +94,10 @@ class RingtoneV1 implements Ringtone.ApiInterface {
private boolean mHapticGeneratorEnabled = false;
private final Object mPlaybackSettingsLock = new Object();
- /** @hide */
+ /** {@hide} */
+ @UnsupportedAppUsage
public RingtoneV1(Context context, boolean allowRemote) {
- this(context, new Ringtone.Injectables(), allowRemote);
- }
-
- /** @hide */
- RingtoneV1(Context context, @NonNull Ringtone.Injectables injectables, boolean allowRemote) {
mContext = context;
- mInjectables = injectables;
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
mAllowRemote = allowRemote;
mRemotePlayer = allowRemote ? mAudioManager.getRingtonePlayer() : null;
@@ -203,7 +200,7 @@ class RingtoneV1 implements Ringtone.ApiInterface {
}
destroyLocalPlayer();
// try opening uri locally before delegating to remote player
- mLocalPlayer = mInjectables.newMediaPlayer();
+ mLocalPlayer = new MediaPlayer();
try {
mLocalPlayer.setDataSource(mContext, mUri);
mLocalPlayer.setAudioAttributes(mAudioAttributes);
@@ -243,7 +240,19 @@ class RingtoneV1 implements Ringtone.ApiInterface {
*/
public boolean hasHapticChannels() {
// FIXME: support remote player, or internalize haptic channels support and remove entirely.
- return mInjectables.hasHapticChannels(mLocalPlayer);
+ try {
+ android.os.Trace.beginSection("Ringtone.hasHapticChannels");
+ if (mLocalPlayer != null) {
+ for(MediaPlayer.TrackInfo trackInfo : mLocalPlayer.getTrackInfo()) {
+ if (trackInfo.hasHapticChannels()) {
+ return true;
+ }
+ }
+ }
+ } finally {
+ android.os.Trace.endSection();
+ }
+ return false;
}
/**
@@ -325,7 +334,7 @@ class RingtoneV1 implements Ringtone.ApiInterface {
* @see android.media.audiofx.HapticGenerator#isAvailable()
*/
public boolean setHapticGeneratorEnabled(boolean enabled) {
- if (!mInjectables.isHapticGeneratorAvailable()) {
+ if (!HapticGenerator.isAvailable()) {
return false;
}
synchronized (mPlaybackSettingsLock) {
@@ -353,7 +362,7 @@ class RingtoneV1 implements Ringtone.ApiInterface {
mLocalPlayer.setVolume(mVolume);
mLocalPlayer.setLooping(mIsLooping);
if (mHapticGenerator == null && mHapticGeneratorEnabled) {
- mHapticGenerator = mInjectables.createHapticGenerator(mLocalPlayer);
+ mHapticGenerator = HapticGenerator.create(mLocalPlayer.getAudioSessionId());
}
if (mHapticGenerator != null) {
mHapticGenerator.setEnabled(mHapticGeneratorEnabled);
@@ -388,6 +397,7 @@ class RingtoneV1 implements Ringtone.ApiInterface {
*
* @hide
*/
+ @UnsupportedAppUsage
public void setUri(Uri uri) {
setUri(uri, null);
}
@@ -415,6 +425,7 @@ class RingtoneV1 implements Ringtone.ApiInterface {
}
/** {@hide} */
+ @UnsupportedAppUsage
public Uri getUri() {
return mUri;
}
@@ -545,7 +556,7 @@ class RingtoneV1 implements Ringtone.ApiInterface {
Log.e(TAG, "Could not load fallback ringtone");
return false;
}
- mLocalPlayer = mInjectables.newMediaPlayer();
+ mLocalPlayer = new MediaPlayer();
if (afd.getDeclaredLength() < 0) {
mLocalPlayer.setDataSource(afd.getFileDescriptor());
} else {
@@ -583,12 +594,12 @@ class RingtoneV1 implements Ringtone.ApiInterface {
}
public boolean isLocalOnly() {
- return !mAllowRemote;
+ return mAllowRemote;
}
public boolean isUsingRemotePlayer() {
// V2 testing api, but this is the v1 approximation.
- return (mLocalPlayer == null) && mAllowRemote && (mRemotePlayer != null) && (mUri != null);
+ return (mLocalPlayer == null) && mAllowRemote && (mRemotePlayer != null);
}
class MyOnCompletionListener implements MediaPlayer.OnCompletionListener {
diff --git a/media/java/android/media/midi/MidiUmpDeviceService.java b/media/java/android/media/midi/MidiUmpDeviceService.java
index bbbe7f683b05..c54bfce840aa 100644
--- a/media/java/android/media/midi/MidiUmpDeviceService.java
+++ b/media/java/android/media/midi/MidiUmpDeviceService.java
@@ -16,6 +16,9 @@
package android.media.midi;
+import static com.android.media.midi.flags.Flags.FLAG_VIRTUAL_UMP;
+
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Service;
@@ -54,9 +57,11 @@ import java.util.List;
* android:resource="@xml/device_info" />
* &lt;/service></pre>
*/
+@FlaggedApi(FLAG_VIRTUAL_UMP)
public abstract class MidiUmpDeviceService extends Service {
private static final String TAG = "MidiUmpDeviceService";
+ @FlaggedApi(FLAG_VIRTUAL_UMP)
public static final String SERVICE_INTERFACE = "android.media.midi.MidiUmpDeviceService";
private IMidiManager mMidiManager;
@@ -75,6 +80,7 @@ public abstract class MidiUmpDeviceService extends Service {
}
};
+ @FlaggedApi(FLAG_VIRTUAL_UMP)
@Override
public void onCreate() {
mMidiManager = IMidiManager.Stub.asInterface(
@@ -112,6 +118,7 @@ public abstract class MidiUmpDeviceService extends Service {
* The number of input and output ports must be equal and non-zero.
* @return list of MidiReceivers
*/
+ @FlaggedApi(FLAG_VIRTUAL_UMP)
public abstract @NonNull List<MidiReceiver> onGetInputPortReceivers();
/**
@@ -120,6 +127,7 @@ public abstract class MidiUmpDeviceService extends Service {
* The number of input and output ports must be equal and non-zero.
* @return the list of MidiReceivers
*/
+ @FlaggedApi(FLAG_VIRTUAL_UMP)
public final @NonNull List<MidiReceiver> getOutputPortReceivers() {
if (mServer == null) {
return new ArrayList<MidiReceiver>();
@@ -132,6 +140,7 @@ public abstract class MidiUmpDeviceService extends Service {
* Returns the {@link MidiDeviceInfo} instance for this service
* @return the MidiDeviceInfo of the virtual MIDI device if it was successfully created
*/
+ @FlaggedApi(FLAG_VIRTUAL_UMP)
public final @Nullable MidiDeviceInfo getDeviceInfo() {
return mDeviceInfo;
}
@@ -140,6 +149,7 @@ public abstract class MidiUmpDeviceService extends Service {
* Called to notify when the {@link MidiDeviceStatus} has changed
* @param status the current status of the MIDI device
*/
+ @FlaggedApi(FLAG_VIRTUAL_UMP)
public void onDeviceStatusChanged(@NonNull MidiDeviceStatus status) {
}
@@ -147,9 +157,11 @@ public abstract class MidiUmpDeviceService extends Service {
* Called to notify when the virtual MIDI device running in this service has been closed by
* all its clients
*/
+ @FlaggedApi(FLAG_VIRTUAL_UMP)
public void onClose() {
}
+ @FlaggedApi(FLAG_VIRTUAL_UMP)
@Override
public @Nullable IBinder onBind(@NonNull Intent intent) {
if (SERVICE_INTERFACE.equals(intent.getAction()) && mServer != null) {
diff --git a/media/java/android/media/projection/IMediaProjectionManager.aidl b/media/java/android/media/projection/IMediaProjectionManager.aidl
index 80e22477efed..31e65eb13926 100644
--- a/media/java/android/media/projection/IMediaProjectionManager.aidl
+++ b/media/java/android/media/projection/IMediaProjectionManager.aidl
@@ -175,5 +175,5 @@ interface IMediaProjectionManager {
@EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION")
@JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+ ".permission.MANAGE_MEDIA_PROJECTION)")
- void notifyPermissionRequestStateChange(int hostUid, int state, int sessionCreationSource);
+ oneway void notifyPermissionRequestStateChange(int hostUid, int state, int sessionCreationSource);
}
diff --git a/media/tests/ringtone/src/com/android/media/RingtoneBuilderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RingtoneTest.java
index 2c8daba86d19..3c0c6847f557 100644
--- a/media/tests/ringtone/src/com/android/media/RingtoneBuilderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RingtoneTest.java
@@ -14,22 +14,20 @@
* limitations under the License.
*/
-package com.android.media;
+package com.android.mediaframeworktest.unit;
import static android.media.Ringtone.MEDIA_SOUND;
import static android.media.Ringtone.MEDIA_SOUND_AND_VIBRATION;
import static android.media.Ringtone.MEDIA_VIBRATION;
-import static com.android.media.testing.MediaPlayerTestHelper.verifyPlayerFallbackSetup;
-import static com.android.media.testing.MediaPlayerTestHelper.verifyPlayerSetup;
-import static com.android.media.testing.MediaPlayerTestHelper.verifyPlayerStarted;
-import static com.android.media.testing.MediaPlayerTestHelper.verifyPlayerStopped;
-
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
@@ -55,29 +53,34 @@ import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.testing.TestableContext;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import com.android.framework.base.media.ringtone.tests.R;
-import com.android.media.testing.RingtoneInjectablesTrackingTestRule;
+import com.android.mediaframeworktest.R;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
import org.junit.runner.RunWith;
+import org.junit.runners.model.Statement;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.io.FileNotFoundException;
+import java.util.ArrayDeque;
+import java.util.Map;
+import java.util.Queue;
-/**
- * Test behavior of {@link Ringtone} when it's created via {@link Ringtone.Builder}.
- */
@RunWith(AndroidJUnit4.class)
-public class RingtoneBuilderTest {
+public class RingtoneTest {
private static final Uri SOUND_URI = Uri.parse("content://fake-sound-uri");
@@ -90,8 +93,11 @@ public class RingtoneBuilderTest {
private static final VibrationEffect VIBRATION_EFFECT =
VibrationEffect.createWaveform(new long[] { 0, 100, 50, 100}, -1);
+ private static final VibrationEffect VIBRATION_EFFECT_REPEATING =
+ VibrationEffect.createWaveform(new long[] { 0, 100, 50, 100, 50}, 1);
- @Rule public final RingtoneInjectablesTrackingTestRule
+ @Rule
+ public final RingtoneInjectablesTrackingTestRule
mMediaPlayerRule = new RingtoneInjectablesTrackingTestRule();
@Captor private ArgumentCaptor<IBinder> mIBinderCaptor;
@@ -116,7 +122,6 @@ public class RingtoneBuilderTest {
mContext = spy(testContext);
}
-
@Test
public void testRingtone_fullLifecycleUsingLocalMediaPlayer() throws Exception {
MediaPlayer mockMediaPlayer = mMediaPlayerRule.expectLocalMediaPlayer();
@@ -137,14 +142,14 @@ public class RingtoneBuilderTest {
assertThat(ringtone.isLocalOnly()).isFalse();
// Prepare
- verifyPlayerSetup(mContext, mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES);
+ verifyLocalPlayerSetup(mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES);
verify(mockMediaPlayer).setVolume(1.0f);
verify(mockMediaPlayer).setLooping(false);
verify(mockMediaPlayer).prepare();
// Play
ringtone.play();
- verifyPlayerStarted(mockMediaPlayer);
+ verifyLocalPlay(mockMediaPlayer);
// Verify dynamic controls.
ringtone.setVolume(0.8f);
@@ -160,7 +165,7 @@ public class RingtoneBuilderTest {
// Release
ringtone.stop();
- verifyPlayerStopped(mockMediaPlayer);
+ verifyLocalStop(mockMediaPlayer);
// This test is intended to strictly verify all interactions with MediaPlayer in a local
// playback case. This shouldn't be necessary in other tests that have the same basic
@@ -194,16 +199,16 @@ public class RingtoneBuilderTest {
assertThat(ringtone.getAudioAttributes()).isEqualTo(audioAttributes);
// Prepare
- verifyPlayerSetup(mContext, mockMediaPlayer, SOUND_URI, audioAttributes);
+ verifyLocalPlayerSetup(mockMediaPlayer, SOUND_URI, audioAttributes);
verify(mockMediaPlayer).prepare();
// Play
ringtone.play();
- verifyPlayerStarted(mockMediaPlayer);
+ verifyLocalPlay(mockMediaPlayer);
// Release
ringtone.stop();
- verifyPlayerStopped(mockMediaPlayer);
+ verifyLocalStop(mockMediaPlayer);
verifyZeroInteractions(mMockRemotePlayer);
verifyZeroInteractions(mMockVibrator);
@@ -215,8 +220,8 @@ public class RingtoneBuilderTest {
setupFileNotFound(mockMediaPlayer, SOUND_URI);
Ringtone ringtone =
newBuilder(MEDIA_SOUND, RINGTONE_ATTRIBUTES)
- .setUri(SOUND_URI)
- .build();
+ .setUri(SOUND_URI)
+ .build();
assertThat(ringtone).isNotNull();
assertThat(ringtone.isUsingRemotePlayer()).isTrue();
@@ -279,7 +284,7 @@ public class RingtoneBuilderTest {
// Prepare
// Uses attributes with haptic channels enabled, but will use the effect when there aren't
// any present.
- verifyPlayerSetup(mContext, mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES_WITH_HC);
+ verifyLocalPlayerSetup(mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES_WITH_HC);
verify(mockMediaPlayer).setVolume(1.0f);
verify(mockMediaPlayer).setLooping(false);
verify(mockMediaPlayer).prepare();
@@ -287,7 +292,7 @@ public class RingtoneBuilderTest {
// Play
ringtone.play();
- verifyPlayerStarted(mockMediaPlayer);
+ verifyLocalPlay(mockMediaPlayer);
verify(mMockVibrator).vibrate(VIBRATION_EFFECT, RINGTONE_VIB_ATTRIBUTES);
// Verify dynamic controls.
@@ -305,7 +310,7 @@ public class RingtoneBuilderTest {
// Release
ringtone.stop();
- verifyPlayerStopped(mockMediaPlayer);
+ verifyLocalStop(mockMediaPlayer);
verify(mMockVibrator).cancel(VibrationAttributes.USAGE_RINGTONE);
// This test is intended to strictly verify all interactions with MediaPlayer in a local
@@ -383,7 +388,7 @@ public class RingtoneBuilderTest {
// Prepare
// Uses attributes with haptic channels enabled, but will abandon the MediaPlayer when it
// knows there aren't any.
- verifyPlayerSetup(mContext, mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES_WITH_HC);
+ verifyLocalPlayerSetup(mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES_WITH_HC);
verify(mockMediaPlayer).setVolume(0.0f); // Vibration-only: sound muted.
verify(mockMediaPlayer).setLooping(false);
verify(mockMediaPlayer).prepare();
@@ -438,7 +443,7 @@ public class RingtoneBuilderTest {
// Prepare
// Uses attributes with haptic channels enabled, but will use the effect when there aren't
// any present.
- verifyPlayerSetup(mContext, mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES_WITH_HC);
+ verifyLocalPlayerSetup(mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES_WITH_HC);
verify(mockMediaPlayer).setVolume(0.0f); // Vibration-only: sound muted.
verify(mockMediaPlayer).setLooping(false);
verify(mockMediaPlayer).prepare();
@@ -446,7 +451,7 @@ public class RingtoneBuilderTest {
// Play
ringtone.play();
// Vibrator.vibrate isn't called because the vibration comes from the sound.
- verifyPlayerStarted(mockMediaPlayer);
+ verifyLocalPlay(mockMediaPlayer);
// Verify dynamic controls (no-op without sound)
ringtone.setVolume(0.8f);
@@ -461,7 +466,7 @@ public class RingtoneBuilderTest {
// Release
ringtone.stop();
- verifyPlayerStopped(mockMediaPlayer);
+ verifyLocalStop(mockMediaPlayer);
// This test is intended to strictly verify all interactions with MediaPlayer in a local
// playback case. This shouldn't be necessary in other tests that have the same basic
@@ -491,17 +496,17 @@ public class RingtoneBuilderTest {
// Prepare
// The attributes here have haptic channels enabled (unlike above)
- verifyPlayerSetup(mContext, mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES_WITH_HC);
+ verifyLocalPlayerSetup(mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES_WITH_HC);
verify(mockMediaPlayer).prepare();
// Play
ringtone.play();
when(mockMediaPlayer.isPlaying()).thenReturn(true);
- verifyPlayerStarted(mockMediaPlayer);
+ verifyLocalPlay(mockMediaPlayer);
// Release
ringtone.stop();
- verifyPlayerStopped(mockMediaPlayer);
+ verifyLocalStop(mockMediaPlayer);
verifyZeroInteractions(mMockRemotePlayer);
// Nothing after the initial hasVibrator - it uses audio-coupled.
@@ -531,7 +536,7 @@ public class RingtoneBuilderTest {
// Prepare
// The attributes here have haptic channels enabled (unlike above)
- verifyPlayerSetup(mContext, mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES_WITH_HC);
+ verifyLocalPlayerSetup(mockMediaPlayer, SOUND_URI, RINGTONE_ATTRIBUTES_WITH_HC);
verify(mockMediaPlayer).prepare();
// Play
@@ -554,7 +559,7 @@ public class RingtoneBuilderTest {
@Test
public void testRingtone_nullMediaOnBuilderUsesFallback() throws Exception {
AssetFileDescriptor testResourceFd =
- mContext.getResources().openRawResourceFd(R.raw.test_sound_file);
+ mContext.getResources().openRawResourceFd(R.raw.shortmp3);
// Ensure it will flow as expected.
assertThat(testResourceFd).isNotNull();
assertThat(testResourceFd.getDeclaredLength()).isAtLeast(0);
@@ -570,18 +575,18 @@ public class RingtoneBuilderTest {
// Delegates straight to fallback in local player.
// Prepare
- verifyPlayerFallbackSetup(mockMediaPlayer, testResourceFd, RINGTONE_ATTRIBUTES);
+ verifyLocalPlayerFallbackSetup(mockMediaPlayer, testResourceFd, RINGTONE_ATTRIBUTES);
verify(mockMediaPlayer).setVolume(1.0f);
verify(mockMediaPlayer).setLooping(false);
verify(mockMediaPlayer).prepare();
// Play
ringtone.play();
- verifyPlayerStarted(mockMediaPlayer);
+ verifyLocalPlay(mockMediaPlayer);
// Release
ringtone.stop();
- verifyPlayerStopped(mockMediaPlayer);
+ verifyLocalStop(mockMediaPlayer);
verifyNoMoreInteractions(mockMediaPlayer);
verifyNoMoreInteractions(mMockRemotePlayer);
@@ -610,10 +615,24 @@ public class RingtoneBuilderTest {
verifyNoMoreInteractions(mMockRemotePlayer);
}
+ @Test
+ public void testRingtone_noMediaSetOnBuilderFallbackFailsAndNoRemote() throws Exception {
+ mContext.getOrCreateTestableResources()
+ .addOverride(com.android.internal.R.raw.fallbackring, null);
+ Ringtone ringtone = newBuilder(MEDIA_SOUND, RINGTONE_ATTRIBUTES)
+ .setUri(null)
+ .setLocalOnly()
+ .build();
+ // Local player fallback fails as the resource isn't found (no media player creation is
+ // attempted), and since there is no local player, the ringtone ends up having nothing to
+ // do.
+ assertThat(ringtone).isNull();
+ }
+
private Ringtone.Builder newBuilder(@Ringtone.RingtoneMedia int ringtoneMedia,
AudioAttributes audioAttributes) {
return new Ringtone.Builder(mContext, ringtoneMedia, audioAttributes)
- .setInjectables(mMediaPlayerRule.getRingtoneTestInjectables());
+ .setInjectables(mMediaPlayerRule.injectables);
}
private static AudioAttributes audioAttributes(int audioUsage) {
@@ -628,4 +647,194 @@ public class RingtoneBuilderTest {
doThrow(new FileNotFoundException("Fake file not found"))
.when(mockMediaPlayer).setDataSource(any(Context.class), eq(uri));
}
+
+ private void verifyLocalPlayerSetup(MediaPlayer mockPlayer, Uri expectedUri,
+ AudioAttributes expectedAudioAttributes) throws Exception {
+ verify(mockPlayer).setDataSource(mContext, expectedUri);
+ verify(mockPlayer).setAudioAttributes(expectedAudioAttributes);
+ verify(mockPlayer).setPreferredDevice(null);
+ verify(mockPlayer).prepare();
+ }
+
+ private void verifyLocalPlayerFallbackSetup(MediaPlayer mockPlayer, AssetFileDescriptor afd,
+ AudioAttributes expectedAudioAttributes) throws Exception {
+ // This is very specific but it's a simple way to test that the test resource matches.
+ if (afd.getDeclaredLength() < 0) {
+ verify(mockPlayer).setDataSource(afd.getFileDescriptor());
+ } else {
+ verify(mockPlayer).setDataSource(afd.getFileDescriptor(),
+ afd.getStartOffset(),
+ afd.getDeclaredLength());
+ }
+ verify(mockPlayer).setAudioAttributes(expectedAudioAttributes);
+ verify(mockPlayer).setPreferredDevice(null);
+ verify(mockPlayer).prepare();
+ }
+
+ private void verifyLocalPlay(MediaPlayer mockMediaPlayer) {
+ verify(mockMediaPlayer).setOnCompletionListener(any());
+ verify(mockMediaPlayer).start();
+ }
+
+ private void verifyLocalStop(MediaPlayer mockMediaPlayer) {
+ verify(mockMediaPlayer).stop();
+ verify(mockMediaPlayer).setOnCompletionListener(isNull());
+ verify(mockMediaPlayer).reset();
+ verify(mockMediaPlayer).release();
+ }
+
+ /**
+ * This rule ensures that all expected media player creations from the factory do actually
+ * occur. The reason for this level of control is that creating a media player is fairly
+ * expensive and blocking, so we do want unit tests of this class to "declare" interactions
+ * of all created media players.
+ *
+ * This needs to be a TestRule so that the teardown assertions can be skipped if the test has
+ * failed (and media player assertions may just be a distracting side effect). Otherwise, the
+ * teardown failures hide the real test ones.
+ */
+ public static class RingtoneInjectablesTrackingTestRule implements TestRule {
+ public Ringtone.Injectables injectables = new TestInjectables();
+ public boolean hapticGeneratorAvailable = true;
+
+ // Queue of (local) media players, in order of expected creation. Enqueue using
+ // expectNewMediaPlayer(), dequeued by the media player factory passed to Ringtone.
+ // This queue is asserted to be empty at the end of the test.
+ private Queue<MediaPlayer> mMockMediaPlayerQueue = new ArrayDeque<>();
+
+ // Similar to media players, but for haptic generator, which also needs releasing.
+ private Map<MediaPlayer, HapticGenerator> mMockHapticGeneratorMap = new ArrayMap<>();
+
+ // Media players with haptic channels.
+ private ArraySet<MediaPlayer> mHapticChannels = new ArraySet<>();
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ base.evaluate();
+ // Only assert if the test didn't fail (base.evaluate() would throw).
+ assertWithMessage("Test setup an expectLocalMediaPlayer but it wasn't consumed")
+ .that(mMockMediaPlayerQueue).isEmpty();
+ // Only assert if the test didn't fail (base.evaluate() would throw).
+ assertWithMessage(
+ "Test setup an expectLocalHapticGenerator but it wasn't consumed")
+ .that(mMockHapticGeneratorMap).isEmpty();
+ }
+ };
+ }
+
+ private TestMediaPlayer expectLocalMediaPlayer() {
+ TestMediaPlayer mockMediaPlayer = Mockito.mock(TestMediaPlayer.class);
+ // Delegate to simulated methods. This means they can be verified but also reflect
+ // realistic transitions from the TestMediaPlayer.
+ doCallRealMethod().when(mockMediaPlayer).start();
+ doCallRealMethod().when(mockMediaPlayer).stop();
+ doCallRealMethod().when(mockMediaPlayer).setLooping(anyBoolean());
+ when(mockMediaPlayer.isLooping()).thenCallRealMethod();
+ when(mockMediaPlayer.isLooping()).thenCallRealMethod();
+ mMockMediaPlayerQueue.add(mockMediaPlayer);
+ return mockMediaPlayer;
+ }
+
+ private HapticGenerator expectHapticGenerator(MediaPlayer mockMediaPlayer) {
+ HapticGenerator mockHapticGenerator = Mockito.mock(HapticGenerator.class);
+ // A test should never want this.
+ assertWithMessage("Can't expect a second haptic generator created "
+ + "for one media player")
+ .that(mMockHapticGeneratorMap.put(mockMediaPlayer, mockHapticGenerator))
+ .isNull();
+ return mockHapticGenerator;
+ }
+
+ private void setHasHapticChannels(MediaPlayer mp, boolean hasHapticChannels) {
+ if (hasHapticChannels) {
+ mHapticChannels.add(mp);
+ } else {
+ mHapticChannels.remove(mp);
+ }
+ }
+
+ private class TestInjectables extends Ringtone.Injectables {
+ @Override
+ public MediaPlayer newMediaPlayer() {
+ assertWithMessage(
+ "Unexpected MediaPlayer creation. Bug or need expectNewMediaPlayer")
+ .that(mMockMediaPlayerQueue)
+ .isNotEmpty();
+ return mMockMediaPlayerQueue.remove();
+ }
+
+ @Override
+ public boolean isHapticGeneratorAvailable() {
+ return hapticGeneratorAvailable;
+ }
+
+ @Override
+ public HapticGenerator createHapticGenerator(MediaPlayer mediaPlayer) {
+ HapticGenerator mockHapticGenerator = mMockHapticGeneratorMap.remove(mediaPlayer);
+ assertWithMessage("Unexpected HapticGenerator creation. "
+ + "Bug or need expectHapticGenerator")
+ .that(mockHapticGenerator)
+ .isNotNull();
+ return mockHapticGenerator;
+ }
+
+ @Override
+ public boolean isHapticPlaybackSupported() {
+ return true;
+ }
+
+ @Override
+ public boolean hasHapticChannels(MediaPlayer mp) {
+ return mHapticChannels.contains(mp);
+ }
+ }
+ }
+
+ /**
+ * MediaPlayer relies on a native backend and so its necessary to intercept calls from
+ * fake usage hitting them.
+ *
+ * Mocks don't work directly on native calls, but if they're overridden then it does work.
+ * Some basic state faking is also done to make the mocks more realistic.
+ */
+ private static class TestMediaPlayer extends MediaPlayer {
+ private boolean mIsPlaying = false;
+ private boolean mIsLooping = false;
+
+ @Override
+ public void start() {
+ mIsPlaying = true;
+ }
+
+ @Override
+ public void stop() {
+ mIsPlaying = false;
+ }
+
+ @Override
+ public void setLooping(boolean value) {
+ mIsLooping = value;
+ }
+
+ @Override
+ public boolean isLooping() {
+ return mIsLooping;
+ }
+
+ @Override
+ public boolean isPlaying() {
+ return mIsPlaying;
+ }
+
+ void simulatePlayingFinished() {
+ if (!mIsPlaying) {
+ throw new IllegalStateException(
+ "Attempted to pretend playing finished when not playing");
+ }
+ mIsPlaying = false;
+ }
+ }
}
diff --git a/media/tests/ringtone/Android.bp b/media/tests/ringtone/Android.bp
index 8d1e5e3a5bab..55b98c4704b1 100644
--- a/media/tests/ringtone/Android.bp
+++ b/media/tests/ringtone/Android.bp
@@ -9,24 +9,15 @@ android_test {
srcs: ["src/**/*.java"],
libs: [
- "android.test.base",
- "android.test.mock",
"android.test.runner",
+ "android.test.base",
],
static_libs: [
- "androidx.test.ext.junit",
- "androidx.test.ext.truth",
"androidx.test.rules",
- "frameworks-base-testutils",
- "mockito-target-inline-minus-junit4",
- "testables",
"testng",
- ],
-
- jni_libs: [
- "libdexmakerjvmtiagent",
- "libstaticjvmtiagent",
+ "androidx.test.ext.truth",
+ "frameworks-base-testutils",
],
test_suites: [
diff --git a/media/tests/ringtone/OWNERS b/media/tests/ringtone/OWNERS
deleted file mode 100644
index 93b44f4788c5..000000000000
--- a/media/tests/ringtone/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-# Bug component: 345036
-
-include /services/core/java/com/android/server/vibrator/OWNERS
diff --git a/media/tests/ringtone/src/com/android/media/testing/MediaPlayerTestHelper.java b/media/tests/ringtone/src/com/android/media/testing/MediaPlayerTestHelper.java
deleted file mode 100644
index e97e1173a1ea..000000000000
--- a/media/tests/ringtone/src/com/android/media/testing/MediaPlayerTestHelper.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.media.testing;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.content.res.AssetFileDescriptor;
-import android.media.AudioAttributes;
-import android.media.MediaPlayer;
-import android.net.Uri;
-
-/**
- * Helper class with assertion methods on mock {@link MediaPlayer} instances.
- */
-public final class MediaPlayerTestHelper {
-
- /** Verify this local media player mock instance was started. */
- public static void verifyPlayerStarted(MediaPlayer mockMediaPlayer) {
- verify(mockMediaPlayer).setOnCompletionListener(any());
- verify(mockMediaPlayer).start();
- }
-
- /** Verify this local media player mock instance was stopped and released. */
- public static void verifyPlayerStopped(MediaPlayer mockMediaPlayer) {
- verify(mockMediaPlayer).stop();
- verify(mockMediaPlayer).setOnCompletionListener(isNull());
- verify(mockMediaPlayer).reset();
- verify(mockMediaPlayer).release();
- }
-
- /** Verify this local media player mock instance was setup with given attributes. */
- public static void verifyPlayerSetup(Context context, MediaPlayer mockPlayer,
- Uri expectedUri, AudioAttributes expectedAudioAttributes) throws Exception {
- verify(mockPlayer).setDataSource(context, expectedUri);
- verify(mockPlayer).setAudioAttributes(expectedAudioAttributes);
- verify(mockPlayer).setPreferredDevice(null);
- verify(mockPlayer).prepare();
- }
-
- /** Verify this local media player mock instance was setup with given fallback attributes. */
- public static void verifyPlayerFallbackSetup(MediaPlayer mockPlayer,
- AssetFileDescriptor afd, AudioAttributes expectedAudioAttributes) throws Exception {
- // This is very specific but it's a simple way to test that the test resource matches.
- if (afd.getDeclaredLength() < 0) {
- verify(mockPlayer).setDataSource(afd.getFileDescriptor());
- } else {
- verify(mockPlayer).setDataSource(afd.getFileDescriptor(),
- afd.getStartOffset(),
- afd.getDeclaredLength());
- }
- verify(mockPlayer).setAudioAttributes(expectedAudioAttributes);
- verify(mockPlayer).setPreferredDevice(null);
- verify(mockPlayer).prepare();
- }
-
- private MediaPlayerTestHelper() {
- }
-}
diff --git a/media/tests/ringtone/src/com/android/media/testing/RingtoneInjectablesTrackingTestRule.java b/media/tests/ringtone/src/com/android/media/testing/RingtoneInjectablesTrackingTestRule.java
deleted file mode 100644
index 25752ce83e5c..000000000000
--- a/media/tests/ringtone/src/com/android/media/testing/RingtoneInjectablesTrackingTestRule.java
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.media.testing;
-
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Mockito.doCallRealMethod;
-import static org.mockito.Mockito.when;
-
-import android.media.MediaPlayer;
-import android.media.Ringtone;
-import android.media.audiofx.HapticGenerator;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-import org.mockito.Mockito;
-
-import java.util.ArrayDeque;
-import java.util.Map;
-import java.util.Queue;
-
-/**
- * This rule ensures that all expected media player creations from the factory do actually
- * occur. The reason for this level of control is that creating a media player is fairly
- * expensive and blocking, so we do want unit tests of this class to "declare" interactions
- * of all created media players.
- * <p>
- * This needs to be a TestRule so that the teardown assertions can be skipped if the test has
- * failed (and media player assertions may just be a distracting side effect). Otherwise, the
- * teardown failures hide the real test ones.
- */
-public class RingtoneInjectablesTrackingTestRule implements TestRule {
-
- private final Ringtone.Injectables mRingtoneTestInjectables = new TestInjectables();
-
- // Queue of (local) media players, in order of expected creation. Enqueue using
- // expectNewMediaPlayer(), dequeued by the media player factory passed to Ringtone.
- // This queue is asserted to be empty at the end of the test.
- private final Queue<MediaPlayer> mMockMediaPlayerQueue = new ArrayDeque<>();
-
- // Similar to media players, but for haptic generator, which also needs releasing.
- private final Map<MediaPlayer, HapticGenerator> mMockHapticGeneratorMap = new ArrayMap<>();
-
- // Media players with haptic channels.
- private final ArraySet<MediaPlayer> mHapticChannels = new ArraySet<>();
-
- private boolean mHapticGeneratorAvailable = true;
-
- @Override
- public Statement apply(Statement base, Description description) {
- return new Statement() {
- @Override
- public void evaluate() throws Throwable {
- base.evaluate();
- // Only assert if the test didn't fail (base.evaluate() would throw).
- assertWithMessage("Test setup an expectLocalMediaPlayer but it wasn't consumed")
- .that(mMockMediaPlayerQueue).isEmpty();
- // Only assert if the test didn't fail (base.evaluate() would throw).
- assertWithMessage(
- "Test setup an expectLocalHapticGenerator but it wasn't consumed")
- .that(mMockHapticGeneratorMap).isEmpty();
- }
- };
- }
-
- /** The {@link Ringtone.Injectables} to be used for creating a testable {@link Ringtone}. */
- public Ringtone.Injectables getRingtoneTestInjectables() {
- return mRingtoneTestInjectables;
- }
-
- /**
- * Create a test {@link MediaPlayer} that will be provided to the {@link Ringtone} instance
- * created with {@link #getRingtoneTestInjectables()}.
- *
- * <p>If a media player is not created during the test execution after this method is called
- * then the test will fail. It will also fail if the ringtone attempts to create one without
- * this method being called first.
- */
- public TestMediaPlayer expectLocalMediaPlayer() {
- TestMediaPlayer mockMediaPlayer = Mockito.mock(TestMediaPlayer.class);
- // Delegate to simulated methods. This means they can be verified but also reflect
- // realistic transitions from the TestMediaPlayer.
- doCallRealMethod().when(mockMediaPlayer).start();
- doCallRealMethod().when(mockMediaPlayer).stop();
- doCallRealMethod().when(mockMediaPlayer).setLooping(anyBoolean());
- when(mockMediaPlayer.isLooping()).thenCallRealMethod();
- mMockMediaPlayerQueue.add(mockMediaPlayer);
- return mockMediaPlayer;
- }
-
- /**
- * Create a test {@link HapticGenerator} that will be provided to the {@link Ringtone} instance
- * created with {@link #getRingtoneTestInjectables()}.
- *
- * <p>If a haptic generator is not created during the test execution after this method is called
- * then the test will fail. It will also fail if the ringtone attempts to create one without
- * this method being called first.
- */
- public HapticGenerator expectHapticGenerator(MediaPlayer mediaPlayer) {
- HapticGenerator mockHapticGenerator = Mockito.mock(HapticGenerator.class);
- // A test should never want this.
- assertWithMessage("Can't expect a second haptic generator created "
- + "for one media player")
- .that(mMockHapticGeneratorMap.put(mediaPlayer, mockHapticGenerator))
- .isNull();
- return mockHapticGenerator;
- }
-
- /**
- * Configures the {@link MediaPlayer} to always return given flag when
- * {@link Ringtone.Injectables#hasHapticChannels(MediaPlayer)} is called.
- */
- public void setHasHapticChannels(MediaPlayer mp, boolean hasHapticChannels) {
- if (hasHapticChannels) {
- mHapticChannels.add(mp);
- } else {
- mHapticChannels.remove(mp);
- }
- }
-
- /** Test implementation of {@link Ringtone.Injectables} that uses the test rule setup. */
- private class TestInjectables extends Ringtone.Injectables {
- @Override
- public MediaPlayer newMediaPlayer() {
- assertWithMessage(
- "Unexpected MediaPlayer creation. Bug or need expectNewMediaPlayer")
- .that(mMockMediaPlayerQueue)
- .isNotEmpty();
- return mMockMediaPlayerQueue.remove();
- }
-
- @Override
- public boolean isHapticGeneratorAvailable() {
- return mHapticGeneratorAvailable;
- }
-
- @Override
- public HapticGenerator createHapticGenerator(MediaPlayer mediaPlayer) {
- HapticGenerator mockHapticGenerator = mMockHapticGeneratorMap.remove(mediaPlayer);
- assertWithMessage("Unexpected HapticGenerator creation. "
- + "Bug or need expectHapticGenerator")
- .that(mockHapticGenerator)
- .isNotNull();
- return mockHapticGenerator;
- }
-
- @Override
- public boolean isHapticPlaybackSupported() {
- return true;
- }
-
- @Override
- public boolean hasHapticChannels(MediaPlayer mp) {
- return mHapticChannels.contains(mp);
- }
- }
-
- /**
- * MediaPlayer relies on a native backend and so its necessary to intercept calls from
- * fake usage hitting them.
- * <p>
- * Mocks don't work directly on native calls, but if they're overridden then it does work.
- * Some basic state faking is also done to make the mocks more realistic.
- */
- public static class TestMediaPlayer extends MediaPlayer {
- private boolean mIsPlaying = false;
- private boolean mIsLooping = false;
-
- @Override
- public void start() {
- mIsPlaying = true;
- }
-
- @Override
- public void stop() {
- mIsPlaying = false;
- }
-
- @Override
- public void setLooping(boolean value) {
- mIsLooping = value;
- }
-
- @Override
- public boolean isLooping() {
- return mIsLooping;
- }
-
- @Override
- public boolean isPlaying() {
- return mIsPlaying;
- }
-
- /**
- * Updates {@link #isPlaying()} result to false, if it's set to true.
- *
- * @throws IllegalStateException is {@link #isPlaying()} is already false
- */
- public void simulatePlayingFinished() {
- if (!mIsPlaying) {
- throw new IllegalStateException(
- "Attempted to pretend playing finished when not playing");
- }
- mIsPlaying = false;
- }
- }
-}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index 01596d2bc004..d62b4907e96c 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -40,6 +40,7 @@ import com.android.settingslib.spa.gallery.page.LoadingBarPageProvider
import com.android.settingslib.spa.gallery.page.ProgressBarPageProvider
import com.android.settingslib.spa.gallery.page.SettingsPagerPageProvider
import com.android.settingslib.spa.gallery.page.SliderPageProvider
+import com.android.settingslib.spa.gallery.preference.ListPreferencePageProvider
import com.android.settingslib.spa.gallery.preference.MainSwitchPreferencePageProvider
import com.android.settingslib.spa.gallery.preference.PreferenceMainPageProvider
import com.android.settingslib.spa.gallery.preference.PreferencePageProvider
@@ -74,6 +75,7 @@ class GallerySpaEnvironment(context: Context) : SpaEnvironment(context) {
PreferencePageProvider,
SwitchPreferencePageProvider,
MainSwitchPreferencePageProvider,
+ ListPreferencePageProvider,
TwoTargetSwitchPreferencePageProvider,
ArgumentPageProvider,
SliderPageProvider,
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/ListPreferencePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/ListPreferencePageProvider.kt
new file mode 100644
index 000000000000..43b6d0b05696
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/ListPreferencePageProvider.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.gallery.preference
+
+import android.os.Bundle
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.common.createSettingsPage
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.preference.ListPreference
+import com.android.settingslib.spa.widget.preference.ListPreferenceModel
+import com.android.settingslib.spa.widget.preference.ListPreferenceOption
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.flow
+
+private const val TITLE = "Sample ListPreference"
+
+object ListPreferencePageProvider : SettingsPageProvider {
+ override val name = "ListPreference"
+ private val owner = createSettingsPage()
+
+ override fun buildEntry(arguments: Bundle?) = listOf(
+ SettingsEntryBuilder.create("ListPreference", owner)
+ .setUiLayoutFn {
+ SampleListPreference()
+ }.build(),
+ SettingsEntryBuilder.create("ListPreference not changeable", owner)
+ .setUiLayoutFn {
+ SampleNotChangeableListPreference()
+ }.build(),
+ )
+
+ fun buildInjectEntry(): SettingsEntryBuilder {
+ return SettingsEntryBuilder.createInject(owner)
+ .setUiLayoutFn {
+ Preference(object : PreferenceModel {
+ override val title = TITLE
+ override val onClick = navigator(name)
+ })
+ }
+ }
+
+ override fun getTitle(arguments: Bundle?) = TITLE
+}
+
+@Composable
+private fun SampleListPreference() {
+ val selectedId = rememberSaveable { mutableIntStateOf(1) }
+ ListPreference(remember {
+ object : ListPreferenceModel {
+ override val title = "Preferred network type"
+ override val options = listOf(
+ ListPreferenceOption(id = 1, text = "5G (recommended)"),
+ ListPreferenceOption(id = 2, text = "LTE"),
+ ListPreferenceOption(id = 3, text = "3G"),
+ )
+ override val selectedId = selectedId
+ override val onIdSelected: (id: Int) -> Unit = { selectedId.intValue = it }
+ }
+ })
+}
+
+@Composable
+private fun SampleNotChangeableListPreference() {
+ val selectedId = rememberSaveable { mutableIntStateOf(1) }
+ val enableFlow = flow {
+ var enabled = true
+ while (true) {
+ delay(3.seconds)
+ enabled = !enabled
+ emit(enabled)
+ }
+ }
+ val enabled = enableFlow.collectAsStateWithLifecycle(initialValue = true)
+ ListPreference(remember {
+ object : ListPreferenceModel {
+ override val title = "Preferred network type"
+ override val enabled = enabled
+ override val options = listOf(
+ ListPreferenceOption(id = 1, text = "5G (recommended)"),
+ ListPreferenceOption(id = 2, text = "LTE"),
+ ListPreferenceOption(id = 3, text = "3G"),
+ )
+ override val selectedId = selectedId
+ override val onIdSelected: (id: Int) -> Unit = { selectedId.intValue = it }
+ }
+ })
+}
+
+@Preview
+@Composable
+private fun ListPreferencePagePreview() {
+ SettingsTheme {
+ ListPreferencePageProvider.Page(null)
+ }
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMain.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMainPageProvider.kt
index eddede752d06..ce9678bab684 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMain.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMainPageProvider.kt
@@ -36,6 +36,7 @@ object PreferenceMainPageProvider : SettingsPageProvider {
PreferencePageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
SwitchPreferencePageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
MainSwitchPreferencePageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
+ ListPreferencePageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
TwoTargetSwitchPreferencePageProvider.buildInjectEntry()
.setLink(fromPage = owner).build(),
)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
index 7962e601999a..4088ffd43986 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
@@ -40,8 +40,18 @@ object SettingsDimension {
/** The size when app icon is displayed in App info page. */
val appIconInfoSize = 48.dp
+ /** The vertical padding for buttons. */
+ val buttonPaddingVertical = 12.dp
+
/** The [PaddingValues] for buttons. */
- val buttonPadding = PaddingValues(horizontal = itemPaddingEnd, vertical = 12.dp)
+ val buttonPadding = PaddingValues(horizontal = itemPaddingEnd, vertical = buttonPaddingVertical)
+
+ /** The horizontal padding for dialog items. */
+ val dialogItemPaddingHorizontal = itemPaddingStart
+
+ /** The [PaddingValues] for dialog items. */
+ val dialogItemPadding =
+ PaddingValues(horizontal = dialogItemPaddingHorizontal, vertical = buttonPaddingVertical)
/** The sizes info of illustration widget. */
val illustrationMaxWidth = 412.dp
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsOpacity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsOpacity.kt
index c8faef6d6703..a9cd0e9cd2e9 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsOpacity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsOpacity.kt
@@ -16,10 +16,15 @@
package com.android.settingslib.spa.framework.theme
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.alpha
+
object SettingsOpacity {
const val Full = 1f
const val Disabled = 0.38f
const val Divider = 0.2f
const val SurfaceTone = 0.14f
const val Hint = 0.9f
+
+ fun Modifier.alphaForEnabled(enabled: Boolean) = alpha(if (enabled) Full else Disabled)
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt
index c66e20a335ef..8c862d401c6b 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt
@@ -22,5 +22,5 @@ import androidx.compose.ui.unit.dp
object SettingsShape {
val CornerMedium = RoundedCornerShape(12.dp)
- val CornerLarge = RoundedCornerShape(24.dp)
+ val CornerExtraLarge = RoundedCornerShape(28.dp)
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt
index 1ad075c11985..979cf3bddae6 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt
@@ -65,7 +65,7 @@ fun ActionButtons(actionButtons: List<ActionButton>) {
Row(
Modifier
.padding(SettingsDimension.buttonPadding)
- .clip(SettingsShape.CornerLarge)
+ .clip(SettingsShape.CornerExtraLarge)
.height(IntrinsicSize.Min)
) {
for ((index, actionButton) in actionButtons.withIndex()) {
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsDialog.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsDialog.kt
new file mode 100644
index 000000000000..8b172da08dd2
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsDialog.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.widget.dialog
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Card
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.window.Dialog
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsShape
+import com.android.settingslib.spa.widget.ui.SettingsTitle
+
+@Composable
+fun SettingsDialog(
+ title: String,
+ onDismissRequest: () -> Unit,
+ content: @Composable () -> Unit,
+) {
+ Dialog(onDismissRequest = onDismissRequest) {
+ Card(shape = SettingsShape.CornerExtraLarge) {
+ Column(modifier = Modifier.padding(vertical = SettingsDimension.itemPaddingAround)) {
+ Box(modifier = Modifier.padding(SettingsDimension.dialogItemPadding)) {
+ SettingsTitle(title = title, useMediumWeight = true)
+ }
+ content()
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt
index 6330ddf5bea4..4d42fbae24fd 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt
@@ -29,13 +29,12 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.alpha
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.android.settingslib.spa.framework.compose.toState
import com.android.settingslib.spa.framework.theme.SettingsDimension
-import com.android.settingslib.spa.framework.theme.SettingsOpacity
+import com.android.settingslib.spa.framework.theme.SettingsOpacity.alphaForEnabled
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.widget.ui.SettingsTitle
@@ -57,8 +56,7 @@ internal fun BaseLayout(
.padding(end = paddingEnd),
verticalAlignment = Alignment.CenterVertically,
) {
- val alphaModifier =
- Modifier.alpha(if (enabled.value) SettingsOpacity.Full else SettingsOpacity.Disabled)
+ val alphaModifier = Modifier.alphaForEnabled(enabled.value)
BaseIcon(icon, alphaModifier, paddingStart)
Titles(
title = title,
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ListPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ListPreference.kt
new file mode 100644
index 000000000000..19779f67ca48
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ListPreference.kt
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.widget.preference
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.selection.selectable
+import androidx.compose.foundation.selection.selectableGroup
+import androidx.compose.material3.RadioButton
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.IntState
+import androidx.compose.runtime.State
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.semantics.Role
+import com.android.settingslib.spa.framework.compose.stateOf
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.widget.dialog.SettingsDialog
+import com.android.settingslib.spa.widget.ui.SettingsDialogItem
+
+data class ListPreferenceOption(
+ val id: Int,
+ val text: String,
+)
+
+/**
+ * The widget model for [ListPreference] widget.
+ */
+interface ListPreferenceModel {
+ /**
+ * The title of this [ListPreference].
+ */
+ val title: String
+
+ /**
+ * The icon of this [ListPreference].
+ *
+ * Default is `null` which means no icon.
+ */
+ val icon: (@Composable () -> Unit)?
+ get() = null
+
+ /**
+ * Indicates whether this [ListPreference] is enabled.
+ *
+ * Disabled [ListPreference] will be displayed in disabled style.
+ */
+ val enabled: State<Boolean>
+ get() = stateOf(true)
+
+ val options: List<ListPreferenceOption>
+
+ val selectedId: IntState
+
+ val onIdSelected: (id: Int) -> Unit
+}
+
+@Composable
+fun ListPreference(model: ListPreferenceModel) {
+ var dialogOpened by rememberSaveable { mutableStateOf(false) }
+ if (dialogOpened) {
+ SettingsDialog(
+ title = model.title,
+ onDismissRequest = { dialogOpened = false },
+ ) {
+ Column(modifier = Modifier.selectableGroup()) {
+ for (option in model.options) {
+ Radio(option, model.selectedId, model.enabled) {
+ dialogOpened = false
+ model.onIdSelected(it)
+ }
+ }
+ }
+ }
+ }
+ Preference(model = remember(model) {
+ object : PreferenceModel {
+ override val title = model.title
+ override val summary = derivedStateOf {
+ model.options.find { it.id == model.selectedId.intValue }?.text ?: ""
+ }
+ override val icon = model.icon
+ override val enabled = model.enabled
+ override val onClick = { dialogOpened = true }.takeIf { model.options.isNotEmpty() }
+ }
+ })
+}
+
+@Composable
+private fun Radio(
+ option: ListPreferenceOption,
+ selectedId: IntState,
+ enabledState: State<Boolean>,
+ onIdSelected: (id: Int) -> Unit,
+) {
+ val selected = option.id == selectedId.intValue
+ val enabled = enabledState.value
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .selectable(
+ selected = selected,
+ enabled = enabled,
+ onClick = { onIdSelected(option.id) },
+ role = Role.RadioButton,
+ )
+ .padding(SettingsDimension.dialogItemPadding),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ RadioButton(selected = selected, onClick = null, enabled = enabled)
+ Spacer(modifier = Modifier.width(SettingsDimension.itemPaddingEnd))
+ SettingsDialogItem(text = option.text, enabled = enabled)
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/MainSwitchPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/MainSwitchPreference.kt
index 3e04b16f08cf..0c16c8bc7229 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/MainSwitchPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/MainSwitchPreference.kt
@@ -39,7 +39,7 @@ fun MainSwitchPreference(model: SwitchPreferenceModel) {
true -> MaterialTheme.colorScheme.primaryContainer
else -> MaterialTheme.colorScheme.secondaryContainer
},
- shape = SettingsShape.CornerLarge,
+ shape = SettingsShape.CornerExtraLarge,
) {
InternalSwitchPreference(
title = model.title,
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt
index 6ef45900a103..5f320f7ade3f 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt
@@ -18,19 +18,14 @@ package com.android.settingslib.spa.widget.scaffold
import androidx.appcompat.R
import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.outlined.ArrowBack
import androidx.compose.material.icons.outlined.Clear
import androidx.compose.material.icons.outlined.FindInPage
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.composed
-import androidx.compose.ui.draw.scale
-import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.unit.LayoutDirection
import com.android.settingslib.spa.framework.compose.LocalNavController
-import androidx.compose.material.icons.automirrored.outlined.ArrowBack
/** Action that navigates back to last page. */
@Composable
@@ -55,7 +50,6 @@ private fun BackAction(contentDescription: String, onClick: () -> Unit) {
Icon(
imageVector = Icons.AutoMirrored.Outlined.ArrowBack,
contentDescription = contentDescription,
- modifier = Modifier.autoMirrored(),
)
}
}
@@ -81,10 +75,3 @@ internal fun ClearAction(onClick: () -> Unit) {
)
}
}
-
-private fun Modifier.autoMirrored() = composed {
- when (LocalLayoutDirection.current) {
- LayoutDirection.Rtl -> scale(scaleX = -1f, scaleY = 1f)
- else -> this
- }
-}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
index 57319e760c69..7f1acffe7a8a 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
@@ -30,6 +30,7 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsOpacity.alphaForEnabled
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.framework.theme.toMediumWeight
@@ -48,6 +49,17 @@ fun SettingsTitle(title: String, useMediumWeight: Boolean = false) {
}
@Composable
+fun SettingsDialogItem(text: String, enabled: Boolean = true) {
+ Text(
+ text = text,
+ modifier = Modifier.alphaForEnabled(enabled),
+ color = MaterialTheme.colorScheme.onSurface,
+ style = MaterialTheme.typography.bodyLarge,
+ overflow = TextOverflow.Ellipsis,
+ )
+}
+
+@Composable
fun SettingsBody(
body: String,
maxLines: Int = Int.MAX_VALUE,
@@ -82,6 +94,9 @@ fun PlaceholderTitle(title: String) {
private fun BasePreferencePreview() {
SettingsTheme {
Column(Modifier.width(100.dp)) {
+ SettingsTitle(
+ title = "Title",
+ )
SettingsBody(
body = "Long long long long long long text",
)
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/dialog/SettingsDialogTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/dialog/SettingsDialogTest.kt
new file mode 100644
index 000000000000..c7582b2601af
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/dialog/SettingsDialogTest.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.widget.dialog
+
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.testutils.onDialogText
+import com.android.settingslib.spa.widget.ui.SettingsDialogItem
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SettingsDialogTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Test
+ fun title_displayed() {
+ composeTestRule.setContent {
+ SettingsDialog(title = TITLE, onDismissRequest = {}) {}
+ }
+
+ composeTestRule.onDialogText(TITLE).assertIsDisplayed()
+ }
+
+ @Test
+ fun text_displayed() {
+ composeTestRule.setContent {
+ SettingsDialog(title = "", onDismissRequest = {}) {
+ SettingsDialogItem(text = TEXT)
+ }
+ }
+
+ composeTestRule.onDialogText(TEXT).assertIsDisplayed()
+ }
+
+ private companion object {
+ const val TITLE = "Title"
+ const val TEXT = "Text"
+ }
+}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ListPreferenceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ListPreferenceTest.kt
new file mode 100644
index 000000000000..997a02369d9f
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ListPreferenceTest.kt
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.widget.preference
+
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.framework.compose.stateOf
+import com.android.settingslib.spa.testutils.onDialogText
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class ListPreferenceTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Test
+ fun title_displayed() {
+ composeTestRule.setContent {
+ ListPreference(remember {
+ object : ListPreferenceModel {
+ override val title = TITLE
+ override val options = emptyList<ListPreferenceOption>()
+ override val selectedId = mutableIntStateOf(0)
+ override val onIdSelected: (Int) -> Unit = {}
+ }
+ })
+ }
+
+ composeTestRule.onNodeWithText(TITLE).assertIsDisplayed()
+ }
+
+ @Test
+ fun summary_showSelectedText() {
+ composeTestRule.setContent {
+ ListPreference(remember {
+ object : ListPreferenceModel {
+ override val title = TITLE
+ override val options = listOf(ListPreferenceOption(id = 1, text = "A"))
+ override val selectedId = mutableIntStateOf(1)
+ override val onIdSelected: (Int) -> Unit = {}
+ }
+ })
+ }
+
+ composeTestRule.onNodeWithText("A").assertIsDisplayed()
+ }
+
+ @Test
+ fun click_optionsIsEmpty_notShowDialog() {
+ composeTestRule.setContent {
+ ListPreference(remember {
+ object : ListPreferenceModel {
+ override val title = TITLE
+ override val options = emptyList<ListPreferenceOption>()
+ override val selectedId = mutableIntStateOf(0)
+ override val onIdSelected: (Int) -> Unit = {}
+ }
+ })
+ }
+
+ composeTestRule.onNodeWithText(TITLE).performClick()
+
+ composeTestRule.onDialogText(TITLE).assertDoesNotExist()
+ }
+
+ @Test
+ fun click_notEnabled_notShowDialog() {
+ composeTestRule.setContent {
+ ListPreference(remember {
+ object : ListPreferenceModel {
+ override val title = TITLE
+ override val enabled = stateOf(false)
+ override val options = listOf(ListPreferenceOption(id = 1, text = "A"))
+ override val selectedId = mutableIntStateOf(1)
+ override val onIdSelected: (Int) -> Unit = {}
+ }
+ })
+ }
+
+ composeTestRule.onNodeWithText(TITLE).performClick()
+
+ composeTestRule.onDialogText(TITLE).assertDoesNotExist()
+ }
+
+ @Test
+ fun click_optionsNotEmpty_showDialog() {
+ composeTestRule.setContent {
+ ListPreference(remember {
+ object : ListPreferenceModel {
+ override val title = TITLE
+ override val options = listOf(ListPreferenceOption(id = 1, text = "A"))
+ override val selectedId = mutableIntStateOf(1)
+ override val onIdSelected: (Int) -> Unit = {}
+ }
+ })
+ }
+
+ composeTestRule.onNodeWithText(TITLE).performClick()
+
+ composeTestRule.onDialogText(TITLE).assertIsDisplayed()
+ }
+
+ @Test
+ fun select() {
+ val selectedId = mutableIntStateOf(1)
+ composeTestRule.setContent {
+ ListPreference(remember {
+ object : ListPreferenceModel {
+ override val title = TITLE
+ override val options = listOf(
+ ListPreferenceOption(id = 1, text = "A"),
+ ListPreferenceOption(id = 2, text = "B"),
+ )
+ override val selectedId = selectedId
+ override val onIdSelected = { id: Int -> selectedId.intValue = id }
+ }
+ })
+ }
+
+ composeTestRule.onNodeWithText(TITLE).performClick()
+ composeTestRule.onDialogText("B").performClick()
+
+ composeTestRule.onNodeWithText("B").assertIsDisplayed()
+ }
+
+ @Test
+ fun select_dialogOpenThenDisable_itemAlsoDisabled() {
+ val selectedId = mutableIntStateOf(1)
+ val enabledState = mutableStateOf(true)
+ composeTestRule.setContent {
+ ListPreference(remember {
+ object : ListPreferenceModel {
+ override val title = TITLE
+ override val enabled = enabledState
+ override val options = listOf(
+ ListPreferenceOption(id = 1, text = "A"),
+ ListPreferenceOption(id = 2, text = "B"),
+ )
+ override val selectedId = selectedId
+ override val onIdSelected = { id: Int -> selectedId.intValue = id }
+ }
+ })
+ }
+
+ composeTestRule.onNodeWithText(TITLE).performClick()
+ enabledState.value = false
+
+ composeTestRule.onDialogText("B").assertIsDisplayed().assertIsNotEnabled()
+ }
+
+ private companion object {
+ const val TITLE = "Title"
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowColorDisplayManager.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowColorDisplayManager.java
new file mode 100644
index 000000000000..a9fd380c2733
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowColorDisplayManager.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.testutils.shadow;
+
+import android.Manifest;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.hardware.display.ColorDisplayManager;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+@Implements(ColorDisplayManager.class)
+public class ShadowColorDisplayManager extends org.robolectric.shadows.ShadowColorDisplayManager {
+
+ private boolean mIsReduceBrightColorsActivated;
+
+ @Implementation
+ @SystemApi
+ @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
+ public boolean setReduceBrightColorsActivated(boolean activated) {
+ mIsReduceBrightColorsActivated = activated;
+ return true;
+ }
+
+ @Implementation
+ @SystemApi
+ public boolean isReduceBrightColorsActivated() {
+ return mIsReduceBrightColorsActivated;
+ }
+
+}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index fe39c4febc80..59c3cd38a97d 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -84,6 +84,7 @@ public class SystemSettings {
Settings.System.RING_VIBRATION_INTENSITY,
Settings.System.HAPTIC_FEEDBACK_INTENSITY,
Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY,
+ Settings.System.KEYBOARD_VIBRATION_ENABLED,
Settings.System.HAPTIC_FEEDBACK_ENABLED,
Settings.System.DISPLAY_COLOR_MODE_VENDOR_HINT, // must precede DISPLAY_COLOR_MODE
Settings.System.DISPLAY_COLOR_MODE,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index eba74ab14f3d..572303a813bf 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -138,6 +138,7 @@ public class SystemSettingsValidators {
VALIDATORS.put(System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
VALIDATORS.put(System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
VALIDATORS.put(System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
+ VALIDATORS.put(System.KEYBOARD_VIBRATION_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.HAPTIC_FEEDBACK_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.RINGTONE, URI_VALIDATOR);
VALIDATORS.put(System.NOTIFICATION_SOUND, URI_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 46cd725ad582..4e2fad0bece2 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3243,7 +3243,7 @@ public class SettingsProvider extends ContentProvider {
}
if (success && criticalSettings != null && criticalSettings.contains(name)) {
- settingsState.persistSyncLocked();
+ settingsState.persistSettingsLocked();
}
if (forceNotify || success) {
@@ -3294,7 +3294,7 @@ public class SettingsProvider extends ContentProvider {
}
if (success && criticalSettings != null && criticalSettings.contains(name)) {
- settingsState.persistSyncLocked();
+ settingsState.persistSettingsLocked();
}
if (forceNotify || success) {
@@ -3319,7 +3319,7 @@ public class SettingsProvider extends ContentProvider {
}
if (success && criticalSettings != null && criticalSettings.contains(name)) {
- settingsState.persistSyncLocked();
+ settingsState.persistSettingsLocked();
}
if (forceNotify || success) {
@@ -3385,7 +3385,7 @@ public class SettingsProvider extends ContentProvider {
}
}
if (someSettingChanged) {
- settingsState.persistSyncLocked();
+ settingsState.persistSettingsLocked();
success = true;
}
}
@@ -3407,7 +3407,7 @@ public class SettingsProvider extends ContentProvider {
}
}
if (someSettingChanged) {
- settingsState.persistSyncLocked();
+ settingsState.persistSettingsLocked();
success = true;
}
}
@@ -3435,7 +3435,7 @@ public class SettingsProvider extends ContentProvider {
}
}
if (someSettingChanged) {
- settingsState.persistSyncLocked();
+ settingsState.persistSettingsLocked();
success = true;
}
}
@@ -3460,7 +3460,7 @@ public class SettingsProvider extends ContentProvider {
logSettingChanged(userId, name, type, CHANGE_TYPE_DELETE);
}
if (someSettingChanged) {
- settingsState.persistSyncLocked();
+ settingsState.persistSettingsLocked();
success = true;
}
}
@@ -3559,7 +3559,7 @@ public class SettingsProvider extends ContentProvider {
ensureSettingsStateLocked(systemKey);
SettingsState systemSettings = mSettingsStates.get(systemKey);
migrateLegacySettingsLocked(systemSettings, database, TABLE_SYSTEM);
- systemSettings.persistSyncLocked();
+ systemSettings.persistSettingsLocked();
// Move over the secure settings.
// Do this after System settings, since this is the first thing we check when deciding
@@ -3569,7 +3569,7 @@ public class SettingsProvider extends ContentProvider {
SettingsState secureSettings = mSettingsStates.get(secureKey);
migrateLegacySettingsLocked(secureSettings, database, TABLE_SECURE);
ensureSecureSettingAndroidIdSetLocked(secureSettings);
- secureSettings.persistSyncLocked();
+ secureSettings.persistSettingsLocked();
// Move over the global settings if owner.
// Do this last, since this is the first thing we check when deciding
@@ -3585,7 +3585,7 @@ public class SettingsProvider extends ContentProvider {
mSettingsCreationBuildId, null, true,
SettingsState.SYSTEM_PACKAGE_NAME);
}
- globalSettings.persistSyncLocked();
+ globalSettings.persistSettingsLocked();
}
// Drop the database as now all is moved and persisted.
@@ -4404,16 +4404,16 @@ public class SettingsProvider extends ContentProvider {
if (userId == UserHandle.USER_SYSTEM) {
SettingsState globalSettings = getGlobalSettingsLocked();
ensureLegacyDefaultValueAndSystemSetUpdatedLocked(globalSettings, userId);
- globalSettings.persistSyncLocked();
+ globalSettings.persistSettingsLocked();
}
SettingsState secureSettings = getSecureSettingsLocked(mUserId);
ensureLegacyDefaultValueAndSystemSetUpdatedLocked(secureSettings, userId);
- secureSettings.persistSyncLocked();
+ secureSettings.persistSettingsLocked();
SettingsState systemSettings = getSystemSettingsLocked(mUserId);
ensureLegacyDefaultValueAndSystemSetUpdatedLocked(systemSettings, userId);
- systemSettings.persistSyncLocked();
+ systemSettings.persistSettingsLocked();
currentVersion = 146;
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index e9533e598336..7cec99d4189f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -72,6 +72,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.CountDownLatch;
/**
* This class contains the state for one type of settings. It is responsible
@@ -589,9 +590,10 @@ final class SettingsState {
}
// The settings provider must hold its lock when calling here.
- public void persistSyncLocked() {
+ public void persistSettingsLocked() {
mHandler.removeMessages(MyHandler.MSG_PERSIST_SETTINGS);
- doWriteState();
+ // schedule a write operation right away
+ mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS).sendToTarget();
}
// The settings provider must hold its lock when calling here.
@@ -1725,4 +1727,20 @@ final class SettingsState {
return mPackageToMemoryUsage.getOrDefault(packageName, 0);
}
}
+
+ /**
+ * Allow tests to wait for the handler to finish handling all the remaining messages
+ */
+ @VisibleForTesting
+ public void waitForHandler() {
+ final CountDownLatch latch = new CountDownLatch(1);
+ synchronized (mLock) {
+ mHandler.post(latch::countDown);
+ }
+ try {
+ latch.await();
+ } catch (InterruptedException e) {
+ // ignored
+ }
+ }
}
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
index df4d2a1ed89f..02a7bc1646ba 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
@@ -76,6 +76,14 @@ public class SettingsStateTest extends AndroidTestCase {
mSettingsFile.delete();
}
+ @Override
+ protected void tearDown() throws Exception {
+ if (mSettingsFile != null) {
+ mSettingsFile.delete();
+ }
+ super.tearDown();
+ }
+
public void testIsBinary() {
assertFalse(SettingsState.isBinary(" abc 日本語"));
@@ -149,11 +157,10 @@ public class SettingsStateTest extends AndroidTestCase {
* Make sure settings can be written to a file and also can be read.
*/
public void testReadWrite() {
- final File file = new File(getContext().getCacheDir(), "setting.xml");
- file.delete();
final Object lock = new Object();
- final SettingsState ssWriter = new SettingsState(getContext(), lock, file, 1,
+ assertFalse(mSettingsFile.exists());
+ final SettingsState ssWriter = new SettingsState(getContext(), lock, mSettingsFile, 1,
SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
ssWriter.setVersionLocked(SettingsState.SETTINGS_VERSION_NEW_ENCODING);
@@ -162,11 +169,13 @@ public class SettingsStateTest extends AndroidTestCase {
ssWriter.insertSettingLocked("k3", null, null, false, "p2");
ssWriter.insertSettingLocked("k4", CRAZY_STRING, null, false, "p3");
synchronized (lock) {
- ssWriter.persistSyncLocked();
+ ssWriter.persistSettingsLocked();
}
-
- final SettingsState ssReader = new SettingsState(getContext(), lock, file, 1,
+ ssWriter.waitForHandler();
+ assertTrue(mSettingsFile.exists());
+ final SettingsState ssReader = new SettingsState(getContext(), lock, mSettingsFile, 1,
SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
+
synchronized (lock) {
assertEquals("\u0000", ssReader.getSettingLocked("k1").getValue());
assertEquals("abc", ssReader.getSettingLocked("k2").getValue());
@@ -179,10 +188,8 @@ public class SettingsStateTest extends AndroidTestCase {
* In version 120, value "null" meant {code NULL}.
*/
public void testUpgrade() throws Exception {
- final File file = new File(getContext().getCacheDir(), "setting.xml");
- file.delete();
final Object lock = new Object();
- final PrintStream os = new PrintStream(new FileOutputStream(file));
+ final PrintStream os = new PrintStream(new FileOutputStream(mSettingsFile));
os.print(
"<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>" +
"<settings version=\"120\">" +
@@ -192,7 +199,7 @@ public class SettingsStateTest extends AndroidTestCase {
"</settings>");
os.close();
- final SettingsState ss = new SettingsState(getContext(), lock, file, 1,
+ final SettingsState ss = new SettingsState(getContext(), lock, mSettingsFile, 1,
SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
synchronized (lock) {
SettingsState.Setting s;
@@ -213,7 +220,8 @@ public class SettingsStateTest extends AndroidTestCase {
public void testInitializeSetting_preserveFlagNotSet() {
SettingsState settingsWriter = getSettingStateObject();
settingsWriter.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE);
- settingsWriter.persistSyncLocked();
+ settingsWriter.persistSettingsLocked();
+ settingsWriter.waitForHandler();
SettingsState settingsReader = getSettingStateObject();
assertFalse(settingsReader.getSettingLocked(SETTING_NAME).isValuePreservedInRestore());
@@ -223,7 +231,8 @@ public class SettingsStateTest extends AndroidTestCase {
SettingsState settingsWriter = getSettingStateObject();
settingsWriter.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE);
settingsWriter.insertSettingLocked(SETTING_NAME, "2", null, false, TEST_PACKAGE);
- settingsWriter.persistSyncLocked();
+ settingsWriter.persistSettingsLocked();
+ settingsWriter.waitForHandler();
SettingsState settingsReader = getSettingStateObject();
assertTrue(settingsReader.getSettingLocked(SETTING_NAME).isValuePreservedInRestore());
@@ -234,7 +243,8 @@ public class SettingsStateTest extends AndroidTestCase {
settingsWriter.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE);
settingsWriter.insertSettingLocked(SETTING_NAME, "2", null, false, false, TEST_PACKAGE,
/* overrideableByRestore */ true);
- settingsWriter.persistSyncLocked();
+ settingsWriter.persistSettingsLocked();
+ settingsWriter.waitForHandler();
SettingsState settingsReader = getSettingStateObject();
assertFalse(settingsReader.getSettingLocked(SETTING_NAME).isValuePreservedInRestore());
@@ -250,7 +260,8 @@ public class SettingsStateTest extends AndroidTestCase {
// already been set to true.
settingsWriter.insertSettingLocked(SETTING_NAME, "2", null, false, false, TEST_PACKAGE,
/* overrideableByRestore */ true);
- settingsWriter.persistSyncLocked();
+ settingsWriter.persistSettingsLocked();
+ settingsWriter.waitForHandler();
SettingsState settingsReader = getSettingStateObject();
assertTrue(settingsReader.getSettingLocked(SETTING_NAME).isValuePreservedInRestore());
@@ -481,8 +492,11 @@ public class SettingsStateTest extends AndroidTestCase {
settingsState.insertSettingLocked(
FLAG_NAME_1_STAGED, VALUE1, null, false, TEST_PACKAGE);
settingsState.insertSettingLocked(FLAG_NAME_2, VALUE2, null, false, TEST_PACKAGE);
- settingsState.persistSyncLocked();
+ settingsState.persistSettingsLocked();
+ }
+ settingsState.waitForHandler();
+ synchronized (lock) {
assertEquals(VALUE1, settingsState.getSettingLocked(FLAG_NAME_1_STAGED).getValue());
assertEquals(VALUE2, settingsState.getSettingLocked(FLAG_NAME_2).getValue());
}
@@ -522,7 +536,10 @@ public class SettingsStateTest extends AndroidTestCase {
synchronized (lock) {
settingsState.insertSettingLocked(INVALID_STAGED_FLAG_1,
VALUE2, null, false, TEST_PACKAGE);
- settingsState.persistSyncLocked();
+ settingsState.persistSettingsLocked();
+ }
+ settingsState.waitForHandler();
+ synchronized (lock) {
assertEquals(VALUE2, settingsState.getSettingLocked(INVALID_STAGED_FLAG_1).getValue());
}
diff --git a/packages/SoundPicker/Android.bp b/packages/SoundPicker/Android.bp
index 235e6724f91d..2c89d6dbce56 100644
--- a/packages/SoundPicker/Android.bp
+++ b/packages/SoundPicker/Android.bp
@@ -7,40 +7,22 @@ package {
default_applicable_licenses: ["frameworks_base_license"],
}
-android_library {
- name: "SoundPickerLib",
- srcs: [
- "src/**/*.java",
+android_app {
+ name: "SoundPicker",
+ defaults: ["platform_app_defaults"],
+ manifest: "AndroidManifest.xml",
+
+ static_libs: [
+ "androidx.appcompat_appcompat",
],
resource_dirs: [
"res",
],
- static_libs: [
- "androidx.appcompat_appcompat",
- "hilt_android",
- "guava",
- "androidx.recyclerview_recyclerview",
- "androidx-constraintlayout_constraintlayout",
- "androidx.viewpager2_viewpager2",
- "com.google.android.material_material",
+ srcs: [
+ "src/**/*.java",
],
-}
-android_app {
- name: "SoundPicker",
- defaults: ["platform_app_defaults"],
- manifest: "AndroidManifest.xml",
- static_libs: ["SoundPickerLib"],
platform_apis: true,
certificate: "media",
privileged: true,
-
- optimize: {
- enabled: true,
- optimize: true,
- shrink: true,
- shrink_resources: true,
- obfuscate: false,
- proguard_compatibility: false,
- },
}
diff --git a/packages/SoundPicker/AndroidManifest.xml b/packages/SoundPicker/AndroidManifest.xml
index 934b003c605c..44295a5cbdd2 100644
--- a/packages/SoundPicker/AndroidManifest.xml
+++ b/packages/SoundPicker/AndroidManifest.xml
@@ -1,6 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.soundpicker"
- android:sharedUserId="android.media">
+ package="com.android.soundpicker"
+ android:sharedUserId="android.media">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
@@ -9,16 +9,12 @@
<uses-permission android:name="android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
- <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
-
<application
- android:name=".RingtonePickerApplication"
- android:allowBackup="false"
- android:label="@string/app_label"
- android:theme="@style/Theme.AppCompat"
- android:supportsRtl="true">
+ android:allowBackup="false"
+ android:label="@string/app_label"
+ android:supportsRtl="true">
<receiver android:name="RingtoneReceiver"
- android:exported="true">
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.DEVICE_CUSTOMIZATION_READY"/>
</intent-filter>
@@ -27,17 +23,14 @@
<service android:name="RingtoneOverlayService" />
<activity android:name="RingtonePickerActivity"
- android:theme="@style/Theme.AppCompat.Dialog"
- android:enabled="@*android:bool/config_defaultRingtonePickerEnabled"
- android:excludeFromRecents="true"
- android:exported="true">
+ android:theme="@style/PickerDialogTheme"
+ android:enabled="@*android:bool/config_defaultRingtonePickerEnabled"
+ android:excludeFromRecents="true"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.RINGTONE_PICKER" />
<category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.RINGTONE_PICKER_SOUND" />
- <category android:name="android.intent.category.RINGTONE_PICKER_VIBRATION" />
- <category android:name="android.intent.category.RINGTONE_PICKER_RINGTONE" />
</intent-filter>
</activity>
</application>
-</manifest>
+</manifest> \ No newline at end of file
diff --git a/packages/SoundPicker/res/layout/add_new_sound_item.xml b/packages/SoundPicker/res/layout/add_new_sound_item.xml
index 024b97ef23be..57b70d7db0a9 100644
--- a/packages/SoundPicker/res/layout/add_new_sound_item.xml
+++ b/packages/SoundPicker/res/layout/add_new_sound_item.xml
@@ -19,9 +19,7 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
- android:background="?android:attr/selectableItemBackground"
- android:focusable="true"
- android:clickable="true">
+ android:background="?android:attr/selectableItemBackground">
<ImageView
android:layout_width="24dp"
@@ -31,19 +29,19 @@
android:scaleType="centerCrop"
android:layout_marginRight="24dp"
android:layout_marginLeft="24dp"
- android:src="@drawable/ic_add"/>
+ android:src="@drawable/ic_add" />
- <TextView
- android:id="@+id/add_new_sound_text"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeightSmall"
- android:text="@null"
- android:textColor="?android:attr/colorAccent"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:maxLines="3"
- android:gravity="center_vertical"
- android:paddingEnd="?android:attr/dialogPreferredPadding"
- android:drawablePadding="20dp"
- android:ellipsize="marquee"/>
+ <TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/add_new_sound_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:text="@null"
+ android:textColor="?android:attr/colorAccent"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:maxLines="3"
+ android:gravity="center_vertical"
+ android:paddingEnd="?android:attr/dialogPreferredPadding"
+ android:drawablePadding="20dp"
+ android:ellipsize="marquee" />
</LinearLayout> \ No newline at end of file
diff --git a/packages/SoundPicker/res/layout/radio_with_work_badge.xml b/packages/SoundPicker/res/layout/radio_with_work_badge.xml
index 36ac93ed630b..2e44b6f4dd7a 100644
--- a/packages/SoundPicker/res/layout/radio_with_work_badge.xml
+++ b/packages/SoundPicker/res/layout/radio_with_work_badge.xml
@@ -14,14 +14,12 @@
limitations under the License.
-->
-<com.android.soundpicker.CheckedListItem
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:background="?android:attr/selectableItemBackground"
- android:focusable="true"
- android:clickable="true">
+<com.android.soundpicker.CheckedListItem xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:background="?android:attr/selectableItemBackground"
+ >
<CheckedTextView
android:id="@+id/checked_text_view"
@@ -37,7 +35,7 @@
android:drawablePadding="20dp"
android:ellipsize="marquee"
android:layout_toLeftOf="@+id/work_icon"
- android:maxLines="3"/>
+ android:maxLines="3" />
<ImageView
android:id="@id/work_icon"
@@ -46,5 +44,5 @@
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:scaleType="centerCrop"
- android:layout_marginRight="20dp"/>
-</com.android.soundpicker.CheckedListItem>
+ android:layout_marginRight="20dp" />
+</com.android.soundpicker.CheckedListItem> \ No newline at end of file
diff --git a/packages/SoundPicker/res/values/strings.xml b/packages/SoundPicker/res/values/strings.xml
index ab7b95a09028..04a2c2bb83c3 100644
--- a/packages/SoundPicker/res/values/strings.xml
+++ b/packages/SoundPicker/res/values/strings.xml
@@ -40,8 +40,4 @@
<!-- Text for the name of the app. [CHAR LIMIT=12] -->
<string name="app_label">Sounds</string>
-
- <string name="empty_list">The list is empty</string>
- <string name="sound_page_title">Sound</string>
- <string name="vibration_page_title">Vibration</string>
</resources>
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
index 90a14f9717db..ea46c0cee6b9 100644
--- a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
+++ b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
@@ -16,19 +16,43 @@
package com.android.soundpicker;
+import android.content.ContentProvider;
+import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.database.Cursor;
+import android.database.CursorWrapper;
+import android.media.AudioAttributes;
+import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.MediaStore;
+import android.provider.Settings;
import android.util.Log;
-
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentTransaction;
-import androidx.lifecycle.ViewModelProvider;
-
-import dagger.hilt.android.AndroidEntryPoint;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.CursorAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+
+import java.io.IOException;
+import java.util.regex.Pattern;
/**
* The {@link RingtonePickerActivity} allows the user to choose one from all of the
@@ -36,183 +60,727 @@ import dagger.hilt.android.AndroidEntryPoint;
*
* @see RingtoneManager#ACTION_RINGTONE_PICKER
*/
-@AndroidEntryPoint(AppCompatActivity.class)
-public final class RingtonePickerActivity extends Hilt_RingtonePickerActivity {
+public final class RingtonePickerActivity extends AlertActivity implements
+ AdapterView.OnItemSelectedListener, Runnable, DialogInterface.OnClickListener,
+ AlertController.AlertParams.OnPrepareListViewListener {
+
+ private static final int POS_UNKNOWN = -1;
private static final String TAG = "RingtonePickerActivity";
- // TODO: Use the extra keys from RingtoneManager once they're added.
- private static final String EXTRA_RINGTONE_PICKER_CATEGORY = "EXTRA_RINGTONE_PICKER_CATEGORY";
- private static final String EXTRA_VIBRATION_SHOW_DEFAULT = "EXTRA_VIBRATION_SHOW_DEFAULT";
- private static final String EXTRA_VIBRATION_DEFAULT_URI = "EXTRA_VIBRATION_DEFAULT_URI";
- private static final String EXTRA_VIBRATION_SHOW_SILENT = "EXTRA_VIBRATION_SHOW_SILENT";
- private static final String EXTRA_VIBRATION_EXISTING_URI = "EXTRA_VIBRATION_EXISTING_URI";
- private static final boolean RINGTONE_PICKER_CATEGORY_FEATURE_ENABLED = false;
-
- private RingtonePickerViewModel mRingtonePickerViewModel;
+
+ private static final int DELAY_MS_SELECTION_PLAYED = 300;
+
+ private static final String COLUMN_LABEL = MediaStore.Audio.Media.TITLE;
+
+ private static final String SAVE_CLICKED_POS = "clicked_pos";
+
+ private static final String SOUND_NAME_RES_PREFIX = "sound_name_";
+
+ private static final int ADD_FILE_REQUEST_CODE = 300;
+
+ private RingtoneManager mRingtoneManager;
+ private int mType;
+
+ private Cursor mCursor;
+ private Handler mHandler;
+ private BadgedRingtoneAdapter mAdapter;
+
+ /** The position in the list of the 'Silent' item. */
+ private int mSilentPos = POS_UNKNOWN;
+
+ /** The position in the list of the 'Default' item. */
+ private int mDefaultRingtonePos = POS_UNKNOWN;
+
+ /** The position in the list of the ringtone to sample. */
+ private int mSampleRingtonePos = POS_UNKNOWN;
+
+ /** Whether this list has the 'Silent' item. */
+ private boolean mHasSilentItem;
+
+ /** The Uri to place a checkmark next to. */
+ private Uri mExistingUri;
+
+ /** The number of static items in the list. */
+ private int mStaticItemCount;
+
+ /** Whether this list has the 'Default' item. */
+ private boolean mHasDefaultItem;
+
+ /** The Uri to play when the 'Default' item is clicked. */
+ private Uri mUriForDefaultItem;
+
+ /** Id of the user to which the ringtone picker should list the ringtones */
+ private int mPickerUserId;
+
+ /** Context of the user specified by mPickerUserId */
+ private Context mTargetContext;
+
+ /**
+ * A Ringtone for the default ringtone. In most cases, the RingtoneManager
+ * will stop the previous ringtone. However, the RingtoneManager doesn't
+ * manage the default ringtone for us, so we should stop this one manually.
+ */
+ private Ringtone mDefaultRingtone;
+
+ /**
+ * The ringtone that's currently playing, unless the currently playing one is the default
+ * ringtone.
+ */
+ private Ringtone mCurrentRingtone;
+
+ /**
+ * Stable ID for the ringtone that is currently checked (may be -1 if no ringtone is checked).
+ */
+ private long mCheckedItemId = -1;
+
private int mAttributesFlags;
+ private boolean mShowOkCancelButtons;
+
+ /**
+ * Keep the currently playing ringtone around when changing orientation, so that it
+ * can be stopped later, after the activity is recreated.
+ */
+ private static Ringtone sPlayingRingtone;
+
+ private DialogInterface.OnClickListener mRingtoneClickListener =
+ new DialogInterface.OnClickListener() {
+
+ /*
+ * On item clicked
+ */
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == mCursor.getCount() + mStaticItemCount) {
+ // The "Add new ringtone" item was clicked. Start a file picker intent to select
+ // only audio files (MIME type "audio/*")
+ final Intent chooseFile = getMediaFilePickerIntent();
+ startActivityForResult(chooseFile, ADD_FILE_REQUEST_CODE);
+ return;
+ }
+
+ // Save the position of most recently clicked item
+ setCheckedItem(which);
+
+ // In the buttonless (watch-only) version, preemptively set our result since we won't
+ // have another chance to do so before the activity closes.
+ if (!mShowOkCancelButtons) {
+ setSuccessResultWithRingtone(getCurrentlySelectedRingtoneUri());
+ }
+
+ // Play clip
+ playRingtone(which, 0);
+ }
+
+ };
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_ringtone_picker);
- mRingtonePickerViewModel = new ViewModelProvider(this).get(RingtonePickerViewModel.class);
+ mHandler = new Handler();
Intent intent = getIntent();
- /**
- * Id of the user to which the ringtone picker should list the ringtones
- */
- int pickerUserId = UserHandle.myUserId();
+ mPickerUserId = UserHandle.myUserId();
+ mTargetContext = this;
// Get the types of ringtones to show
- int ringtoneType = intent.getIntExtra(RingtoneManager.EXTRA_RINGTONE_TYPE,
- RingtonePickerViewModel.RINGTONE_TYPE_UNKNOWN);
+ mType = intent.getIntExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, -1);
+ initRingtoneManager();
+ /*
+ * Get whether to show the 'Default' item, and the URI to play when the
+ * default is clicked
+ */
+ mHasDefaultItem = intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);
+ mUriForDefaultItem = intent.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI);
+ if (mUriForDefaultItem == null) {
+ if (mType == RingtoneManager.TYPE_NOTIFICATION) {
+ mUriForDefaultItem = Settings.System.DEFAULT_NOTIFICATION_URI;
+ } else if (mType == RingtoneManager.TYPE_ALARM) {
+ mUriForDefaultItem = Settings.System.DEFAULT_ALARM_ALERT_URI;
+ } else if (mType == RingtoneManager.TYPE_RINGTONE) {
+ mUriForDefaultItem = Settings.System.DEFAULT_RINGTONE_URI;
+ } else {
+ // or leave it null for silence.
+ mUriForDefaultItem = Settings.System.DEFAULT_RINGTONE_URI;
+ }
+ }
+
+ // Get whether to show the 'Silent' item
+ mHasSilentItem = intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true);
// AudioAttributes flags
mAttributesFlags |= intent.getIntExtra(
RingtoneManager.EXTRA_RINGTONE_AUDIO_ATTRIBUTES_FLAGS,
0 /*defaultValue == no flags*/);
- boolean showOkCancelButtons = getResources().getBoolean(R.bool.config_showOkCancelButtons);
+ mShowOkCancelButtons = getResources().getBoolean(R.bool.config_showOkCancelButtons);
+
+ // The volume keys will control the stream that we are choosing a ringtone for
+ setVolumeControlStream(mRingtoneManager.inferStreamType());
+
+ // Get the URI whose list item should have a checkmark
+ mExistingUri = intent
+ .getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI);
- String title = intent.getStringExtra(RingtoneManager.EXTRA_RINGTONE_TITLE);
- if (title == null) {
- title = getString(RingtonePickerViewModel.getTitleByType(ringtoneType));
+ // Create the list of ringtones and hold on to it so we can update later.
+ mAdapter = new BadgedRingtoneAdapter(this, mCursor,
+ /* isManagedProfile = */ UserManager.get(this).isManagedProfile(mPickerUserId));
+ if (savedInstanceState != null) {
+ setCheckedItem(savedInstanceState.getInt(SAVE_CLICKED_POS, POS_UNKNOWN));
}
- String ringtonePickerCategory = intent.getStringExtra(EXTRA_RINGTONE_PICKER_CATEGORY);
- RingtonePickerViewModel.PickerType pickerType = mapCategoryToPickerType(
- ringtonePickerCategory);
- RingtoneListHandler.Config soundListConfig = getSoundListConfig(pickerType, intent,
- ringtoneType);
- RingtoneListHandler.Config vibrationListConfig = getVibrationListConfig(pickerType, intent);
+ final AlertController.AlertParams p = mAlertParams;
+ p.mAdapter = mAdapter;
+ p.mOnClickListener = mRingtoneClickListener;
+ p.mLabelColumn = COLUMN_LABEL;
+ p.mIsSingleChoice = true;
+ p.mOnItemSelectedListener = this;
+ if (mShowOkCancelButtons) {
+ p.mPositiveButtonText = getString(com.android.internal.R.string.ok);
+ p.mPositiveButtonListener = this;
+ p.mNegativeButtonText = getString(com.android.internal.R.string.cancel);
+ p.mPositiveButtonListener = this;
+ }
+ p.mOnPrepareListViewListener = this;
+
+ p.mTitle = intent.getCharSequenceExtra(RingtoneManager.EXTRA_RINGTONE_TITLE);
+ if (p.mTitle == null) {
+ if (mType == RingtoneManager.TYPE_ALARM) {
+ p.mTitle = getString(com.android.internal.R.string.ringtone_picker_title_alarm);
+ } else if (mType == RingtoneManager.TYPE_NOTIFICATION) {
+ p.mTitle =
+ getString(com.android.internal.R.string.ringtone_picker_title_notification);
+ } else {
+ p.mTitle = getString(com.android.internal.R.string.ringtone_picker_title);
+ }
+ }
- RingtonePickerViewModel.Config pickerConfig =
- new RingtonePickerViewModel.Config(title, pickerUserId, ringtoneType,
- showOkCancelButtons, mAttributesFlags, pickerType);
+ setupAlert();
- mRingtonePickerViewModel.init(pickerConfig, soundListConfig, vibrationListConfig);
+ ListView listView = mAlert.getListView();
+ if (listView != null) {
+ // List view needs to gain focus in order for RSB to work.
+ if (!listView.requestFocus()) {
+ Log.e(TAG, "Unable to gain focus! RSB may not work properly.");
+ }
+ }
+ }
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putInt(SAVE_CLICKED_POS, getCheckedItem());
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ if (requestCode == ADD_FILE_REQUEST_CODE && resultCode == RESULT_OK) {
+ // Add the custom ringtone in a separate thread
+ final AsyncTask<Uri, Void, Uri> installTask = new AsyncTask<Uri, Void, Uri>() {
+ @Override
+ protected Uri doInBackground(Uri... params) {
+ try {
+ return mRingtoneManager.addCustomExternalRingtone(params[0], mType);
+ } catch (IOException | IllegalArgumentException e) {
+ Log.e(TAG, "Unable to add new ringtone", e);
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Uri ringtoneUri) {
+ if (ringtoneUri != null) {
+ requeryForAdapter();
+ } else {
+ // Ringtone was not added, display error Toast
+ Toast.makeText(RingtonePickerActivity.this, R.string.unable_to_add_ringtone,
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+ };
+ installTask.execute(data.getData());
+ }
+ }
- if (savedInstanceState == null) {
- TabbedDialogFragment dialogFragment = new TabbedDialogFragment();
+ // Disabled because context menus aren't Material Design :(
+ /*
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
+ int position = ((AdapterContextMenuInfo) menuInfo).position;
+
+ Ringtone ringtone = getRingtone(getRingtoneManagerPosition(position));
+ if (ringtone != null && mRingtoneManager.isCustomRingtone(ringtone.getUri())) {
+ // It's a custom ringtone so we display the context menu
+ menu.setHeaderTitle(ringtone.getTitle(this));
+ menu.add(Menu.NONE, Menu.FIRST, Menu.NONE, R.string.delete_ringtone_text);
+ }
+ }
- FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
- Fragment prev = getSupportFragmentManager().findFragmentByTag(TabbedDialogFragment.TAG);
- if (prev != null) {
- ft.remove(prev);
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case Menu.FIRST: {
+ int deletedRingtonePos = ((AdapterContextMenuInfo) item.getMenuInfo()).position;
+ Uri deletedRingtoneUri = getRingtone(
+ getRingtoneManagerPosition(deletedRingtonePos)).getUri();
+ if(mRingtoneManager.deleteExternalRingtone(deletedRingtoneUri)) {
+ requeryForAdapter();
+ } else {
+ Toast.makeText(this, R.string.unable_to_delete_ringtone, Toast.LENGTH_SHORT)
+ .show();
+ }
+ return true;
+ }
+ default: {
+ return false;
}
- ft.addToBackStack(null);
- dialogFragment.show(ft, TabbedDialogFragment.TAG);
}
+ }
+ */
- // The volume keys will control the stream that we are choosing a ringtone for
- setVolumeControlStream(mRingtonePickerViewModel.getRingtoneStreamType());
+ @Override
+ public void onDestroy() {
+ if (mHandler != null) {
+ mHandler.removeCallbacksAndMessages(null);
+ }
+ if (mCursor != null) {
+ mCursor.close();
+ mCursor = null;
+ }
+ super.onDestroy();
}
- private RingtoneListHandler.Config getSoundListConfig(
- RingtonePickerViewModel.PickerType pickerType, Intent intent, int ringtoneType) {
- if (pickerType != RingtonePickerViewModel.PickerType.SOUND_PICKER
- && pickerType != RingtonePickerViewModel.PickerType.RINGTONE_PICKER) {
- // This ringtone picker does not require a sound picker.
- return null;
+ public void onPrepareListView(ListView listView) {
+ // Reset the static item count, as this method can be called multiple times
+ mStaticItemCount = 0;
+
+ if (mHasDefaultItem) {
+ mDefaultRingtonePos = addDefaultRingtoneItem(listView);
+
+ if (getCheckedItem() == POS_UNKNOWN && RingtoneManager.isDefault(mExistingUri)) {
+ setCheckedItem(mDefaultRingtonePos);
+ }
}
- // Get whether to show the 'Default' sound item, and the URI to play when it's clicked
- boolean hasDefaultSoundItem =
- intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);
+ if (mHasSilentItem) {
+ mSilentPos = addSilentItem(listView);
- // The Uri to play when the 'Default' sound item is clicked.
- Uri uriForDefaultSoundItem =
- intent.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI);
- if (uriForDefaultSoundItem == null) {
- uriForDefaultSoundItem = RingtonePickerViewModel.getDefaultItemUriByType(ringtoneType);
+ // The 'Silent' item should use a null Uri
+ if (getCheckedItem() == POS_UNKNOWN && mExistingUri == null) {
+ setCheckedItem(mSilentPos);
+ }
}
- // Get whether this list has the 'Silent' sound item.
- boolean hasSilentSoundItem =
- intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true);
+ if (getCheckedItem() == POS_UNKNOWN) {
+ setCheckedItem(getListPosition(mRingtoneManager.getRingtonePosition(mExistingUri)));
+ }
- // AudioAttributes flags
- mAttributesFlags |= intent.getIntExtra(
- RingtoneManager.EXTRA_RINGTONE_AUDIO_ATTRIBUTES_FLAGS,
- 0 /*defaultValue == no flags*/);
+ // In the buttonless (watch-only) version, preemptively set our result since we won't
+ // have another chance to do so before the activity closes.
+ if (!mShowOkCancelButtons) {
+ setSuccessResultWithRingtone(getCurrentlySelectedRingtoneUri());
+ }
+ // If external storage is available, add a button to install sounds from storage.
+ if (resolvesMediaFilePicker()
+ && Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ addNewSoundItem(listView);
+ }
- // Get the sound URI whose list item should have a checkmark
- Uri existingSoundUri = intent
- .getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI);
+ // Enable context menu in ringtone items
+ registerForContextMenu(listView);
+ }
- return new RingtoneListHandler.Config(hasDefaultSoundItem,
- uriForDefaultSoundItem, hasSilentSoundItem, existingSoundUri);
+ /**
+ * Re-query RingtoneManager for the most recent set of installed ringtones. May move the
+ * selected item position to match the new position of the chosen sound.
+ *
+ * This should only need to happen after adding or removing a ringtone.
+ */
+ private void requeryForAdapter() {
+ // Refresh and set a new cursor, closing the old one.
+ initRingtoneManager();
+ mAdapter.changeCursor(mCursor);
+
+ // Update checked item location.
+ int checkedPosition = POS_UNKNOWN;
+ for (int i = 0; i < mAdapter.getCount(); i++) {
+ if (mAdapter.getItemId(i) == mCheckedItemId) {
+ checkedPosition = getListPosition(i);
+ break;
+ }
+ }
+ if (mHasSilentItem && checkedPosition == POS_UNKNOWN) {
+ checkedPosition = mSilentPos;
+ }
+ setCheckedItem(checkedPosition);
+ setupAlert();
}
- private RingtoneListHandler.Config getVibrationListConfig(
- RingtonePickerViewModel.PickerType pickerType, Intent intent) {
- if (pickerType != RingtonePickerViewModel.PickerType.VIBRATION_PICKER
- && pickerType != RingtonePickerViewModel.PickerType.RINGTONE_PICKER) {
- // This ringtone picker does not require a vibration picker.
+ /**
+ * Adds a static item to the top of the list. A static item is one that is not from the
+ * RingtoneManager.
+ *
+ * @param listView The ListView to add to.
+ * @param textResId The resource ID of the text for the item.
+ * @return The position of the inserted item.
+ */
+ private int addStaticItem(ListView listView, int textResId) {
+ TextView textView = (TextView) getLayoutInflater().inflate(
+ com.android.internal.R.layout.select_dialog_singlechoice_material, listView, false);
+ textView.setText(textResId);
+ listView.addHeaderView(textView);
+ mStaticItemCount++;
+ return listView.getHeaderViewsCount() - 1;
+ }
+
+ private int addDefaultRingtoneItem(ListView listView) {
+ if (mType == RingtoneManager.TYPE_NOTIFICATION) {
+ return addStaticItem(listView, R.string.notification_sound_default);
+ } else if (mType == RingtoneManager.TYPE_ALARM) {
+ return addStaticItem(listView, R.string.alarm_sound_default);
+ }
+
+ return addStaticItem(listView, R.string.ringtone_default);
+ }
+
+ private int addSilentItem(ListView listView) {
+ return addStaticItem(listView, com.android.internal.R.string.ringtone_silent);
+ }
+
+ private void addNewSoundItem(ListView listView) {
+ View view = getLayoutInflater().inflate(R.layout.add_new_sound_item, listView,
+ false /* attachToRoot */);
+ TextView text = (TextView)view.findViewById(R.id.add_new_sound_text);
+
+ if (mType == RingtoneManager.TYPE_ALARM) {
+ text.setText(R.string.add_alarm_text);
+ } else if (mType == RingtoneManager.TYPE_NOTIFICATION) {
+ text.setText(R.string.add_notification_text);
+ } else {
+ text.setText(R.string.add_ringtone_text);
+ }
+ listView.addFooterView(view);
+ }
+
+ private void initRingtoneManager() {
+ // Reinstantiate the RingtoneManager. Cursor.requery() was deprecated and calling it
+ // causes unexpected behavior.
+ mRingtoneManager = new RingtoneManager(mTargetContext, /* includeParentRingtones */ true);
+ if (mType != -1) {
+ mRingtoneManager.setType(mType);
+ }
+ mCursor = new LocalizedCursor(mRingtoneManager.getCursor(), getResources(), COLUMN_LABEL);
+ }
+
+ private Ringtone getRingtone(int ringtoneManagerPosition) {
+ if (ringtoneManagerPosition < 0) {
return null;
}
+ return mRingtoneManager.getRingtone(ringtoneManagerPosition);
+ }
+
+ private int getCheckedItem() {
+ return mAlertParams.mCheckedItem;
+ }
- // Get whether to show the 'Default' vibration item, and the URI to play when it's clicked
- boolean hasDefaultVibrationItem =
- intent.getBooleanExtra(EXTRA_VIBRATION_SHOW_DEFAULT, false);
+ private void setCheckedItem(int pos) {
+ mAlertParams.mCheckedItem = pos;
+ mCheckedItemId = mAdapter.getItemId(getRingtoneManagerPosition(pos));
+ }
- // The Uri to play when the 'Default' vibration item is clicked.
- Uri uriForDefaultVibrationItem = intent.getParcelableExtra(EXTRA_VIBRATION_DEFAULT_URI);
+ /*
+ * On click of Ok/Cancel buttons
+ */
+ public void onClick(DialogInterface dialog, int which) {
+ boolean positiveResult = which == DialogInterface.BUTTON_POSITIVE;
- // Get whether this list has the 'Silent' vibration item.
- boolean hasSilentVibrationItem =
- intent.getBooleanExtra(EXTRA_VIBRATION_SHOW_SILENT, true);
+ // Stop playing the previous ringtone
+ mRingtoneManager.stopPreviousRingtone();
- // Get the vibration URI whose list item should have a checkmark
- Uri existingVibrationUri = intent.getParcelableExtra(EXTRA_VIBRATION_EXISTING_URI);
+ if (positiveResult) {
+ setSuccessResultWithRingtone(getCurrentlySelectedRingtoneUri());
+ } else {
+ setResult(RESULT_CANCELED);
+ }
- return new RingtoneListHandler.Config(
- hasDefaultVibrationItem, uriForDefaultVibrationItem, hasSilentVibrationItem,
- existingVibrationUri);
+ finish();
}
- @Override
- public void onDestroy() {
- mRingtonePickerViewModel.cancelPendingAsyncTasks();
- super.onDestroy();
+ /*
+ * On item selected via keys
+ */
+ public void onItemSelected(AdapterView parent, View view, int position, long id) {
+ // footer view
+ if (position >= mCursor.getCount() + mStaticItemCount) {
+ return;
+ }
+
+ playRingtone(position, DELAY_MS_SELECTION_PLAYED);
+
+ // In the buttonless (watch-only) version, preemptively set our result since we won't
+ // have another chance to do so before the activity closes.
+ if (!mShowOkCancelButtons) {
+ setSuccessResultWithRingtone(getCurrentlySelectedRingtoneUri());
+ }
+ }
+
+ public void onNothingSelected(AdapterView parent) {
+ }
+
+ private void playRingtone(int position, int delayMs) {
+ mHandler.removeCallbacks(this);
+ mSampleRingtonePos = position;
+ mHandler.postDelayed(this, delayMs);
+ }
+
+ public void run() {
+ stopAnyPlayingRingtone();
+ if (mSampleRingtonePos == mSilentPos) {
+ return;
+ }
+
+ Ringtone ringtone;
+ if (mSampleRingtonePos == mDefaultRingtonePos) {
+ if (mDefaultRingtone == null) {
+ mDefaultRingtone = RingtoneManager.getRingtone(this, mUriForDefaultItem);
+ }
+ /*
+ * Stream type of mDefaultRingtone is not set explicitly here.
+ * It should be set in accordance with mRingtoneManager of this Activity.
+ */
+ if (mDefaultRingtone != null) {
+ mDefaultRingtone.setStreamType(mRingtoneManager.inferStreamType());
+ }
+ ringtone = mDefaultRingtone;
+ mCurrentRingtone = null;
+ } else {
+ ringtone = mRingtoneManager.getRingtone(getRingtoneManagerPosition(mSampleRingtonePos));
+ mCurrentRingtone = ringtone;
+ }
+
+ if (ringtone != null) {
+ if (mAttributesFlags != 0) {
+ ringtone.setAudioAttributes(
+ new AudioAttributes.Builder(ringtone.getAudioAttributes())
+ .setFlags(mAttributesFlags)
+ .build());
+ }
+ ringtone.play();
+ }
}
@Override
protected void onStop() {
super.onStop();
- mRingtonePickerViewModel.onStop(isChangingConfigurations());
+
+ if (!isChangingConfigurations()) {
+ stopAnyPlayingRingtone();
+ } else {
+ saveAnyPlayingRingtone();
+ }
}
@Override
protected void onPause() {
super.onPause();
- mRingtonePickerViewModel.onPause(isChangingConfigurations());
+ if (!isChangingConfigurations()) {
+ stopAnyPlayingRingtone();
+ }
}
- /**
- * Maps the ringtone picker category to the appropriate PickerType.
- * If the category is null or the feature is still not released, then it defaults to sound
- * picker.
- *
- * @param category the ringtone picker category.
- * @return the corresponding picker type.
- */
- private static RingtonePickerViewModel.PickerType mapCategoryToPickerType(String category) {
- if (category == null || !RINGTONE_PICKER_CATEGORY_FEATURE_ENABLED) {
- return RingtonePickerViewModel.PickerType.SOUND_PICKER;
+ private void setSuccessResultWithRingtone(Uri ringtoneUri) {
+ setResult(RESULT_OK,
+ new Intent().putExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI, ringtoneUri));
+ }
+
+ private Uri getCurrentlySelectedRingtoneUri() {
+ if (getCheckedItem() == POS_UNKNOWN) {
+ // When the getCheckItem is POS_UNKNOWN, it is not the case we expected.
+ // We return null for this case.
+ return null;
+ } else if (getCheckedItem() == mDefaultRingtonePos) {
+ // Use the default Uri that they originally gave us.
+ return mUriForDefaultItem;
+ } else if (getCheckedItem() == mSilentPos) {
+ // Use a null Uri for the 'Silent' item.
+ return null;
+ } else {
+ return mRingtoneManager.getRingtoneUri(getRingtoneManagerPosition(getCheckedItem()));
+ }
+ }
+
+ private void saveAnyPlayingRingtone() {
+ if (mDefaultRingtone != null && mDefaultRingtone.isPlaying()) {
+ sPlayingRingtone = mDefaultRingtone;
+ } else if (mCurrentRingtone != null && mCurrentRingtone.isPlaying()) {
+ sPlayingRingtone = mCurrentRingtone;
+ }
+ }
+
+ private void stopAnyPlayingRingtone() {
+ if (sPlayingRingtone != null && sPlayingRingtone.isPlaying()) {
+ sPlayingRingtone.stop();
+ }
+ sPlayingRingtone = null;
+
+ if (mDefaultRingtone != null && mDefaultRingtone.isPlaying()) {
+ mDefaultRingtone.stop();
}
- switch (category) {
- case "android.intent.category.RINGTONE_PICKER_RINGTONE":
- return RingtonePickerViewModel.PickerType.RINGTONE_PICKER;
- case "android.intent.category.RINGTONE_PICKER_SOUND":
- return RingtonePickerViewModel.PickerType.SOUND_PICKER;
- case "android.intent.category.RINGTONE_PICKER_VIBRATION":
- return RingtonePickerViewModel.PickerType.VIBRATION_PICKER;
- default:
- Log.w(TAG, "Unrecognized category: " + category + ". Defaulting to sound picker.");
- return RingtonePickerViewModel.PickerType.SOUND_PICKER;
+ if (mRingtoneManager != null) {
+ mRingtoneManager.stopPreviousRingtone();
+ }
+ }
+
+ private int getRingtoneManagerPosition(int listPos) {
+ return listPos - mStaticItemCount;
+ }
+
+ private int getListPosition(int ringtoneManagerPos) {
+
+ // If the manager position is -1 (for not found), return that
+ if (ringtoneManagerPos < 0) return ringtoneManagerPos;
+
+ return ringtoneManagerPos + mStaticItemCount;
+ }
+
+ private Intent getMediaFilePickerIntent() {
+ final Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
+ chooseFile.setType("audio/*");
+ chooseFile.putExtra(Intent.EXTRA_MIME_TYPES,
+ new String[] { "audio/*", "application/ogg" });
+ return chooseFile;
+ }
+
+ private boolean resolvesMediaFilePicker() {
+ return getMediaFilePickerIntent().resolveActivity(getPackageManager()) != null;
+ }
+
+ private static class LocalizedCursor extends CursorWrapper {
+
+ final int mTitleIndex;
+ final Resources mResources;
+ String mNamePrefix;
+ final Pattern mSanitizePattern;
+
+ LocalizedCursor(Cursor cursor, Resources resources, String columnLabel) {
+ super(cursor);
+ mTitleIndex = mCursor.getColumnIndex(columnLabel);
+ mResources = resources;
+ mSanitizePattern = Pattern.compile("[^a-zA-Z0-9]");
+ if (mTitleIndex == -1) {
+ Log.e(TAG, "No index for column " + columnLabel);
+ mNamePrefix = null;
+ } else {
+ try {
+ // Build the prefix for the name of the resource to look up
+ // format is: "ResourcePackageName::ResourceTypeName/"
+ // (the type name is expected to be "string" but let's not hardcode it).
+ // Here we use an existing resource "notification_sound_default" which is
+ // always expected to be found.
+ mNamePrefix = String.format("%s:%s/%s",
+ mResources.getResourcePackageName(R.string.notification_sound_default),
+ mResources.getResourceTypeName(R.string.notification_sound_default),
+ SOUND_NAME_RES_PREFIX);
+ } catch (NotFoundException e) {
+ mNamePrefix = null;
+ }
+ }
+ }
+
+ /**
+ * Process resource name to generate a valid resource name.
+ * @param input
+ * @return a non-null String
+ */
+ private String sanitize(String input) {
+ if (input == null) {
+ return "";
+ }
+ return mSanitizePattern.matcher(input).replaceAll("_").toLowerCase();
+ }
+
+ @Override
+ public String getString(int columnIndex) {
+ final String defaultName = mCursor.getString(columnIndex);
+ if ((columnIndex != mTitleIndex) || (mNamePrefix == null)) {
+ return defaultName;
+ }
+ TypedValue value = new TypedValue();
+ try {
+ // the name currently in the database is used to derive a name to match
+ // against resource names in this package
+ mResources.getValue(mNamePrefix + sanitize(defaultName), value, false);
+ } catch (NotFoundException e) {
+ // no localized string, use the default string
+ return defaultName;
+ }
+ if ((value != null) && (value.type == TypedValue.TYPE_STRING)) {
+ Log.d(TAG, String.format("Replacing name %s with %s",
+ defaultName, value.string.toString()));
+ return value.string.toString();
+ } else {
+ Log.e(TAG, "Invalid value when looking up localized name, using " + defaultName);
+ return defaultName;
+ }
+ }
+ }
+
+ private class BadgedRingtoneAdapter extends CursorAdapter {
+ private final boolean mIsManagedProfile;
+
+ public BadgedRingtoneAdapter(Context context, Cursor cursor, boolean isManagedProfile) {
+ super(context, cursor);
+ mIsManagedProfile = isManagedProfile;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ if (position < 0) {
+ return position;
+ }
+ return super.getItemId(position);
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ LayoutInflater inflater = LayoutInflater.from(context);
+ return inflater.inflate(R.layout.radio_with_work_badge, parent, false);
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ // Set text as the title of the ringtone
+ ((TextView) view.findViewById(R.id.checked_text_view))
+ .setText(cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX));
+
+ boolean isWorkRingtone = false;
+ if (mIsManagedProfile) {
+ /*
+ * Display the work icon if the ringtone belongs to a work profile. We can tell that
+ * a ringtone belongs to a work profile if the picker user is a managed profile, the
+ * ringtone Uri is in external storage, and either the uri has no user id or has the
+ * id of the picker user
+ */
+ Uri currentUri = mRingtoneManager.getRingtoneUri(cursor.getPosition());
+ int uriUserId = ContentProvider.getUserIdFromUri(currentUri, mPickerUserId);
+ Uri uriWithoutUserId = ContentProvider.getUriWithoutUserId(currentUri);
+
+ if (uriUserId == mPickerUserId && uriWithoutUserId.toString()
+ .startsWith(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI.toString())) {
+ isWorkRingtone = true;
+ }
+ }
+
+ ImageView workIcon = (ImageView) view.findViewById(R.id.work_icon);
+ if(isWorkRingtone) {
+ workIcon.setImageDrawable(getPackageManager().getUserBadgeForDensityNoBackground(
+ UserHandle.of(mPickerUserId), -1 /* density */));
+ workIcon.setVisibility(View.VISIBLE);
+ } else {
+ workIcon.setVisibility(View.GONE);
+ }
}
}
-}
+} \ No newline at end of file
diff --git a/packages/SoundPicker2/Android.bp b/packages/SoundPicker2/Android.bp
new file mode 100644
index 000000000000..f4d8bf2c76b5
--- /dev/null
+++ b/packages/SoundPicker2/Android.bp
@@ -0,0 +1,46 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_library {
+ name: "SoundPicker2Lib",
+ srcs: [
+ "src/**/*.java",
+ ],
+ resource_dirs: [
+ "res",
+ ],
+ static_libs: [
+ "androidx.appcompat_appcompat",
+ "hilt_android",
+ "guava",
+ "androidx.recyclerview_recyclerview",
+ "androidx-constraintlayout_constraintlayout",
+ "androidx.viewpager2_viewpager2",
+ "com.google.android.material_material",
+ ],
+}
+
+android_app {
+ name: "SoundPicker2",
+ defaults: ["platform_app_defaults"],
+ manifest: "AndroidManifest.xml",
+ static_libs: ["SoundPicker2Lib"],
+ platform_apis: true,
+ certificate: "media",
+ privileged: true,
+
+ optimize: {
+ enabled: true,
+ optimize: true,
+ shrink: true,
+ shrink_resources: true,
+ obfuscate: false,
+ proguard_compatibility: false,
+ },
+}
diff --git a/packages/SoundPicker2/AndroidManifest.xml b/packages/SoundPicker2/AndroidManifest.xml
new file mode 100644
index 000000000000..934b003c605c
--- /dev/null
+++ b/packages/SoundPicker2/AndroidManifest.xml
@@ -0,0 +1,43 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.soundpicker"
+ android:sharedUserId="android.media">
+
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+ <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+ <uses-permission android:name="android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY" />
+ <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
+
+ <application
+ android:name=".RingtonePickerApplication"
+ android:allowBackup="false"
+ android:label="@string/app_label"
+ android:theme="@style/Theme.AppCompat"
+ android:supportsRtl="true">
+ <receiver android:name="RingtoneReceiver"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.DEVICE_CUSTOMIZATION_READY"/>
+ </intent-filter>
+ </receiver>
+
+ <service android:name="RingtoneOverlayService" />
+
+ <activity android:name="RingtonePickerActivity"
+ android:theme="@style/Theme.AppCompat.Dialog"
+ android:enabled="@*android:bool/config_defaultRingtonePickerEnabled"
+ android:excludeFromRecents="true"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.RINGTONE_PICKER" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.RINGTONE_PICKER_SOUND" />
+ <category android:name="android.intent.category.RINGTONE_PICKER_VIBRATION" />
+ <category android:name="android.intent.category.RINGTONE_PICKER_RINGTONE" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/packages/SoundPicker2/OWNERS b/packages/SoundPicker2/OWNERS
new file mode 100644
index 000000000000..5bf46e039e96
--- /dev/null
+++ b/packages/SoundPicker2/OWNERS
@@ -0,0 +1,2 @@
+# Haptics team works on the SoundPicker
+include platform/frameworks/base:/services/core/java/com/android/server/vibrator/OWNERS
diff --git a/packages/SoundPicker2/res/drawable/ic_add.xml b/packages/SoundPicker2/res/drawable/ic_add.xml
new file mode 100644
index 000000000000..22b3fe9176e5
--- /dev/null
+++ b/packages/SoundPicker2/res/drawable/ic_add.xml
@@ -0,0 +1,24 @@
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path
+ android:fillColor="?android:attr/colorAccent"
+ android:pathData="M38.0,26.0L26.0,26.0l0.0,12.0l-4.0,0.0L22.0,26.0L10.0,26.0l0.0,-4.0l12.0,0.0L22.0,10.0l4.0,0.0l0.0,12.0l12.0,0.0l0.0,4.0z"/>
+</vector> \ No newline at end of file
diff --git a/packages/SoundPicker2/res/drawable/ic_add_padded.xml b/packages/SoundPicker2/res/drawable/ic_add_padded.xml
new file mode 100644
index 000000000000..c376867896d0
--- /dev/null
+++ b/packages/SoundPicker2/res/drawable/ic_add_padded.xml
@@ -0,0 +1,22 @@
+<!--
+ Copyright (C) 2017 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.
+-->
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/ic_add"
+ android:insetTop="4dp"
+ android:insetRight="4dp"
+ android:insetBottom="4dp"
+ android:insetLeft="4dp"/>
diff --git a/packages/SoundPicker2/res/layout-watch/add_new_sound_item.xml b/packages/SoundPicker2/res/layout-watch/add_new_sound_item.xml
new file mode 100644
index 000000000000..edfc0aba5be7
--- /dev/null
+++ b/packages/SoundPicker2/res/layout-watch/add_new_sound_item.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 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.
+-->
+
+<!--
+ Currently, no file manager app on watch could handle ACTION_GET_CONTENT intent.
+ Make the visibility to "gone" to prevent failures.
+ -->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/add_new_sound_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@null"
+ android:textColor="?android:attr/colorAccent"
+ android:gravity="center_vertical"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:drawableStart="@drawable/ic_add_padded"
+ android:drawablePadding="8dp"
+ android:ellipsize="marquee"
+ android:visibility="gone" />
diff --git a/packages/SoundPicker2/res/layout-watch/radio_with_work_badge.xml b/packages/SoundPicker2/res/layout-watch/radio_with_work_badge.xml
new file mode 100644
index 000000000000..ee29a3710143
--- /dev/null
+++ b/packages/SoundPicker2/res/layout-watch/radio_with_work_badge.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.soundpicker.CheckedListItem xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:background="?android:attr/selectableItemBackground"
+ >
+
+ <CheckedTextView
+ android:id="@+id/checked_text_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:attr/textColorAlertDialogListItem"
+ android:gravity="center_vertical"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:drawableStart="?android:attr/listChoiceIndicatorSingle"
+ android:drawablePadding="8dp"
+ android:ellipsize="marquee"
+ android:layout_toLeftOf="@+id/work_icon"
+ android:maxLines="3" />
+
+ <ImageView
+ android:id="@id/work_icon"
+ android:layout_width="18dp"
+ android:layout_height="18dp"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true"
+ android:scaleType="centerCrop"
+ android:layout_marginRight="20dp" />
+</com.android.soundpicker.CheckedListItem>
diff --git a/packages/SoundPicker/res/layout/activity_ringtone_picker.xml b/packages/SoundPicker2/res/layout/activity_ringtone_picker.xml
index 6fc60801ad3a..6fc60801ad3a 100644
--- a/packages/SoundPicker/res/layout/activity_ringtone_picker.xml
+++ b/packages/SoundPicker2/res/layout/activity_ringtone_picker.xml
diff --git a/packages/SoundPicker2/res/layout/add_new_sound_item.xml b/packages/SoundPicker2/res/layout/add_new_sound_item.xml
new file mode 100644
index 000000000000..024b97ef23be
--- /dev/null
+++ b/packages/SoundPicker2/res/layout/add_new_sound_item.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:background="?android:attr/selectableItemBackground"
+ android:focusable="true"
+ android:clickable="true">
+
+ <ImageView
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true"
+ android:scaleType="centerCrop"
+ android:layout_marginRight="24dp"
+ android:layout_marginLeft="24dp"
+ android:src="@drawable/ic_add"/>
+
+ <TextView
+ android:id="@+id/add_new_sound_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:text="@null"
+ android:textColor="?android:attr/colorAccent"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:maxLines="3"
+ android:gravity="center_vertical"
+ android:paddingEnd="?android:attr/dialogPreferredPadding"
+ android:drawablePadding="20dp"
+ android:ellipsize="marquee"/>
+</LinearLayout> \ No newline at end of file
diff --git a/packages/SoundPicker/res/layout/fragment_ringtone_picker.xml b/packages/SoundPicker2/res/layout/fragment_ringtone_picker.xml
index 787f92ec06d6..787f92ec06d6 100644
--- a/packages/SoundPicker/res/layout/fragment_ringtone_picker.xml
+++ b/packages/SoundPicker2/res/layout/fragment_ringtone_picker.xml
diff --git a/packages/SoundPicker/res/layout/fragment_tabbed_dialog.xml b/packages/SoundPicker2/res/layout/fragment_tabbed_dialog.xml
index 7efd91191b79..7efd91191b79 100644
--- a/packages/SoundPicker/res/layout/fragment_tabbed_dialog.xml
+++ b/packages/SoundPicker2/res/layout/fragment_tabbed_dialog.xml
diff --git a/packages/SoundPicker2/res/layout/radio_with_work_badge.xml b/packages/SoundPicker2/res/layout/radio_with_work_badge.xml
new file mode 100644
index 000000000000..36ac93ed630b
--- /dev/null
+++ b/packages/SoundPicker2/res/layout/radio_with_work_badge.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<com.android.soundpicker.CheckedListItem
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:background="?android:attr/selectableItemBackground"
+ android:focusable="true"
+ android:clickable="true">
+
+ <CheckedTextView
+ android:id="@+id/checked_text_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:attr/textColorAlertDialogListItem"
+ android:gravity="center_vertical"
+ android:paddingStart="20dp"
+ android:paddingEnd="?android:attr/dialogPreferredPadding"
+ android:drawableStart="?android:attr/listChoiceIndicatorSingle"
+ android:drawablePadding="20dp"
+ android:ellipsize="marquee"
+ android:layout_toLeftOf="@+id/work_icon"
+ android:maxLines="3"/>
+
+ <ImageView
+ android:id="@id/work_icon"
+ android:layout_width="18dp"
+ android:layout_height="18dp"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true"
+ android:scaleType="centerCrop"
+ android:layout_marginRight="20dp"/>
+</com.android.soundpicker.CheckedListItem>
diff --git a/packages/SoundPicker2/res/raw/default_alarm_alert.ogg b/packages/SoundPicker2/res/raw/default_alarm_alert.ogg
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/packages/SoundPicker2/res/raw/default_alarm_alert.ogg
diff --git a/packages/SoundPicker2/res/raw/default_notification_sound.ogg b/packages/SoundPicker2/res/raw/default_notification_sound.ogg
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/packages/SoundPicker2/res/raw/default_notification_sound.ogg
diff --git a/packages/SoundPicker2/res/raw/default_ringtone.ogg b/packages/SoundPicker2/res/raw/default_ringtone.ogg
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/packages/SoundPicker2/res/raw/default_ringtone.ogg
diff --git a/packages/SoundPicker2/res/values/config.xml b/packages/SoundPicker2/res/values/config.xml
new file mode 100644
index 000000000000..4e237a2f1644
--- /dev/null
+++ b/packages/SoundPicker2/res/values/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. Do not translate.
+
+ NOTE: The naming convention is "config_camelCaseValue". -->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- True if the ringtone picker should show the ok/cancel buttons. If it is not shown, the
+ ringtone will be automatically selected when the picker is closed. -->
+ <bool name="config_showOkCancelButtons">true</bool>
+</resources>
diff --git a/packages/SoundPicker2/res/values/strings.xml b/packages/SoundPicker2/res/values/strings.xml
new file mode 100644
index 000000000000..ab7b95a09028
--- /dev/null
+++ b/packages/SoundPicker2/res/values/strings.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Choice in the ringtone picker. If chosen, the default ringtone will be used. -->
+ <string name="ringtone_default">Default ringtone</string>
+
+ <!-- Choice in the notification sound picker. If chosen, the default notification sound will be
+ used. -->
+ <string name="notification_sound_default">Default notification sound</string>
+
+ <!-- Choice in the alarm sound picker. If chosen, the default alarm sound will be used. -->
+ <string name="alarm_sound_default">Default alarm sound</string>
+
+ <!-- Text for the RingtonePicker item that allows adding a new ringtone. -->
+ <string name="add_ringtone_text">Add ringtone</string>
+ <!-- Text for the RingtonePicker item that allows adding a new alarm. -->
+ <string name="add_alarm_text">Add alarm</string>
+ <!-- Text for the RingtonePicker item that allows adding a new notification. -->
+ <string name="add_notification_text">Add notification</string>
+ <!-- Text for the RingtonePicker item ContextMenu that allows deleting a custom ringtone. -->
+ <string name="delete_ringtone_text">Delete</string>
+ <!-- Text for the Toast displayed when adding a custom ringtone fails. -->
+ <string name="unable_to_add_ringtone">Unable to add custom ringtone</string>
+ <!-- Text for the Toast displayed when deleting a custom ringtone fails. -->
+ <string name="unable_to_delete_ringtone">Unable to delete custom ringtone</string>
+
+ <!-- Text for the name of the app. [CHAR LIMIT=12] -->
+ <string name="app_label">Sounds</string>
+
+ <string name="empty_list">The list is empty</string>
+ <string name="sound_page_title">Sound</string>
+ <string name="vibration_page_title">Vibration</string>
+</resources>
diff --git a/packages/SoundPicker2/res/values/styles.xml b/packages/SoundPicker2/res/values/styles.xml
new file mode 100644
index 000000000000..d22d9c43d0fb
--- /dev/null
+++ b/packages/SoundPicker2/res/values/styles.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <style name="PickerDialogTheme" parent="@*android:style/Theme.DeviceDefault.Settings.Dialog">
+ </style>
+
+</resources>
diff --git a/packages/SoundPicker/src/com/android/soundpicker/BasePickerFragment.java b/packages/SoundPicker2/src/com/android/soundpicker/BasePickerFragment.java
index 4fc2a86537c1..4fc2a86537c1 100644
--- a/packages/SoundPicker/src/com/android/soundpicker/BasePickerFragment.java
+++ b/packages/SoundPicker2/src/com/android/soundpicker/BasePickerFragment.java
diff --git a/packages/SoundPicker2/src/com/android/soundpicker/CheckedListItem.java b/packages/SoundPicker2/src/com/android/soundpicker/CheckedListItem.java
new file mode 100644
index 000000000000..819ae987269d
--- /dev/null
+++ b/packages/SoundPicker2/src/com/android/soundpicker/CheckedListItem.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.soundpicker;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.Checkable;
+import android.widget.CheckedTextView;
+import android.widget.RelativeLayout;
+
+/**
+ * The {@link CheckedListItem} is a layout item that represents a ringtone, and is used in
+ * {@link RingtonePickerActivity}. It contains the ringtone's name, and a work badge to right of the
+ * name if the ringtone belongs to a work profile.
+ */
+public class CheckedListItem extends RelativeLayout implements Checkable {
+
+ public CheckedListItem(Context context) {
+ super(context);
+ }
+
+ public CheckedListItem(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public CheckedListItem(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public CheckedListItem(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ public void setChecked(boolean checked) {
+ getCheckedTextView().setChecked(checked);
+ }
+
+ @Override
+ public boolean isChecked() {
+ return getCheckedTextView().isChecked();
+ }
+
+ @Override
+ public void toggle() {
+ getCheckedTextView().toggle();
+ }
+
+ private CheckedTextView getCheckedTextView() {
+ return (CheckedTextView) findViewById(R.id.checked_text_view);
+ }
+
+}
diff --git a/packages/SoundPicker/src/com/android/soundpicker/ListeningExecutorServiceFactory.java b/packages/SoundPicker2/src/com/android/soundpicker/ListeningExecutorServiceFactory.java
index afdbf053ac22..afdbf053ac22 100644
--- a/packages/SoundPicker/src/com/android/soundpicker/ListeningExecutorServiceFactory.java
+++ b/packages/SoundPicker2/src/com/android/soundpicker/ListeningExecutorServiceFactory.java
diff --git a/packages/SoundPicker/src/com/android/soundpicker/LocalizedCursor.java b/packages/SoundPicker2/src/com/android/soundpicker/LocalizedCursor.java
index 83d04a345f8b..83d04a345f8b 100644
--- a/packages/SoundPicker/src/com/android/soundpicker/LocalizedCursor.java
+++ b/packages/SoundPicker2/src/com/android/soundpicker/LocalizedCursor.java
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtoneFactory.java b/packages/SoundPicker2/src/com/android/soundpicker/RingtoneFactory.java
index 6817f534c00b..6817f534c00b 100644
--- a/packages/SoundPicker/src/com/android/soundpicker/RingtoneFactory.java
+++ b/packages/SoundPicker2/src/com/android/soundpicker/RingtoneFactory.java
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtoneListHandler.java b/packages/SoundPicker2/src/com/android/soundpicker/RingtoneListHandler.java
index bb38e0e2ecaa..bb38e0e2ecaa 100644
--- a/packages/SoundPicker/src/com/android/soundpicker/RingtoneListHandler.java
+++ b/packages/SoundPicker2/src/com/android/soundpicker/RingtoneListHandler.java
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtoneListViewAdapter.java b/packages/SoundPicker2/src/com/android/soundpicker/RingtoneListViewAdapter.java
index 4ca8943b5fd4..4ca8943b5fd4 100644
--- a/packages/SoundPicker/src/com/android/soundpicker/RingtoneListViewAdapter.java
+++ b/packages/SoundPicker2/src/com/android/soundpicker/RingtoneListViewAdapter.java
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtoneManagerFactory.java b/packages/SoundPicker2/src/com/android/soundpicker/RingtoneManagerFactory.java
index f08eb24ec20d..f08eb24ec20d 100644
--- a/packages/SoundPicker/src/com/android/soundpicker/RingtoneManagerFactory.java
+++ b/packages/SoundPicker2/src/com/android/soundpicker/RingtoneManagerFactory.java
diff --git a/packages/SoundPicker2/src/com/android/soundpicker/RingtoneOverlayService.java b/packages/SoundPicker2/src/com/android/soundpicker/RingtoneOverlayService.java
new file mode 100644
index 000000000000..b94ebebd825b
--- /dev/null
+++ b/packages/SoundPicker2/src/com/android/soundpicker/RingtoneOverlayService.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.soundpicker;
+
+import android.app.Service;
+import android.content.Intent;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.IBinder;
+import android.provider.MediaStore;
+import android.provider.Settings.System;
+import android.util.Log;
+
+import androidx.annotation.IdRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Service to copy and set customization of default sounds
+ */
+public class RingtoneOverlayService extends Service {
+ private static final String TAG = "RingtoneOverlayService";
+ private static final boolean DEBUG = false;
+
+ @Override
+ public int onStartCommand(@Nullable final Intent intent, final int flags, final int startId) {
+ AsyncTask.execute(() -> {
+ updateRingtones();
+ stopSelf();
+ });
+
+ // Try again later if we are killed before we finish.
+ return Service.START_REDELIVER_INTENT;
+ }
+
+ @Override
+ public IBinder onBind(@Nullable final Intent intent) {
+ return null;
+ }
+
+ private void updateRingtones() {
+ copyResourceAndSetAsSound(R.raw.default_ringtone,
+ System.RINGTONE, Environment.DIRECTORY_RINGTONES);
+ copyResourceAndSetAsSound(R.raw.default_notification_sound,
+ System.NOTIFICATION_SOUND, Environment.DIRECTORY_NOTIFICATIONS);
+ copyResourceAndSetAsSound(R.raw.default_alarm_alert,
+ System.ALARM_ALERT, Environment.DIRECTORY_ALARMS);
+ }
+
+ /* If the resource contains any data, copy a resource to the file system, scan it, and set the
+ * file URI as the default for a sound. */
+ private void copyResourceAndSetAsSound(@IdRes final int id, @NonNull final String name,
+ @NonNull final String subPath) {
+ final File destDir = Environment.getExternalStoragePublicDirectory(subPath);
+ if (!destDir.exists() && !destDir.mkdirs()) {
+ Log.e(TAG, "can't create " + destDir.getAbsolutePath());
+ return;
+ }
+
+ final File dest = new File(destDir, "default_" + name + ".ogg");
+ try (
+ InputStream is = getResources().openRawResource(id);
+ FileOutputStream os = new FileOutputStream(dest);
+ ) {
+ if (is.available() > 0) {
+ FileUtils.copy(is, os);
+ final Uri uri = scanFile(dest);
+ if (uri != null) {
+ set(name, uri);
+ }
+ } else {
+ // TODO Shall we remove any former copied resource in this case and unset
+ // the defaults if we use this event a second time to clear the data?
+ if (DEBUG) Log.d(TAG, "Resource for " + name + " has no overlay");
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to open resource for " + name + ": " + e);
+ }
+ }
+
+ private Uri scanFile(@NonNull final File file) {
+ return MediaStore.scanFile(getContentResolver(), file);
+ }
+
+ private void set(@NonNull final String name, @NonNull final Uri uri) {
+ final Uri settingUri = System.getUriFor(name);
+ RingtoneManager.setActualDefaultRingtoneUri(this,
+ RingtoneManager.getDefaultType(settingUri), uri);
+ System.putInt(getContentResolver(), name + "_set", 1);
+ }
+}
diff --git a/packages/SoundPicker2/src/com/android/soundpicker/RingtonePickerActivity.java b/packages/SoundPicker2/src/com/android/soundpicker/RingtonePickerActivity.java
new file mode 100644
index 000000000000..90a14f9717db
--- /dev/null
+++ b/packages/SoundPicker2/src/com/android/soundpicker/RingtonePickerActivity.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2007 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.soundpicker;
+
+import android.content.Intent;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.util.Log;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentTransaction;
+import androidx.lifecycle.ViewModelProvider;
+
+import dagger.hilt.android.AndroidEntryPoint;
+
+/**
+ * The {@link RingtonePickerActivity} allows the user to choose one from all of the
+ * available ringtones. The chosen ringtone's URI will be persisted as a string.
+ *
+ * @see RingtoneManager#ACTION_RINGTONE_PICKER
+ */
+@AndroidEntryPoint(AppCompatActivity.class)
+public final class RingtonePickerActivity extends Hilt_RingtonePickerActivity {
+
+ private static final String TAG = "RingtonePickerActivity";
+ // TODO: Use the extra keys from RingtoneManager once they're added.
+ private static final String EXTRA_RINGTONE_PICKER_CATEGORY = "EXTRA_RINGTONE_PICKER_CATEGORY";
+ private static final String EXTRA_VIBRATION_SHOW_DEFAULT = "EXTRA_VIBRATION_SHOW_DEFAULT";
+ private static final String EXTRA_VIBRATION_DEFAULT_URI = "EXTRA_VIBRATION_DEFAULT_URI";
+ private static final String EXTRA_VIBRATION_SHOW_SILENT = "EXTRA_VIBRATION_SHOW_SILENT";
+ private static final String EXTRA_VIBRATION_EXISTING_URI = "EXTRA_VIBRATION_EXISTING_URI";
+ private static final boolean RINGTONE_PICKER_CATEGORY_FEATURE_ENABLED = false;
+
+ private RingtonePickerViewModel mRingtonePickerViewModel;
+ private int mAttributesFlags;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_ringtone_picker);
+
+ mRingtonePickerViewModel = new ViewModelProvider(this).get(RingtonePickerViewModel.class);
+
+ Intent intent = getIntent();
+ /**
+ * Id of the user to which the ringtone picker should list the ringtones
+ */
+ int pickerUserId = UserHandle.myUserId();
+
+ // Get the types of ringtones to show
+ int ringtoneType = intent.getIntExtra(RingtoneManager.EXTRA_RINGTONE_TYPE,
+ RingtonePickerViewModel.RINGTONE_TYPE_UNKNOWN);
+
+ // AudioAttributes flags
+ mAttributesFlags |= intent.getIntExtra(
+ RingtoneManager.EXTRA_RINGTONE_AUDIO_ATTRIBUTES_FLAGS,
+ 0 /*defaultValue == no flags*/);
+
+ boolean showOkCancelButtons = getResources().getBoolean(R.bool.config_showOkCancelButtons);
+
+ String title = intent.getStringExtra(RingtoneManager.EXTRA_RINGTONE_TITLE);
+ if (title == null) {
+ title = getString(RingtonePickerViewModel.getTitleByType(ringtoneType));
+ }
+ String ringtonePickerCategory = intent.getStringExtra(EXTRA_RINGTONE_PICKER_CATEGORY);
+ RingtonePickerViewModel.PickerType pickerType = mapCategoryToPickerType(
+ ringtonePickerCategory);
+
+ RingtoneListHandler.Config soundListConfig = getSoundListConfig(pickerType, intent,
+ ringtoneType);
+ RingtoneListHandler.Config vibrationListConfig = getVibrationListConfig(pickerType, intent);
+
+ RingtonePickerViewModel.Config pickerConfig =
+ new RingtonePickerViewModel.Config(title, pickerUserId, ringtoneType,
+ showOkCancelButtons, mAttributesFlags, pickerType);
+
+ mRingtonePickerViewModel.init(pickerConfig, soundListConfig, vibrationListConfig);
+
+ if (savedInstanceState == null) {
+ TabbedDialogFragment dialogFragment = new TabbedDialogFragment();
+
+ FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+ Fragment prev = getSupportFragmentManager().findFragmentByTag(TabbedDialogFragment.TAG);
+ if (prev != null) {
+ ft.remove(prev);
+ }
+ ft.addToBackStack(null);
+ dialogFragment.show(ft, TabbedDialogFragment.TAG);
+ }
+
+ // The volume keys will control the stream that we are choosing a ringtone for
+ setVolumeControlStream(mRingtonePickerViewModel.getRingtoneStreamType());
+ }
+
+ private RingtoneListHandler.Config getSoundListConfig(
+ RingtonePickerViewModel.PickerType pickerType, Intent intent, int ringtoneType) {
+ if (pickerType != RingtonePickerViewModel.PickerType.SOUND_PICKER
+ && pickerType != RingtonePickerViewModel.PickerType.RINGTONE_PICKER) {
+ // This ringtone picker does not require a sound picker.
+ return null;
+ }
+
+ // Get whether to show the 'Default' sound item, and the URI to play when it's clicked
+ boolean hasDefaultSoundItem =
+ intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);
+
+ // The Uri to play when the 'Default' sound item is clicked.
+ Uri uriForDefaultSoundItem =
+ intent.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI);
+ if (uriForDefaultSoundItem == null) {
+ uriForDefaultSoundItem = RingtonePickerViewModel.getDefaultItemUriByType(ringtoneType);
+ }
+
+ // Get whether this list has the 'Silent' sound item.
+ boolean hasSilentSoundItem =
+ intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true);
+
+ // AudioAttributes flags
+ mAttributesFlags |= intent.getIntExtra(
+ RingtoneManager.EXTRA_RINGTONE_AUDIO_ATTRIBUTES_FLAGS,
+ 0 /*defaultValue == no flags*/);
+
+ // Get the sound URI whose list item should have a checkmark
+ Uri existingSoundUri = intent
+ .getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI);
+
+ return new RingtoneListHandler.Config(hasDefaultSoundItem,
+ uriForDefaultSoundItem, hasSilentSoundItem, existingSoundUri);
+ }
+
+ private RingtoneListHandler.Config getVibrationListConfig(
+ RingtonePickerViewModel.PickerType pickerType, Intent intent) {
+ if (pickerType != RingtonePickerViewModel.PickerType.VIBRATION_PICKER
+ && pickerType != RingtonePickerViewModel.PickerType.RINGTONE_PICKER) {
+ // This ringtone picker does not require a vibration picker.
+ return null;
+ }
+
+ // Get whether to show the 'Default' vibration item, and the URI to play when it's clicked
+ boolean hasDefaultVibrationItem =
+ intent.getBooleanExtra(EXTRA_VIBRATION_SHOW_DEFAULT, false);
+
+ // The Uri to play when the 'Default' vibration item is clicked.
+ Uri uriForDefaultVibrationItem = intent.getParcelableExtra(EXTRA_VIBRATION_DEFAULT_URI);
+
+ // Get whether this list has the 'Silent' vibration item.
+ boolean hasSilentVibrationItem =
+ intent.getBooleanExtra(EXTRA_VIBRATION_SHOW_SILENT, true);
+
+ // Get the vibration URI whose list item should have a checkmark
+ Uri existingVibrationUri = intent.getParcelableExtra(EXTRA_VIBRATION_EXISTING_URI);
+
+ return new RingtoneListHandler.Config(
+ hasDefaultVibrationItem, uriForDefaultVibrationItem, hasSilentVibrationItem,
+ existingVibrationUri);
+ }
+
+ @Override
+ public void onDestroy() {
+ mRingtonePickerViewModel.cancelPendingAsyncTasks();
+ super.onDestroy();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ mRingtonePickerViewModel.onStop(isChangingConfigurations());
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mRingtonePickerViewModel.onPause(isChangingConfigurations());
+ }
+
+ /**
+ * Maps the ringtone picker category to the appropriate PickerType.
+ * If the category is null or the feature is still not released, then it defaults to sound
+ * picker.
+ *
+ * @param category the ringtone picker category.
+ * @return the corresponding picker type.
+ */
+ private static RingtonePickerViewModel.PickerType mapCategoryToPickerType(String category) {
+ if (category == null || !RINGTONE_PICKER_CATEGORY_FEATURE_ENABLED) {
+ return RingtonePickerViewModel.PickerType.SOUND_PICKER;
+ }
+
+ switch (category) {
+ case "android.intent.category.RINGTONE_PICKER_RINGTONE":
+ return RingtonePickerViewModel.PickerType.RINGTONE_PICKER;
+ case "android.intent.category.RINGTONE_PICKER_SOUND":
+ return RingtonePickerViewModel.PickerType.SOUND_PICKER;
+ case "android.intent.category.RINGTONE_PICKER_VIBRATION":
+ return RingtonePickerViewModel.PickerType.VIBRATION_PICKER;
+ default:
+ Log.w(TAG, "Unrecognized category: " + category + ". Defaulting to sound picker.");
+ return RingtonePickerViewModel.PickerType.SOUND_PICKER;
+ }
+ }
+}
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerApplication.java b/packages/SoundPicker2/src/com/android/soundpicker/RingtonePickerApplication.java
index 48fd4fe2f15e..48fd4fe2f15e 100644
--- a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerApplication.java
+++ b/packages/SoundPicker2/src/com/android/soundpicker/RingtonePickerApplication.java
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerViewModel.java b/packages/SoundPicker2/src/com/android/soundpicker/RingtonePickerViewModel.java
index 2c0971121ccd..2c0971121ccd 100644
--- a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerViewModel.java
+++ b/packages/SoundPicker2/src/com/android/soundpicker/RingtonePickerViewModel.java
diff --git a/packages/SoundPicker2/src/com/android/soundpicker/RingtoneReceiver.java b/packages/SoundPicker2/src/com/android/soundpicker/RingtoneReceiver.java
new file mode 100644
index 000000000000..6a349366e744
--- /dev/null
+++ b/packages/SoundPicker2/src/com/android/soundpicker/RingtoneReceiver.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2007 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.soundpicker;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+public class RingtoneReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (Intent.ACTION_DEVICE_CUSTOMIZATION_READY.equals(action)) {
+ initResourceRingtones(context);
+ }
+ }
+
+ private void initResourceRingtones(Context context) {
+ context.startService(
+ new Intent(context, RingtoneOverlayService.class));
+ }
+}
diff --git a/packages/SoundPicker/src/com/android/soundpicker/SoundPickerFragment.java b/packages/SoundPicker2/src/com/android/soundpicker/SoundPickerFragment.java
index a37191f33668..a37191f33668 100644
--- a/packages/SoundPicker/src/com/android/soundpicker/SoundPickerFragment.java
+++ b/packages/SoundPicker2/src/com/android/soundpicker/SoundPickerFragment.java
diff --git a/packages/SoundPicker/src/com/android/soundpicker/TabbedDialogFragment.java b/packages/SoundPicker2/src/com/android/soundpicker/TabbedDialogFragment.java
index 50ea9d7d3056..50ea9d7d3056 100644
--- a/packages/SoundPicker/src/com/android/soundpicker/TabbedDialogFragment.java
+++ b/packages/SoundPicker2/src/com/android/soundpicker/TabbedDialogFragment.java
diff --git a/packages/SoundPicker/src/com/android/soundpicker/VibrationPickerFragment.java b/packages/SoundPicker2/src/com/android/soundpicker/VibrationPickerFragment.java
index 7412c1995b5a..7412c1995b5a 100644
--- a/packages/SoundPicker/src/com/android/soundpicker/VibrationPickerFragment.java
+++ b/packages/SoundPicker2/src/com/android/soundpicker/VibrationPickerFragment.java
diff --git a/packages/SoundPicker/src/com/android/soundpicker/ViewPagerAdapter.java b/packages/SoundPicker2/src/com/android/soundpicker/ViewPagerAdapter.java
index 179068e9f20f..179068e9f20f 100644
--- a/packages/SoundPicker/src/com/android/soundpicker/ViewPagerAdapter.java
+++ b/packages/SoundPicker2/src/com/android/soundpicker/ViewPagerAdapter.java
diff --git a/packages/SoundPicker/tests/Android.bp b/packages/SoundPicker2/tests/Android.bp
index c38426fa6746..d88d442afa17 100644
--- a/packages/SoundPicker/tests/Android.bp
+++ b/packages/SoundPicker2/tests/Android.bp
@@ -17,7 +17,7 @@ package {
}
android_test {
- name: "SoundPickerTests",
+ name: "SoundPicker2Tests",
certificate: "platform",
libs: [
"android.test.runner",
@@ -30,7 +30,7 @@ android_test {
"androidx.test.ext.truth",
"mockito-target-minus-junit4",
"guava-android-testlib",
- "SoundPickerLib",
+ "SoundPicker2Lib",
],
srcs: [
"src/**/*.java",
diff --git a/packages/SoundPicker/tests/AndroidManifest.xml b/packages/SoundPicker2/tests/AndroidManifest.xml
index 295aeb1faa55..295aeb1faa55 100644
--- a/packages/SoundPicker/tests/AndroidManifest.xml
+++ b/packages/SoundPicker2/tests/AndroidManifest.xml
diff --git a/packages/SoundPicker/tests/src/com/android/soundpicker/RingtoneListHandlerTest.java b/packages/SoundPicker2/tests/src/com/android/soundpicker/RingtoneListHandlerTest.java
index 80e71e200a53..80e71e200a53 100644
--- a/packages/SoundPicker/tests/src/com/android/soundpicker/RingtoneListHandlerTest.java
+++ b/packages/SoundPicker2/tests/src/com/android/soundpicker/RingtoneListHandlerTest.java
diff --git a/packages/SoundPicker/tests/src/com/android/soundpicker/RingtonePickerViewModelTest.java b/packages/SoundPicker2/tests/src/com/android/soundpicker/RingtonePickerViewModelTest.java
index cde6c76d27ff..cde6c76d27ff 100644
--- a/packages/SoundPicker/tests/src/com/android/soundpicker/RingtonePickerViewModelTest.java
+++ b/packages/SoundPicker2/tests/src/com/android/soundpicker/RingtonePickerViewModelTest.java
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 2509cfd4af40..211af908a877 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -29,3 +29,10 @@ flag {
"Notification Manager Service"
bug: "299448097"
}
+
+flag {
+ name: "scene_container"
+ namespace: "systemui"
+ description: "Enables the scene container framework go/flexiglass."
+ bug: "283121968"
+}
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 ce96bbfc7976..abc62c4682cc 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
@@ -43,6 +43,7 @@ import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.round
import com.android.compose.animation.scene.transformation.PropertyTransformation
+import com.android.compose.animation.scene.transformation.SharedElementTransformation
import com.android.compose.modifiers.thenIf
import com.android.compose.ui.util.lerp
@@ -196,29 +197,44 @@ private fun shouldDrawElement(
state.fromScene == state.toScene ||
!layoutImpl.isTransitionReady(state) ||
state.fromScene !in element.sceneValues ||
- state.toScene !in element.sceneValues ||
- !isSharedElementEnabled(layoutImpl, state, element.key)
+ state.toScene !in element.sceneValues
) {
return true
}
- val otherScene =
- layoutImpl.scenes.getValue(
- if (scene.key == state.fromScene) {
- state.toScene
- } else {
- state.fromScene
- }
- )
-
- // When the element is shared, draw the one in the highest scene unless it is a background, i.e.
- // it is usually drawn below everything else.
- val isHighestScene = scene.zIndex > otherScene.zIndex
- return if (element.key.isBackground) {
- !isHighestScene
- } else {
- isHighestScene
+ val sharedTransformation = sharedElementTransformation(layoutImpl, state, element.key)
+ if (sharedTransformation?.enabled == false) {
+ return true
}
+
+ return shouldDrawOrComposeSharedElement(
+ layoutImpl,
+ state,
+ scene.key,
+ element.key,
+ sharedTransformation,
+ )
+}
+
+internal fun shouldDrawOrComposeSharedElement(
+ layoutImpl: SceneTransitionLayoutImpl,
+ transition: TransitionState.Transition,
+ scene: SceneKey,
+ element: ElementKey,
+ sharedTransformation: SharedElementTransformation?
+): Boolean {
+ val scenePicker = sharedTransformation?.scenePicker ?: DefaultSharedElementScenePicker
+ val fromScene = transition.fromScene
+ val toScene = transition.toScene
+
+ return scenePicker.sceneDuringTransition(
+ element = element,
+ fromScene = fromScene,
+ toScene = toScene,
+ progress = transition::progress,
+ fromSceneZIndex = layoutImpl.scenes.getValue(fromScene).zIndex,
+ toSceneZIndex = layoutImpl.scenes.getValue(toScene).zIndex,
+ ) == scene
}
private fun isSharedElementEnabled(
@@ -226,6 +242,14 @@ private fun isSharedElementEnabled(
transition: TransitionState.Transition,
element: ElementKey,
): Boolean {
+ return sharedElementTransformation(layoutImpl, transition, element)?.enabled ?: true
+}
+
+internal fun sharedElementTransformation(
+ layoutImpl: SceneTransitionLayoutImpl,
+ transition: TransitionState.Transition,
+ element: ElementKey,
+): SharedElementTransformation? {
val spec = layoutImpl.transitions.transitionSpec(transition.fromScene, transition.toScene)
val sharedInFromScene = spec.transformations(element, transition.fromScene).shared
val sharedInToScene = spec.transformations(element, transition.toScene).shared
@@ -238,7 +262,7 @@ private fun isSharedElementEnabled(
)
}
- return sharedInFromScene?.enabled ?: true
+ return sharedInFromScene
}
/**
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 6dbeb69ff450..fa385d014ccb 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
@@ -22,6 +22,8 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.snapshots.Snapshot
import androidx.compose.ui.Modifier
@@ -60,7 +62,16 @@ internal fun MovableElement(
// which case we still need to draw it.
val picture = remember { Picture() }
- if (shouldComposeMovableElement(layoutImpl, scene.key, element)) {
+ // 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
+ // transition progress, so we put this in a derivedStateOf to prevent many recompositions
+ // during the transition.
+ val shouldComposeMovableElement by
+ remember(layoutImpl, scene.key, element) {
+ derivedStateOf { shouldComposeMovableElement(layoutImpl, scene.key, element) }
+ }
+
+ if (shouldComposeMovableElement) {
Box(
Modifier.drawWithCache {
val width = size.width.toInt()
@@ -172,14 +183,13 @@ private fun shouldComposeMovableElement(
return scene == fromScene
}
- // If we are ready in both scenes, then compose in the scene that has the highest zIndex (unless
- // it is a background) given that this is the one that is going to be drawn.
- val isHighestScene = layoutImpl.scene(scene).zIndex > layoutImpl.scene(otherScene).zIndex
- return if (element.key.isBackground) {
- !isHighestScene
- } else {
- isHighestScene
- }
+ return shouldDrawOrComposeSharedElement(
+ layoutImpl,
+ transitionState,
+ scene,
+ element.key,
+ sharedElementTransformation(layoutImpl, transitionState, element.key),
+ )
}
private class MovableElementScopeImpl(
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
index 49669775fedd..7b7ddfa5ec4e 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
@@ -120,8 +120,14 @@ interface TransitionBuilder : PropertyTransformationBuilder {
*
* @param enabled whether the matched element(s) should actually be shared in this transition.
* Defaults to true.
+ * @param scenePicker the [SharedElementScenePicker] to use when deciding in which scene we
+ * should draw or compose this shared element.
*/
- fun sharedElement(matcher: ElementMatcher, enabled: Boolean = true)
+ fun sharedElement(
+ matcher: ElementMatcher,
+ enabled: Boolean = true,
+ scenePicker: SharedElementScenePicker = DefaultSharedElementScenePicker,
+ )
/**
* Punch a hole in the element(s) matching [matcher] that has the same bounds as [bounds] and
@@ -144,6 +150,44 @@ interface TransitionBuilder : PropertyTransformationBuilder {
fun reversed(builder: TransitionBuilder.() -> Unit)
}
+interface SharedElementScenePicker {
+ /**
+ * Return the scene in which [element] should be drawn (when using `Modifier.element(key)`) or
+ * composed (when using `MovableElement(key)`) during the transition from [fromScene] to
+ * [toScene].
+ */
+ fun sceneDuringTransition(
+ element: ElementKey,
+ fromScene: SceneKey,
+ toScene: SceneKey,
+ progress: () -> Float,
+ fromSceneZIndex: Float,
+ toSceneZIndex: Float,
+ ): SceneKey
+}
+
+object DefaultSharedElementScenePicker : SharedElementScenePicker {
+ override fun sceneDuringTransition(
+ element: ElementKey,
+ fromScene: SceneKey,
+ toScene: SceneKey,
+ progress: () -> Float,
+ fromSceneZIndex: Float,
+ toSceneZIndex: Float
+ ): SceneKey {
+ // By default shared elements are drawn in the highest scene possible, unless it is a
+ // background.
+ return if (
+ (fromSceneZIndex > toSceneZIndex && !element.isBackground) ||
+ (fromSceneZIndex < toSceneZIndex && element.isBackground)
+ ) {
+ fromScene
+ } else {
+ toScene
+ }
+ }
+}
+
@TransitionDsl
interface PropertyTransformationBuilder {
/**
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
index f1c27178391c..d2bfd91842ae 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
@@ -111,8 +111,12 @@ internal class TransitionBuilderImpl : TransitionBuilder {
range = null
}
- override fun sharedElement(matcher: ElementMatcher, enabled: Boolean) {
- transformations.add(SharedElementTransformation(matcher, enabled))
+ override fun sharedElement(
+ matcher: ElementMatcher,
+ enabled: Boolean,
+ scenePicker: SharedElementScenePicker,
+ ) {
+ transformations.add(SharedElementTransformation(matcher, enabled, scenePicker))
}
override fun timestampRange(
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
index 2ef8d56c6bc6..0db8469466ef 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
@@ -21,6 +21,7 @@ import com.android.compose.animation.scene.Element
import com.android.compose.animation.scene.ElementMatcher
import com.android.compose.animation.scene.Scene
import com.android.compose.animation.scene.SceneTransitionLayoutImpl
+import com.android.compose.animation.scene.SharedElementScenePicker
import com.android.compose.animation.scene.TransitionState
/** A transformation applied to one or more elements during a transition. */
@@ -48,6 +49,7 @@ sealed interface Transformation {
internal class SharedElementTransformation(
override val matcher: ElementMatcher,
internal val enabled: Boolean,
+ internal val scenePicker: SharedElementScenePicker,
) : Transformation
/** A transformation that is applied on the element during the whole transition. */
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/grid/Grids.kt b/packages/SystemUI/compose/scene/src/com/android/compose/grid/Grids.kt
index 27f0948d5377..790665aebe3e 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/grid/Grids.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/grid/Grids.kt
@@ -116,9 +116,14 @@ private fun Grid(
if (sizeCache.rowHeights.size != rows) {
sizeCache.rowHeights = IntArray(rows) { 0 }
+ } else {
+ repeat(rows) { i -> sizeCache.rowHeights[i] = 0 }
}
+
if (sizeCache.columnWidths.size != columns) {
sizeCache.columnWidths = IntArray(columns) { 0 }
+ } else {
+ repeat(columns) { i -> sizeCache.columnWidths[i] = 0 }
}
val totalHorizontalSpacingBetweenChildren =
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 4204cd5f0da0..83af630ab098 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
@@ -144,7 +144,36 @@ class MovableElementTest {
rule.testTransition(
fromSceneContent = { MovableCounter(TestElements.Foo, Modifier.size(50.dp)) },
toSceneContent = { MovableCounter(TestElements.Foo, Modifier.size(100.dp)) },
- transition = { spec = tween(durationMillis = 16 * 4, easing = LinearEasing) },
+ transition = {
+ spec = tween(durationMillis = 16 * 4, easing = LinearEasing)
+ sharedElement(
+ TestElements.Foo,
+ scenePicker =
+ object : SharedElementScenePicker {
+ override fun sceneDuringTransition(
+ element: ElementKey,
+ fromScene: SceneKey,
+ toScene: SceneKey,
+ progress: () -> Float,
+ fromSceneZIndex: Float,
+ toSceneZIndex: Float
+ ): SceneKey {
+ assertThat(fromScene).isEqualTo(TestScenes.SceneA)
+ assertThat(toScene).isEqualTo(TestScenes.SceneB)
+ assertThat(fromSceneZIndex).isEqualTo(0)
+ assertThat(toSceneZIndex).isEqualTo(1)
+
+ // Compose Foo in Scene A if progress < 0.65f, otherwise compose it
+ // in Scene B.
+ return if (progress() < 0.65f) {
+ TestScenes.SceneA
+ } else {
+ TestScenes.SceneB
+ }
+ }
+ }
+ )
+ },
fromScene = TestScenes.SceneA,
toScene = TestScenes.SceneB,
) {
@@ -170,9 +199,12 @@ class MovableElementTest {
at(32) {
// During the transition, there is a single counter that is moved, with the current
- // value.
+ // value. Given that progress = 0.5f, it is currently composed in SceneA.
rule
- .onNode(hasText("count: 3"))
+ .onNode(
+ hasText("count: 3") and
+ hasParent(isElement(TestElements.Foo, scene = TestScenes.SceneA))
+ )
.assertIsDisplayed()
.assertSizeIsEqualTo(75.dp, 75.dp)
@@ -186,6 +218,26 @@ class MovableElementTest {
.isEqualTo(1)
}
+ at(48) {
+ // During the transition, there is a single counter that is moved, with the current
+ // value. Given that progress = 0.75f, it is currently composed in SceneB.
+ rule
+ .onNode(
+ hasText("count: 3") and
+ hasParent(isElement(TestElements.Foo, scene = TestScenes.SceneB))
+ )
+ .assertIsDisplayed()
+
+ // There are no other counters.
+ assertThat(
+ rule
+ .onAllNodesWithText("count: ", substring = true)
+ .fetchSemanticsNodes()
+ .size
+ )
+ .isEqualTo(1)
+ }
+
after {
// At the end of the transition, the counter still has the current value.
rule
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 75e71e414262..9ac1e9f58dbc 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -730,6 +730,9 @@
<!-- Whether the communal service should be enabled -->
<bool name="config_communalServiceEnabled">false</bool>
+ <!-- Component names of allowed communal widgets -->
+ <string-array name="config_communalWidgetAllowlist" translatable="false" />
+
<!-- Component name of communal source service -->
<string name="config_communalSourceComponent" translatable="false">@null</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
index 1e896142f718..400f6529eed6 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
@@ -702,13 +702,18 @@ public class RotationButtonController {
@Override
public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) {
- // Only hide the icon if the top task changes its requestedOrientation
- // Launcher can alter its requestedOrientation while it's not on top, don't hide on this
- Optional.ofNullable(ActivityManagerWrapper.getInstance())
- .map(ActivityManagerWrapper::getRunningTask)
- .ifPresent(a -> {
- if (a.id == taskId) setRotateSuggestionButtonState(false /* visible */);
- });
+ mBgExecutor.execute(() -> {
+ // Only hide the icon if the top task changes its requestedOrientation Launcher can
+ // alter its requestedOrientation while it's not on top, don't hide on this
+ Optional.ofNullable(ActivityManagerWrapper.getInstance())
+ .map(ActivityManagerWrapper::getRunningTask)
+ .ifPresent(a -> {
+ if (a.id == taskId) {
+ mMainThreadHandler.post(() ->
+ setRotateSuggestionButtonState(false /* visible */));
+ }
+ });
+ });
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
index c4f1ce8f5d3b..b186018ba78a 100644
--- a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
@@ -33,11 +33,11 @@ import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_WAKE
import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS
import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD
import android.util.Log
-import com.android.keyguard.KeyguardUpdateMonitor.getCurrentUser
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.settings.SecureSettings
import java.io.PrintWriter
import javax.inject.Inject
@@ -50,6 +50,7 @@ class ActiveUnlockConfig @Inject constructor(
@Main private val handler: Handler,
private val secureSettings: SecureSettings,
private val contentResolver: ContentResolver,
+ private val selectedUserInteractor: SelectedUserInteractor,
dumpManager: DumpManager
) : Dumpable {
@@ -134,7 +135,7 @@ class ActiveUnlockConfig @Inject constructor(
)
)
- onChange(true, ArrayList(), 0, getCurrentUser())
+ onChange(true, ArrayList(), 0, selectedUserInteractor.getSelectedUserId())
}
private fun registerUri(uris: Collection<Uri>) {
@@ -153,29 +154,31 @@ class ActiveUnlockConfig @Inject constructor(
flags: Int,
userId: Int
) {
- if (getCurrentUser() != userId) {
+ if (selectedUserInteractor.getSelectedUserId() != userId) {
return
}
if (selfChange || uris.contains(wakeUri)) {
requestActiveUnlockOnWakeup = secureSettings.getIntForUser(
- ACTIVE_UNLOCK_ON_WAKE, 0, getCurrentUser()) == 1
+ ACTIVE_UNLOCK_ON_WAKE, 0, selectedUserInteractor.getSelectedUserId()) == 1
}
if (selfChange || uris.contains(unlockIntentUri)) {
requestActiveUnlockOnUnlockIntent = secureSettings.getIntForUser(
- ACTIVE_UNLOCK_ON_UNLOCK_INTENT, 0, getCurrentUser()) == 1
+ ACTIVE_UNLOCK_ON_UNLOCK_INTENT, 0,
+ selectedUserInteractor.getSelectedUserId()) == 1
}
if (selfChange || uris.contains(bioFailUri)) {
requestActiveUnlockOnBioFail = secureSettings.getIntForUser(
- ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 0, getCurrentUser()) == 1
+ ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 0,
+ selectedUserInteractor.getSelectedUserId()) == 1
}
if (selfChange || uris.contains(faceErrorsUri)) {
processStringArray(
secureSettings.getStringForUser(ACTIVE_UNLOCK_ON_FACE_ERRORS,
- getCurrentUser()),
+ selectedUserInteractor.getSelectedUserId()),
faceErrorsToTriggerBiometricFailOn,
setOf(FACE_ERROR_TIMEOUT))
}
@@ -183,7 +186,7 @@ class ActiveUnlockConfig @Inject constructor(
if (selfChange || uris.contains(faceAcquireInfoUri)) {
processStringArray(
secureSettings.getStringForUser(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO,
- getCurrentUser()),
+ selectedUserInteractor.getSelectedUserId()),
faceAcquireInfoToTriggerBiometricFailOn,
emptySet())
}
@@ -192,7 +195,7 @@ class ActiveUnlockConfig @Inject constructor(
processStringArray(
secureSettings.getStringForUser(
ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
- getCurrentUser()),
+ selectedUserInteractor.getSelectedUserId()),
onUnlockIntentWhenBiometricEnrolled,
setOf(BiometricType.NONE.intValue))
}
@@ -201,7 +204,7 @@ class ActiveUnlockConfig @Inject constructor(
processStringArray(
secureSettings.getStringForUser(
ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
- getCurrentUser()),
+ selectedUserInteractor.getSelectedUserId()),
wakeupsConsideredUnlockIntents,
setOf(WAKE_REASON_UNFOLD_DEVICE))
}
@@ -210,7 +213,7 @@ class ActiveUnlockConfig @Inject constructor(
processStringArray(
secureSettings.getStringForUser(
ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
- getCurrentUser()),
+ selectedUserInteractor.getSelectedUserId()),
wakeupsToForceDismissKeyguard,
setOf(WAKE_REASON_UNFOLD_DEVICE))
}
@@ -316,7 +319,8 @@ class ActiveUnlockConfig @Inject constructor(
keyguardUpdateMonitor?.let {
val anyFaceEnrolled = it.isFaceEnrolled
val anyFingerprintEnrolled =
- it.getCachedIsUnlockWithFingerprintPossible(getCurrentUser())
+ it.getCachedIsUnlockWithFingerprintPossible(
+ selectedUserInteractor.getSelectedUserId())
val udfpsEnrolled = it.isUdfpsEnrolled
if (!anyFaceEnrolled && !anyFingerprintEnrolled) {
@@ -371,7 +375,8 @@ class ActiveUnlockConfig @Inject constructor(
"${shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment()}")
pw.println(" faceEnrolled=${it.isFaceEnrolled}")
pw.println(" fpEnrolled=${
- it.getCachedIsUnlockWithFingerprintPossible(getCurrentUser())}")
+ it.getCachedIsUnlockWithFingerprintPossible(
+ selectedUserInteractor.getSelectedUserId())}")
pw.println(" udfpsEnrolled=${it.isUdfpsEnrolled}")
} ?: pw.println(" keyguardUpdateMonitor is uninitialized")
}
diff --git a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
index 207f3440d38b..58bbdeb35ea0 100644
--- a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
@@ -45,6 +45,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.keyguard.dagger.KeyguardBouncerScope;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import java.util.NoSuchElementException;
@@ -63,6 +64,7 @@ public class AdminSecondaryLockScreenController {
private Handler mHandler;
private IKeyguardClient mClient;
private KeyguardSecurityCallback mKeyguardCallback;
+ private SelectedUserInteractor mSelectedUserInteractor;
private final ServiceConnection mConnection = new ServiceConnection() {
@Override
@@ -76,7 +78,7 @@ public class AdminSecondaryLockScreenController {
} catch (RemoteException e) {
// Failed to link to death, just dismiss and unbind the service for now.
Log.e(TAG, "Lost connection to secondary lockscreen service", e);
- dismiss(KeyguardUpdateMonitor.getCurrentUser());
+ dismiss(mSelectedUserInteractor.getSelectedUserId());
}
}
}
@@ -110,7 +112,7 @@ public class AdminSecondaryLockScreenController {
mView.setChildSurfacePackage(surfacePackage);
} else {
mHandler.post(() -> {
- dismiss(KeyguardUpdateMonitor.getCurrentUser());
+ dismiss(mSelectedUserInteractor.getSelectedUserId());
});
}
}
@@ -131,7 +133,7 @@ public class AdminSecondaryLockScreenController {
protected SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
- final int userId = KeyguardUpdateMonitor.getCurrentUser();
+ final int userId = mSelectedUserInteractor.getSelectedUserId();
mUpdateMonitor.registerCallback(mUpdateCallback);
if (mClient != null) {
@@ -158,7 +160,7 @@ public class AdminSecondaryLockScreenController {
private AdminSecondaryLockScreenController(Context context, KeyguardSecurityContainer parent,
KeyguardUpdateMonitor updateMonitor, KeyguardSecurityCallback callback,
- @Main Handler handler) {
+ @Main Handler handler, SelectedUserInteractor selectedUserInteractor) {
mContext = context;
mHandler = handler;
mParent = parent;
@@ -166,6 +168,7 @@ public class AdminSecondaryLockScreenController {
mKeyguardCallback = callback;
mView = new AdminSecurityView(mContext, mSurfaceHolderCallback);
mView.setId(View.generateViewId());
+ mSelectedUserInteractor = selectedUserInteractor;
}
/**
@@ -218,13 +221,13 @@ public class AdminSecondaryLockScreenController {
}
} catch (RemoteException e) {
Log.e(TAG, "Error in onCreateKeyguardSurface", e);
- dismiss(KeyguardUpdateMonitor.getCurrentUser());
+ dismiss(mSelectedUserInteractor.getSelectedUserId());
}
}
private void dismiss(int userId) {
mHandler.removeCallbacksAndMessages(null);
- if (mView.isAttachedToWindow() && userId == KeyguardUpdateMonitor.getCurrentUser()) {
+ if (mView.isAttachedToWindow() && userId == mSelectedUserInteractor.getSelectedUserId()) {
hide();
if (mKeyguardCallback != null) {
mKeyguardCallback.dismiss(/* securityVerified= */ true, userId,
@@ -265,19 +268,24 @@ public class AdminSecondaryLockScreenController {
private final KeyguardSecurityContainer mParent;
private final KeyguardUpdateMonitor mUpdateMonitor;
private final Handler mHandler;
+ private final SelectedUserInteractor mSelectedUserInteractor;
@Inject
- public Factory(Context context, KeyguardSecurityContainer parent,
- KeyguardUpdateMonitor updateMonitor, @Main Handler handler) {
+ public Factory(Context context,
+ KeyguardSecurityContainer parent,
+ KeyguardUpdateMonitor updateMonitor,
+ @Main Handler handler,
+ SelectedUserInteractor selectedUserInteractor) {
mContext = context;
mParent = parent;
mUpdateMonitor = updateMonitor;
mHandler = handler;
+ mSelectedUserInteractor = selectedUserInteractor;
}
public AdminSecondaryLockScreenController create(KeyguardSecurityCallback callback) {
return new AdminSecondaryLockScreenController(mContext, mParent, mUpdateMonitor,
- callback, mHandler);
+ callback, mHandler, mSelectedUserInteractor);
}
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
index f7e8eb492584..5de370f22eb8 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
+++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
@@ -43,6 +43,7 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.EmergencyDialerConstants;
import com.android.systemui.util.ViewController;
@@ -67,6 +68,7 @@ public class EmergencyButtonController extends ViewController<EmergencyButton> {
private LockPatternUtils mLockPatternUtils;
private Executor mMainExecutor;
private Executor mBackgroundExecutor;
+ private SelectedUserInteractor mSelectedUserInteractor;
private final KeyguardUpdateMonitorCallback mInfoCallback =
new KeyguardUpdateMonitorCallback() {
@@ -96,7 +98,8 @@ public class EmergencyButtonController extends ViewController<EmergencyButton> {
ShadeController shadeController,
@Nullable TelecomManager telecomManager, MetricsLogger metricsLogger,
LockPatternUtils lockPatternUtils,
- Executor mainExecutor, Executor backgroundExecutor) {
+ Executor mainExecutor, Executor backgroundExecutor,
+ SelectedUserInteractor selectedUserInteractor) {
super(view);
mConfigurationController = configurationController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -109,6 +112,7 @@ public class EmergencyButtonController extends ViewController<EmergencyButton> {
mLockPatternUtils = lockPatternUtils;
mMainExecutor = mainExecutor;
mBackgroundExecutor = backgroundExecutor;
+ mSelectedUserInteractor = selectedUserInteractor;
}
@Override
@@ -142,7 +146,7 @@ public class EmergencyButtonController extends ViewController<EmergencyButton> {
mBackgroundExecutor.execute(() -> {
boolean isInCall = mTelecomManager != null && mTelecomManager.isInCall();
boolean isSecure = mLockPatternUtils
- .isSecure(KeyguardUpdateMonitor.getCurrentUser());
+ .isSecure(mSelectedUserInteractor.getSelectedUserId());
mMainExecutor.execute(() -> mView.updateEmergencyCallButton(
/* isInCall= */ isInCall,
/* hasTelephonyRadio= */ getContext().getPackageManager()
@@ -192,7 +196,7 @@ public class EmergencyButtonController extends ViewController<EmergencyButton> {
getContext().startActivityAsUser(emergencyDialIntent,
ActivityOptions.makeCustomAnimation(getContext(), 0, 0).toBundle(),
- new UserHandle(KeyguardUpdateMonitor.getCurrentUser()));
+ new UserHandle(mSelectedUserInteractor.getSelectedUserId()));
}
});
});
@@ -218,6 +222,7 @@ public class EmergencyButtonController extends ViewController<EmergencyButton> {
private final LockPatternUtils mLockPatternUtils;
private final Executor mMainExecutor;
private final Executor mBackgroundExecutor;
+ private final SelectedUserInteractor mSelectedUserInteractor;
@Inject
public Factory(ConfigurationController configurationController,
@@ -227,7 +232,8 @@ public class EmergencyButtonController extends ViewController<EmergencyButton> {
@Nullable TelecomManager telecomManager, MetricsLogger metricsLogger,
LockPatternUtils lockPatternUtils,
@Main Executor mainExecutor,
- @Background Executor backgroundExecutor) {
+ @Background Executor backgroundExecutor,
+ SelectedUserInteractor selectedUserInteractor) {
mConfigurationController = configurationController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -240,6 +246,7 @@ public class EmergencyButtonController extends ViewController<EmergencyButton> {
mLockPatternUtils = lockPatternUtils;
mMainExecutor = mainExecutor;
mBackgroundExecutor = backgroundExecutor;
+ mSelectedUserInteractor = selectedUserInteractor;
}
/** Construct an {@link com.android.keyguard.EmergencyButtonController}. */
@@ -247,7 +254,7 @@ public class EmergencyButtonController extends ViewController<EmergencyButton> {
return new EmergencyButtonController(view, mConfigurationController,
mKeyguardUpdateMonitor, mTelephonyManager, mPowerManager, mActivityTaskManager,
mShadeController, mTelecomManager, mMetricsLogger, mLockPatternUtils,
- mMainExecutor, mBackgroundExecutor);
+ mMainExecutor, mBackgroundExecutor, mSelectedUserInteractor);
}
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index 167bd59d4e8d..dad440083f70 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -34,10 +34,11 @@ import com.android.internal.widget.LockscreenCredential;
import com.android.keyguard.EmergencyButtonController.EmergencyButtonCallback;
import com.android.keyguard.KeyguardAbsKeyInputView.KeyDownListener;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.res.R;
import com.android.systemui.classifier.FalsingClassifier;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.res.R;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import java.util.HashMap;
import java.util.Map;
@@ -80,9 +81,9 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
LatencyTracker latencyTracker, FalsingCollector falsingCollector,
EmergencyButtonController emergencyButtonController,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags, SelectedUserInteractor selectedUserInteractor) {
super(view, securityMode, keyguardSecurityCallback, emergencyButtonController,
- messageAreaControllerFactory, featureFlags);
+ messageAreaControllerFactory, featureFlags, selectedUserInteractor);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mLatencyTracker = latencyTracker;
@@ -104,7 +105,7 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey
mEmergencyButtonController.setEmergencyButtonCallback(mEmergencyButtonCallback);
// if the user is currently locked out, enforce it.
long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
- KeyguardUpdateMonitor.getCurrentUser());
+ mSelectedUserInteractor.getSelectedUserId());
if (shouldLockout(deadline)) {
handleAttemptLockout(deadline);
}
@@ -175,7 +176,7 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey
}
void onPasswordChecked(int userId, boolean matched, int timeoutMs, boolean isValidPassword) {
- boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId;
+ boolean dismissKeyguard = mSelectedUserInteractor.getSelectedUserId() == userId;
if (matched) {
getKeyguardSecurityCallback().reportUnlockAttempt(userId, true, 0);
if (dismissKeyguard) {
@@ -212,7 +213,7 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey
mPendingLockCheck.cancel(false);
}
- final int userId = KeyguardUpdateMonitor.getCurrentUser();
+ final int userId = mSelectedUserInteractor.getSelectedUserId();
if (password.size() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) {
// to avoid accidental lockout, only count attempts that are long enough to be a
// real password. This may require some tweaking.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt
index e6a2bfa1af12..d26caa985431 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt
@@ -29,6 +29,7 @@ import com.android.keyguard.KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEv
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.SessionTracker
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import java.io.PrintWriter
import javax.inject.Inject
@@ -42,7 +43,8 @@ import javax.inject.Inject
class KeyguardBiometricLockoutLogger @Inject constructor(
private val uiEventLogger: UiEventLogger,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
- private val sessionTracker: SessionTracker
+ private val sessionTracker: SessionTracker,
+ private val selectedUserInteractor: SelectedUserInteractor
) : CoreStartable {
private var fingerprintLockedOut = false
private var faceLockedOut = false
@@ -52,7 +54,7 @@ class KeyguardBiometricLockoutLogger @Inject constructor(
override fun start() {
mKeyguardUpdateMonitorCallback.onStrongAuthStateChanged(
- KeyguardUpdateMonitor.getCurrentUser())
+ selectedUserInteractor.getSelectedUserId())
keyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback)
}
@@ -79,7 +81,7 @@ class KeyguardBiometricLockoutLogger @Inject constructor(
}
override fun onStrongAuthStateChanged(userId: Int) {
- if (userId != KeyguardUpdateMonitor.getCurrentUser()) {
+ if (userId != selectedUserInteractor.getSelectedUserId()) {
return
}
val strongAuthFlags = keyguardUpdateMonitor.strongAuthTracker
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index 29ce18c60833..b3094838452a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -29,7 +29,6 @@ import android.view.inputmethod.InputMethodManager;
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.res.R;
import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor;
import com.android.systemui.bouncer.ui.BouncerMessageView;
import com.android.systemui.bouncer.ui.binder.BouncerMessageViewBinder;
@@ -38,7 +37,9 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.log.BouncerLogger;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.DevicePostureController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -51,7 +52,6 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
private final SecurityMode mSecurityMode;
private final KeyguardSecurityCallback mKeyguardSecurityCallback;
- private final EmergencyButton mEmergencyButton;
private final EmergencyButtonController mEmergencyButtonController;
private boolean mPaused;
protected KeyguardMessageAreaController<BouncerKeyguardMessageArea> mMessageAreaController;
@@ -61,18 +61,20 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
// state for the current security method.
private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {};
private final FeatureFlags mFeatureFlags;
+ protected final SelectedUserInteractor mSelectedUserInteractor;
protected KeyguardInputViewController(T view, SecurityMode securityMode,
KeyguardSecurityCallback keyguardSecurityCallback,
EmergencyButtonController emergencyButtonController,
@Nullable KeyguardMessageAreaController.Factory messageAreaControllerFactory,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags,
+ SelectedUserInteractor selectedUserInteractor) {
super(view);
mSecurityMode = securityMode;
mKeyguardSecurityCallback = keyguardSecurityCallback;
- mEmergencyButton = view == null ? null : view.findViewById(R.id.emergency_call_button);
mEmergencyButtonController = emergencyButtonController;
mFeatureFlags = featureFlags;
+ mSelectedUserInteractor = selectedUserInteractor;
if (messageAreaControllerFactory != null) {
try {
BouncerKeyguardMessageArea kma = view.requireViewById(R.id.bouncer_message_area);
@@ -207,6 +209,7 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
private final DevicePostureController mDevicePostureController;
private final KeyguardViewController mKeyguardViewController;
private final FeatureFlags mFeatureFlags;
+ private final SelectedUserInteractor mSelectedUserInteractor;
@Inject
public Factory(KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -219,7 +222,7 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
EmergencyButtonController.Factory emergencyButtonControllerFactory,
DevicePostureController devicePostureController,
KeyguardViewController keyguardViewController,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags, SelectedUserInteractor selectedUserInteractor) {
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mLatencyTracker = latencyTracker;
@@ -234,6 +237,7 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
mDevicePostureController = devicePostureController;
mKeyguardViewController = keyguardViewController;
mFeatureFlags = featureFlags;
+ mSelectedUserInteractor = selectedUserInteractor;
}
/** Create a new {@link KeyguardInputViewController}. */
@@ -248,32 +252,32 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mLatencyTracker, mFalsingCollector,
emergencyButtonController, mMessageAreaControllerFactory,
- mDevicePostureController, mFeatureFlags);
+ mDevicePostureController, mFeatureFlags, mSelectedUserInteractor);
} else if (keyguardInputView instanceof KeyguardPasswordView) {
return new KeyguardPasswordViewController((KeyguardPasswordView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
mInputMethodManager, emergencyButtonController, mMainExecutor, mResources,
mFalsingCollector, mKeyguardViewController,
- mDevicePostureController, mFeatureFlags);
+ mDevicePostureController, mFeatureFlags, mSelectedUserInteractor);
} else if (keyguardInputView instanceof KeyguardPINView) {
return new KeyguardPinViewController((KeyguardPINView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
mLiftToActivateListener, emergencyButtonController, mFalsingCollector,
- mDevicePostureController, mFeatureFlags);
+ mDevicePostureController, mFeatureFlags, mSelectedUserInteractor);
} else if (keyguardInputView instanceof KeyguardSimPinView) {
return new KeyguardSimPinViewController((KeyguardSimPinView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
mLiftToActivateListener, mTelephonyManager, mFalsingCollector,
- emergencyButtonController, mFeatureFlags);
+ emergencyButtonController, mFeatureFlags, mSelectedUserInteractor);
} else if (keyguardInputView instanceof KeyguardSimPukView) {
return new KeyguardSimPukViewController((KeyguardSimPukView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
mLiftToActivateListener, mTelephonyManager, mFalsingCollector,
- emergencyButtonController, mFeatureFlags);
+ emergencyButtonController, mFeatureFlags, mSelectedUserInteractor);
}
throw new RuntimeException("Unable to find controller for " + keyguardInputView);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
index 959cf6fb8565..2e212552caaa 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
@@ -39,11 +39,12 @@ import android.widget.TextView.OnEditorActionListener;
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.res.R;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.DevicePostureController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.concurrency.DelayableExecutor;
import java.util.List;
@@ -51,7 +52,6 @@ import java.util.List;
public class KeyguardPasswordViewController
extends KeyguardAbsKeyInputViewController<KeyguardPasswordView> {
- private static final int DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON = 500; // 500ms
private final KeyguardSecurityCallback mKeyguardSecurityCallback;
private final DevicePostureController mPostureController;
private final DevicePostureController.Callback mPostureCallback = posture ->
@@ -112,10 +112,11 @@ public class KeyguardPasswordViewController
FalsingCollector falsingCollector,
KeyguardViewController keyguardViewController,
DevicePostureController postureController,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags,
+ SelectedUserInteractor selectedUserInteractor) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, falsingCollector,
- emergencyButtonController, featureFlags);
+ emergencyButtonController, featureFlags, selectedUserInteractor);
mKeyguardSecurityCallback = keyguardSecurityCallback;
mInputMethodManager = inputMethodManager;
mPostureController = postureController;
@@ -132,7 +133,8 @@ public class KeyguardPasswordViewController
@Override
protected void onViewAttached() {
super.onViewAttached();
- mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser()));
+ mPasswordEntry.setTextOperationUser(
+ UserHandle.of(mSelectedUserInteractor.getSelectedUserId()));
mPasswordEntry.setKeyListener(TextKeyListener.getInstance());
mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT
| InputType.TYPE_TEXT_VARIATION_PASSWORD);
@@ -164,13 +166,6 @@ public class KeyguardPasswordViewController
// If there's more than one IME, enable the IME switcher button
updateSwitchImeButton();
-
- // When we the current user is switching, InputMethodManagerService sometimes has not
- // switched internal state yet here. As a quick workaround, we check the keyboard state
- // again.
- // TODO: Remove this workaround by ensuring such a race condition never happens.
- mMainExecutor.executeDelayed(
- this::updateSwitchImeButton, DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON);
}
@Override
@@ -187,7 +182,8 @@ public class KeyguardPasswordViewController
@Override
void resetState() {
- mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser()));
+ mPasswordEntry.setTextOperationUser(
+ UserHandle.of(mSelectedUserInteractor.getSelectedUserId()));
mMessageAreaController.setMessage(getInitialMessageResId());
final boolean wasDisabled = mPasswordEntry.isEnabled();
mView.setPasswordEntryEnabled(true);
@@ -280,7 +276,7 @@ public class KeyguardPasswordViewController
final boolean shouldIncludeAuxiliarySubtypes) {
final List<InputMethodInfo> enabledImis =
imm.getEnabledInputMethodListAsUser(
- UserHandle.of(KeyguardUpdateMonitor.getCurrentUser()));
+ UserHandle.of(mSelectedUserInteractor.getSelectedUserId()));
// Number of the filtered IMEs
int filteredImisCount = 0;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index 57151ae32db0..db7ff888356c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -36,11 +36,12 @@ import com.android.internal.widget.LockPatternView.Cell;
import com.android.internal.widget.LockscreenCredential;
import com.android.keyguard.EmergencyButtonController.EmergencyButtonCallback;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.res.R;
import com.android.systemui.classifier.FalsingClassifier;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.DevicePostureController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import java.util.HashMap;
import java.util.List;
@@ -110,7 +111,7 @@ public class KeyguardPatternViewController
mPendingLockCheck.cancel(false);
}
- final int userId = KeyguardUpdateMonitor.getCurrentUser();
+ final int userId = mSelectedUserInteractor.getSelectedUserId();
if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
// Treat single-sized patterns as erroneous taps.
if (pattern.size() == 1) {
@@ -163,7 +164,7 @@ public class KeyguardPatternViewController
private void onPatternChecked(int userId, boolean matched, int timeoutMs,
boolean isValidPattern) {
- boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId;
+ boolean dismissKeyguard = mSelectedUserInteractor.getSelectedUserId() == userId;
if (matched) {
getKeyguardSecurityCallback().reportUnlockAttempt(userId, true, 0);
if (dismissKeyguard) {
@@ -198,9 +199,10 @@ public class KeyguardPatternViewController
FalsingCollector falsingCollector,
EmergencyButtonController emergencyButtonController,
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
- DevicePostureController postureController, FeatureFlags featureFlags) {
+ DevicePostureController postureController, FeatureFlags featureFlags,
+ SelectedUserInteractor selectedUserInteractor) {
super(view, securityMode, keyguardSecurityCallback, emergencyButtonController,
- messageAreaControllerFactory, featureFlags);
+ messageAreaControllerFactory, featureFlags, selectedUserInteractor);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mLatencyTracker = latencyTracker;
@@ -223,7 +225,7 @@ public class KeyguardPatternViewController
mLockPatternView.setOnPatternListener(new UnlockPatternListener());
mLockPatternView.setSaveEnabled(false);
mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(
- KeyguardUpdateMonitor.getCurrentUser()));
+ mSelectedUserInteractor.getSelectedUserId()));
mLockPatternView.setOnTouchListener((v, event) -> {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
mFalsingCollector.avoidGesture();
@@ -243,7 +245,7 @@ public class KeyguardPatternViewController
mPostureController.addCallback(mPostureCallback);
// if the user is currently locked out, enforce it.
long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
- KeyguardUpdateMonitor.getCurrentUser());
+ mSelectedUserInteractor.getSelectedUserId());
if (deadline != 0) {
handleAttemptLockout(deadline);
}
@@ -266,7 +268,7 @@ public class KeyguardPatternViewController
public void reset() {
// reset lock pattern
mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(
- KeyguardUpdateMonitor.getCurrentUser()));
+ mSelectedUserInteractor.getSelectedUserId()));
mLockPatternView.enableInput();
mLockPatternView.setEnabled(true);
mLockPatternView.clearPattern();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
index aacf8662b2b8..b7d1171431a8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
@@ -25,9 +25,10 @@ import android.view.View.OnTouchListener;
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.res.R;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.res.R;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinBasedInputView>
extends KeyguardAbsKeyInputViewController<T> {
@@ -60,10 +61,11 @@ public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinB
LiftToActivateListener liftToActivateListener,
EmergencyButtonController emergencyButtonController,
FalsingCollector falsingCollector,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags,
+ SelectedUserInteractor selectedUserInteractor) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, falsingCollector,
- emergencyButtonController, featureFlags);
+ emergencyButtonController, featureFlags, selectedUserInteractor);
mLiftToActivateListener = liftToActivateListener;
mFalsingCollector = falsingCollector;
mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId());
@@ -74,7 +76,7 @@ public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinB
super.onViewAttached();
boolean showAnimations = !mLockPatternUtils
- .isPinEnhancedPrivacyEnabled(KeyguardUpdateMonitor.getCurrentUser());
+ .isPinEnhancedPrivacyEnabled(mSelectedUserInteractor.getSelectedUserId());
mPasswordEntry.setShowPassword(showAnimations);
for (NumPadKey button : mView.getButtons()) {
button.setOnTouchListener((v, event) -> {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index 9a788688c9d1..947d90f488d1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -23,11 +23,12 @@ import android.view.View;
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.res.R;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.DevicePostureController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
public class KeyguardPinViewController
extends KeyguardPinBasedInputViewController<KeyguardPINView> {
@@ -55,17 +56,17 @@ public class KeyguardPinViewController
EmergencyButtonController emergencyButtonController,
FalsingCollector falsingCollector,
DevicePostureController postureController,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags, SelectedUserInteractor selectedUserInteractor) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
- emergencyButtonController, falsingCollector, featureFlags);
+ emergencyButtonController, falsingCollector, featureFlags, selectedUserInteractor);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mPostureController = postureController;
mLockPatternUtils = lockPatternUtils;
mFeatureFlags = featureFlags;
view.setIsLockScreenLandscapeEnabled(mFeatureFlags.isEnabled(LOCKSCREEN_ENABLE_LANDSCAPE));
mBackspaceKey = view.findViewById(R.id.delete_button);
- mPinLength = mLockPatternUtils.getPinLength(KeyguardUpdateMonitor.getCurrentUser());
+ mPinLength = mLockPatternUtils.getPinLength(selectedUserInteractor.getSelectedUserId());
}
@Override
@@ -124,7 +125,7 @@ public class KeyguardPinViewController
private void updateAutoConfirmationState() {
mDisabledAutoConfirmation = mLockPatternUtils.getCurrentFailedPasswordAttempts(
- KeyguardUpdateMonitor.getCurrentUser()) >= MIN_FAILED_PIN_ATTEMPTS;
+ mSelectedUserInteractor.getSelectedUserId()) >= MIN_FAILED_PIN_ATTEMPTS;
updateOKButtonVisibility();
updateBackSpaceVisibility();
updatePinHinting();
@@ -179,7 +180,8 @@ public class KeyguardPinViewController
*/
private boolean isAutoPinConfirmEnabledInSettings() {
//Checks if user has enabled the auto confirm in Settings
- return mLockPatternUtils.isAutoPinConfirmEnabled(KeyguardUpdateMonitor.getCurrentUser())
+ return mLockPatternUtils.isAutoPinConfirmEnabled(
+ mSelectedUserInteractor.getSelectedUserId())
&& mPinLength != LockPatternUtils.PIN_LENGTH_UNAVAILABLE;
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 51dafac7b421..7101ed599b86 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -91,7 +91,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
-import com.android.systemui.user.domain.interactor.UserInteractor;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.settings.GlobalSettings;
@@ -157,23 +157,25 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
private int mCurrentUser = UserHandle.USER_NULL;
private UserSwitcherController.UserSwitchCallback mUserSwitchCallback =
new UserSwitcherController.UserSwitchCallback() {
- @Override
- public void onUserSwitched() {
- if (mCurrentUser == KeyguardUpdateMonitor.getCurrentUser()) {
- return;
- }
- mCurrentUser = KeyguardUpdateMonitor.getCurrentUser();
- showPrimarySecurityScreen(false);
- if (mCurrentSecurityMode != SecurityMode.SimPin
- && mCurrentSecurityMode != SecurityMode.SimPuk) {
- reinflateViewFlipper((l) -> {});
- }
- }
- };
+ @Override
+ public void onUserSwitched() {
+ if (mCurrentUser == mSelectedUserInteractor.getSelectedUserId()) {
+ return;
+ }
+ mCurrentUser = mSelectedUserInteractor.getSelectedUserId();
+ showPrimarySecurityScreen(false);
+ if (mCurrentSecurityMode != SecurityMode.SimPin
+ && mCurrentSecurityMode != SecurityMode.SimPuk) {
+ reinflateViewFlipper((l) -> {
+ });
+ }
+ }
+ };
@VisibleForTesting
final Gefingerpoken mGlobalTouchListener = new Gefingerpoken() {
private MotionEvent mTouchDown;
+
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
@@ -267,7 +269,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
ThreadUtils.postOnBackgroundThread(() -> {
try {
Thread.sleep(5000);
- } catch (InterruptedException ignored) { }
+ } catch (InterruptedException ignored) {
+ }
System.gc();
System.runFinalization();
System.gc();
@@ -281,7 +284,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER)
.setType(success ? MetricsEvent.TYPE_SUCCESS : MetricsEvent.TYPE_FAILURE));
mUiEventLogger.log(success ? BouncerUiEvent.BOUNCER_PASSWORD_SUCCESS
- : BouncerUiEvent.BOUNCER_PASSWORD_FAILURE, getSessionId());
+ : BouncerUiEvent.BOUNCER_PASSWORD_FAILURE, getSessionId());
}
@Override
@@ -404,7 +407,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
}
mKeyguardSecurityCallback.dismiss(
false /* authenticated */,
- KeyguardUpdateMonitor.getCurrentUser(),
+ mSelectedUserInteractor.getSelectedUserId(),
/* bypassSecondaryLockScreen */ false,
SecurityMode.Invalid
);
@@ -420,12 +423,13 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
showPrimarySecurityScreen(false);
}
};
- private final UserInteractor mUserInteractor;
+ private final SelectedUserInteractor mSelectedUserInteractor;
private final Provider<DeviceEntryInteractor> mDeviceEntryInteractor;
private final Provider<JavaAdapter> mJavaAdapter;
private final DeviceProvisionedController mDeviceProvisionedController;
private final Lazy<PrimaryBouncerInteractor> mPrimaryBouncerInteractor;
- @Nullable private Job mSceneTransitionCollectionJob;
+ @Nullable
+ private Job mSceneTransitionCollectionJob;
@Inject
public KeyguardSecurityContainerController(KeyguardSecurityContainer view,
@@ -453,7 +457,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
BouncerMessageInteractor bouncerMessageInteractor,
Provider<JavaAdapter> javaAdapter,
- UserInteractor userInteractor,
+ SelectedUserInteractor selectedUserInteractor,
DeviceProvisionedController deviceProvisionedController,
FaceAuthAccessibilityDelegate faceAuthAccessibilityDelegate,
KeyguardTransitionInteractor keyguardTransitionInteractor,
@@ -487,7 +491,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
mAudioManager = audioManager;
mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
mBouncerMessageInteractor = bouncerMessageInteractor;
- mUserInteractor = userInteractor;
+ mSelectedUserInteractor = selectedUserInteractor;
mDeviceEntryInteractor = deviceEntryInteractor;
mJavaAdapter = javaAdapter;
mKeyguardTransitionInteractor = keyguardTransitionInteractor;
@@ -520,10 +524,10 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
// When the scene framework says that the lockscreen has been dismissed, dismiss the
// keyguard here, revealing the underlying app or launcher:
mSceneTransitionCollectionJob = mJavaAdapter.get().alwaysCollectFlow(
- mDeviceEntryInteractor.get().isDeviceEntered(),
+ mDeviceEntryInteractor.get().isDeviceEntered(),
isDeviceEntered -> {
if (isDeviceEntered) {
- final int selectedUserId = mUserInteractor.getSelectedUserId();
+ final int selectedUserId = mSelectedUserInteractor.getSelectedUserId();
showNextSecurityScreenOrFinish(
/* authenticated= */ true,
selectedUserId,
@@ -548,7 +552,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
}
}
- /** */
+ /** */
public void onPause() {
if (DEBUG) {
Log.d(TAG, String.format("screen off, instance %s at %s",
@@ -586,12 +590,13 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
/**
* Shows the primary security screen for the user. This will be either the multi-selector
* or the user's security method.
+ *
* @param turningOff true if the device is being turned off
*/
public void showPrimarySecurityScreen(boolean turningOff) {
if (DEBUG) Log.d(TAG, "show()");
SecurityMode securityMode = whitelistIpcs(() -> mSecurityModel.getSecurityMode(
- KeyguardUpdateMonitor.getCurrentUser()));
+ mSelectedUserInteractor.getSelectedUserId()));
if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")");
showSecurityScreen(securityMode);
}
@@ -671,6 +676,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
/**
* Dismisses the keyguard by going to the next screen or making it gone.
+ *
* @param targetUserId a user that needs to be the foreground user at the dismissal completion.
* @return True if the keyguard is done.
*/
@@ -716,7 +722,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
}
/**
- * Resets the state of the views.
+ * Resets the state of the views.
*/
public void reset() {
mView.reset();
@@ -748,7 +754,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
getCurrentSecurityController(controller -> controller.onResume(reason));
}
mView.onResume(
- mSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser()),
+ mSecurityModel.getSecurityMode(mSelectedUserInteractor.getSelectedUserId()),
mKeyguardStateController.isFaceAuthEnabled());
}
@@ -764,7 +770,6 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
/**
* Show the bouncer and start appear animations.
- *
*/
public void appear() {
// We might still be collapsed and the view didn't have time to layout yet or still
@@ -823,13 +828,16 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
/**
* Shows the next security screen if there is one.
- * @param authenticated true if the user entered the correct authentication
- * @param targetUserId a user that needs to be the foreground user at the finish (if called)
- * completion.
+ *
+ * @param authenticated true if the user entered the correct authentication
+ * @param targetUserId a user that needs to be the foreground user at the finish
+ * (if called)
+ * completion.
* @param bypassSecondaryLockScreen true if the user is allowed to bypass the secondary
- * secondary lock screen requirement, if any.
- * @param expectedSecurityMode SecurityMode that is invoking this request. SecurityMode.Invalid
- * indicates that no check should be done
+ * secondary lock screen requirement, if any.
+ * @param expectedSecurityMode SecurityMode that is invoking this request.
+ * SecurityMode.Invalid
+ * indicates that no check should be done
* @return true if keyguard is done
*/
public boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId,
@@ -879,7 +887,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
// Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
boolean isLockscreenDisabled = mLockPatternUtils.isLockScreenDisabled(
- KeyguardUpdateMonitor.getCurrentUser())
+ mSelectedUserInteractor.getSelectedUserId())
|| !mDeviceProvisionedController.isUserSetup(targetUserId);
if (securityMode == SecurityMode.None && isLockscreenDisabled) {
@@ -955,6 +963,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
* Allows the media keys to work when the keyguard is showing.
* The media keys should be of no interest to the actual keyguard view(s),
* so intercepting them here should not be of any harm.
+ *
* @param event The key event
* @return whether the event was consumed as a media key.
*/
@@ -1050,8 +1059,6 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
/**
* Switches to the given security view unless it's already being shown, in which case
* this is a no-op.
- *
- * @param securityMode
*/
@VisibleForTesting
void showSecurityScreen(SecurityMode securityMode) {
@@ -1230,6 +1237,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
* Fades and translates in/out the security screen.
* Fades in as expansion approaches 0.
* Animation duration is between 0.33f and 0.67f of panel expansion fraction.
+ *
* @param fraction amount of the screen that should show.
*/
public void setExpansion(float fraction) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index d2d051735643..6e242084d68c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -43,6 +43,7 @@ import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.res.R;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
public class KeyguardSimPinViewController
extends KeyguardPinBasedInputViewController<KeyguardSimPinView> {
@@ -83,10 +84,11 @@ public class KeyguardSimPinViewController
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
TelephonyManager telephonyManager, FalsingCollector falsingCollector,
- EmergencyButtonController emergencyButtonController, FeatureFlags featureFlags) {
+ EmergencyButtonController emergencyButtonController, FeatureFlags featureFlags,
+ SelectedUserInteractor selectedUserInteractor) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
- emergencyButtonController, falsingCollector, featureFlags);
+ emergencyButtonController, falsingCollector, featureFlags, selectedUserInteractor);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mTelephonyManager = telephonyManager;
mSimImageView = mView.findViewById(R.id.keyguard_sim);
@@ -168,7 +170,7 @@ public class KeyguardSimPinViewController
mRemainingAttempts = -1;
mShowDefaultMessage = true;
getKeyguardSecurityCallback().dismiss(
- true, KeyguardUpdateMonitor.getCurrentUser(),
+ true, mSelectedUserInteractor.getSelectedUserId(),
SecurityMode.SimPin);
} else {
mShowDefaultMessage = false;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index b52a36b8199e..13f9d3e1038e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -40,6 +40,7 @@ import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.res.R;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
public class KeyguardSimPukViewController
extends KeyguardPinBasedInputViewController<KeyguardSimPukView> {
@@ -70,7 +71,8 @@ public class KeyguardSimPukViewController
if (simState == TelephonyManager.SIM_STATE_READY) {
mRemainingAttempts = -1;
mShowDefaultMessage = true;
- getKeyguardSecurityCallback().dismiss(true, KeyguardUpdateMonitor.getCurrentUser(),
+ getKeyguardSecurityCallback().dismiss(
+ true, mSelectedUserInteractor.getSelectedUserId(),
SecurityMode.SimPuk);
} else {
resetState();
@@ -87,10 +89,11 @@ public class KeyguardSimPukViewController
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
TelephonyManager telephonyManager, FalsingCollector falsingCollector,
- EmergencyButtonController emergencyButtonController, FeatureFlags featureFlags) {
+ EmergencyButtonController emergencyButtonController, FeatureFlags featureFlags,
+ SelectedUserInteractor selectedUserInteractor) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
- emergencyButtonController, falsingCollector, featureFlags);
+ emergencyButtonController, falsingCollector, featureFlags, selectedUserInteractor);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mTelephonyManager = telephonyManager;
mSimImageView = mView.findViewById(R.id.keyguard_sim);
@@ -284,7 +287,7 @@ public class KeyguardSimPukViewController
mShowDefaultMessage = true;
getKeyguardSecurityCallback().dismiss(
- true, KeyguardUpdateMonitor.getCurrentUser(),
+ true, mSelectedUserInteractor.getSelectedUserId(),
SecurityMode.SimPuk);
} else {
mShowDefaultMessage = false;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 205c297cebc4..7d6240b0dc2c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -186,8 +186,8 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.statusbar.policy.DevicePostureController.DevicePostureInt;
import com.android.systemui.telephony.TelephonyListenerManager;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.Assert;
-import com.android.systemui.util.settings.SecureSettings;
import dalvik.annotation.optimization.NeverCompile;
@@ -410,7 +410,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private final DevicePolicyManager mDevicePolicyManager;
private final DevicePostureController mPostureController;
private final BroadcastDispatcher mBroadcastDispatcher;
- private final SecureSettings mSecureSettings;
private final InteractionJankMonitor mInteractionJankMonitor;
private final LatencyTracker mLatencyTracker;
private final StatusBarStateController mStatusBarStateController;
@@ -429,6 +428,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private final IActivityTaskManager mActivityTaskManager;
private final WakefulnessLifecycle mWakefulness;
private final DisplayTracker mDisplayTracker;
+ private final SelectedUserInteractor mSelectedUserInteractor;
private final LockPatternUtils mLockPatternUtils;
@VisibleForTesting
@DevicePostureInt
@@ -537,13 +537,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private static int sCurrentUser;
+ @Deprecated
public synchronized static void setCurrentUser(int currentUser) {
sCurrentUser = currentUser;
}
/**
* @deprecated This can potentially return unexpected values in a multi user scenario
- * as this state is managed by another component. Consider using {@link UserTracker}.
+ * as this state is managed by another component. Consider using {@link SelectedUserInteractor}.
*/
@Deprecated
public synchronized static int getCurrentUser() {
@@ -577,7 +578,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
if (enabled) {
String message = null;
- if (KeyguardUpdateMonitor.getCurrentUser() == userId
+ if (mSelectedUserInteractor.getSelectedUserId() == userId
&& trustGrantedMessages != null) {
// Show the first non-empty string provided by a trust agent OR intentionally pass
// an empty string through (to prevent the default trust agent string from showing)
@@ -590,7 +591,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
mLogger.logTrustGrantedWithFlags(flags, newlyUnlocked, userId, message);
- if (userId == getCurrentUser()) {
+ if (userId == mSelectedUserInteractor.getSelectedUserId()) {
if (newlyUnlocked) {
// if this callback is ever removed, this should then be logged in
// TrustRepository
@@ -1038,7 +1039,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mHandler.removeCallbacks(mFpCancelNotReceived);
}
try {
- final int userId = mUserTracker.getUserId();
+ final int userId = mSelectedUserInteractor.getSelectedUserId(true);
if (userId != authUserId) {
mLogger.logFingerprintAuthForWrongUser(authUserId);
return;
@@ -1127,8 +1128,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
lockedOutStateChanged = !mFingerprintLockedOutPermanent;
mFingerprintLockedOutPermanent = true;
mLogger.d("Fingerprint permanently locked out - requiring stronger auth");
- mLockPatternUtils.requireStrongAuth(
- STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, getCurrentUser());
+ mLockPatternUtils.requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_LOCKOUT,
+ mSelectedUserInteractor.getSelectedUserId());
}
if (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT
@@ -1309,7 +1310,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mLogger.d("Aborted successful auth because device is going to sleep.");
return;
}
- final int userId = mUserTracker.getUserId();
+ final int userId = mSelectedUserInteractor.getSelectedUserId(true);
if (userId != authUserId) {
mLogger.logFaceAuthForWrongUser(authUserId);
return;
@@ -1565,7 +1566,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
@Deprecated
public boolean getIsFaceAuthenticated() {
boolean faceAuthenticated = false;
- BiometricAuthenticated bioFaceAuthenticated = mUserFaceAuthenticated.get(getCurrentUser());
+ BiometricAuthenticated bioFaceAuthenticated =
+ mUserFaceAuthenticated.get(mSelectedUserInteractor.getSelectedUserId());
if (bioFaceAuthenticated != null) {
faceAuthenticated = bioFaceAuthenticated.mAuthenticated;
}
@@ -1754,9 +1756,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
cb.onStrongAuthStateChanged(userId);
}
}
- if (userId == getCurrentUser()) {
+ if (userId == mSelectedUserInteractor.getSelectedUserId()) {
FACE_AUTH_UPDATED_STRONG_AUTH_CHANGED.setExtraInfo(
- mStrongAuthTracker.getStrongAuthForUser(getCurrentUser()));
+ mStrongAuthTracker.getStrongAuthForUser(
+ mSelectedUserInteractor.getSelectedUserId()));
// Strong auth is only reset when primary auth is used to enter the device,
// so we only check whether to stop biometric listening states here
@@ -1783,10 +1786,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
cb.onNonStrongBiometricAllowedChanged(userId);
}
}
- if (userId == getCurrentUser()) {
+ if (userId == mSelectedUserInteractor.getSelectedUserId()) {
FACE_AUTH_NON_STRONG_BIOMETRIC_ALLOWED_CHANGED.setExtraInfo(
mStrongAuthTracker.isNonStrongBiometricAllowedAfterIdleTimeout(
- getCurrentUser()) ? -1 : 1);
+ mSelectedUserInteractor.getSelectedUserId()) ? -1 : 1);
// This is only reset when primary auth is used to enter the device, so we only check
// whether to stop biometric listening states here
@@ -2191,12 +2194,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
public boolean isUnlockingWithBiometricAllowed(boolean isStrongBiometric) {
- int userId = getCurrentUser();
+ int userId = mSelectedUserInteractor.getSelectedUserId();
return isBiometricAllowedForUser(isStrongBiometric, userId);
}
public boolean hasUserAuthenticatedSinceBoot() {
- int userId = getCurrentUser();
+ int userId = mSelectedUserInteractor.getSelectedUserId();
return (getStrongAuthForUser(userId)
& STRONG_AUTH_REQUIRED_AFTER_BOOT) == 0;
}
@@ -2344,7 +2347,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
UserTracker userTracker,
@Main Looper mainLooper,
BroadcastDispatcher broadcastDispatcher,
- SecureSettings secureSettings,
DumpManager dumpManager,
@Background Executor backgroundExecutor,
@Main Executor mainExecutor,
@@ -2376,7 +2378,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
TaskStackChangeListeners taskStackChangeListeners,
IActivityTaskManager activityTaskManagerService,
DisplayTracker displayTracker,
- WakefulnessLifecycle wakefulness) {
+ WakefulnessLifecycle wakefulness,
+ SelectedUserInteractor selectedUserInteractor) {
mContext = context;
mSubscriptionManager = subscriptionManager;
mUserTracker = userTracker;
@@ -2392,7 +2395,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mStatusBarState = mStatusBarStateController.getState();
mLockPatternUtils = lockPatternUtils;
mAuthController = authController;
- mSecureSettings = secureSettings;
dumpManager.registerDumpable(this);
mSensorPrivacyManager = sensorPrivacyManager;
mActiveUnlockConfig = activeUnlockConfiguration;
@@ -2426,6 +2428,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mWakefulness = wakefulness;
mDisplayTracker = displayTracker;
mDisplayTracker.addDisplayChangeCallback(mDisplayCallback, mainExecutor);
+ mSelectedUserInteractor = selectedUserInteractor;
mHandler = new Handler(mainLooper) {
@Override
@@ -2648,7 +2651,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mTaskStackChangeListeners.registerTaskStackListener(mTaskStackListener);
mIsSystemUser = mUserManager.isSystemUser();
- int user = mUserTracker.getUserId();
+ int user = mSelectedUserInteractor.getSelectedUserId(true);
mUserIsUnlocked.put(user, mUserManager.isUserUnlocked(user));
mLogoutEnabled = mDevicePolicyManager.isLogoutEnabled();
updateSecondaryLockscreenRequirement(user);
@@ -2720,7 +2723,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
* @return true if there's at least one udfps enrolled for the current user.
*/
public boolean isUdfpsEnrolled() {
- return mAuthController.isUdfpsEnrolled(getCurrentUser());
+ return mAuthController.isUdfpsEnrolled(mSelectedUserInteractor.getSelectedUserId());
}
/**
@@ -2735,7 +2738,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
* @return true if there's at least one sfps enrollment for the current user.
*/
public boolean isSfpsEnrolled() {
- return mAuthController.isSfpsEnrolled(getCurrentUser());
+ return mAuthController.isSfpsEnrolled(mSelectedUserInteractor.getSelectedUserId());
}
/**
@@ -2906,7 +2909,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
if (shouldTriggerActiveUnlock()) {
mLogger.logActiveUnlockTriggered(reason);
- mTrustManager.reportUserMayRequestUnlock(KeyguardUpdateMonitor.getCurrentUser());
+ mTrustManager.reportUserMayRequestUnlock(mSelectedUserInteractor.getSelectedUserId());
}
}
@@ -2960,7 +2963,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
if (allowRequest && shouldTriggerActiveUnlock()) {
mLogger.logUserRequestedUnlock(requestOrigin, reason, dismissKeyguard);
- mTrustManager.reportUserRequestedUnlock(KeyguardUpdateMonitor.getCurrentUser(),
+ mTrustManager.reportUserRequestedUnlock(mSelectedUserInteractor.getSelectedUserId(),
dismissKeyguard);
}
}
@@ -3031,7 +3034,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
&& mStatusBarState != StatusBarState.SHADE_LOCKED);
// Gates:
- final int user = getCurrentUser();
+ final int user = mSelectedUserInteractor.getSelectedUserId();
// No need to trigger active unlock if we're already unlocked or don't have
// pin/pattern/password setup
@@ -3073,30 +3076,33 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
private boolean shouldListenForFingerprintAssistant() {
- BiometricAuthenticated fingerprint = mUserFingerprintAuthenticated.get(getCurrentUser());
+ BiometricAuthenticated fingerprint = mUserFingerprintAuthenticated.get(
+ mSelectedUserInteractor.getSelectedUserId());
return mAssistantVisible && mKeyguardOccluded
&& !(fingerprint != null && fingerprint.mAuthenticated)
- && !mUserHasTrust.get(getCurrentUser(), false);
+ && !mUserHasTrust.get(
+ mSelectedUserInteractor.getSelectedUserId(), false);
}
private boolean shouldListenForFaceAssistant() {
- BiometricAuthenticated face = mUserFaceAuthenticated.get(getCurrentUser());
+ BiometricAuthenticated face = mUserFaceAuthenticated.get(
+ mSelectedUserInteractor.getSelectedUserId());
return mAssistantVisible
// There can be intermediate states where mKeyguardShowing is false but
// mKeyguardOccluded is true, we don't want to run face auth in such a scenario.
&& (mKeyguardShowing && mKeyguardOccluded)
&& !(face != null && face.mAuthenticated)
- && !mUserHasTrust.get(getCurrentUser(), false);
+ && !mUserHasTrust.get(mSelectedUserInteractor.getSelectedUserId(), false);
}
private boolean shouldTriggerActiveUnlockForAssistant() {
return mAssistantVisible && mKeyguardOccluded
- && !mUserHasTrust.get(getCurrentUser(), false);
+ && !mUserHasTrust.get(mSelectedUserInteractor.getSelectedUserId(), false);
}
@VisibleForTesting
protected boolean shouldListenForFingerprint(boolean isUdfps) {
- final int user = getCurrentUser();
+ final int user = mSelectedUserInteractor.getSelectedUserId();
final boolean userDoesNotHaveTrust = !getUserHasTrust(user);
final boolean shouldListenForFingerprintAssistant = shouldListenForFingerprintAssistant();
final boolean shouldListenKeyguardState =
@@ -3185,7 +3191,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
final boolean statusBarShadeLocked = mStatusBarState == StatusBarState.SHADE_LOCKED;
final boolean awakeKeyguard = isKeyguardVisible() && mDeviceInteractive
&& !statusBarShadeLocked;
- final int user = getCurrentUser();
+ final int user = mSelectedUserInteractor.getSelectedUserId();
final boolean faceAuthAllowed = isUnlockingWithBiometricAllowed(FACE);
final boolean canBypass = mKeyguardBypassController != null
&& mKeyguardBypassController.canBypass();
@@ -3285,7 +3291,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
private void startListeningForFingerprint() {
- final int userId = getCurrentUser();
+ final int userId = mSelectedUserInteractor.getSelectedUserId();
final boolean unlockPossible = isUnlockWithFingerprintPossible(userId);
if (mFingerprintCancelSignal != null) {
mLogger.logUnexpectedFpCancellationSignalState(
@@ -3332,7 +3338,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
private void startListeningForFace(@NonNull FaceAuthUiEvent faceAuthUiEvent) {
- final int userId = getCurrentUser();
+ final int userId = mSelectedUserInteractor.getSelectedUserId();
final boolean unlockPossible = isUnlockWithFacePossible(userId);
if (mFaceCancelSignal != null) {
mLogger.logUnexpectedFaceCancellationSignalState(mFaceRunningState, unlockPossible);
@@ -3875,12 +3881,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
private boolean resolveNeedsSlowUnlockTransition() {
- if (isUserUnlocked(getCurrentUser())) {
+ if (isUserUnlocked(mSelectedUserInteractor.getSelectedUserId())) {
return false;
}
Intent homeIntent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME);
ResolveInfo resolveInfo = mPackageManager.resolveActivityAsUser(homeIntent,
- 0 /* flags */, getCurrentUser());
+ 0 /* flags */, mSelectedUserInteractor.getSelectedUserId());
if (resolveInfo == null) {
mLogger.w("resolveNeedsSlowUnlockTransition: returning false since activity could "
@@ -4066,9 +4072,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
@AnyThread
public void setSwitchingUser(boolean switching) {
if (switching) {
- mLogger.logUserSwitching(getCurrentUser(), "from setSwitchingUser");
+ mLogger.logUserSwitching(
+ mSelectedUserInteractor.getSelectedUserId(), "from setSwitchingUser");
} else {
- mLogger.logUserSwitchComplete(getCurrentUser(), "from setSwitchingUser");
+ mLogger.logUserSwitchComplete(
+ mSelectedUserInteractor.getSelectedUserId(), "from setSwitchingUser");
}
mSwitchingUser = switching;
// Since this comes in on a binder thread, we need to post it first
@@ -4443,9 +4451,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
@Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("KeyguardUpdateMonitor state:");
- pw.println(" getUserHasTrust()=" + getUserHasTrust(getCurrentUser()));
+ pw.println(" getUserHasTrust()=" + getUserHasTrust(
+ mSelectedUserInteractor.getSelectedUserId()));
pw.println(" getUserUnlockedWithBiometric()="
- + getUserUnlockedWithBiometric(getCurrentUser()));
+ + getUserUnlockedWithBiometric(mSelectedUserInteractor.getSelectedUserId()));
pw.println(" isFaceAuthInteractorEnabled: " + isFaceAuthInteractorEnabled());
pw.println(" SIM States:");
for (SimData data : mSimDatas.values()) {
@@ -4463,7 +4472,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
pw.println(" " + subId + "=" + mServiceStates.get(subId));
}
if (isFingerprintSupported()) {
- final int userId = mUserTracker.getUserId();
+ final int userId = mSelectedUserInteractor.getSelectedUserId(true);
final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId);
BiometricAuthenticated fingerprint = mUserFingerprintAuthenticated.get(userId);
pw.println(" Fingerprint state (user=" + userId + ")");
@@ -4506,7 +4515,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mFingerprintListenBuffer.toList()
).printTableData(pw);
} else if (mFpm != null && mFingerprintSensorProperties.isEmpty()) {
- final int userId = mUserTracker.getUserId();
+ final int userId = mSelectedUserInteractor.getSelectedUserId(true);
pw.println(" Fingerprint state (user=" + userId + ")");
pw.println(" mFingerprintSensorProperties.isEmpty="
+ mFingerprintSensorProperties.isEmpty());
@@ -4520,7 +4529,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
).printTableData(pw);
}
if (isFaceSupported()) {
- final int userId = mUserTracker.getUserId();
+ final int userId = mSelectedUserInteractor.getSelectedUserId(true);
final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId);
BiometricAuthenticated face = mUserFaceAuthenticated.get(userId);
pw.println(" Face authentication state (user=" + userId + ")");
@@ -4550,7 +4559,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mFaceListenBuffer.toList()
).printTableData(pw);
} else if (mFaceManager != null && mFaceSensorProperties.isEmpty()) {
- final int userId = mUserTracker.getUserId();
+ final int userId = mSelectedUserInteractor.getSelectedUserId(true);
pw.println(" Face state (user=" + userId + ")");
pw.println(" mFaceSensorProperties.isEmpty="
+ mFaceSensorProperties.isEmpty());
@@ -4564,7 +4573,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
).printTableData(pw);
}
pw.println("ActiveUnlockRunning="
- + mTrustManager.isActiveUnlockRunning(KeyguardUpdateMonitor.getCurrentUser()));
+ + mTrustManager.isActiveUnlockRunning(mSelectedUserInteractor.getSelectedUserId()));
new DumpsysTableLogger(
"KeyguardActiveUnlockTriggers",
KeyguardActiveUnlockModel.TABLE_HEADERS,
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index 40d0be1173fa..ff6a3d0cc6f0 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -25,7 +25,6 @@ import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
@@ -105,18 +104,6 @@ public class LockIconView extends FrameLayout implements Dumpable {
mLockIcon.setImageTintList(ColorStateList.valueOf(mLockIconColor));
}
- void setImageDrawable(Drawable drawable) {
- mLockIcon.setImageDrawable(drawable);
-
- if (!mUseBackground) return;
-
- if (drawable == null) {
- mBgView.setVisibility(View.INVISIBLE);
- } else {
- mBgView.setVisibility(View.VISIBLE);
- }
- }
-
/**
* Whether or not to render the lock icon background. Mainly used for UDPFS.
*/
@@ -197,6 +184,7 @@ public class LockIconView extends FrameLayout implements Dumpable {
mLockIcon = new ImageView(context, attrs);
mLockIcon.setId(R.id.lock_icon);
mLockIcon.setScaleType(ImageView.ScaleType.CENTER_CROP);
+ mLockIcon.setImageDrawable(context.getDrawable(R.drawable.super_lock_icon));
addView(mLockIcon);
LayoutParams lp = (LayoutParams) mLockIcon.getLayoutParams();
lp.height = MATCH_PARENT;
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 83da80f4123a..611283f12984 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -35,7 +35,6 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
-import android.graphics.drawable.AnimatedStateListDrawable;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricSourceType;
import android.os.Process;
@@ -120,9 +119,6 @@ public class LockIconViewController implements Dumpable {
private boolean mUdfpsEnrolled;
private Resources mResources;
private Context mContext;
-
- @NonNull private final AnimatedStateListDrawable mIcon;
-
@NonNull private CharSequence mUnlockedLabel;
@NonNull private CharSequence mLockedLabel;
@NonNull private final VibratorHelper mVibrator;
@@ -147,7 +143,6 @@ public class LockIconViewController implements Dumpable {
private boolean mCanDismissLockScreen;
private int mStatusBarState;
private boolean mIsKeyguardShowing;
- private Runnable mOnGestureDetectedRunnable;
private Runnable mLongPressCancelRunnable;
private boolean mUdfpsSupported;
@@ -232,9 +227,6 @@ public class LockIconViewController implements Dumpable {
mMaxBurnInOffsetX = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x);
mMaxBurnInOffsetY = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y);
-
- mIcon = (AnimatedStateListDrawable)
- resources.getDrawable(R.drawable.super_lock_icon, context.getTheme());
mUnlockedLabel = resources.getString(R.string.accessibility_unlock_button);
mLockedLabel = resources.getString(R.string.accessibility_lock_icon);
mLongPressTimeout = resources.getInteger(R.integer.config_lockIconLongPress);
@@ -270,7 +262,6 @@ public class LockIconViewController implements Dumpable {
@SuppressLint("ClickableViewAccessibility")
public void setLockIconView(LockIconView lockIconView) {
mView = lockIconView;
- mView.setImageDrawable(mIcon);
mView.setAccessibilityDelegate(mAccessibilityDelegate);
if (mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) {
@@ -492,10 +483,6 @@ public class LockIconViewController implements Dumpable {
pw.println("mUdfpsSupported: " + mUdfpsSupported);
pw.println("mUdfpsEnrolled: " + mUdfpsEnrolled);
pw.println("mIsKeyguardShowing: " + mIsKeyguardShowing);
- pw.println(" mIcon: ");
- for (int state : mIcon.getState()) {
- pw.print(" " + state);
- }
pw.println();
pw.println(" mShowUnlockIcon: " + mShowUnlockIcon);
pw.println(" mShowLockIcon: " + mShowLockIcon);
diff --git a/packages/SystemUI/src/com/android/systemui/LatencyTester.java b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
index b33d50137155..c860979afb10 100644
--- a/packages/SystemUI/src/com/android/systemui/LatencyTester.java
+++ b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
@@ -31,7 +31,7 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.statusbar.phone.BiometricUnlockController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -53,25 +53,25 @@ public class LatencyTester implements CoreStartable {
private static final String
ACTION_FACE_WAKE =
"com.android.systemui.latency.ACTION_FACE_WAKE";
- private final BiometricUnlockController mBiometricUnlockController;
private final BroadcastDispatcher mBroadcastDispatcher;
private final DeviceConfigProxy mDeviceConfigProxy;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final SelectedUserInteractor mSelectedUserInteractor;
private boolean mEnabled;
@Inject
public LatencyTester(
- BiometricUnlockController biometricUnlockController,
BroadcastDispatcher broadcastDispatcher,
DeviceConfigProxy deviceConfigProxy,
@Main DelayableExecutor mainExecutor,
- KeyguardUpdateMonitor keyguardUpdateMonitor
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ SelectedUserInteractor selectedUserInteractor
) {
- mBiometricUnlockController = biometricUnlockController;
mBroadcastDispatcher = broadcastDispatcher;
mDeviceConfigProxy = deviceConfigProxy;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mSelectedUserInteractor = selectedUserInteractor;
updateEnabled();
mDeviceConfigProxy.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_LATENCY_TRACKER,
@@ -87,11 +87,11 @@ public class LatencyTester implements CoreStartable {
return;
}
if (type == BiometricSourceType.FACE) {
- mKeyguardUpdateMonitor.onFaceAuthenticated(KeyguardUpdateMonitor.getCurrentUser(),
+ mKeyguardUpdateMonitor.onFaceAuthenticated(mSelectedUserInteractor.getSelectedUserId(),
true);
} else if (type == BiometricSourceType.FINGERPRINT) {
mKeyguardUpdateMonitor.onFingerprintAuthenticated(
- KeyguardUpdateMonitor.getCurrentUser(), true);
+ mSelectedUserInteractor.getSelectedUserId(), true);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index 0e339dd68b4f..9305ab6f6968 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -28,17 +28,17 @@ import com.android.internal.app.IVisualQueryRecognitionStatusListener;
import com.android.internal.app.IVoiceInteractionSessionListener;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.res.R;
import com.android.systemui.assist.ui.DefaultUiController;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.res.R;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.settings.SecureSettings;
import dagger.Lazy;
@@ -144,6 +144,7 @@ public class AssistManager {
private final UserTracker mUserTracker;
private final DisplayTracker mDisplayTracker;
private final SecureSettings mSecureSettings;
+ private final SelectedUserInteractor mSelectedUserInteractor;
private final DeviceProvisionedController mDeviceProvisionedController;
@@ -152,16 +153,16 @@ public class AssistManager {
private final IVisualQueryDetectionAttentionListener mVisualQueryDetectionAttentionListener =
new IVisualQueryDetectionAttentionListener.Stub() {
- @Override
- public void onAttentionGained() {
- handleVisualAttentionChanged(true);
- }
+ @Override
+ public void onAttentionGained() {
+ handleVisualAttentionChanged(true);
+ }
- @Override
- public void onAttentionLost() {
- handleVisualAttentionChanged(false);
- }
- };
+ @Override
+ public void onAttentionLost() {
+ handleVisualAttentionChanged(false);
+ }
+ };
private final CommandQueue mCommandQueue;
protected final AssistUtils mAssistUtils;
@@ -183,7 +184,8 @@ public class AssistManager {
@Main Handler uiHandler,
UserTracker userTracker,
DisplayTracker displayTracker,
- SecureSettings secureSettings) {
+ SecureSettings secureSettings,
+ SelectedUserInteractor selectedUserInteractor) {
mContext = context;
mDeviceProvisionedController = controller;
mCommandQueue = commandQueue;
@@ -195,6 +197,7 @@ public class AssistManager {
mUserTracker = userTracker;
mDisplayTracker = displayTracker;
mSecureSettings = secureSettings;
+ mSelectedUserInteractor = selectedUserInteractor;
registerVoiceInteractionSessionListener();
registerVisualQueryRecognitionStatusListener();
@@ -316,12 +319,13 @@ public class AssistManager {
public boolean shouldOverrideAssist(int invocationType) {
return mAssistOverrideInvocationTypes != null
&& Arrays.stream(mAssistOverrideInvocationTypes).anyMatch(
- override -> override == invocationType);
+ override -> override == invocationType);
}
/**
* @param invocationTypes The invocation types that will henceforth be handled via
- * OverviewProxy (Launcher); other invocation types should be handled by this class.
+ * OverviewProxy (Launcher); other invocation types should be handled by
+ * this class.
*/
public void setAssistantOverridesRequested(int[] invocationTypes) {
mAssistOverrideInvocationTypes = invocationTypes;
@@ -478,7 +482,7 @@ public class AssistManager {
@Nullable
private ComponentName getAssistInfo() {
- return getAssistInfoForUser(KeyguardUpdateMonitor.getCurrentUser());
+ return getAssistInfoForUser(mSelectedUserInteractor.getSelectedUserId());
}
public void showDisclosure() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index c9e4cbe30a2c..92eacf10e424 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -97,6 +97,7 @@ import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.Execution;
import com.android.systemui.util.time.SystemClock;
@@ -167,6 +168,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
@NonNull private final AlternateBouncerInteractor mAlternateBouncerInteractor;
@NonNull private final InputManager mInputManager;
@NonNull private final UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate;
+ @NonNull private final SelectedUserInteractor mSelectedUserInteractor;
private final boolean mIgnoreRefreshRate;
// Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
@@ -281,7 +283,8 @@ public class UdfpsController implements DozeReceiver, Dumpable {
mPrimaryBouncerInteractor,
mAlternateBouncerInteractor,
mUdfpsKeyguardAccessibilityDelegate,
- mUdfpsKeyguardViewModels
+ mUdfpsKeyguardViewModels,
+ mSelectedUserInteractor
)));
}
@@ -644,7 +647,8 @@ public class UdfpsController implements DozeReceiver, Dumpable {
@NonNull InputManager inputManager,
@NonNull KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
@NonNull UdfpsKeyguardAccessibilityDelegate udfpsKeyguardAccessibilityDelegate,
- @NonNull Provider<UdfpsKeyguardViewModels> udfpsKeyguardViewModelsProvider) {
+ @NonNull Provider<UdfpsKeyguardViewModels> udfpsKeyguardViewModelsProvider,
+ @NonNull SelectedUserInteractor selectedUserInteractor) {
mContext = context;
mExecution = execution;
mVibrator = vibrator;
@@ -687,6 +691,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
mAlternateBouncerInteractor = alternateBouncerInteractor;
mInputManager = inputManager;
mUdfpsKeyguardAccessibilityDelegate = udfpsKeyguardAccessibilityDelegate;
+ mSelectedUserInteractor = selectedUserInteractor;
mTouchProcessor = singlePointerTouchProcessor;
mSessionTracker = sessionTracker;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index 7130bfb462e3..272e0f21e74a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -44,7 +44,6 @@ import android.view.accessibility.AccessibilityManager.TouchExplorationStateChan
import androidx.annotation.LayoutRes
import androidx.annotation.VisibleForTesting
import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.res.R
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.biometrics.ui.controller.UdfpsKeyguardViewController
@@ -56,12 +55,14 @@ import com.android.systemui.flags.Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS
import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter
import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.res.R
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.phone.SystemUIDialogManager
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import kotlinx.coroutines.ExperimentalCoroutinesApi
import javax.inject.Provider
@@ -78,31 +79,32 @@ const val SETTING_REMOVE_ENROLLMENT_UI = "udfps_overlay_remove_enrollment_ui"
@ExperimentalCoroutinesApi
@UiThread
class UdfpsControllerOverlay @JvmOverloads constructor(
- private val context: Context,
- private val inflater: LayoutInflater,
- private val windowManager: WindowManager,
- private val accessibilityManager: AccessibilityManager,
- private val statusBarStateController: StatusBarStateController,
- private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
- private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
- private val dialogManager: SystemUIDialogManager,
- private val dumpManager: DumpManager,
- private val transitionController: LockscreenShadeTransitionController,
- private val configurationController: ConfigurationController,
- private val keyguardStateController: KeyguardStateController,
- private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController,
- private var udfpsDisplayModeProvider: UdfpsDisplayModeProvider,
- val requestId: Long,
- @ShowReason val requestReason: Int,
- private val controllerCallback: IUdfpsOverlayControllerCallback,
- private val onTouch: (View, MotionEvent, Boolean) -> Boolean,
- private val activityLaunchAnimator: ActivityLaunchAnimator,
- private val featureFlags: FeatureFlags,
- private val primaryBouncerInteractor: PrimaryBouncerInteractor,
- private val alternateBouncerInteractor: AlternateBouncerInteractor,
- private val isDebuggable: Boolean = Build.IS_DEBUGGABLE,
- private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate,
- private val udfpsKeyguardViewModels: Provider<UdfpsKeyguardViewModels>,
+ private val context: Context,
+ private val inflater: LayoutInflater,
+ private val windowManager: WindowManager,
+ private val accessibilityManager: AccessibilityManager,
+ private val statusBarStateController: StatusBarStateController,
+ private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+ private val dialogManager: SystemUIDialogManager,
+ private val dumpManager: DumpManager,
+ private val transitionController: LockscreenShadeTransitionController,
+ private val configurationController: ConfigurationController,
+ private val keyguardStateController: KeyguardStateController,
+ private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController,
+ private var udfpsDisplayModeProvider: UdfpsDisplayModeProvider,
+ val requestId: Long,
+ @ShowReason val requestReason: Int,
+ private val controllerCallback: IUdfpsOverlayControllerCallback,
+ private val onTouch: (View, MotionEvent, Boolean) -> Boolean,
+ private val activityLaunchAnimator: ActivityLaunchAnimator,
+ private val featureFlags: FeatureFlags,
+ private val primaryBouncerInteractor: PrimaryBouncerInteractor,
+ private val alternateBouncerInteractor: AlternateBouncerInteractor,
+ private val isDebuggable: Boolean = Build.IS_DEBUGGABLE,
+ private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate,
+ private val udfpsKeyguardViewModels: Provider<UdfpsKeyguardViewModels>,
+ private val selectedUserInteractor: SelectedUserInteractor,
) {
/** The view, when [isShowing], or null. */
var overlayView: UdfpsView? = null
@@ -268,6 +270,7 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
primaryBouncerInteractor,
alternateBouncerInteractor,
udfpsKeyguardAccessibilityDelegate,
+ selectedUserInteractor,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
index 3d5be6fb9f8c..d7df0e585dcf 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
@@ -45,6 +45,7 @@ import com.android.systemui.statusbar.phone.SystemUIDialogManager
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import java.io.PrintWriter
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
@@ -68,6 +69,7 @@ open class UdfpsKeyguardViewControllerLegacy(
primaryBouncerInteractor: PrimaryBouncerInteractor,
private val alternateBouncerInteractor: AlternateBouncerInteractor,
private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate,
+ private val selectedUserInteractor: SelectedUserInteractor,
) :
UdfpsAnimationViewController<UdfpsKeyguardViewLegacy>(
view,
@@ -384,7 +386,7 @@ open class UdfpsKeyguardViewControllerLegacy(
}
if (
keyguardUpdateMonitor.getUserUnlockedWithBiometric(
- KeyguardUpdateMonitor.getCurrentUser()
+ selectedUserInteractor.getSelectedUserId()
)
) {
// If the device was unlocked by a biometric, immediately hide the UDFPS icon to avoid
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 2a1047ac6f76..38043b4020e5 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
@@ -17,13 +17,13 @@
package com.android.systemui.biometrics.domain.interactor
import android.view.MotionEvent
-import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.biometrics.AuthController
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
@@ -35,11 +35,16 @@ import kotlinx.coroutines.flow.stateIn
@SysUISingleton
class UdfpsOverlayInteractor
@Inject
-constructor(private val authController: AuthController, @Application scope: CoroutineScope) {
+constructor(
+ private val authController: AuthController,
+ private val selectedUserInteractor: SelectedUserInteractor,
+ @Application scope: CoroutineScope
+) {
/** Whether a touch is within the under-display fingerprint sensor area */
fun isTouchWithinUdfpsArea(ev: MotionEvent): Boolean {
- val isUdfpsEnrolled = authController.isUdfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser())
+ val isUdfpsEnrolled =
+ authController.isUdfpsEnrolled(selectedUserInteractor.getSelectedUserId())
val isWithinOverlayBounds =
udfpsOverlayParams.value.overlayBounds.contains(ev.rawX.toInt(), ev.rawY.toInt())
return isUdfpsEnrolled && isWithinOverlayBounds
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
index 6e26fe952749..2bd62587834d 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
@@ -28,7 +28,6 @@ import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.DejankUtils
-import com.android.systemui.res.R
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN
@@ -41,8 +40,10 @@ import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.TrustRepository
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
import com.android.systemui.shared.system.SysUiStatsLog
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
@@ -73,6 +74,7 @@ constructor(
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val trustRepository: TrustRepository,
@Application private val applicationScope: CoroutineScope,
+ private val selectedUserInteractor: SelectedUserInteractor,
) {
private val passiveAuthBouncerDelay =
context.resources.getInteger(R.integer.primary_bouncer_passive_auth_delay).toLong()
@@ -384,7 +386,7 @@ constructor(
/** Returns whether the bouncer should be full screen. */
private fun needsFullscreenBouncer(): Boolean {
val mode: KeyguardSecurityModel.SecurityMode =
- keyguardSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser())
+ keyguardSecurityModel.getSecurityMode(selectedUserInteractor.getSelectedUserId())
return mode == KeyguardSecurityModel.SecurityMode.SimPin ||
mode == KeyguardSecurityModel.SecurityMode.SimPuk
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
index 36e5db4c62e0..ac3d4b6c853b 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
@@ -26,7 +26,6 @@ import com.android.keyguard.KeyguardMessageAreaController
import com.android.keyguard.KeyguardSecurityContainerController
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardSecurityView
-import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.dagger.KeyguardBouncerComponent
import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE
@@ -37,6 +36,7 @@ import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransition
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.log.BouncerLogger
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
@@ -53,6 +53,7 @@ object KeyguardBouncerViewBinder {
bouncerMessageInteractor: BouncerMessageInteractor,
bouncerLogger: BouncerLogger,
featureFlags: FeatureFlags,
+ selectedUserInteractor: SelectedUserInteractor,
) {
// Builds the KeyguardSecurityContainerController from bouncer view group.
val securityContainerController: KeyguardSecurityContainerController =
@@ -84,7 +85,7 @@ object KeyguardBouncerViewBinder {
override fun showNextSecurityScreenOrFinish(): Boolean {
return securityContainerController.dismiss(
- KeyguardUpdateMonitor.getCurrentUser()
+ selectedUserInteractor.getSelectedUserId()
)
}
@@ -220,7 +221,7 @@ object KeyguardBouncerViewBinder {
launch {
viewModel.keyguardAuthenticated.collect {
securityContainerController.finish(
- KeyguardUpdateMonitor.getCurrentUser()
+ selectedUserInteractor.getSelectedUserId()
)
viewModel.notifyKeyguardAuthenticated()
}
diff --git a/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt b/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
index b2680950d9c5..11c7a3196913 100644
--- a/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
@@ -27,17 +27,16 @@ import android.content.pm.ResolveInfo
import android.os.RemoteException
import android.util.Log
import android.view.WindowManager
-import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.ActivityIntentHelper
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.settings.UserTracker
import com.android.systemui.shared.system.ActivityManagerKt.isInForeground
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -59,7 +58,7 @@ class CameraGestureHelper @Inject constructor(
private val cameraIntents: CameraIntentsWrapper,
private val contentResolver: ContentResolver,
@Main private val uiExecutor: Executor,
- private val userTracker: UserTracker
+ private val selectedUserInteractor: SelectedUserInteractor,
) {
/**
* Whether the camera application can be launched for the camera launch gesture.
@@ -72,12 +71,12 @@ class CameraGestureHelper @Inject constructor(
val resolveInfo: ResolveInfo? = packageManager.resolveActivityAsUser(
getStartCameraIntent(),
PackageManager.MATCH_DEFAULT_ONLY,
- KeyguardUpdateMonitor.getCurrentUser()
+ selectedUserInteractor.getSelectedUserId()
)
val resolvedPackage = resolveInfo?.activityInfo?.packageName
return (resolvedPackage != null &&
(statusBarState != StatusBarState.SHADE ||
- !activityManager.isInForeground(resolvedPackage)))
+ !activityManager.isInForeground(resolvedPackage)))
}
/**
@@ -89,7 +88,7 @@ class CameraGestureHelper @Inject constructor(
val intent: Intent = getStartCameraIntent()
intent.putExtra(CameraIntents.EXTRA_LAUNCH_SOURCE, source)
val wouldLaunchResolverActivity = activityIntentHelper.wouldLaunchResolverActivity(
- intent, KeyguardUpdateMonitor.getCurrentUser()
+ intent, selectedUserInteractor.getSelectedUserId()
)
if (CameraIntents.isSecureCameraIntent(intent) && !wouldLaunchResolverActivity) {
uiExecutor.execute {
@@ -102,7 +101,7 @@ class CameraGestureHelper @Inject constructor(
val activityOptions = ActivityOptions.makeBasic()
activityOptions.setDisallowEnterPictureInPictureWhileLaunching(true)
activityOptions.rotationAnimationHint =
- WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS
+ WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS
try {
activityTaskManager.startActivityAsUser(
null,
@@ -116,7 +115,7 @@ class CameraGestureHelper @Inject constructor(
Intent.FLAG_ACTIVITY_NEW_TASK,
null,
activityOptions.toBundle(),
- userTracker.userId,
+ selectedUserInteractor.getSelectedUserId(true),
)
} catch (e: RemoteException) {
Log.w(
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
index 39c01f759654..a6b073da2530 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
@@ -37,12 +37,15 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.sensors.ThresholdSensor;
import com.android.systemui.util.sensors.ThresholdSensorEvent;
import com.android.systemui.util.time.SystemClock;
+import dagger.Lazy;
+
import java.util.Collections;
import javax.inject.Inject;
@@ -66,6 +69,7 @@ class FalsingCollectorImpl implements FalsingCollector {
private final DockManager mDockManager;
private final DelayableExecutor mMainExecutor;
private final SystemClock mSystemClock;
+ private final Lazy<SelectedUserInteractor> mUserInteractor;
private int mState;
private boolean mShowingAod;
@@ -93,7 +97,7 @@ class FalsingCollectorImpl implements FalsingCollector {
public void onBiometricAuthenticated(int userId,
BiometricSourceType biometricSourceType,
boolean isStrongBiometric) {
- if (userId == KeyguardUpdateMonitor.getCurrentUser()
+ if (userId == mUserInteractor.get().getSelectedUserId()
&& biometricSourceType == BiometricSourceType.FACE) {
mFalsingDataProvider.setJustUnlockedWithFace(true);
}
@@ -136,7 +140,8 @@ class FalsingCollectorImpl implements FalsingCollector {
BatteryController batteryController,
DockManager dockManager,
@Main DelayableExecutor mainExecutor,
- SystemClock systemClock) {
+ SystemClock systemClock,
+ Lazy<SelectedUserInteractor> userInteractor) {
mFalsingDataProvider = falsingDataProvider;
mFalsingManager = falsingManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -148,6 +153,7 @@ class FalsingCollectorImpl implements FalsingCollector {
mDockManager = dockManager;
mMainExecutor = mainExecutor;
mSystemClock = systemClock;
+ mUserInteractor = userInteractor;
mProximitySensor.setTag(PROXIMITY_SENSOR_TAG);
mProximitySensor.setDelay(SensorManager.SENSOR_DELAY_GAME);
diff --git a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
index bf4fba89ca55..0bf50693344a 100644
--- a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
+++ b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
@@ -26,11 +26,13 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.colorextraction.types.ExtractionType;
import com.android.internal.colorextraction.types.Tonal;
-import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+
+import dagger.Lazy;
import java.io.PrintWriter;
import java.util.Arrays;
@@ -48,19 +50,22 @@ public class SysuiColorExtractor extends ColorExtractor implements Dumpable,
private boolean mHasMediaArtwork;
private final GradientColors mNeutralColorsLock;
private final GradientColors mBackdropColors;
+ private Lazy<SelectedUserInteractor> mUserInteractor;
@Inject
public SysuiColorExtractor(
Context context,
ConfigurationController configurationController,
- DumpManager dumpManager) {
+ DumpManager dumpManager,
+ Lazy<SelectedUserInteractor> userInteractor) {
this(
context,
new Tonal(context),
configurationController,
context.getSystemService(WallpaperManager.class),
dumpManager,
- false /* immediately */);
+ false /* immediately */,
+ userInteractor);
}
@VisibleForTesting
@@ -70,7 +75,8 @@ public class SysuiColorExtractor extends ColorExtractor implements Dumpable,
ConfigurationController configurationController,
WallpaperManager wallpaperManager,
DumpManager dumpManager,
- boolean immediately) {
+ boolean immediately,
+ Lazy<SelectedUserInteractor> userInteractor) {
super(context, type, immediately, wallpaperManager);
mTonal = type instanceof Tonal ? (Tonal) type : new Tonal(context);
mNeutralColorsLock = new GradientColors();
@@ -79,6 +85,7 @@ public class SysuiColorExtractor extends ColorExtractor implements Dumpable,
mBackdropColors = new GradientColors();
mBackdropColors.setMainColor(Color.BLACK);
+ mUserInteractor = userInteractor;
// Listen to all users instead of only the current one.
if (wallpaperManager.isWallpaperSupported()) {
@@ -100,7 +107,7 @@ public class SysuiColorExtractor extends ColorExtractor implements Dumpable,
@Override
public void onColorsChanged(WallpaperColors colors, int which, int userId) {
- if (userId != KeyguardUpdateMonitor.getCurrentUser()) {
+ if (userId != mUserInteractor.get().getSelectedUserId()) {
// Colors do not belong to current user, ignoring.
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetMetadata.kt b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetMetadata.kt
new file mode 100644
index 000000000000..f9c4f29afee9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetMetadata.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.communal.data.model
+
+import com.android.systemui.communal.shared.CommunalContentSize
+
+/** Metadata for the default widgets */
+data class CommunalWidgetMetadata(
+ /* Widget provider component name */
+ val componentName: String,
+
+ /* Defines the order in which the widget will be rendered in the grid. */
+ val priority: Int,
+
+ /* Supported sizes */
+ val sizes: List<CommunalContentSize>
+)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
index e2a7d077a32c..f13b62fbfeb9 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
@@ -27,13 +27,17 @@ import android.content.pm.PackageManager
import android.os.UserManager
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.communal.data.model.CommunalWidgetMetadata
import com.android.systemui.communal.shared.CommunalAppWidgetInfo
+import com.android.systemui.communal.shared.CommunalContentSize
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.log.dagger.CommunalLog
+import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
@@ -45,15 +49,20 @@ import kotlinx.coroutines.flow.map
interface CommunalWidgetRepository {
/** A flow of provider info for the stopwatch widget, or null if widget is unavailable. */
val stopwatchAppWidgetInfo: Flow<CommunalAppWidgetInfo?>
+
+ /** Widgets that are allowed to render in the glanceable hub */
+ val communalWidgetAllowlist: List<CommunalWidgetMetadata>
}
@SysUISingleton
class CommunalWidgetRepositoryImpl
@Inject
constructor(
+ @Application private val applicationContext: Context,
private val appWidgetManager: AppWidgetManager,
private val appWidgetHost: AppWidgetHost,
broadcastDispatcher: BroadcastDispatcher,
+ communalRepository: CommunalRepository,
private val packageManager: PackageManager,
private val userManager: UserManager,
private val userTracker: UserTracker,
@@ -64,12 +73,18 @@ constructor(
const val TAG = "CommunalWidgetRepository"
const val WIDGET_LABEL = "Stopwatch"
}
+ override val communalWidgetAllowlist: List<CommunalWidgetMetadata>
private val logger = Logger(logBuffer, TAG)
// Whether the [AppWidgetHost] is listening for updates.
private var isHostListening = false
+ init {
+ communalWidgetAllowlist =
+ if (communalRepository.isCommunalEnabled) getWidgetAllowlist() else emptyList()
+ }
+
// Widgets that should be rendered in communal mode.
private val widgets: HashMap<Int, CommunalAppWidgetInfo> = hashMapOf()
@@ -129,6 +144,18 @@ constructor(
return@map addWidget(providerInfo)
}
+ private fun getWidgetAllowlist(): List<CommunalWidgetMetadata> {
+ val componentNames =
+ applicationContext.resources.getStringArray(R.array.config_communalWidgetAllowlist)
+ return componentNames.mapIndexed { index, name ->
+ CommunalWidgetMetadata(
+ componentName = name,
+ priority = componentNames.size - index,
+ sizes = listOf(CommunalContentSize.HALF)
+ )
+ }
+ }
+
private fun startListening() {
if (isHostListening) {
return
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalContentSize.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalContentSize.kt
new file mode 100644
index 000000000000..0bd7d86c972d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalContentSize.kt
@@ -0,0 +1,8 @@
+package com.android.systemui.communal.shared
+
+/** Supported sizes for communal content in the layout grid. */
+enum class CommunalContentSize {
+ FULL,
+ HALF,
+ THIRD,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
index e7f835f7b858..c3aaef76cb2f 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
@@ -1,5 +1,6 @@
package com.android.systemui.deviceentry
+import com.android.systemui.deviceentry.data.repository.DeviceEntryHapticsRepositoryModule
import com.android.systemui.deviceentry.data.repository.DeviceEntryRepositoryModule
import dagger.Module
@@ -7,6 +8,7 @@ import dagger.Module
includes =
[
DeviceEntryRepositoryModule::class,
+ DeviceEntryHapticsRepositoryModule::class,
],
)
object DeviceEntryModule
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepository.kt
new file mode 100644
index 000000000000..1458404446e6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsRepository.kt
@@ -0,0 +1,72 @@
+/*
+ * 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.deviceentry.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/** Interface for classes that can access device-entry haptics application state. */
+interface DeviceEntryHapticsRepository {
+ /**
+ * Whether a successful biometric haptic has been requested. Has not yet been handled if true.
+ */
+ val successHapticRequest: Flow<Boolean>
+
+ /** Whether an error biometric haptic has been requested. Has not yet been handled if true. */
+ val errorHapticRequest: Flow<Boolean>
+
+ fun requestSuccessHaptic()
+ fun handleSuccessHaptic()
+ fun requestErrorHaptic()
+ fun handleErrorHaptic()
+}
+
+/** Encapsulates application state for device entry haptics. */
+@SysUISingleton
+class DeviceEntryHapticsRepositoryImpl @Inject constructor() : DeviceEntryHapticsRepository {
+ private val _successHapticRequest = MutableStateFlow(false)
+ override val successHapticRequest: Flow<Boolean> = _successHapticRequest.asStateFlow()
+
+ private val _errorHapticRequest = MutableStateFlow(false)
+ override val errorHapticRequest: Flow<Boolean> = _errorHapticRequest.asStateFlow()
+
+ override fun requestSuccessHaptic() {
+ _successHapticRequest.value = true
+ }
+
+ override fun handleSuccessHaptic() {
+ _successHapticRequest.value = false
+ }
+
+ override fun requestErrorHaptic() {
+ _errorHapticRequest.value = true
+ }
+
+ override fun handleErrorHaptic() {
+ _errorHapticRequest.value = false
+ }
+}
+
+@Module
+interface DeviceEntryHapticsRepositoryModule {
+ @Binds fun repository(impl: DeviceEntryHapticsRepositoryImpl): DeviceEntryHapticsRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt
new file mode 100644
index 000000000000..53d6f737af8d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt
@@ -0,0 +1,133 @@
+/*
+ * 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.deviceentry.domain.interactor
+
+import com.android.keyguard.logging.BiometricUnlockLogger
+import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.data.repository.DeviceEntryHapticsRepository
+import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor
+import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.util.kotlin.sample
+import com.android.systemui.util.time.SystemClock
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.combineTransform
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+
+/**
+ * Business logic for device entry haptic events. Determines whether the haptic should play. In
+ * particular, there are extra guards for whether device entry error and successes hatpics should
+ * play when the physical fingerprint sensor is located on the power button.
+ */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class DeviceEntryHapticsInteractor
+@Inject
+constructor(
+ private val repository: DeviceEntryHapticsRepository,
+ fingerprintPropertyRepository: FingerprintPropertyRepository,
+ biometricSettingsRepository: BiometricSettingsRepository,
+ keyEventInteractor: KeyEventInteractor,
+ powerInteractor: PowerInteractor,
+ private val systemClock: SystemClock,
+ private val logger: BiometricUnlockLogger,
+) {
+ private val powerButtonSideFpsEnrolled =
+ combineTransform(
+ fingerprintPropertyRepository.sensorType,
+ biometricSettingsRepository.isFingerprintEnrolledAndEnabled,
+ ) { sensorType, enrolledAndEnabled ->
+ if (sensorType == FingerprintSensorType.POWER_BUTTON) {
+ emit(enrolledAndEnabled)
+ } else {
+ emit(false)
+ }
+ }
+ .distinctUntilChanged()
+ private val powerButtonDown: Flow<Boolean> = keyEventInteractor.isPowerButtonDown
+ private val lastPowerButtonWakeup: Flow<Long> =
+ powerInteractor.detailedWakefulness
+ .filter { it.isAwakeFrom(WakeSleepReason.POWER_BUTTON) }
+ .map { systemClock.uptimeMillis() }
+ .onStart {
+ // If the power button hasn't been pressed, we still want this to evaluate to true:
+ // `uptimeMillis() - lastPowerButtonWakeup > recentPowerButtonPressThresholdMs`
+ emit(recentPowerButtonPressThresholdMs * -1L - 1L)
+ }
+
+ val playSuccessHaptic: Flow<Boolean> =
+ repository.successHapticRequest
+ .filter { it }
+ .sample(
+ combine(
+ powerButtonSideFpsEnrolled,
+ powerButtonDown,
+ lastPowerButtonWakeup,
+ ::Triple
+ )
+ )
+ .map { (sideFpsEnrolled, powerButtonDown, lastPowerButtonWakeup) ->
+ val sideFpsAllowsHaptic =
+ !powerButtonDown &&
+ systemClock.uptimeMillis() - lastPowerButtonWakeup >
+ recentPowerButtonPressThresholdMs
+ val allowHaptic = !sideFpsEnrolled || sideFpsAllowsHaptic
+ if (!allowHaptic) {
+ logger.d("Skip success haptic. Recent power button press or button is down.")
+ handleSuccessHaptic() // immediately handle, don't vibrate
+ }
+ allowHaptic
+ }
+ val playErrorHaptic: Flow<Boolean> =
+ repository.errorHapticRequest
+ .filter { it }
+ .sample(combine(powerButtonSideFpsEnrolled, powerButtonDown, ::Pair))
+ .map { (sideFpsEnrolled, powerButtonDown) ->
+ val allowHaptic = !sideFpsEnrolled || !powerButtonDown
+ if (!allowHaptic) {
+ logger.d("Skip error haptic. Power button is down.")
+ handleErrorHaptic() // immediately handle, don't vibrate
+ }
+ allowHaptic
+ }
+
+ fun vibrateSuccess() {
+ repository.requestSuccessHaptic()
+ }
+
+ fun vibrateError() {
+ repository.requestErrorHaptic()
+ }
+
+ fun handleSuccessHaptic() {
+ repository.handleSuccessHaptic()
+ }
+
+ fun handleErrorHaptic() {
+ repository.handleErrorHaptic()
+ }
+
+ private val recentPowerButtonPressThresholdMs = 400L
+}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java b/packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java
index 5eb9808ebd7c..9c13a8c82b11 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java
@@ -18,6 +18,7 @@ package com.android.systemui.doze;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.doze.dagger.DozeScope;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import javax.inject.Inject;
@@ -28,16 +29,19 @@ import javax.inject.Inject;
public class DozeAuthRemover implements DozeMachine.Part {
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final SelectedUserInteractor mSelectedUserInteractor;
@Inject
- public DozeAuthRemover(KeyguardUpdateMonitor keyguardUpdateMonitor) {
+ public DozeAuthRemover(KeyguardUpdateMonitor keyguardUpdateMonitor,
+ SelectedUserInteractor selectedUserInteractor) {
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mSelectedUserInteractor = selectedUserInteractor;
}
@Override
public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
if (newState == DozeMachine.State.DOZE || newState == DozeMachine.State.DOZE_AOD) {
- int currentUser = KeyguardUpdateMonitor.getCurrentUser();
+ int currentUser = mSelectedUserInteractor.getSelectedUserId();
if (mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(currentUser)) {
mKeyguardUpdateMonitor.clearBiometricRecognized();
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
index 7da2cf150147..ba579188a8c9 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
@@ -31,13 +31,13 @@ import android.view.Display;
import androidx.annotation.Nullable;
-import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.UdfpsController;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.doze.dagger.WrappedService;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.wakelock.SettableWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
@@ -81,6 +81,7 @@ public class DozeScreenState implements DozeMachine.Part {
@Nullable private UdfpsController mUdfpsController;
private final DozeLog mDozeLog;
private final DozeScreenBrightness mDozeScreenBrightness;
+ private final SelectedUserInteractor mSelectedUserInteractor;
private int mPendingScreenState = Display.STATE_UNKNOWN;
private SettableWakeLock mWakeLock;
@@ -95,7 +96,8 @@ public class DozeScreenState implements DozeMachine.Part {
AuthController authController,
Provider<UdfpsController> udfpsControllerProvider,
DozeLog dozeLog,
- DozeScreenBrightness dozeScreenBrightness) {
+ DozeScreenBrightness dozeScreenBrightness,
+ SelectedUserInteractor selectedUserInteractor) {
mDozeService = service;
mHandler = handler;
mParameters = parameters;
@@ -105,6 +107,7 @@ public class DozeScreenState implements DozeMachine.Part {
mUdfpsControllerProvider = udfpsControllerProvider;
mDozeLog = dozeLog;
mDozeScreenBrightness = dozeScreenBrightness;
+ mSelectedUserInteractor = selectedUserInteractor;
updateUdfpsController();
if (mUdfpsController == null) {
@@ -113,7 +116,7 @@ public class DozeScreenState implements DozeMachine.Part {
}
private void updateUdfpsController() {
- if (mAuthController.isUdfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser())) {
+ if (mAuthController.isUdfpsEnrolled(mSelectedUserInteractor.getSelectedUserId())) {
mUdfpsController = mUdfpsControllerProvider.get();
} else {
mUdfpsController = null;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 07efbfef732b..319494252786 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -48,12 +48,11 @@ import com.android.internal.R;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
-import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.plugins.SensorManagerPlugin;
-import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.policy.DevicePostureController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.settings.SecureSettings;
@@ -101,7 +100,7 @@ public class DozeSensors {
private final SecureSettings mSecureSettings;
private final DevicePostureController mDevicePostureController;
private final AuthController mAuthController;
- private final UserTracker mUserTracker;
+ private final SelectedUserInteractor mSelectedUserInteractor;
private final boolean mScreenOffUdfpsEnabled;
// Sensors
@@ -158,7 +157,7 @@ public class DozeSensors {
SecureSettings secureSettings,
AuthController authController,
DevicePostureController devicePostureController,
- UserTracker userTracker
+ SelectedUserInteractor selectedUserInteractor
) {
mSensorManager = sensorManager;
mConfig = config;
@@ -171,15 +170,15 @@ public class DozeSensors {
mProximitySensor.setTag(TAG);
mSelectivelyRegisterProxSensors = dozeParameters.getSelectivelyRegisterSensorsUsingProx();
mListeningProxSensors = !mSelectivelyRegisterProxSensors;
+ mSelectedUserInteractor = selectedUserInteractor;
mScreenOffUdfpsEnabled =
- config.screenOffUdfpsEnabled(KeyguardUpdateMonitor.getCurrentUser());
+ config.screenOffUdfpsEnabled(mSelectedUserInteractor.getSelectedUserId());
mDevicePostureController = devicePostureController;
mDevicePosture = mDevicePostureController.getDevicePosture();
mAuthController = authController;
- mUserTracker = userTracker;
mUdfpsEnrolled =
- mAuthController.isUdfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser());
+ mAuthController.isUdfpsEnrolled(mSelectedUserInteractor.getSelectedUserId());
mAuthController.addCallback(mAuthControllerCallback);
mTriggerSensors = new TriggerSensor[] {
new TriggerSensor(
@@ -255,7 +254,8 @@ public class DozeSensors {
new SensorManagerPlugin.Sensor(TYPE_WAKE_DISPLAY),
Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE,
mConfig.wakeScreenGestureAvailable()
- && mConfig.alwaysOnEnabled(mUserTracker.getUserId()),
+ && mConfig.alwaysOnEnabled(
+ mSelectedUserInteractor.getSelectedUserId(true)),
DozeLog.REASON_SENSOR_WAKE_UP_PRESENCE,
false /* reports touch coordinates */,
false /* touchscreen */
@@ -296,12 +296,13 @@ public class DozeSensors {
private boolean udfpsLongPressConfigured() {
return mUdfpsEnrolled
- && (mConfig.alwaysOnEnabled(mUserTracker.getUserId()) || mScreenOffUdfpsEnabled);
+ && (mConfig.alwaysOnEnabled(mSelectedUserInteractor.getSelectedUserId(true))
+ || mScreenOffUdfpsEnabled);
}
private boolean quickPickUpConfigured() {
return mUdfpsEnrolled
- && mConfig.quickPickupSensorEnabled(KeyguardUpdateMonitor.getCurrentUser());
+ && mConfig.quickPickupSensorEnabled(mSelectedUserInteractor.getSelectedUserId());
}
/**
@@ -471,7 +472,7 @@ public class DozeSensors {
private final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange, Collection<Uri> uris, int flags, int userId) {
- if (userId != mUserTracker.getUserId()) {
+ if (userId != mSelectedUserInteractor.getSelectedUserId(true)) {
return;
}
for (TriggerSensor s : mTriggerSensors) {
@@ -697,13 +698,13 @@ public class DozeSensors {
}
protected boolean enabledBySetting() {
- if (!mConfig.enabled(mUserTracker.getUserId())) {
+ if (!mConfig.enabled(mSelectedUserInteractor.getSelectedUserId(true))) {
return false;
} else if (TextUtils.isEmpty(mSetting)) {
return true;
}
return mSecureSettings.getIntForUser(mSetting, mSettingDefault ? 1 : 0,
- mUserTracker.getUserId()) != 0;
+ mSelectedUserInteractor.getSelectedUserId(true)) != 0;
}
@Override
@@ -873,7 +874,7 @@ public class DozeSensors {
private void updateUdfpsEnrolled() {
mUdfpsEnrolled = mAuthController.isUdfpsEnrolled(
- KeyguardUpdateMonitor.getCurrentUser());
+ mSelectedUserInteractor.getSelectedUserId());
for (TriggerSensor sensor : mTriggerSensors) {
if (REASON_SENSOR_QUICK_PICKUP == sensor.mPulseReason) {
sensor.setConfigured(quickPickUpConfigured());
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 85272a609588..795c3d4528c5 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -50,6 +50,7 @@ import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.Assert;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.ProximityCheck;
@@ -102,6 +103,7 @@ public class DozeTriggers implements DozeMachine.Part {
private final AuthController mAuthController;
private final KeyguardStateController mKeyguardStateController;
private final UserTracker mUserTracker;
+ private final SelectedUserInteractor mSelectedUserInteractor;
private final UiEventLogger mUiEventLogger;
private long mNotificationPulseTime;
@@ -201,7 +203,8 @@ public class DozeTriggers implements DozeMachine.Part {
SessionTracker sessionTracker,
KeyguardStateController keyguardStateController,
DevicePostureController devicePostureController,
- UserTracker userTracker) {
+ UserTracker userTracker,
+ SelectedUserInteractor selectedUserInteractor) {
mContext = context;
mDozeHost = dozeHost;
mConfig = config;
@@ -213,7 +216,7 @@ public class DozeTriggers implements DozeMachine.Part {
mDozeSensors = new DozeSensors(mContext.getResources(), mSensorManager, dozeParameters,
config, wakeLock, this::onSensor, this::onProximityFar, dozeLog, proximitySensor,
- secureSettings, authController, devicePostureController, userTracker);
+ secureSettings, authController, devicePostureController, selectedUserInteractor);
mDockManager = dockManager;
mProxCheck = proxCheck;
mDozeLog = dozeLog;
@@ -222,6 +225,7 @@ public class DozeTriggers implements DozeMachine.Part {
mUiEventLogger = uiEventLogger;
mKeyguardStateController = keyguardStateController;
mUserTracker = userTracker;
+ mSelectedUserInteractor = selectedUserInteractor;
}
@Override
@@ -246,7 +250,7 @@ public class DozeTriggers implements DozeMachine.Part {
return;
}
mNotificationPulseTime = SystemClock.elapsedRealtime();
- if (!mConfig.pulseOnNotificationEnabled(mUserTracker.getUserId())) {
+ if (!mConfig.pulseOnNotificationEnabled(mSelectedUserInteractor.getSelectedUserId(true))) {
runIfNotNull(onPulseSuppressedListener);
mDozeLog.tracePulseDropped("pulseOnNotificationsDisabled");
return;
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 081618ef9d63..ecd8b0e7fd4e 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -180,6 +180,10 @@ object Flags {
@JvmField
val NEW_AOD_TRANSITION = unreleasedFlag("new_aod_transition", teamfood = true)
+ // TODO(b/305984787):
+ @JvmField
+ val REFACTOR_GETCURRENTUSER = unreleasedFlag("refactor_getcurrentuser", teamfood = true)
+
/** Flag to control the migration of face auth to modern architecture. */
// TODO(b/262838215): Tracking bug
@JvmField val FACE_AUTH_REFACTOR = releasedFlag("face_auth_refactor")
@@ -419,7 +423,7 @@ object Flags {
releasedFlag("incompatible_charging_battery_icon")
// TODO(b/293585143): Tracking Bug
- val INSTANT_TETHER = unreleasedFlag("instant_tether")
+ val INSTANT_TETHER = unreleasedFlag("instant_tether", teamfood = true)
// TODO(b/294588085): Tracking Bug
val WIFI_SECONDARY_NETWORKS = releasedFlag("wifi_secondary_networks")
@@ -769,8 +773,7 @@ object Flags {
// TODO(b/283740863): Tracking Bug
@JvmField
- val ENABLE_NEW_PRIVACY_DIALOG =
- unreleasedFlag("enable_new_privacy_dialog", teamfood = true)
+ val ENABLE_NEW_PRIVACY_DIALOG = releasedFlag("enable_new_privacy_dialog")
// TODO(b/289573946): Tracking Bug
@JvmField val PRECOMPUTED_TEXT = unreleasedFlag("precomputed_text", teamfood = true)
@@ -796,8 +799,7 @@ object Flags {
/** TODO(b/296223317): Enables the new keyguard presentation containing a clock. */
@JvmField
- val ENABLE_CLOCK_KEYGUARD_PRESENTATION =
- unreleasedFlag("enable_clock_keyguard_presentation", teamfood = true)
+ val ENABLE_CLOCK_KEYGUARD_PRESENTATION = releasedFlag("enable_clock_keyguard_presentation")
/** Enable the Compose implementation of the PeopleSpaceActivity. */
@JvmField
@@ -817,8 +819,7 @@ object Flags {
// TODO(b/287205379): Tracking bug
@JvmField
- val QS_CONTAINER_GRAPH_OPTIMIZER = unreleasedFlag( "qs_container_graph_optimizer",
- teamfood = true)
+ val QS_CONTAINER_GRAPH_OPTIMIZER = releasedFlag( "qs_container_graph_optimizer")
/** Enable showing a dialog when clicking on Quick Settings bluetooth tile. */
@JvmField
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index c6c1f79c7113..5cc2e0aa811c 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -134,6 +134,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.window.StatusBarWindowController;
import com.android.systemui.telephony.TelephonyListenerManager;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.EmergencyDialerConstants;
import com.android.systemui.util.RingerModeTracker;
import com.android.systemui.util.settings.GlobalSettings;
@@ -195,6 +196,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private final IDreamManager mDreamManager;
private final DevicePolicyManager mDevicePolicyManager;
private final LockPatternUtils mLockPatternUtils;
+ private final SelectedUserInteractor mSelectedUserInteractor;
private final TelephonyListenerManager mTelephonyListenerManager;
private final KeyguardStateController mKeyguardStateController;
private final BroadcastDispatcher mBroadcastDispatcher;
@@ -364,7 +366,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
PackageManager packageManager,
ShadeController shadeController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
- DialogLaunchAnimator dialogLaunchAnimator) {
+ DialogLaunchAnimator dialogLaunchAnimator,
+ SelectedUserInteractor selectedUserInteractor) {
mContext = context;
mWindowManagerFuncs = windowManagerFuncs;
mAudioManager = audioManager;
@@ -399,6 +402,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mShadeController = shadeController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mDialogLaunchAnimator = dialogLaunchAnimator;
+ mSelectedUserInteractor = selectedUserInteractor;
// receive broadcasts
IntentFilter filter = new IntentFilter();
@@ -713,7 +717,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mUiEventLogger,
mShadeController,
mKeyguardUpdateMonitor,
- mLockPatternUtils);
+ mLockPatternUtils,
+ mSelectedUserInteractor);
dialog.setOnDismissListener(this);
dialog.setOnShowListener(this);
@@ -2222,6 +2227,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private GestureDetector mGestureDetector;
private final ShadeController mShadeController;
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private SelectedUserInteractor mSelectedUserInteractor;
private LockPatternUtils mLockPatternUtils;
private float mWindowDimAmount;
@@ -2300,7 +2306,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
UiEventLogger uiEventLogger,
ShadeController shadeController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
- LockPatternUtils lockPatternUtils) {
+ LockPatternUtils lockPatternUtils,
+ SelectedUserInteractor selectedUserInteractor) {
// We set dismissOnDeviceLock to false because we have a custom broadcast receiver to
// dismiss this dialog when the device is locked.
super(context, themeRes, false /* dismissOnDeviceLock */);
@@ -2321,6 +2328,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mGestureDetector = new GestureDetector(mContext, mGestureListener);
+ mSelectedUserInteractor = selectedUserInteractor;
}
@Override
@@ -2453,10 +2461,10 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
}
// If user entered from the lock screen and smart lock was enabled, disable it
- int user = KeyguardUpdateMonitor.getCurrentUser();
+ int user = mSelectedUserInteractor.getSelectedUserId();
boolean userHasTrust = mKeyguardUpdateMonitor.getUserHasTrust(user);
if (mKeyguardShowing && userHasTrust) {
- mLockPatternUtils.requireCredentialEntry(KeyguardUpdateMonitor.getCurrentUser());
+ mLockPatternUtils.requireCredentialEntry(user);
showSmartLockDisabledMessage();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt
index 20d99d1e75fb..7b33e11a0c9c 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt
@@ -32,6 +32,8 @@ data class SliderHapticFeedbackConfig(
@FloatRange(from = 0.0, to = 1.0) val additionalVelocityMaxBump: Float = 0.15f,
/** Additional time delta to wait between drag texture vibrations */
@FloatRange(from = 0.0) val deltaMillisForDragInterval: Float = 0f,
+ /** Progress threshold beyond which a new drag texture is delivered */
+ @FloatRange(from = 0.0, to = 1.0) val deltaProgressForDragThreshold: Float = 0.015f,
/** Number of low ticks in a drag texture composition. This is not expected to change */
val numberOfLowTicks: Int = 5,
/** Maximum velocity allowed for vibration scaling. This is not expected to change. */
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt
index e6de156de0c4..f313fb3eef0f 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt
@@ -46,6 +46,8 @@ class SliderHapticFeedbackProvider(
private val positionAccelerateInterpolator =
AccelerateInterpolator(config.progressInterpolatorFactor)
private var dragTextureLastTime = clock.elapsedRealtime()
+ var dragTextureLastProgress = -1f
+ private set
private val lowTickDurationMs =
vibratorHelper.getPrimitiveDurations(VibrationEffect.Composition.PRIMITIVE_LOW_TICK)[0]
private var hasVibratedAtLowerBookend = false
@@ -91,6 +93,9 @@ class SliderHapticFeedbackProvider(
val elapsedSinceLastDrag = currentTime - dragTextureLastTime
if (elapsedSinceLastDrag < thresholdUntilNextDragCallMillis) return
+ val deltaProgress = abs(normalizedSliderProgress - dragTextureLastProgress)
+ if (deltaProgress < config.deltaProgressForDragThreshold) return
+
val velocityInterpolated =
velocityAccelerateInterpolator.getInterpolation(
min(absoluteVelocity / config.maxVelocityToScale, 1f)
@@ -116,11 +121,14 @@ class SliderHapticFeedbackProvider(
}
vibratorHelper.vibrate(composition.compose(), VIBRATION_ATTRIBUTES_PIPELINING)
dragTextureLastTime = currentTime
+ dragTextureLastProgress = normalizedSliderProgress
}
override fun onHandleAcquiredByTouch() {}
- override fun onHandleReleasedFromTouch() {}
+ override fun onHandleReleasedFromTouch() {
+ dragTextureLastProgress = -1f
+ }
override fun onLowerBookend() {
if (!hasVibratedAtLowerBookend) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index b45613e0e182..47798955b43d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -35,6 +35,8 @@ import static android.view.WindowManager.TransitionFlags;
import static android.view.WindowManager.TransitionOldType;
import static android.view.WindowManager.TransitionType;
+import static com.android.systemui.flags.Flags.REFACTOR_GETCURRENTUSER;
+
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
@@ -76,13 +78,13 @@ import com.android.systemui.SystemUIApplication;
import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
-import com.android.systemui.power.shared.model.ScreenPowerState;
import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier;
import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindViewBinder;
import com.android.systemui.keyguard.ui.binder.WindowManagerLockscreenVisibilityViewBinder;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSurfaceBehindViewModel;
import com.android.systemui.keyguard.ui.viewmodel.WindowManagerLockscreenVisibilityViewModel;
import com.android.systemui.power.domain.interactor.PowerInteractor;
+import com.android.systemui.power.shared.model.ScreenPowerState;
import com.android.systemui.settings.DisplayTracker;
import com.android.wm.shell.transition.ShellTransitions;
import com.android.wm.shell.transition.Transitions;
@@ -599,11 +601,18 @@ public class KeyguardService extends Service {
mKeyguardViewMediator.setSwitchingUser(switching);
}
+ /**
+ * @deprecated This binder call is not listened to anymore. Instead the current user is
+ * tracked in SelectedUserInteractor.getSelectedUserId()
+ */
@Override // Binder interface
+ @Deprecated
public void setCurrentUser(int userId) {
- trace("setCurrentUser userId=" + userId);
+ trace("Deprecated/NOT USED: setCurrentUser userId=" + userId);
checkPermission();
- mKeyguardViewMediator.setCurrentUser(userId);
+ if (!mFlags.isEnabled(REFACTOR_GETCURRENTUSER)) {
+ mKeyguardViewMediator.setCurrentUser(userId);
+ }
}
@Override // Binder interface
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index a511713eddd3..119ade48d4f7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -28,6 +28,7 @@ import com.android.keyguard.LockIconViewController
import com.android.keyguard.dagger.KeyguardStatusViewComponent
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder
@@ -44,6 +45,7 @@ import com.android.systemui.res.R
import com.android.systemui.shade.NotificationShadeWindowView
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.KeyguardIndicationController
+import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
import javax.inject.Inject
@@ -72,7 +74,9 @@ constructor(
private val keyguardIndicationController: KeyguardIndicationController,
private val lockIconViewController: LockIconViewController,
private val shadeInteractor: ShadeInteractor,
- private val interactionJankMonitor: InteractionJankMonitor
+ private val interactionJankMonitor: InteractionJankMonitor,
+ private val deviceEntryHapticsInteractor: DeviceEntryHapticsInteractor,
+ private val vibratorHelper: VibratorHelper,
) : CoreStartable {
private var rootViewHandle: DisposableHandle? = null
@@ -143,6 +147,8 @@ constructor(
shadeInteractor,
{ keyguardStatusViewController!!.getClockController() },
interactionJankMonitor,
+ deviceEntryHapticsInteractor,
+ vibratorHelper,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 39742a02000b..e893c6305dee 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -38,6 +38,7 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
import static com.android.systemui.DejankUtils.whitelistIpcs;
+import static com.android.systemui.flags.Flags.REFACTOR_GETCURRENTUSER;
import static com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel.DREAMING_ANIMATION_DURATION_MS;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
@@ -163,6 +164,7 @@ import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.settings.SecureSettings;
@@ -617,6 +619,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
public void onUserSwitching(int userId) {
if (DEBUG) Log.d(TAG, String.format("onUserSwitching %d", userId));
synchronized (KeyguardViewMediator.this) {
+ if (mFeatureFlags.isEnabled(REFACTOR_GETCURRENTUSER)) {
+ notifyTrustedChangedLocked(mUpdateMonitor.getUserHasTrust(userId));
+ }
resetKeyguardDonePendingLocked();
dismiss(null /* callback */, null /* message */);
adjustStatusBarLocked();
@@ -742,7 +747,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
@Override
public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) {
- final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
+ final int currentUser = mSelectedUserInteractor.getSelectedUserId();
if (mLockPatternUtils.isSecure(currentUser)) {
mLockPatternUtils.getDevicePolicyManager().reportFailedBiometricAttempt(
currentUser);
@@ -760,7 +765,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
@Override
public void onTrustChanged(int userId) {
- if (userId == KeyguardUpdateMonitor.getCurrentUser()) {
+ if (userId == mSelectedUserInteractor.getSelectedUserId()) {
synchronized (KeyguardViewMediator.this) {
notifyTrustedChangedLocked(mUpdateMonitor.getUserHasTrust(userId));
}
@@ -769,7 +774,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
@Override
public void onStrongAuthStateChanged(int userId) {
- if (mLockPatternUtils.isUserInLockdown(KeyguardUpdateMonitor.getCurrentUser())) {
+ if (mLockPatternUtils.isUserInLockdown(mSelectedUserInteractor.getSelectedUserId())) {
doKeyguardLocked(null);
}
}
@@ -784,7 +789,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
@Override
public void keyguardDone(int targetUserId) {
- if (targetUserId != KeyguardUpdateMonitor.getCurrentUser()) {
+ if (targetUserId != mSelectedUserInteractor.getSelectedUserId()) {
return;
}
if (DEBUG) Log.d(TAG, "keyguardDone");
@@ -807,7 +812,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
public void keyguardDonePending(int targetUserId) {
Trace.beginSection("KeyguardViewMediator.mViewMediatorCallback#keyguardDonePending");
if (DEBUG) Log.d(TAG, "keyguardDonePending");
- if (targetUserId != KeyguardUpdateMonitor.getCurrentUser()) {
+ if (targetUserId != mSelectedUserInteractor.getSelectedUserId()) {
Trace.endSection();
return;
}
@@ -888,7 +893,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
@Override
public int getBouncerPromptReason() {
- int currentUser = KeyguardUpdateMonitor.getCurrentUser();
+ int currentUser = mSelectedUserInteractor.getSelectedUserId();
boolean trustAgentsEnabled = mUpdateMonitor.isTrustUsuallyManaged(currentUser);
boolean biometricsEnrolled =
mUpdateMonitor.isUnlockingWithBiometricsPossible(currentUser);
@@ -1316,6 +1321,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
private DeviceConfigProxy mDeviceConfig;
private DozeParameters mDozeParameters;
+ private SelectedUserInteractor mSelectedUserInteractor;
private final KeyguardStateController mKeyguardStateController;
private final KeyguardStateController.Callback mKeyguardStateControllerCallback =
@@ -1396,7 +1402,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
@Main CoroutineDispatcher mainDispatcher,
Lazy<DreamingToLockscreenTransitionViewModel> dreamingToLockscreenTransitionViewModel,
SystemPropertiesHelper systemPropertiesHelper,
- Lazy<WindowManagerLockscreenVisibilityManager> wmLockscreenVisibilityManager) {
+ Lazy<WindowManagerLockscreenVisibilityManager> wmLockscreenVisibilityManager,
+ SelectedUserInteractor selectedUserInteractor) {
mContext = context;
mUserTracker = userTracker;
mFalsingCollector = falsingCollector;
@@ -1436,6 +1443,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
mInGestureNavigationMode = QuickStepContract.isGesturalMode(mode);
}));
mDozeParameters = dozeParameters;
+ mSelectedUserInteractor = selectedUserInteractor;
mStatusBarStateController = statusBarStateController;
statusBarStateController.addCallback(this);
@@ -1493,14 +1501,17 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
- KeyguardUpdateMonitor.setCurrentUser(mUserTracker.getUserId());
+ if (!mFeatureFlags.isEnabled(REFACTOR_GETCURRENTUSER)) {
+ KeyguardUpdateMonitor.setCurrentUser(mUserTracker.getUserId());
+ }
// Assume keyguard is showing (unless it's disabled) until we know for sure, unless Keyguard
// is disabled.
if (isKeyguardServiceEnabled()) {
setShowingLocked(!shouldWaitForProvisioning()
&& !mLockPatternUtils.isLockScreenDisabled(
- KeyguardUpdateMonitor.getCurrentUser()), true /* forceCallbacks */);
+ mSelectedUserInteractor.getSelectedUserId()),
+ true /* forceCallbacks */);
} else {
// The system's keyguard is disabled or missing.
setShowingLocked(false /* showing */, true /* forceCallbacks */);
@@ -1622,11 +1633,11 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
// Lock immediately based on setting if secure (user has a pin/pattern/password).
// This also "locks" the device when not secure to provide easy access to the
// camera while preventing unwanted input.
- int currentUser = KeyguardUpdateMonitor.getCurrentUser();
+ int currentUser = mSelectedUserInteractor.getSelectedUserId();
final boolean lockImmediately =
mLockPatternUtils.getPowerButtonInstantlyLocks(currentUser)
|| !mLockPatternUtils.isSecure(currentUser);
- long timeout = getLockTimeout(KeyguardUpdateMonitor.getCurrentUser());
+ long timeout = getLockTimeout(mSelectedUserInteractor.getSelectedUserId());
mLockLater = false;
if (mShowing && !mKeyguardStateController.isKeyguardGoingAway()) {
// If we are going to sleep but the keyguard is showing (and will continue to be
@@ -1807,7 +1818,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
}
private void doKeyguardLaterLocked() {
- long timeout = getLockTimeout(KeyguardUpdateMonitor.getCurrentUser());
+ long timeout = getLockTimeout(mSelectedUserInteractor.getSelectedUserId());
if (timeout == 0) {
doKeyguardLocked(null);
} else {
@@ -1916,7 +1927,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
private void maybeSendUserPresentBroadcast() {
if (mSystemReady && mLockPatternUtils.isLockScreenDisabled(
- KeyguardUpdateMonitor.getCurrentUser())) {
+ mSelectedUserInteractor.getSelectedUserId())) {
// Lock screen is disabled because the user has set the preference to "None".
// In this case, send out ACTION_USER_PRESENT here instead of in
// handleKeyguardDone()
@@ -1925,7 +1936,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
// Skipping the lockscreen because we're not yet provisioned, but we still need to
// notify the StrongAuthTracker that it's now safe to run trust agents, in case the
// user sets a credential later.
- mLockPatternUtils.userPresent(KeyguardUpdateMonitor.getCurrentUser());
+ mLockPatternUtils.userPresent(mSelectedUserInteractor.getSelectedUserId());
}
}
@@ -1966,7 +1977,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
mExternallyEnabled = enabled;
if (!enabled && mShowing) {
- if (mLockPatternUtils.isUserInLockdown(KeyguardUpdateMonitor.getCurrentUser())) {
+ if (mLockPatternUtils.isUserInLockdown(
+ mSelectedUserInteractor.getSelectedUserId())) {
Log.d(TAG, "keyguardEnabled(false) overridden by user lockdown");
return;
}
@@ -2197,7 +2209,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
private void doKeyguardLocked(Bundle options) {
// if another app is disabling us, don't show
if (!mExternallyEnabled
- && !mLockPatternUtils.isUserInLockdown(KeyguardUpdateMonitor.getCurrentUser())) {
+ && !mLockPatternUtils.isUserInLockdown(
+ mSelectedUserInteractor.getSelectedUserId())) {
if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");
mNeedToReshowWhenReenabled = true;
@@ -2253,7 +2266,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
}
boolean forceShow = options != null && options.getBoolean(OPTION_FORCE_SHOW, false);
- if (mLockPatternUtils.isLockScreenDisabled(KeyguardUpdateMonitor.getCurrentUser())
+ if (mLockPatternUtils.isLockScreenDisabled(mSelectedUserInteractor.getSelectedUserId())
&& !lockedOrMissing && !forceShow) {
if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off");
return;
@@ -2384,7 +2397,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
}
public boolean isSecure() {
- return isSecure(KeyguardUpdateMonitor.getCurrentUser());
+ return isSecure(mSelectedUserInteractor.getSelectedUserId());
}
public boolean isSecure(int userId) {
@@ -2605,7 +2618,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
*/
private void handleKeyguardDone() {
Trace.beginSection("KeyguardViewMediator#handleKeyguardDone");
- final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
+ final int currentUser = mSelectedUserInteractor.getSelectedUserId();
mUiBgExecutor.execute(() -> {
if (mLockPatternUtils.isSecure(currentUser)) {
mLockPatternUtils.getDevicePolicyManager().reportKeyguardDismissed(currentUser);
@@ -2631,7 +2644,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
private void sendUserPresentBroadcast() {
synchronized (this) {
if (mBootCompleted) {
- int currentUserId = KeyguardUpdateMonitor.getCurrentUser();
+ int currentUserId = mSelectedUserInteractor.getSelectedUserId();
final UserHandle currentUser = new UserHandle(currentUserId);
final UserManager um = (UserManager) mContext.getSystemService(
Context.USER_SERVICE);
@@ -2679,7 +2692,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
private void playSound(int soundId) {
if (soundId == 0) return;
int lockscreenSoundsEnabled = mSystemSettings.getIntForUser(LOCKSCREEN_SOUNDS_ENABLED, 1,
- KeyguardUpdateMonitor.getCurrentUser());
+ mSelectedUserInteractor.getSelectedUserId());
if (lockscreenSoundsEnabled == 1) {
mLockSounds.stop(mLockSoundStreamId);
@@ -2732,7 +2745,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
*/
private void handleShow(Bundle options) {
Trace.beginSection("KeyguardViewMediator#handleShow");
- final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
+ final int currentUser = mSelectedUserInteractor.getSelectedUserId();
if (mLockPatternUtils.isSecure(currentUser)) {
mLockPatternUtils.getDevicePolicyManager().reportKeyguardSecured(currentUser);
}
@@ -2787,7 +2800,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
* Schedule 4-hour idle timeout for non-strong biometrics when the device is locked
*/
private void scheduleNonStrongBiometricIdleTimeout() {
- final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
+ final int currentUser = mSelectedUserInteractor.getSelectedUserId();
// If unlocking with non-strong (i.e. weak or convenience) biometrics is possible, schedule
// 4hr idle timeout after which non-strong biometrics can't be used to unlock device until
// unlocking with strong biometric or primary auth (i.e. PIN/pattern/password)
@@ -3378,7 +3391,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
if (forceClearFlags) {
try {
mStatusBarService.disableForUser(flags, mStatusBarDisableToken,
- mContext.getPackageName(), mUserTracker.getUserId());
+ mContext.getPackageName(),
+ mSelectedUserInteractor.getSelectedUserId(true));
} catch (RemoteException e) {
Log.d(TAG, "Failed to force clear flags", e);
}
@@ -3405,7 +3419,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
try {
mStatusBarService.disableForUser(flags, mStatusBarDisableToken,
- mContext.getPackageName(), mUserTracker.getUserId());
+ mContext.getPackageName(),
+ mSelectedUserInteractor.getSelectedUserId(true));
} catch (RemoteException e) {
Log.d(TAG, "Failed to set disable flags: " + flags, e);
}
@@ -3728,7 +3743,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
for (int i = size - 1; i >= 0; i--) {
IKeyguardStateCallback callback = mKeyguardStateCallbacks.get(i);
try {
- callback.onShowingStateChanged(showing, KeyguardUpdateMonitor.getCurrentUser());
+ callback.onShowingStateChanged(showing,
+ mSelectedUserInteractor.getSelectedUserId());
} catch (RemoteException e) {
Slog.w(TAG, "Failed to call onShowingStateChanged", e);
if (e instanceof DeadObjectException) {
@@ -3771,10 +3787,11 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
mKeyguardStateCallbacks.add(callback);
try {
callback.onSimSecureStateChanged(mUpdateMonitor.isSimPinSecure());
- callback.onShowingStateChanged(mShowing, KeyguardUpdateMonitor.getCurrentUser());
+ callback.onShowingStateChanged(mShowing,
+ mSelectedUserInteractor.getSelectedUserId());
callback.onInputRestrictedStateChanged(mInputRestricted);
callback.onTrustedChanged(mUpdateMonitor.getUserHasTrust(
- KeyguardUpdateMonitor.getCurrentUser()));
+ mSelectedUserInteractor.getSelectedUserId()));
} catch (RemoteException e) {
Slog.w(TAG, "Failed to call to IKeyguardStateCallback", e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 019d4283a03c..8b93b171d241 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -68,6 +68,7 @@ import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.settings.SecureSettings;
@@ -150,7 +151,8 @@ public class KeyguardModule {
@Main CoroutineDispatcher mainDispatcher,
Lazy<DreamingToLockscreenTransitionViewModel> dreamingToLockscreenTransitionViewModel,
SystemPropertiesHelper systemPropertiesHelper,
- Lazy<WindowManagerLockscreenVisibilityManager> wmLockscreenVisibilityManager) {
+ Lazy<WindowManagerLockscreenVisibilityManager> wmLockscreenVisibilityManager,
+ SelectedUserInteractor selectedUserInteractor) {
return new KeyguardViewMediator(
context,
uiEventLogger,
@@ -194,7 +196,8 @@ public class KeyguardModule {
mainDispatcher,
dreamingToLockscreenTransitionViewModel,
systemPropertiesHelper,
- wmLockscreenVisibilityManager);
+ wmLockscreenVisibilityManager,
+ selectedUserInteractor);
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index a4a312656039..2dc490886c3d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -31,7 +31,6 @@ import com.android.systemui.doze.DozeMachine
import com.android.systemui.doze.DozeTransitionCallback
import com.android.systemui.doze.DozeTransitionListener
import com.android.systemui.dreams.DreamOverlayCallbackController
-import com.android.systemui.keyguard.ScreenLifecycle
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.keyguard.shared.model.DismissAction
@@ -41,9 +40,6 @@ import com.android.systemui.keyguard.shared.model.KeyguardDone
import com.android.systemui.keyguard.shared.model.KeyguardRootViewVisibilityState
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.statusbar.phone.BiometricUnlockController
-import com.android.systemui.statusbar.phone.BiometricUnlockController.WakeAndUnlockMode
-import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
@@ -111,6 +107,8 @@ interface KeyguardRepository {
/** Is the always-on display available to be used? */
val isAodAvailable: Flow<Boolean>
+ fun setAodAvailable(value: Boolean)
+
/**
* Observable for whether we are in doze state.
*
@@ -160,6 +158,8 @@ interface KeyguardRepository {
/** Observable for biometric unlock modes */
val biometricUnlockState: Flow<BiometricUnlockModel>
+ fun setBiometricUnlockState(value: BiometricUnlockModel)
+
/** Approximate location on the screen of the fingerprint sensor. */
val fingerprintSensorLocation: Flow<Point?>
@@ -240,12 +240,9 @@ class KeyguardRepositoryImpl
@Inject
constructor(
statusBarStateController: StatusBarStateController,
- screenLifecycle: ScreenLifecycle,
- biometricUnlockController: BiometricUnlockController,
private val keyguardStateController: KeyguardStateController,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val dozeTransitionListener: DozeTransitionListener,
- private val dozeParameters: DozeParameters,
private val authController: AuthController,
private val dreamOverlayCallbackController: DreamOverlayCallbackController,
@Main private val mainDispatcher: CoroutineDispatcher,
@@ -303,24 +300,12 @@ constructor(
}
.distinctUntilChanged()
- override val isAodAvailable: Flow<Boolean> =
- conflatedCallbackFlow {
- val callback =
- DozeParameters.Callback {
- trySendWithFailureLogging(
- dozeParameters.alwaysOn,
- TAG,
- "updated isAodAvailable"
- )
- }
-
- dozeParameters.addCallback(callback)
- // Adding the callback does not send an initial update.
- trySendWithFailureLogging(dozeParameters.alwaysOn, TAG, "initial isAodAvailable")
+ private val _isAodAvailable = MutableStateFlow(false)
+ override val isAodAvailable: Flow<Boolean> = _isAodAvailable.asStateFlow()
- awaitClose { dozeParameters.removeCallback(callback) }
- }
- .distinctUntilChanged()
+ override fun setAodAvailable(value: Boolean) {
+ _isAodAvailable.value = value
+ }
override val isKeyguardOccluded: Flow<Boolean> =
conflatedCallbackFlow {
@@ -506,30 +491,11 @@ constructor(
statusBarStateIntToObject(statusBarStateController.state)
)
- override val biometricUnlockState: Flow<BiometricUnlockModel> = conflatedCallbackFlow {
- fun dispatchUpdate() {
- trySendWithFailureLogging(
- biometricModeIntToObject(biometricUnlockController.mode),
- TAG,
- "biometric mode"
- )
- }
-
- val callback =
- object : BiometricUnlockController.BiometricUnlockEventsListener {
- override fun onModeChanged(@WakeAndUnlockMode mode: Int) {
- dispatchUpdate()
- }
-
- override fun onResetMode() {
- dispatchUpdate()
- }
- }
-
- biometricUnlockController.addListener(callback)
- dispatchUpdate()
+ private val _biometricUnlockState = MutableStateFlow(BiometricUnlockModel.NONE)
+ override val biometricUnlockState = _biometricUnlockState.asStateFlow()
- awaitClose { biometricUnlockController.removeListener(callback) }
+ override fun setBiometricUnlockState(value: BiometricUnlockModel) {
+ _biometricUnlockState.value = value
}
override val fingerprintSensorLocation: Flow<Point?> = conflatedCallbackFlow {
@@ -662,20 +628,6 @@ constructor(
}
}
- private fun biometricModeIntToObject(@WakeAndUnlockMode value: Int): BiometricUnlockModel {
- return when (value) {
- 0 -> BiometricUnlockModel.NONE
- 1 -> BiometricUnlockModel.WAKE_AND_UNLOCK
- 2 -> BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING
- 3 -> BiometricUnlockModel.SHOW_BOUNCER
- 4 -> BiometricUnlockModel.ONLY_WAKE
- 5 -> BiometricUnlockModel.UNLOCK_COLLAPSING
- 6 -> BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM
- 7 -> BiometricUnlockModel.DISMISS_BOUNCER
- else -> throw IllegalArgumentException("Invalid BiometricUnlockModel value: $value")
- }
- }
-
private fun dozeMachineStateToModel(state: DozeMachine.State): DozeStateModel {
return when (state) {
DozeMachine.State.UNINITIALIZED -> DozeStateModel.UNINITIALIZED
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BiometricUnlockInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BiometricUnlockInteractor.kt
new file mode 100644
index 000000000000..cb003a760b26
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BiometricUnlockInteractor.kt
@@ -0,0 +1,42 @@
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_DISMISS_BOUNCER
+import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_NONE
+import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_ONLY_WAKE
+import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_SHOW_BOUNCER
+import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_UNLOCK_COLLAPSING
+import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK
+import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM
+import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
+import com.android.systemui.statusbar.phone.BiometricUnlockController.WakeAndUnlockMode
+import javax.inject.Inject
+
+@SysUISingleton
+class BiometricUnlockInteractor
+@Inject
+constructor(
+ private val keyguardRepository: KeyguardRepository,
+) {
+
+ fun setBiometricUnlockState(@WakeAndUnlockMode unlockStateInt: Int) {
+ val state = biometricModeIntToObject(unlockStateInt)
+ keyguardRepository.setBiometricUnlockState(state)
+ }
+
+ private fun biometricModeIntToObject(@WakeAndUnlockMode value: Int): BiometricUnlockModel {
+ return when (value) {
+ MODE_NONE -> BiometricUnlockModel.NONE
+ MODE_WAKE_AND_UNLOCK -> BiometricUnlockModel.WAKE_AND_UNLOCK
+ MODE_WAKE_AND_UNLOCK_PULSING -> BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING
+ MODE_SHOW_BOUNCER -> BiometricUnlockModel.SHOW_BOUNCER
+ MODE_ONLY_WAKE -> BiometricUnlockModel.ONLY_WAKE
+ MODE_UNLOCK_COLLAPSING -> BiometricUnlockModel.UNLOCK_COLLAPSING
+ MODE_WAKE_AND_UNLOCK_FROM_DREAM -> BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM
+ MODE_DISMISS_BOUNCER -> BiometricUnlockModel.DISMISS_BOUNCER
+ else -> throw IllegalArgumentException("Invalid BiometricUnlockModel value: $value")
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DozeInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DozeInteractor.kt
index 0c898befe6a0..af1802fac3cc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DozeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DozeInteractor.kt
@@ -28,6 +28,10 @@ constructor(
private val keyguardRepository: KeyguardRepository,
) {
+ fun setAodAvailable(value: Boolean) {
+ keyguardRepository.setAodAvailable(value)
+ }
+
fun setIsDozing(isDozing: Boolean) {
keyguardRepository.setIsDozing(isDozing)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index ad2ec69b77ce..f1649fb2851e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -18,7 +18,6 @@ package com.android.systemui.keyguard.domain.interactor
import android.animation.ValueAnimator
import com.android.keyguard.KeyguardSecurityModel
-import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.flags.FeatureFlags
@@ -27,19 +26,20 @@ import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepositor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardSurfaceBehindModel
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.kotlin.Utils.Companion.toQuad
import com.android.systemui.util.kotlin.Utils.Companion.toQuint
import com.android.systemui.util.kotlin.Utils.Companion.toTriple
import com.android.systemui.util.kotlin.sample
import com.android.wm.shell.animation.Interpolators
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
-import javax.inject.Inject
-import kotlin.time.Duration.Companion.milliseconds
@SysUISingleton
class FromPrimaryBouncerTransitionInteractor
@@ -51,6 +51,7 @@ constructor(
private val keyguardInteractor: KeyguardInteractor,
private val flags: FeatureFlags,
private val keyguardSecurityModel: KeyguardSecurityModel,
+ private val selectedUserInteractor: SelectedUserInteractor,
private val powerInteractor: PowerInteractor,
) :
TransitionInteractor(
@@ -132,7 +133,7 @@ constructor(
.collect {
(
isBouncerShowing,
- isAwake,
+ isAwake,
lastStartedTransitionStep,
occluded,
isActiveDreamLockscreenHosted) ->
@@ -162,8 +163,7 @@ constructor(
),
::toQuad
)
- .collect {
- (isBouncerShowing, isAsleep, lastStartedTransitionStep, isAodAvailable)
+ .collect { (isBouncerShowing, isAsleep, lastStartedTransitionStep, isAodAvailable)
->
if (
!isBouncerShowing &&
@@ -181,25 +181,24 @@ constructor(
private fun listenForPrimaryBouncerToDreamingLockscreenHosted() {
scope.launch {
keyguardInteractor.primaryBouncerShowing
- .sample(
- combine(
- keyguardInteractor.isActiveDreamLockscreenHosted,
- transitionInteractor.startedKeyguardTransitionStep,
- ::Pair
- ),
- ::toTriple
- )
- .collect { (isBouncerShowing,
- isActiveDreamLockscreenHosted,
- lastStartedTransitionStep) ->
- if (
- !isBouncerShowing &&
- isActiveDreamLockscreenHosted &&
- lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER
- ) {
- startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
- }
+ .sample(
+ combine(
+ keyguardInteractor.isActiveDreamLockscreenHosted,
+ transitionInteractor.startedKeyguardTransitionStep,
+ ::Pair
+ ),
+ ::toTriple
+ )
+ .collect {
+ (isBouncerShowing, isActiveDreamLockscreenHosted, lastStartedTransitionStep) ->
+ if (
+ !isBouncerShowing &&
+ isActiveDreamLockscreenHosted &&
+ lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER
+ ) {
+ startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
}
+ }
}
}
@@ -221,7 +220,7 @@ constructor(
) {
val securityMode =
keyguardSecurityModel.getSecurityMode(
- KeyguardUpdateMonitor.getCurrentUser()
+ selectedUserInteractor.getSelectedUserId()
)
// IME for password requires a slightly faster animation
val duration =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt
index cab69285cc94..628e912253eb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt
@@ -24,7 +24,7 @@ import com.android.systemui.keyguard.data.repository.TrustRepository
import com.android.systemui.keyguard.shared.model.DismissAction
import com.android.systemui.keyguard.shared.model.KeyguardDone
import com.android.systemui.power.domain.interactor.PowerInteractor
-import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.kotlin.Utils.Companion.toQuad
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
@@ -40,11 +40,11 @@ class KeyguardDismissInteractor
@Inject
constructor(
trustRepository: TrustRepository,
- val keyguardRepository: KeyguardRepository,
- val primaryBouncerInteractor: PrimaryBouncerInteractor,
- val alternateBouncerInteractor: AlternateBouncerInteractor,
- val powerInteractor: PowerInteractor,
- val userInteractor: UserInteractor,
+ private val keyguardRepository: KeyguardRepository,
+ primaryBouncerInteractor: PrimaryBouncerInteractor,
+ alternateBouncerInteractor: AlternateBouncerInteractor,
+ powerInteractor: PowerInteractor,
+ private val selectedUserInteractor: SelectedUserInteractor,
) {
/*
* Updates when a biometric has authenticated the device and is requesting to dismiss
@@ -82,7 +82,7 @@ constructor(
*/
private val primaryAuthenticated: Flow<Unit> =
primaryBouncerInteractor.keyguardAuthenticatedPrimaryAuth
- .filter { authedUserId -> authedUserId == userInteractor.getSelectedUserId() }
+ .filter { authedUserId -> authedUserId == selectedUserInteractor.getSelectedUserId() }
.map {} // map to Unit
/*
@@ -92,7 +92,7 @@ constructor(
*/
private val userRequestedBouncerWhenAlreadyAuthenticated: Flow<Unit> =
primaryBouncerInteractor.userRequestedBouncerWhenAlreadyAuthenticated
- .filter { authedUserId -> authedUserId == userInteractor.getSelectedUserId() }
+ .filter { authedUserId -> authedUserId == selectedUserInteractor.getSelectedUserId() }
.map {} // map to Unit
/** Updates when keyguardDone should be requested. */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissBinder.kt
index f14552ba0685..87d8164e1cb9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissBinder.kt
@@ -25,20 +25,18 @@ import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissInteractor
import com.android.systemui.keyguard.shared.model.KeyguardDone
import com.android.systemui.log.core.LogLevel
-import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
/** Handles keyguard dismissal requests. */
-@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class KeyguardDismissBinder
@Inject
constructor(
private val interactor: KeyguardDismissInteractor,
- private val userInteractor: UserInteractor,
+ private val selectedUserInteractor: SelectedUserInteractor,
private val viewMediatorCallback: ViewMediatorCallback,
@Application private val scope: CoroutineScope,
private val keyguardLogger: KeyguardLogger,
@@ -55,11 +53,15 @@ constructor(
when (keyguardDoneTiming) {
KeyguardDone.LATER -> {
log("keyguardDonePending")
- viewMediatorCallback.keyguardDonePending(userInteractor.getSelectedUserId())
+ viewMediatorCallback.keyguardDonePending(
+ selectedUserInteractor.getSelectedUserId()
+ )
}
else -> {
log("keyguardDone")
- viewMediatorCallback.keyguardDone(userInteractor.getSelectedUserId())
+ viewMediatorCallback.keyguardDone(
+ selectedUserInteractor.getSelectedUserId()
+ )
}
}
}
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 c72e6ce0b7d6..4d5c503d1c4e 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
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.ui.binder
import android.annotation.DrawableRes
+import android.view.HapticFeedbackConstants
import android.view.View
import android.view.View.OnLayoutChangeListener
import android.view.ViewGroup
@@ -29,6 +30,7 @@ import com.android.keyguard.KeyguardClockSwitch.MISSING_CLOCK_ID
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.shared.model.TintedIcon
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.model.TransitionState
@@ -38,6 +40,7 @@ import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.ClockController
import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.temporarydisplay.ViewPriority
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
@@ -45,6 +48,7 @@ import com.android.systemui.temporarydisplay.chipbar.ChipbarInfo
import javax.inject.Provider
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
/** Bind occludingAppDeviceEntryMessageViewModel to run whenever the keyguard view is attached. */
@@ -62,6 +66,8 @@ object KeyguardRootViewBinder {
shadeInteractor: ShadeInteractor,
clockControllerProvider: Provider<ClockController>?,
interactionJankMonitor: InteractionJankMonitor?,
+ deviceEntryHapticsInteractor: DeviceEntryHapticsInteractor?,
+ vibratorHelper: VibratorHelper?,
): DisposableHandle {
var onLayoutChangeListener: OnLayoutChange? = null
val childViews = mutableMapOf<Int, View?>()
@@ -177,6 +183,44 @@ object KeyguardRootViewBinder {
}
}
}
+
+ if (deviceEntryHapticsInteractor != null && vibratorHelper != null) {
+ launch {
+ deviceEntryHapticsInteractor.playSuccessHaptic
+ .filter { it }
+ .collect {
+ if (
+ featureFlags.isEnabled(Flags.ONE_WAY_HAPTICS_API_MIGRATION)
+ ) {
+ vibratorHelper.performHapticFeedback(
+ view,
+ HapticFeedbackConstants.CONFIRM,
+ )
+ } else {
+ vibratorHelper.vibrateAuthSuccess("device-entry::success")
+ }
+ deviceEntryHapticsInteractor.handleSuccessHaptic()
+ }
+ }
+
+ launch {
+ deviceEntryHapticsInteractor.playErrorHaptic
+ .filter { it }
+ .collect {
+ if (
+ featureFlags.isEnabled(Flags.ONE_WAY_HAPTICS_API_MIGRATION)
+ ) {
+ vibratorHelper.performHapticFeedback(
+ view,
+ HapticFeedbackConstants.REJECT,
+ )
+ } else {
+ vibratorHelper.vibrateAuthSuccess("device-entry::error")
+ }
+ deviceEntryHapticsInteractor.handleErrorHaptic()
+ }
+ }
+ }
}
}
viewModel.clockControllerProvider = clockControllerProvider
@@ -189,7 +233,7 @@ object KeyguardRootViewBinder {
view.setOnHierarchyChangeListener(
object : OnHierarchyChangeListener {
override fun onChildViewAdded(parent: View, child: View) {
- childViews.put(child.id, view)
+ childViews.put(child.id, child)
}
override fun onChildViewRemoved(parent: View, child: View) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index 5a4bbef587af..692984a90a14 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -46,6 +46,7 @@ import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.binder.KeyguardPreviewClockViewBinder
@@ -114,6 +115,7 @@ constructor(
private val chipbarCoordinator: ChipbarCoordinator,
private val keyguardStateController: KeyguardStateController,
private val shadeInteractor: ShadeInteractor,
+ private val deviceEntryHapticsInteractor: DeviceEntryHapticsInteractor,
) {
val hostToken: IBinder? = bundle.getBinder(KEY_HOST_TOKEN)
@@ -339,6 +341,8 @@ constructor(
shadeInteractor,
null, // clock provider only needed for burn in
null, // jank monitor not required for preview mode
+ null, // device entry haptics not required for preview mode
+ null, // device entry haptics not required for preview mode
)
)
rootView.addView(
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLogger.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLogger.kt
index 8634b0911391..a53f0f11c380 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLogger.kt
@@ -19,7 +19,11 @@ import android.media.projection.IMediaProjectionManager
import android.os.Process
import android.os.RemoteException
import android.util.Log
-import com.android.internal.util.FrameworkStatsLog
+import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_APP as METRICS_CREATION_SOURCE_APP
+import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_CAST as METRICS_CREATION_SOURCE_CAST
+import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_SYSTEM_UI_SCREEN_RECORDER as METRICS_CREATION_SOURCE_SYSTEM_UI_SCREEN_RECORDER
+import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN as METRICS_CREATION_SOURCE_UNKNOWN
+import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED
import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
@@ -36,21 +40,23 @@ constructor(private val service: IMediaProjectionManager) {
*
* @param sessionCreationSource The entry point requesting permission to capture.
*/
- fun notifyPermissionProgress(state: Int, sessionCreationSource: Int) {
- // TODO check that state & SessionCreationSource matches expected values
- notifyToServer(state, sessionCreationSource)
+ fun notifyProjectionInitiated(sessionCreationSource: SessionCreationSource) {
+ notifyToServer(
+ MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED,
+ sessionCreationSource
+ )
}
/**
* Request to log that the permission request moved to the given state.
*
- * Should not be used for the initialization state, since that
+ * Should not be used for the initialization state, since that should use {@link
+ * MediaProjectionMetricsLogger#notifyProjectionInitiated(Int)} and pass the
+ * sessionCreationSource.
*/
fun notifyPermissionProgress(state: Int) {
// TODO validate state is valid
- notifyToServer(
- state,
- FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN)
+ notifyToServer(state, SessionCreationSource.UNKNOWN)
}
/**
@@ -64,16 +70,21 @@ constructor(private val service: IMediaProjectionManager) {
* Indicates the entry point for requesting the permission. Must be a valid state defined in
* the SessionCreationSource enum.
*/
- private fun notifyToServer(state: Int, sessionCreationSource: Int) {
+ private fun notifyToServer(state: Int, sessionCreationSource: SessionCreationSource) {
Log.v(TAG, "FOO notifyToServer of state $state and source $sessionCreationSource")
try {
service.notifyPermissionRequestStateChange(
- Process.myUid(), state, sessionCreationSource)
+ Process.myUid(),
+ state,
+ sessionCreationSource.toMetricsConstant()
+ )
} catch (e: RemoteException) {
Log.e(
TAG,
- "Error notifying server of permission flow state $state from source $sessionCreationSource",
- e)
+ "Error notifying server of permission flow state $state from source " +
+ "$sessionCreationSource",
+ e
+ )
}
}
@@ -81,3 +92,18 @@ constructor(private val service: IMediaProjectionManager) {
const val TAG = "MediaProjectionMetricsLogger"
}
}
+
+enum class SessionCreationSource {
+ APP,
+ CAST,
+ SYSTEM_UI_SCREEN_RECORDER,
+ UNKNOWN;
+
+ fun toMetricsConstant(): Int =
+ when (this) {
+ APP -> METRICS_CREATION_SOURCE_APP
+ CAST -> METRICS_CREATION_SOURCE_CAST
+ SYSTEM_UI_SCREEN_RECORDER -> METRICS_CREATION_SOURCE_SYSTEM_UI_SCREEN_RECORDER
+ UNKNOWN -> METRICS_CREATION_SOURCE_UNKNOWN
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
index b5d3e913cadb..0bbcfd9de24c 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
@@ -87,14 +87,15 @@ class MediaProjectionAppSelectorActivity(
override fun getLayoutResource() = R.layout.media_projection_app_selector
- public override fun onCreate(bundle: Bundle?) {
+ public override fun onCreate(savedInstanceState: Bundle?) {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
component =
componentFactory.create(
hostUserHandle = hostUserHandle,
callingPackage = callingPackage,
view = this,
- resultHandler = this
+ resultHandler = this,
+ isFirstStart = savedInstanceState == null
)
component.lifecycleObservers.forEach { lifecycle.addObserver(it) }
@@ -113,7 +114,7 @@ class MediaProjectionAppSelectorActivity(
reviewGrantedConsentRequired =
intent.getBooleanExtra(EXTRA_USER_REVIEW_GRANTED_CONSENT, false)
- super.onCreate(bundle)
+ super.onCreate(savedInstanceState)
controller.init()
// we override AppList's AccessibilityDelegate set in ResolverActivity.onCreate because in
// our case this delegate must extend RecyclerViewAccessibilityDelegate, otherwise
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
index 2217509167ef..8c6f307c84d6 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
@@ -146,6 +146,9 @@ interface MediaProjectionAppSelectorComponent {
@BindsInstance @MediaProjectionAppSelector callingPackage: String?,
@BindsInstance view: MediaProjectionAppSelectorView,
@BindsInstance resultHandler: MediaProjectionAppSelectorResultHandler,
+ // Whether the app selector is starting for the first time. False when it is re-starting
+ // due to a config change.
+ @BindsInstance @MediaProjectionAppSelector isFirstStart: Boolean,
): MediaProjectionAppSelectorComponent
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
index fced117a8132..69132d3662d4 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
@@ -18,6 +18,8 @@ package com.android.systemui.mediaprojection.appselector
import android.content.ComponentName
import android.os.UserHandle
+import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_APP_SELECTOR_DISPLAYED as STATE_APP_SELECTOR_DISPLAYED
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.mediaprojection.appselector.data.RecentTask
import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider
import com.android.systemui.mediaprojection.appselector.data.RecentTaskThumbnailLoader
@@ -43,9 +45,17 @@ constructor(
@MediaProjectionAppSelector private val appSelectorComponentName: ComponentName,
@MediaProjectionAppSelector private val callerPackageName: String?,
private val thumbnailLoader: RecentTaskThumbnailLoader,
+ @MediaProjectionAppSelector private val isFirstStart: Boolean,
+ private val logger: MediaProjectionMetricsLogger,
) {
fun init() {
+ // Only log during the first start of the app selector.
+ // Don't log when the app selector restarts due to a config change.
+ if (isFirstStart) {
+ logger.notifyPermissionProgress(STATE_APP_SELECTOR_DISPLAYED)
+ }
+
scope.launch {
val recentTasks = recentTaskListProvider.loadRecentTasks()
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
index a9e6c53b3bcd..e9b458271ef7 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
@@ -22,6 +22,7 @@ import android.content.ComponentName
data class RecentTask(
val taskId: Int,
+ val displayId: Int,
@UserIdInt val userId: Int,
val topActivityComponent: ComponentName?,
val baseIntentComponent: ComponentName?,
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
index aa4c4e55c718..730aa620690a 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
@@ -60,6 +60,7 @@ constructor(
.map {
RecentTask(
it.taskId,
+ it.displayId,
it.userId,
it.topActivity,
it.baseIntent?.component,
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
index fd1a683dc78f..ba837dba5354 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
@@ -130,10 +130,10 @@ constructor(
view.width,
view.height
)
- activityOptions.setPendingIntentBackgroundActivityStartMode(
+ activityOptions.pendingIntentBackgroundActivityStartMode =
MODE_BACKGROUND_ACTIVITY_START_ALLOWED
- )
activityOptions.launchCookie = launchCookie
+ activityOptions.launchDisplayId = task.displayId
activityTaskManager.startActivityFromRecents(task.taskId, activityOptions.toBundle())
resultHandler.returnSelectedApp(launchCookie)
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index d08d0400f354..fa418fc8b98b 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -53,7 +53,9 @@ import android.view.Window;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger;
import com.android.systemui.mediaprojection.MediaProjectionServiceHelper;
+import com.android.systemui.mediaprojection.SessionCreationSource;
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog;
@@ -74,6 +76,7 @@ public class MediaProjectionPermissionActivity extends Activity
private final FeatureFlags mFeatureFlags;
private final Lazy<ScreenCaptureDevicePolicyResolver> mScreenCaptureDevicePolicyResolver;
private final StatusBarManager mStatusBarManager;
+ private final MediaProjectionMetricsLogger mMediaProjectionMetricsLogger;
private String mPackageName;
private int mUid;
@@ -90,15 +93,17 @@ public class MediaProjectionPermissionActivity extends Activity
@Inject
public MediaProjectionPermissionActivity(FeatureFlags featureFlags,
Lazy<ScreenCaptureDevicePolicyResolver> screenCaptureDevicePolicyResolver,
- StatusBarManager statusBarManager) {
+ StatusBarManager statusBarManager,
+ MediaProjectionMetricsLogger mediaProjectionMetricsLogger) {
mFeatureFlags = featureFlags;
mScreenCaptureDevicePolicyResolver = screenCaptureDevicePolicyResolver;
mStatusBarManager = statusBarManager;
+ mMediaProjectionMetricsLogger = mediaProjectionMetricsLogger;
}
@Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
final Intent launchingIntent = getIntent();
mReviewGrantedConsentRequired = launchingIntent.getBooleanExtra(
@@ -133,6 +138,10 @@ public class MediaProjectionPermissionActivity extends Activity
try {
if (MediaProjectionServiceHelper.hasProjectionPermission(mUid, mPackageName)) {
+ if (savedInstanceState == null) {
+ mMediaProjectionMetricsLogger.notifyProjectionInitiated(
+ SessionCreationSource.APP);
+ }
final IMediaProjection projection =
MediaProjectionServiceHelper.createOrReuseProjection(mUid, mPackageName,
mReviewGrantedConsentRequired);
@@ -231,6 +240,13 @@ public class MediaProjectionPermissionActivity extends Activity
mDialog = dialogBuilder.create();
}
+ if (savedInstanceState == null) {
+ mMediaProjectionMetricsLogger.notifyProjectionInitiated(
+ appName == null
+ ? SessionCreationSource.CAST
+ : SessionCreationSource.APP);
+ }
+
setUpDialog(mDialog);
mDialog.show();
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
index 733383e344b8..58c4f0d029c7 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
@@ -96,6 +96,44 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
+/** Variables and functions that is related to Emoji. */
+class EmojiHelper {
+ static final CharSequence EMOJI_CAKE = "\ud83c\udf82";
+
+ // This regex can be used to match Unicode emoji characters and character sequences. It's from
+ // the official Unicode site (https://unicode.org/reports/tr51/#EBNF_and_Regex) with minor
+ // changes to fit our needs. It should be updated once new emoji categories are added.
+ //
+ // Emoji categories that can be matched by this regex:
+ // - Country flags. "\p{RI}\p{RI}" matches country flags since they always consist of 2 Unicode
+ // scalars.
+ // - Single-Character Emoji. "\p{Emoji}" matches Single-Character Emojis.
+ // - Emoji with modifiers. E.g. Emojis with different skin tones. "\p{Emoji}\p{EMod}" matches
+ // them.
+ // - Emoji Presentation. Those are characters which can normally be drawn as either text or as
+ // Emoji. "\p{Emoji}\x{FE0F}" matches them.
+ // - Emoji Keycap. E.g. Emojis for number 0 to 9. "\p{Emoji}\x{FE0F}\x{20E3}" matches them.
+ // - Emoji tag sequence. "\p{Emoji}[\x{E0020}-\x{E007E}]+\x{E007F}" matches them.
+ // - Emoji Zero-Width Joiner (ZWJ) Sequence. A ZWJ emoji is actually multiple emojis joined by
+ // the jointer "0x200D".
+ //
+ // Note: since "\p{Emoji}" also matches some ASCII characters like digits 0-9, we use
+ // "\p{Emoji}&&\p{So}" to exclude them. This is the change we made from the official emoji
+ // regex.
+ private static final String UNICODE_EMOJI_REGEX =
+ "\\p{RI}\\p{RI}|"
+ + "("
+ + "\\p{Emoji}(\\p{EMod}|\\x{FE0F}\\x{20E3}?|[\\x{E0020}-\\x{E007E}]+\\x{E007F})"
+ + "|[\\p{Emoji}&&\\p{So}]"
+ + ")"
+ + "("
+ + "\\x{200D}"
+ + "\\p{Emoji}(\\p{EMod}|\\x{FE0F}\\x{20E3}?|[\\x{E0020}-\\x{E007E}]+\\x{E007F})"
+ + "?)*";
+
+ static final Pattern EMOJI_PATTERN = Pattern.compile(UNICODE_EMOJI_REGEX);
+}
+
/** Functions that help creating the People tile layouts. */
public class PeopleTileViewHelper {
/** Turns on debugging information about People Space. */
@@ -125,8 +163,6 @@ public class PeopleTileViewHelper {
private static final int MESSAGES_COUNT_OVERFLOW = 6;
- private static final CharSequence EMOJI_CAKE = "\ud83c\udf82";
-
private static final Pattern DOUBLE_EXCLAMATION_PATTERN = Pattern.compile("[!][!]+");
private static final Pattern DOUBLE_QUESTION_PATTERN = Pattern.compile("[?][?]+");
private static final Pattern ANY_DOUBLE_MARK_PATTERN = Pattern.compile("[!?][!?]+");
@@ -134,39 +170,6 @@ public class PeopleTileViewHelper {
static final String BRIEF_PAUSE_ON_TALKBACK = "\n\n";
- // This regex can be used to match Unicode emoji characters and character sequences. It's from
- // the official Unicode site (https://unicode.org/reports/tr51/#EBNF_and_Regex) with minor
- // changes to fit our needs. It should be updated once new emoji categories are added.
- //
- // Emoji categories that can be matched by this regex:
- // - Country flags. "\p{RI}\p{RI}" matches country flags since they always consist of 2 Unicode
- // scalars.
- // - Single-Character Emoji. "\p{Emoji}" matches Single-Character Emojis.
- // - Emoji with modifiers. E.g. Emojis with different skin tones. "\p{Emoji}\p{EMod}" matches
- // them.
- // - Emoji Presentation. Those are characters which can normally be drawn as either text or as
- // Emoji. "\p{Emoji}\x{FE0F}" matches them.
- // - Emoji Keycap. E.g. Emojis for number 0 to 9. "\p{Emoji}\x{FE0F}\x{20E3}" matches them.
- // - Emoji tag sequence. "\p{Emoji}[\x{E0020}-\x{E007E}]+\x{E007F}" matches them.
- // - Emoji Zero-Width Joiner (ZWJ) Sequence. A ZWJ emoji is actually multiple emojis joined by
- // the jointer "0x200D".
- //
- // Note: since "\p{Emoji}" also matches some ASCII characters like digits 0-9, we use
- // "\p{Emoji}&&\p{So}" to exclude them. This is the change we made from the official emoji
- // regex.
- private static final String UNICODE_EMOJI_REGEX =
- "\\p{RI}\\p{RI}|"
- + "("
- + "\\p{Emoji}(\\p{EMod}|\\x{FE0F}\\x{20E3}?|[\\x{E0020}-\\x{E007E}]+\\x{E007F})"
- + "|[\\p{Emoji}&&\\p{So}]"
- + ")"
- + "("
- + "\\x{200D}"
- + "\\p{Emoji}(\\p{EMod}|\\x{FE0F}\\x{20E3}?|[\\x{E0020}-\\x{E007E}]+\\x{E007F})"
- + "?)*";
-
- private static final Pattern EMOJI_PATTERN = Pattern.compile(UNICODE_EMOJI_REGEX);
-
public static final String EMPTY_STRING = "";
private int mMediumVerticalPadding;
@@ -831,7 +834,7 @@ public class PeopleTileViewHelper {
if (status.getActivity() == ACTIVITY_BIRTHDAY
|| status.getActivity() == ACTIVITY_UPCOMING_BIRTHDAY) {
- setEmojiBackground(views, EMOJI_CAKE);
+ setEmojiBackground(views, EmojiHelper.EMOJI_CAKE);
}
Icon statusIcon = status.getIcon();
@@ -1072,7 +1075,7 @@ public class PeopleTileViewHelper {
/** Returns emoji if {@code message} has two of the same emoji in sequence. */
@VisibleForTesting
CharSequence getDoubleEmoji(CharSequence message) {
- Matcher unicodeEmojiMatcher = EMOJI_PATTERN.matcher(message);
+ Matcher unicodeEmojiMatcher = EmojiHelper.EMOJI_PATTERN.matcher(message);
// Stores the start and end indices of each matched emoji.
List<Pair<Integer, Integer>> emojiIndices = new ArrayList<>();
// Stores each emoji text.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt
index c91ed133a11e..8e307408ba86 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt
@@ -42,7 +42,7 @@ import com.android.systemui.qs.footer.domain.model.SecurityButtonConfig
import com.android.systemui.security.data.repository.SecurityRepository
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.user.data.repository.UserSwitcherRepository
-import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
@@ -102,7 +102,7 @@ constructor(
private val deviceProvisionedController: DeviceProvisionedController,
private val qsSecurityFooterUtils: QSSecurityFooterUtils,
private val fgsManagerController: FgsManagerController,
- private val userInteractor: UserInteractor,
+ private val userSwitcherInteractor: UserSwitcherInteractor,
securityRepository: SecurityRepository,
foregroundServicesRepository: ForegroundServicesRepository,
userSwitcherRepository: UserSwitcherRepository,
@@ -178,6 +178,6 @@ constructor(
}
override fun showUserSwitcher(expandable: Expandable) {
- userInteractor.showUserSwitcher(expandable)
+ userSwitcherInteractor.showUserSwitcher(expandable)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index 7a0c087caacf..f469c6b78e87 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -38,6 +38,8 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger;
+import com.android.systemui.mediaprojection.SessionCreationSource;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog;
import com.android.systemui.plugins.ActivityStarter;
@@ -45,13 +47,13 @@ import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.CallbackController;
+import dagger.Lazy;
+
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import javax.inject.Inject;
-import dagger.Lazy;
-
/**
* Helper class to initiate a screen recording
*/
@@ -71,6 +73,7 @@ public class RecordingController
private final FeatureFlags mFlags;
private final UserContextProvider mUserContextProvider;
private final UserTracker mUserTracker;
+ private final MediaProjectionMetricsLogger mMediaProjectionMetricsLogger;
protected static final String INTENT_UPDATE_STATE =
"com.android.systemui.screenrecord.UPDATE_STATE";
@@ -115,7 +118,8 @@ public class RecordingController
FeatureFlags flags,
UserContextProvider userContextProvider,
Lazy<ScreenCaptureDevicePolicyResolver> devicePolicyResolver,
- UserTracker userTracker) {
+ UserTracker userTracker,
+ MediaProjectionMetricsLogger mediaProjectionMetricsLogger) {
mMainExecutor = mainExecutor;
mContext = context;
mFlags = flags;
@@ -123,6 +127,7 @@ public class RecordingController
mBroadcastDispatcher = broadcastDispatcher;
mUserContextProvider = userContextProvider;
mUserTracker = userTracker;
+ mMediaProjectionMetricsLogger = mediaProjectionMetricsLogger;
BroadcastOptions options = BroadcastOptions.makeBasic();
options.setInteractive(true);
@@ -149,6 +154,9 @@ public class RecordingController
return new ScreenCaptureDisabledDialog(mContext);
}
+ mMediaProjectionMetricsLogger.notifyProjectionInitiated(
+ SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER);
+
return flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)
? new ScreenRecordPermissionDialog(context, getHostUserHandle(), this,
activityStarter, mUserContextProvider, onStartRecordingClicked)
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index 393a698bcdb7..99127ea928bf 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -90,6 +90,7 @@ open class UserTrackerImpl internal constructor(
private val isBackgroundUserSwitchEnabled: Boolean get() =
featureFlagsProvider.get().isEnabled(Flags.USER_TRACKER_BACKGROUND_CALLBACKS)
+ @Deprecated("Use UserInteractor.getSelectedUserId()")
override var userId: Int by SynchronizedDelegate(context.userId)
protected set
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
index 6783afa3eb9d..1ecb127f0c92 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
@@ -21,6 +21,7 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.WindowManagerPolicyConstants.EXTRA_FROM_BRIGHTNESS_KEY;
import android.app.Activity;
+import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.Gravity;
@@ -35,8 +36,8 @@ import android.widget.FrameLayout;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -74,21 +75,26 @@ public class BrightnessDialog extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setWindowAttributes();
+ setContentView(R.layout.brightness_mirror_container);
+ setBrightnessDialogViewAttributes();
+ }
+ private void setWindowAttributes() {
final Window window = getWindow();
- window.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL);
+ window.setGravity(Gravity.TOP | Gravity.LEFT);
window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
window.requestFeature(Window.FEATURE_NO_TITLE);
// Calling this creates the decor View, so setLayout takes proper effect
// (see Dialog#onWindowAttributesChanged)
window.getDecorView();
- window.setLayout(
- WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT);
+ window.setLayout(WRAP_CONTENT, WRAP_CONTENT);
getTheme().applyStyle(R.style.Theme_SystemUI_QuickSettings, false);
+ }
- setContentView(R.layout.brightness_mirror_container);
+ void setBrightnessDialogViewAttributes() {
FrameLayout frame = findViewById(R.id.brightness_mirror_container);
// The brightness mirror container is INVISIBLE by default.
frame.setVisibility(View.VISIBLE);
@@ -97,6 +103,14 @@ public class BrightnessDialog extends Activity {
getResources().getDimensionPixelSize(R.dimen.notification_side_paddings);
lp.leftMargin = horizontalMargin;
lp.rightMargin = horizontalMargin;
+
+ int verticalMargin =
+ getResources().getDimensionPixelSize(
+ R.dimen.notification_guts_option_vertical_padding);
+
+ lp.topMargin = verticalMargin;
+ lp.bottomMargin = verticalMargin;
+
frame.setLayoutParams(lp);
Rect bounds = new Rect();
frame.addOnLayoutChangeListener(
@@ -113,6 +127,20 @@ public class BrightnessDialog extends Activity {
frame.addView(controller.getRootView(), MATCH_PARENT, WRAP_CONTENT);
mBrightnessController = mBrightnessControllerFactory.create(controller);
+
+ Configuration configuration = getResources().getConfiguration();
+ int orientation = configuration.orientation;
+
+ if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ lp.width = getWindowManager().getDefaultDisplay().getWidth() / 2
+ - lp.leftMargin * 2;
+ } else if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+ lp.width = getWindowManager().getDefaultDisplay().getWidth()
+ - lp.leftMargin * 2;
+ }
+
+ frame.setLayoutParams(lp);
+
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 6f5e41f626de..04263882d3a8 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -47,7 +47,6 @@ import android.view.WindowManager.LayoutParams;
import android.view.WindowManagerGlobal;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dumpable;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
@@ -72,6 +71,7 @@ import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import dagger.Lazy;
@@ -112,6 +112,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
private final KeyguardBypassController mKeyguardBypassController;
private final Executor mBackgroundExecutor;
private final AuthController mAuthController;
+ private final Lazy<SelectedUserInteractor> mUserInteractor;
private final Lazy<ShadeInteractor> mShadeInteractorLazy;
private ViewGroup mWindowRootView;
private LayoutParams mLp;
@@ -157,7 +158,8 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
AuthController authController,
ShadeExpansionStateManager shadeExpansionStateManager,
Lazy<ShadeInteractor> shadeInteractorLazy,
- ShadeWindowLogger logger) {
+ ShadeWindowLogger logger,
+ Lazy<SelectedUserInteractor> userInteractor) {
mContext = context;
mWindowRootViewComponentFactory = windowRootViewComponentFactory;
mWindowManager = windowManager;
@@ -174,6 +176,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
mScreenOffAnimationController = screenOffAnimationController;
dumpManager.registerDumpable(this);
mAuthController = authController;
+ mUserInteractor = userInteractor;
mLastKeyguardRotationAllowed = mKeyguardStateController.isKeyguardScreenRotationAllowed();
mLockScreenDisplayTimeout = context.getResources()
.getInteger(R.integer.config_lockScreenDisplayTimeout);
@@ -348,7 +351,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
boolean onKeyguard = state.statusBarState == StatusBarState.KEYGUARD
&& !state.keyguardFadingAway && !state.keyguardGoingAway;
if (onKeyguard
- && mAuthController.isUdfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser())) {
+ && mAuthController.isUdfpsEnrolled(mUserInteractor.get().getSelectedUserId())) {
// both max and min display refresh rate must be set to take effect:
mLpChanged.preferredMaxDisplayRefreshRate = mKeyguardPreferredRefreshRate;
mLpChanged.preferredMinDisplayRefreshRate = mKeyguardPreferredRefreshRate;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index d05dfe2c11c1..09514404d4db 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -36,7 +36,6 @@ import com.android.keyguard.LockIconViewController;
import com.android.keyguard.dagger.KeyguardBouncerComponent;
import com.android.systemui.Dumpable;
import com.android.systemui.animation.ActivityLaunchAnimator;
-import com.android.systemui.back.domain.interactor.BackActionInteractor;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
@@ -55,7 +54,6 @@ import com.android.systemui.keyguard.shared.model.TransitionState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
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.res.R;
import com.android.systemui.shared.animation.DisableSubpixelTextTransitionListener;
import com.android.systemui.statusbar.DragDownHelper;
@@ -75,6 +73,7 @@ import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
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.time.SystemClock;
import java.io.PrintWriter;
@@ -128,8 +127,6 @@ public class NotificationShadeWindowViewController implements Dumpable {
private final CentralSurfaces mService;
private final DozeServiceHost mDozeServiceHost;
private final DozeScrimController mDozeScrimController;
- private final BackActionInteractor mBackActionInteractor;
- private final PowerInteractor mPowerInteractor;
private final NotificationShadeWindowController mNotificationShadeWindowController;
private DragDownHelper mDragDownHelper;
private boolean mExpandingBelowNotch;
@@ -164,8 +161,6 @@ public class NotificationShadeWindowViewController implements Dumpable {
CentralSurfaces centralSurfaces,
DozeServiceHost dozeServiceHost,
DozeScrimController dozeScrimController,
- BackActionInteractor backActionInteractor,
- PowerInteractor powerInteractor,
NotificationShadeWindowController controller,
Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
@@ -187,7 +182,8 @@ public class NotificationShadeWindowViewController implements Dumpable {
BouncerLogger bouncerLogger,
SysUIKeyEventHandler sysUIKeyEventHandler,
PrimaryBouncerInteractor primaryBouncerInteractor,
- AlternateBouncerInteractor alternateBouncerInteractor) {
+ AlternateBouncerInteractor alternateBouncerInteractor,
+ SelectedUserInteractor selectedUserInteractor) {
mLockscreenShadeTransitionController = transitionController;
mFalsingCollector = falsingCollector;
mStatusBarStateController = statusBarStateController;
@@ -200,12 +196,10 @@ public class NotificationShadeWindowViewController implements Dumpable {
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mStatusBarWindowStateController = statusBarWindowStateController;
mLockIconViewController = lockIconViewController;
- mBackActionInteractor = backActionInteractor;
mShadeLogger = shadeLogger;
mService = centralSurfaces;
mDozeServiceHost = dozeServiceHost;
mDozeScrimController = dozeScrimController;
- mPowerInteractor = powerInteractor;
mNotificationShadeWindowController = controller;
mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
mAmbientState = ambientState;
@@ -229,7 +223,8 @@ public class NotificationShadeWindowViewController implements Dumpable {
messageAreaControllerFactory,
bouncerMessageInteractor,
bouncerLogger,
- featureFlags);
+ featureFlags,
+ selectedUserInteractor);
collectFlow(mView, keyguardTransitionInteractor.getLockscreenToDreamingTransition(),
mLockscreenToDreamingTransition);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index 3bbb2cf83a83..3c68438ff61b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -736,7 +736,11 @@ public class QuickSettingsController implements Dumpable {
/** Returns whether touches from the notification panel should be disallowed */
public boolean disallowTouches() {
- return mQs.disallowPanelTouches();
+ if (mQs != null) {
+ return mQs.disallowPanelTouches();
+ } else {
+ return false;
+ }
}
void setListening(boolean listening) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index e487a6fb9617..a8a20cc8559b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -34,7 +34,7 @@ import com.android.systemui.statusbar.notification.stack.domain.interactor.Share
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository
import com.android.systemui.statusbar.policy.data.repository.DeviceProvisioningRepository
-import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
import com.android.systemui.util.kotlin.pairwise
import javax.inject.Inject
import javax.inject.Provider
@@ -71,7 +71,7 @@ constructor(
keyguardTransitionInteractor: KeyguardTransitionInteractor,
powerInteractor: PowerInteractor,
userSetupRepository: UserSetupRepository,
- userInteractor: UserInteractor,
+ userSwitcherInteractor: UserSwitcherInteractor,
sharedNotificationContainerInteractor: SharedNotificationContainerInteractor,
repository: ShadeRepository,
) {
@@ -227,7 +227,7 @@ constructor(
isDeviceProvisioned &&
// Disallow QS during setup if it's a simple user switcher. (The user intends to
// use the lock screen user switcher, QS is not needed.)
- (isUserSetup || !userInteractor.isSimpleUserSwitcher) &&
+ (isUserSetup || !userSwitcherInteractor.isSimpleUserSwitcher) &&
isShadeEnabled &&
disableFlags.isQuickSettingsEnabled() &&
!isDozing
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt
deleted file mode 100644
index 17b4e3baef13..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar
-
-import android.content.Context
-import android.graphics.Bitmap
-import android.graphics.Canvas
-import android.graphics.Point
-import android.graphics.Rect
-import android.renderscript.Allocation
-import android.renderscript.Element
-import android.renderscript.RenderScript
-import android.renderscript.ScriptIntrinsicBlur
-import android.util.Log
-import android.util.MathUtils
-import com.android.internal.graphics.ColorUtils
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.notification.MediaNotificationProcessor
-import javax.inject.Inject
-
-private const val TAG = "MediaArtworkProcessor"
-private const val COLOR_ALPHA = (255 * 0.7f).toInt()
-private const val BLUR_RADIUS = 25f
-private const val DOWNSAMPLE = 6
-
-@SysUISingleton
-class MediaArtworkProcessor @Inject constructor() {
-
- private val mTmpSize = Point()
- private var mArtworkCache: Bitmap? = null
-
- fun processArtwork(context: Context, artwork: Bitmap): Bitmap? {
- if (mArtworkCache != null) {
- return mArtworkCache
- }
- val renderScript = RenderScript.create(context)
- val blur = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript))
- var input: Allocation? = null
- var output: Allocation? = null
- var inBitmap: Bitmap? = null
- try {
- @Suppress("DEPRECATION")
- context.display?.getSize(mTmpSize)
- val rect = Rect(0, 0, artwork.width, artwork.height)
- MathUtils.fitRect(rect, Math.max(mTmpSize.x / DOWNSAMPLE, mTmpSize.y / DOWNSAMPLE))
- inBitmap = Bitmap.createScaledBitmap(artwork, rect.width(), rect.height(),
- true /* filter */)
- // Render script blurs only support ARGB_8888, we need a conversion if we got a
- // different bitmap config.
- if (inBitmap.config != Bitmap.Config.ARGB_8888) {
- val oldIn = inBitmap
- inBitmap = oldIn.copy(Bitmap.Config.ARGB_8888, false /* isMutable */)
- oldIn.recycle()
- }
- val outBitmap = Bitmap.createBitmap(inBitmap?.width ?: 0, inBitmap?.height ?: 0,
- Bitmap.Config.ARGB_8888)
-
- input = Allocation.createFromBitmap(renderScript, inBitmap,
- Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_GRAPHICS_TEXTURE)
- output = Allocation.createFromBitmap(renderScript, outBitmap)
-
- blur.setRadius(BLUR_RADIUS)
- blur.setInput(input)
- blur.forEach(output)
- output.copyTo(outBitmap)
-
- val swatch = MediaNotificationProcessor.findBackgroundSwatch(artwork)
-
- val canvas = Canvas(outBitmap)
- canvas.drawColor(ColorUtils.setAlphaComponent(swatch.rgb, COLOR_ALPHA))
- return outBitmap
- } catch (ex: IllegalArgumentException) {
- Log.e(TAG, "error while processing artwork", ex)
- return null
- } finally {
- input?.destroy()
- output?.destroy()
- blur.destroy()
- inBitmap?.recycle()
- }
- }
-
- fun clearCache() {
- mArtworkCache?.recycle()
- mArtworkCache = null
- }
-} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 5bd40b8ed714..389486f0ada3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -15,43 +15,29 @@
*/
package com.android.systemui.statusbar;
-import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
-import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG_MEDIA_FAKE_ARTWORK;
-import static com.android.systemui.statusbar.phone.CentralSurfaces.ENABLE_LOCKSCREEN_WALLPAPER;
-import static com.android.systemui.statusbar.phone.CentralSurfaces.SHOW_LOCKSCREEN_MEDIA_ARTWORK;
-
-import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
import android.app.WallpaperManager;
import android.content.Context;
-import android.graphics.Bitmap;
import android.graphics.Point;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.hardware.display.DisplayManager;
import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
-import android.os.AsyncTask;
import android.os.Trace;
import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
-import android.util.ArraySet;
import android.util.Log;
import android.view.Display;
import android.view.View;
import android.widget.ImageView;
-import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dumpable;
import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.media.controls.models.player.MediaData;
import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData;
@@ -65,18 +51,11 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.Di
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.statusbar.phone.ScrimState;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.Utils;
-import com.android.systemui.util.concurrency.DelayableExecutor;
-
-import dagger.Lazy;
import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -85,7 +64,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
-import java.util.Set;
import java.util.stream.Collectors;
/**
@@ -99,7 +77,6 @@ public class NotificationMediaManager implements Dumpable {
private final StatusBarStateController mStatusBarStateController;
private final SysuiColorExtractor mColorExtractor;
private final KeyguardStateController mKeyguardStateController;
- private final KeyguardBypassController mKeyguardBypassController;
private static final HashSet<Integer> PAUSED_MEDIA_STATES = new HashSet<>();
private static final HashSet<Integer> CONNECTING_MEDIA_STATES = new HashSet<>();
static {
@@ -117,9 +94,6 @@ public class NotificationMediaManager implements Dumpable {
private final NotifCollection mNotifCollection;
@Nullable
- private Lazy<NotificationShadeWindowController> mNotificationShadeWindowController;
-
- @Nullable
private BiometricUnlockController mBiometricUnlockController;
@Nullable
private ScrimController mScrimController;
@@ -128,12 +102,8 @@ public class NotificationMediaManager implements Dumpable {
@VisibleForTesting
boolean mIsLockscreenLiveWallpaperEnabled;
- private final DelayableExecutor mMainExecutor;
-
private final Context mContext;
private final ArrayList<MediaListener> mMediaListeners;
- private final MediaArtworkProcessor mMediaArtworkProcessor;
- private final Set<AsyncTask<?, ?, ?>> mProcessArtworkTasks = new ArraySet<>();
protected NotificationPresenter mPresenter;
private MediaController mMediaController;
@@ -150,8 +120,6 @@ public class NotificationMediaManager implements Dumpable {
private List<String> mSmallerInternalDisplayUids;
private Display mCurrentDisplay;
- private LockscreenWallpaper.WallpaperDrawable mWallapperDrawable;
-
private final MediaController.Callback mMediaListener = new MediaController.Callback() {
@Override
public void onPlaybackStateChanged(PlaybackState state) {
@@ -173,7 +141,6 @@ public class NotificationMediaManager implements Dumpable {
if (DEBUG_MEDIA) {
Log.v(TAG, "DEBUG_MEDIA: onMetadataChanged: " + metadata);
}
- mMediaArtworkProcessor.clearCache();
mMediaMetadata = metadata;
dispatchUpdateMediaMetaData(true /* changed */, true /* allowAnimation */);
}
@@ -184,13 +151,9 @@ public class NotificationMediaManager implements Dumpable {
*/
public NotificationMediaManager(
Context context,
- Lazy<NotificationShadeWindowController> notificationShadeWindowController,
NotificationVisibilityProvider visibilityProvider,
- MediaArtworkProcessor mediaArtworkProcessor,
- KeyguardBypassController keyguardBypassController,
NotifPipeline notifPipeline,
NotifCollection notifCollection,
- @Main DelayableExecutor mainExecutor,
MediaDataManager mediaDataManager,
StatusBarStateController statusBarStateController,
SysuiColorExtractor colorExtractor,
@@ -199,12 +162,8 @@ public class NotificationMediaManager implements Dumpable {
WallpaperManager wallpaperManager,
DisplayManager displayManager) {
mContext = context;
- mMediaArtworkProcessor = mediaArtworkProcessor;
- mKeyguardBypassController = keyguardBypassController;
mMediaListeners = new ArrayList<>();
- mNotificationShadeWindowController = notificationShadeWindowController;
mVisibilityProvider = visibilityProvider;
- mMainExecutor = mainExecutor;
mMediaDataManager = mediaDataManager;
mNotifPipeline = notifPipeline;
mNotifCollection = notifCollection;
@@ -476,7 +435,6 @@ public class NotificationMediaManager implements Dumpable {
}
private void clearCurrentMediaNotificationSession() {
- mMediaArtworkProcessor.clearCache();
mMediaMetadata = null;
if (mMediaController != null) {
if (DEBUG_MEDIA) {
@@ -494,9 +452,6 @@ public class NotificationMediaManager implements Dumpable {
public void onDisplayUpdated(Display display) {
Trace.beginSection("NotificationMediaManager#onDisplayUpdated");
mCurrentDisplay = display;
- if (mWallapperDrawable != null) {
- mWallapperDrawable.onDisplayUpdated(isOnSmallerInternalDisplays());
- }
Trace.endSection();
}
@@ -531,18 +486,13 @@ public class NotificationMediaManager implements Dumpable {
}
/**
- * Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper.
+ * Update media state of lockscreen media views and controllers .
*/
- public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
+ public void updateMediaMetaData(boolean metaDataChanged) {
if (mIsLockscreenLiveWallpaperEnabled) return;
Trace.beginSection("CentralSurfaces#updateMediaMetaData");
- if (!SHOW_LOCKSCREEN_MEDIA_ARTWORK) {
- Trace.endSection();
- return;
- }
-
if (getBackDropView() == null) {
Trace.endSection();
return; // called too early
@@ -566,168 +516,12 @@ public class NotificationMediaManager implements Dumpable {
+ " state=" + mStatusBarStateController.getState());
}
- Bitmap artworkBitmap = null;
- if (mediaMetadata != null && !mKeyguardBypassController.getBypassEnabled()) {
- artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
- if (artworkBitmap == null) {
- artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
- }
- }
-
- // Process artwork on a background thread and send the resulting bitmap to
- // finishUpdateMediaMetaData.
- if (metaDataChanged) {
- for (AsyncTask<?, ?, ?> task : mProcessArtworkTasks) {
- task.cancel(true);
- }
- mProcessArtworkTasks.clear();
- }
- if (artworkBitmap != null && !Utils.useQsMediaPlayer(mContext)) {
- mProcessArtworkTasks.add(new ProcessArtworkTask(this, metaDataChanged,
- allowEnterAnimation).execute(artworkBitmap));
- } else {
- finishUpdateMediaMetaData(metaDataChanged, allowEnterAnimation, null);
- }
-
- Trace.endSection();
- }
-
- private void finishUpdateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation,
- @Nullable Bitmap bmp) {
- Drawable artworkDrawable = null;
- if (bmp != null) {
- artworkDrawable = new BitmapDrawable(mBackdropBack.getResources(), bmp);
- }
- boolean hasMediaArtwork = artworkDrawable != null;
- boolean allowWhenShade = false;
- if (ENABLE_LOCKSCREEN_WALLPAPER && artworkDrawable == null) {
- Bitmap lockWallpaper =
- mLockscreenWallpaper != null ? mLockscreenWallpaper.getBitmap() : null;
- if (lockWallpaper != null) {
- artworkDrawable = new LockscreenWallpaper.WallpaperDrawable(
- mBackdropBack.getResources(), lockWallpaper, isOnSmallerInternalDisplays());
- // We're in the SHADE mode on the SIM screen - yet we still need to show
- // the lockscreen wallpaper in that mode.
- allowWhenShade = mStatusBarStateController.getState() == KEYGUARD;
- }
- }
-
- NotificationShadeWindowController windowController =
- mNotificationShadeWindowController.get();
- boolean hideBecauseOccluded = mKeyguardStateController.isOccluded();
-
- final boolean hasArtwork = artworkDrawable != null;
- mColorExtractor.setHasMediaArtwork(hasMediaArtwork);
+ mColorExtractor.setHasMediaArtwork(false);
if (mScrimController != null) {
- mScrimController.setHasBackdrop(hasArtwork);
+ mScrimController.setHasBackdrop(false);
}
- if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK)
- && (mStatusBarStateController.getState() != StatusBarState.SHADE || allowWhenShade)
- && mBiometricUnlockController != null && mBiometricUnlockController.getMode()
- != BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
- && !hideBecauseOccluded) {
- // time to show some art!
- if (mBackdrop.getVisibility() != View.VISIBLE) {
- mBackdrop.setVisibility(View.VISIBLE);
- if (allowEnterAnimation) {
- mBackdrop.setAlpha(0);
- mBackdrop.animate().alpha(1f);
- } else {
- mBackdrop.animate().cancel();
- mBackdrop.setAlpha(1f);
- }
- if (windowController != null) {
- windowController.setBackdropShowing(true);
- }
- metaDataChanged = true;
- if (DEBUG_MEDIA) {
- Log.v(TAG, "DEBUG_MEDIA: Fading in album artwork");
- }
- }
- if (metaDataChanged) {
- if (mBackdropBack.getDrawable() != null) {
- Drawable drawable =
- mBackdropBack.getDrawable().getConstantState()
- .newDrawable(mBackdropFront.getResources()).mutate();
- mBackdropFront.setImageDrawable(drawable);
- mBackdropFront.setAlpha(1f);
- mBackdropFront.setVisibility(View.VISIBLE);
- } else {
- mBackdropFront.setVisibility(View.INVISIBLE);
- }
-
- if (DEBUG_MEDIA_FAKE_ARTWORK) {
- final int c = 0xFF000000 | (int)(Math.random() * 0xFFFFFF);
- Log.v(TAG, String.format("DEBUG_MEDIA: setting new color: 0x%08x", c));
- mBackdropBack.setBackgroundColor(0xFFFFFFFF);
- mBackdropBack.setImageDrawable(new ColorDrawable(c));
- } else {
- if (artworkDrawable instanceof LockscreenWallpaper.WallpaperDrawable) {
- mWallapperDrawable =
- (LockscreenWallpaper.WallpaperDrawable) artworkDrawable;
- }
- mBackdropBack.setImageDrawable(artworkDrawable);
- }
-
- if (mBackdropFront.getVisibility() == View.VISIBLE) {
- if (DEBUG_MEDIA) {
- Log.v(TAG, "DEBUG_MEDIA: Crossfading album artwork from "
- + mBackdropFront.getDrawable()
- + " to "
- + mBackdropBack.getDrawable());
- }
- mBackdropFront.animate()
- .setDuration(250)
- .alpha(0f).withEndAction(mHideBackdropFront);
- }
- }
- } else {
- // need to hide the album art, either because we are unlocked, on AOD
- // or because the metadata isn't there to support it
- if (mBackdrop.getVisibility() != View.GONE) {
- if (DEBUG_MEDIA) {
- Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork");
- }
- boolean cannotAnimateDoze = mStatusBarStateController.isDozing()
- && !ScrimState.AOD.getAnimateChange();
- if (((mBiometricUnlockController != null && mBiometricUnlockController.getMode()
- == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
- || cannotAnimateDoze))
- || hideBecauseOccluded) {
- // We are unlocking directly - no animation!
- mBackdrop.setVisibility(View.GONE);
- mBackdropBack.setImageDrawable(null);
- if (windowController != null) {
- windowController.setBackdropShowing(false);
- }
- } else {
- if (windowController != null) {
- windowController.setBackdropShowing(false);
- }
- mBackdrop.animate()
- .alpha(0)
- .setInterpolator(Interpolators.ACCELERATE_DECELERATE)
- .setDuration(300)
- .setStartDelay(0)
- .withEndAction(() -> {
- mBackdrop.setVisibility(View.GONE);
- mBackdropFront.animate().cancel();
- mBackdropBack.setImageDrawable(null);
- mMainExecutor.execute(mHideBackdropFront);
- });
- if (mKeyguardStateController.isKeyguardFadingAway()) {
- mBackdrop.animate()
- .setDuration(
- mKeyguardStateController.getShortenedFadingAwayDuration())
- .setStartDelay(
- mKeyguardStateController.getKeyguardFadingAwayDelay())
- .setInterpolator(Interpolators.LINEAR)
- .start();
- }
- }
- }
- }
+ Trace.endSection();
}
public void setup(BackDropView backdrop, ImageView backdropFront, ImageView backdropBack,
@@ -758,15 +552,6 @@ public class NotificationMediaManager implements Dumpable {
}
};
- private Bitmap processArtwork(Bitmap artwork) {
- return mMediaArtworkProcessor.processArtwork(mContext, artwork);
- }
-
- @MainThread
- private void removeTask(AsyncTask<?, ?, ?> task) {
- mProcessArtworkTasks.remove(task);
- }
-
// TODO(b/273443374): remove
public boolean isLockscreenWallpaperOnNotificationShade() {
return mBackdrop != null && mLockscreenWallpaper != null
@@ -780,52 +565,6 @@ public class NotificationMediaManager implements Dumpable {
return mBackdrop;
}
- /**
- * {@link AsyncTask} to prepare album art for use as backdrop on lock screen.
- */
- private static final class ProcessArtworkTask extends AsyncTask<Bitmap, Void, Bitmap> {
-
- private final WeakReference<NotificationMediaManager> mManagerRef;
- private final boolean mMetaDataChanged;
- private final boolean mAllowEnterAnimation;
-
- ProcessArtworkTask(NotificationMediaManager manager, boolean changed,
- boolean allowAnimation) {
- mManagerRef = new WeakReference<>(manager);
- mMetaDataChanged = changed;
- mAllowEnterAnimation = allowAnimation;
- }
-
- @Override
- protected Bitmap doInBackground(Bitmap... bitmaps) {
- NotificationMediaManager manager = mManagerRef.get();
- if (manager == null || bitmaps.length == 0 || isCancelled()) {
- return null;
- }
- return manager.processArtwork(bitmaps[0]);
- }
-
- @Override
- protected void onPostExecute(@Nullable Bitmap result) {
- NotificationMediaManager manager = mManagerRef.get();
- if (manager != null && !isCancelled()) {
- manager.removeTask(this);
- manager.finishUpdateMediaMetaData(mMetaDataChanged, mAllowEnterAnimation, result);
- }
- }
-
- @Override
- protected void onCancelled(Bitmap result) {
- if (result != null) {
- result.recycle();
- }
- NotificationMediaManager manager = mManagerRef.get();
- if (manager != null) {
- manager.removeTask(this);
- }
- }
- }
-
public interface MediaListener {
/**
* Called whenever there's new metadata or playback state.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index 7f5829d81c6f..125c8efe1884 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -31,7 +31,6 @@ import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpHandler;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
@@ -45,12 +44,10 @@ import com.android.systemui.shade.ShadeSurface;
import com.android.systemui.shade.carrier.ShadeCarrierGroupController;
import com.android.systemui.statusbar.ActionClickLogger;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.MediaArtworkProcessor;
import com.android.systemui.statusbar.NotificationClickNotifier;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -61,7 +58,6 @@ import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.phone.CentralSurfacesImpl;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ManagedProfileController;
import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -71,7 +67,6 @@ import com.android.systemui.statusbar.phone.StatusBarNotificationPresenterModule
import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
-import com.android.systemui.util.concurrency.DelayableExecutor;
import dagger.Binds;
import dagger.Lazy;
@@ -122,13 +117,9 @@ public interface CentralSurfacesDependenciesModule {
@Provides
static NotificationMediaManager provideNotificationMediaManager(
Context context,
- Lazy<NotificationShadeWindowController> notificationShadeWindowController,
NotificationVisibilityProvider visibilityProvider,
- MediaArtworkProcessor mediaArtworkProcessor,
- KeyguardBypassController keyguardBypassController,
NotifPipeline notifPipeline,
NotifCollection notifCollection,
- @Main DelayableExecutor mainExecutor,
MediaDataManager mediaDataManager,
StatusBarStateController statusBarStateController,
SysuiColorExtractor colorExtractor,
@@ -138,13 +129,9 @@ public interface CentralSurfacesDependenciesModule {
DisplayManager displayManager) {
return new NotificationMediaManager(
context,
- notificationShadeWindowController,
visibilityProvider,
- mediaArtworkProcessor,
- keyguardBypassController,
notifPipeline,
notifCollection,
- mainExecutor,
mediaDataManager,
statusBarStateController,
colorExtractor,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
deleted file mode 100644
index 732c115571f8..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2017 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.graphics.Bitmap;
-import android.graphics.Color;
-
-import androidx.palette.graphics.Palette;
-
-import java.util.List;
-
-/**
- * A gutted class that now contains only a color extraction utility used by the
- * MediaArtworkProcessor, which has otherwise supplanted this.
- *
- * TODO(b/182926117): move this into MediaArtworkProcessor.kt
- */
-public class MediaNotificationProcessor {
-
- /**
- * The population fraction to select a white or black color as the background over a color.
- */
- private static final float POPULATION_FRACTION_FOR_WHITE_OR_BLACK = 2.5f;
- private static final float BLACK_MAX_LIGHTNESS = 0.08f;
- private static final float WHITE_MIN_LIGHTNESS = 0.90f;
- private static final int RESIZE_BITMAP_AREA = 150 * 150;
-
- private MediaNotificationProcessor() {
- }
-
- /**
- * Finds an appropriate background swatch from media artwork.
- *
- * @param artwork Media artwork
- * @return Swatch that should be used as the background of the media notification.
- */
- public static Palette.Swatch findBackgroundSwatch(Bitmap artwork) {
- return findBackgroundSwatch(generateArtworkPaletteBuilder(artwork).generate());
- }
-
- /**
- * Finds an appropriate background swatch from the palette of media artwork.
- *
- * @param palette Artwork palette, should be obtained from {@link generateArtworkPaletteBuilder}
- * @return Swatch that should be used as the background of the media notification.
- */
- public static Palette.Swatch findBackgroundSwatch(Palette palette) {
- // by default we use the dominant palette
- Palette.Swatch dominantSwatch = palette.getDominantSwatch();
- if (dominantSwatch == null) {
- return new Palette.Swatch(Color.WHITE, 100);
- }
-
- if (!isWhiteOrBlack(dominantSwatch.getHsl())) {
- return dominantSwatch;
- }
- // Oh well, we selected black or white. Lets look at the second color!
- List<Palette.Swatch> swatches = palette.getSwatches();
- float highestNonWhitePopulation = -1;
- Palette.Swatch second = null;
- for (Palette.Swatch swatch : swatches) {
- if (swatch != dominantSwatch
- && swatch.getPopulation() > highestNonWhitePopulation
- && !isWhiteOrBlack(swatch.getHsl())) {
- second = swatch;
- highestNonWhitePopulation = swatch.getPopulation();
- }
- }
- if (second == null) {
- return dominantSwatch;
- }
- if (dominantSwatch.getPopulation() / highestNonWhitePopulation
- > POPULATION_FRACTION_FOR_WHITE_OR_BLACK) {
- // The dominant swatch is very dominant, lets take it!
- // We're not filtering on white or black
- return dominantSwatch;
- } else {
- return second;
- }
- }
-
- /**
- * Generate a palette builder for media artwork.
- *
- * For producing a smooth background transition, the palette is extracted from only the left
- * side of the artwork.
- *
- * @param artwork Media artwork
- * @return Builder that generates the {@link Palette} for the media artwork.
- */
- public static Palette.Builder generateArtworkPaletteBuilder(Bitmap artwork) {
- // for the background we only take the left side of the image to ensure
- // a smooth transition
- return Palette.from(artwork)
- .setRegion(0, 0, artwork.getWidth() / 2, artwork.getHeight())
- .clearFilters() // we want all colors, red / white / black ones too!
- .resizeBitmapArea(RESIZE_BITMAP_AREA);
- }
-
- private static boolean isWhiteOrBlack(float[] hsl) {
- return isBlack(hsl) || isWhite(hsl);
- }
-
- /**
- * @return true if the color represents a color which is close to black.
- */
- private static boolean isBlack(float[] hslColor) {
- return hslColor[2] <= BLACK_MAX_LIGHTNESS;
- }
-
- /**
- * @return true if the color represents a color which is close to white.
- */
- private static boolean isWhite(float[] hslColor) {
- return hslColor[2] >= WHITE_MIN_LIGHTNESS;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
index 9ba1f7aa2f66..380cdadd1361 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
@@ -30,6 +30,7 @@ import com.android.systemui.statusbar.notification.collection.coordinator.dagger
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import dagger.Binds
import dagger.Module
import javax.inject.Inject
@@ -52,7 +53,8 @@ class SensitiveContentCoordinatorImpl @Inject constructor(
private val lockscreenUserManager: NotificationLockscreenUserManager,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val statusBarStateController: StatusBarStateController,
- private val keyguardStateController: KeyguardStateController
+ private val keyguardStateController: KeyguardStateController,
+ private val selectedUserInteractor: SelectedUserInteractor,
) : Invalidator("SensitiveContentInvalidator"),
SensitiveContentCoordinator,
DynamicPrivacyController.Listener,
@@ -67,10 +69,10 @@ class SensitiveContentCoordinatorImpl @Inject constructor(
override fun onDynamicPrivacyChanged(): Unit = invalidateList("onDynamicPrivacyChanged")
override fun onBeforeRenderList(entries: List<ListEntry>) {
- if (keyguardStateController.isKeyguardGoingAway() ||
- statusBarStateController.getState() == StatusBarState.KEYGUARD &&
+ if (keyguardStateController.isKeyguardGoingAway ||
+ statusBarStateController.state == StatusBarState.KEYGUARD &&
keyguardUpdateMonitor.getUserUnlockedWithBiometricAndIsBypassing(
- KeyguardUpdateMonitor.getCurrentUser())) {
+ selectedUserInteractor.getSelectedUserId())) {
// don't update yet if:
// - the keyguard is currently going away
// - LS is about to be dismissed by a biometric that bypasses LS (avoid notif flash)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index f750feda9091..1229cb9b49ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -31,6 +31,7 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
/** View-model for the shared notification container, used by both the shade and keyguard spaces */
class SharedNotificationContainerViewModel
@@ -45,8 +46,8 @@ constructor(
) {
private val statesForConstrainedNotifications =
setOf(
- KeyguardState.LOCKSCREEN,
KeyguardState.AOD,
+ KeyguardState.LOCKSCREEN,
KeyguardState.DOZING,
KeyguardState.ALTERNATE_BOUNCER,
KeyguardState.PRIMARY_BOUNCER
@@ -68,8 +69,17 @@ constructor(
/** If the user is visually on one of the unoccluded lockscreen states. */
val isOnLockscreen: Flow<Boolean> =
- keyguardTransitionInteractor.finishedKeyguardState
- .map { statesForConstrainedNotifications.contains(it) }
+ combine(
+ keyguardTransitionInteractor.finishedKeyguardState.map {
+ statesForConstrainedNotifications.contains(it)
+ },
+ keyguardTransitionInteractor
+ .transitionValue(KeyguardState.LOCKSCREEN)
+ .onStart { emit(0f) }
+ .map { it > 0 }
+ ) { constrainedNotificationState, transitioningToOrFromLockscreen ->
+ constrainedNotificationState || transitioningToOrFromLockscreen
+ }
.distinctUntilChanged()
/** Are we purely on the keyguard without the shade/qs? */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 2809cad34143..59f10aed4145 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -17,8 +17,6 @@
package com.android.systemui.statusbar.phone;
import static android.app.StatusBarManager.SESSION_KEYGUARD;
-import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
-import static com.android.systemui.keyguard.WakefulnessLifecycle.UNKNOWN_LAST_WAKE_TIME;
import android.annotation.IntDef;
import android.content.res.Resources;
@@ -30,7 +28,6 @@ import android.metrics.LogMaker;
import android.os.Handler;
import android.os.PowerManager;
import android.os.Trace;
-import android.view.HapticFeedbackConstants;
import androidx.annotation.Nullable;
@@ -47,22 +44,27 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.KeyguardViewController;
import com.android.keyguard.logging.BiometricUnlockLogger;
import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.keyguard.domain.interactor.BiometricUnlockInteractor;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.time.SystemClock;
+import dagger.Lazy;
+
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -73,12 +75,14 @@ import java.util.Set;
import javax.inject.Inject;
+import kotlinx.coroutines.ExperimentalCoroutinesApi;
+
/**
* Controller which coordinates all the biometric unlocking actions with the UI.
*/
+@ExperimentalCoroutinesApi
@SysUISingleton
public class BiometricUnlockController extends KeyguardUpdateMonitorCallback implements Dumpable {
- private static final long RECENT_POWER_BUTTON_PRESS_THRESHOLD_MS = 400L;
private static final long BIOMETRIC_WAKELOCK_TIMEOUT_MS = 15 * 1000;
private static final String BIOMETRIC_WAKE_LOCK_NAME = "wake-and-unlock:wakelock";
private static final UiEventLogger UI_EVENT_LOGGER = new UiEventLoggerImpl();
@@ -172,9 +176,12 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
private final WakefulnessLifecycle mWakefulnessLifecycle;
private final LatencyTracker mLatencyTracker;
private final VibratorHelper mVibratorHelper;
+ private final BiometricUnlockInteractor mBiometricUnlockInteractor;
private final BiometricUnlockLogger mLogger;
private final SystemClock mSystemClock;
private final boolean mOrderUnlockAndWake;
+ private final Lazy<SelectedUserInteractor> mSelectedUserInteractor;
+ private final DeviceEntryHapticsInteractor mHapticsInteractor;
private long mLastFpFailureUptimeMillis;
private int mNumConsecutiveFpFailures;
@@ -284,7 +291,10 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
ScreenOffAnimationController screenOffAnimationController,
VibratorHelper vibrator,
SystemClock systemClock,
- FeatureFlags featureFlags
+ FeatureFlags featureFlags,
+ DeviceEntryHapticsInteractor hapticsInteractor,
+ Lazy<SelectedUserInteractor> selectedUserInteractor,
+ BiometricUnlockInteractor biometricUnlockInteractor
) {
mPowerManager = powerManager;
mUpdateMonitor = keyguardUpdateMonitor;
@@ -293,6 +303,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
mLatencyTracker = latencyTracker;
mWakefulnessLifecycle = wakefulnessLifecycle;
mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
+ mBiometricUnlockInteractor = biometricUnlockInteractor;
mNotificationShadeWindowController = notificationShadeWindowController;
mDozeScrimController = dozeScrimController;
@@ -314,6 +325,8 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
mFeatureFlags = featureFlags;
mOrderUnlockAndWake = resources.getBoolean(
com.android.internal.R.bool.config_orderUnlockAndWake);
+ mHapticsInteractor = hapticsInteractor;
+ mSelectedUserInteractor = selectedUserInteractor;
dumpManager.registerDumpable(this);
}
@@ -434,7 +447,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
if (mode == MODE_WAKE_AND_UNLOCK
|| mode == MODE_WAKE_AND_UNLOCK_PULSING || mode == MODE_UNLOCK_COLLAPSING
|| mode == MODE_WAKE_AND_UNLOCK_FROM_DREAM || mode == MODE_DISMISS_BOUNCER) {
- vibrateSuccess(biometricSourceType);
+ mHapticsInteractor.vibrateSuccess();
onBiometricUnlockedWithKeyguardDismissal(biometricSourceType);
}
startWakeAndUnlock(mode);
@@ -498,8 +511,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
case MODE_WAKE_AND_UNLOCK:
if (mMode == MODE_WAKE_AND_UNLOCK_PULSING) {
Trace.beginSection("MODE_WAKE_AND_UNLOCK_PULSING");
- mMediaManager.updateMediaMetaData(false /* metaDataChanged */,
- true /* allowEnterAnimation */);
+ mMediaManager.updateMediaMetaData(false /* metaDataChanged */);
} else if (mMode == MODE_WAKE_AND_UNLOCK){
Trace.beginSection("MODE_WAKE_AND_UNLOCK");
} else {
@@ -523,6 +535,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
for (BiometricUnlockEventsListener listener : mBiometricUnlockEventsListeners) {
listener.onModeChanged(mode);
}
+ mBiometricUnlockInteractor.setBiometricUnlockState(mode);
}
private void onBiometricUnlockedWithKeyguardDismissal(BiometricSourceType biometricSourceType) {
@@ -535,7 +548,8 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
return mPendingAuthenticated != null
&& mUpdateMonitor
.isUnlockingWithBiometricAllowed(mPendingAuthenticated.isStrongBiometric)
- && mPendingAuthenticated.userId == KeyguardUpdateMonitor.getCurrentUser();
+ && mPendingAuthenticated.userId
+ == mSelectedUserInteractor.get().getSelectedUserId();
}
public @WakeAndUnlockMode int getMode() {
@@ -599,11 +613,11 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
// if unlocking isn't allowed, log more information about why unlocking may not
// have been allowed
final int strongAuthFlags = mUpdateMonitor.getStrongAuthTracker().getStrongAuthForUser(
- KeyguardUpdateMonitor.getCurrentUser());
+ mSelectedUserInteractor.get().getSelectedUserId());
final boolean nonStrongBiometricAllowed =
mUpdateMonitor.getStrongAuthTracker()
.isNonStrongBiometricAllowedAfterIdleTimeout(
- KeyguardUpdateMonitor.getCurrentUser());
+ mSelectedUserInteractor.get().getSelectedUserId());
mLogger.logCalculateModeForFingerprintUnlockingNotAllowed(strongBiometric,
strongAuthFlags, nonStrongBiometricAllowed, deviceInteractive, keyguardShowing);
@@ -669,11 +683,11 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
// if unlocking isn't allowed, log more information about why unlocking may not
// have been allowed
final int strongAuthFlags = mUpdateMonitor.getStrongAuthTracker().getStrongAuthForUser(
- KeyguardUpdateMonitor.getCurrentUser());
+ mSelectedUserInteractor.get().getSelectedUserId());
final boolean nonStrongBiometricAllowed =
mUpdateMonitor.getStrongAuthTracker()
.isNonStrongBiometricAllowedAfterIdleTimeout(
- KeyguardUpdateMonitor.getCurrentUser());
+ mSelectedUserInteractor.get().getSelectedUserId());
mLogger.logCalculateModeForPassiveAuthUnlockingNotAllowed(
strongBiometric, strongAuthFlags, nonStrongBiometricAllowed,
@@ -721,9 +735,9 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
// Suppress all face auth errors if fingerprint can be used to authenticate
if ((biometricSourceType == BiometricSourceType.FACE
&& !mUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
- KeyguardUpdateMonitor.getCurrentUser()))
+ mSelectedUserInteractor.get().getSelectedUserId()))
|| (biometricSourceType == BiometricSourceType.FINGERPRINT)) {
- vibrateError(biometricSourceType);
+ mHapticsInteractor.vibrateError();
}
cleanup();
@@ -750,45 +764,6 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
cleanup();
}
- // these haptics are for device-entry only
- private void vibrateSuccess(BiometricSourceType type) {
- if (mAuthController.isSfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser())
- && lastWakeupFromPowerButtonWithinHapticThreshold()) {
- mLogger.d("Skip auth success haptic. Power button was recently pressed.");
- return;
- }
- if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
- mVibratorHelper.performHapticFeedback(
- mKeyguardViewController.getViewRootImpl().getView(),
- HapticFeedbackConstants.CONFIRM
- );
- } else {
- mVibratorHelper.vibrateAuthSuccess(
- getClass().getSimpleName() + ", type =" + type + "device-entry::success");
- }
- }
-
- private boolean lastWakeupFromPowerButtonWithinHapticThreshold() {
- final boolean lastWakeupFromPowerButton = mWakefulnessLifecycle.getLastWakeReason()
- == PowerManager.WAKE_REASON_POWER_BUTTON;
- return lastWakeupFromPowerButton
- && mWakefulnessLifecycle.getLastWakeTime() != UNKNOWN_LAST_WAKE_TIME
- && mSystemClock.uptimeMillis() - mWakefulnessLifecycle.getLastWakeTime()
- < RECENT_POWER_BUTTON_PRESS_THRESHOLD_MS;
- }
-
- private void vibrateError(BiometricSourceType type) {
- if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
- mVibratorHelper.performHapticFeedback(
- mKeyguardViewController.getViewRootImpl().getView(),
- HapticFeedbackConstants.REJECT
- );
- } else {
- mVibratorHelper.vibrateAuthError(
- getClass().getSimpleName() + ", type =" + type + "device-entry::error");
- }
- }
-
private void cleanup() {
releaseBiometricWakeLock();
}
@@ -818,6 +793,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
for (BiometricUnlockEventsListener listener : mBiometricUnlockEventsListeners) {
listener.onResetMode();
}
+ mBiometricUnlockInteractor.setBiometricUnlockState(MODE_NONE);
mNumConsecutiveFpFailures = 0;
mLastFpFailureUptimeMillis = 0;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 7730f7d99f51..a11cbc3bf231 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -37,14 +37,15 @@ import androidx.annotation.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.AlwaysOnDisplayPolicy;
import com.android.systemui.doze.DozeScreenState;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.domain.interactor.DozeInteractor;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
@@ -55,9 +56,7 @@ import com.android.systemui.unfold.FoldAodAnimationController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
import java.io.PrintWriter;
-import java.util.HashSet;
import java.util.Optional;
-import java.util.Set;
import javax.inject.Inject;
@@ -83,12 +82,11 @@ public class DozeParameters implements
private final Resources mResources;
private final BatteryController mBatteryController;
private final ScreenOffAnimationController mScreenOffAnimationController;
+ private final DozeInteractor mDozeInteractor;
private final FoldAodAnimationController mFoldAodAnimationController;
private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
private final UserTracker mUserTracker;
- private final Set<Callback> mCallbacks = new HashSet<>();
-
private boolean mDozeAlwaysOn;
private boolean mControlScreenOffAnimation;
private boolean mIsQuickPickupEnabled;
@@ -131,7 +129,8 @@ public class DozeParameters implements
KeyguardUpdateMonitor keyguardUpdateMonitor,
ConfigurationController configurationController,
StatusBarStateController statusBarStateController,
- UserTracker userTracker) {
+ UserTracker userTracker,
+ DozeInteractor dozeInteractor) {
mResources = resources;
mAmbientDisplayConfiguration = ambientDisplayConfiguration;
mAlwaysOnPolicy = alwaysOnDisplayPolicy;
@@ -144,6 +143,7 @@ public class DozeParameters implements
mScreenOffAnimationController = screenOffAnimationController;
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
mUserTracker = userTracker;
+ mDozeInteractor = dozeInteractor;
keyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback);
tunerService.addTunable(
@@ -406,20 +406,6 @@ public class DozeParameters implements
return mResources.getStringArray(R.array.doze_brightness_sensor_name_posture_mapping);
}
- /**
- * Callback to listen for DozeParameter changes.
- */
- public void addCallback(Callback callback) {
- mCallbacks.add(callback);
- }
-
- /**
- * Remove callback that listens for DozeParameter changes.
- */
- public void removeCallback(Callback callback) {
- mCallbacks.remove(callback);
- }
-
@Override
public void onTuningChanged(String key, String newValue) {
mDozeAlwaysOn = mAmbientDisplayConfiguration.alwaysOnEnabled(mUserTracker.getUserId());
@@ -465,10 +451,9 @@ public class DozeParameters implements
}
private void dispatchAlwaysOnEvent() {
- for (Callback callback : mCallbacks) {
- callback.onAlwaysOnChange();
- }
mScreenOffAnimationController.onAlwaysOnChanged(getAlwaysOn());
+ mDozeInteractor.setAodAvailable(getAlwaysOn());
+
}
private boolean getPostureSpecificBool(
@@ -485,14 +470,6 @@ public class DozeParameters implements
return bool;
}
- /** Callbacks for doze parameter related information */
- public interface Callback {
- /**
- * Invoked when the value of getAlwaysOn may have changed.
- */
- void onAlwaysOnChange();
- }
-
private final class SettingsObserver extends ContentObserver {
private final Uri mQuickPickupGesture =
Settings.Secure.getUriFor(Settings.Secure.DOZE_QUICK_PICKUP_GESTURE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
index bde5c32c9f41..109e77e92b7b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
@@ -31,6 +31,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.Assert
import com.android.systemui.util.sensors.AsyncSensorManager
import java.io.PrintWriter
@@ -48,7 +49,8 @@ class KeyguardLiftController @Inject constructor(
private val asyncSensorManager: AsyncSensorManager,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val keyguardFaceAuthInteractor: KeyguardFaceAuthInteractor,
- private val dumpManager: DumpManager
+ private val dumpManager: DumpManager,
+ private val selectedUserInteractor: SelectedUserInteractor,
) : Dumpable, CoreStartable {
private val pickupSensor = asyncSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE)
@@ -115,7 +117,7 @@ class KeyguardLiftController @Inject constructor(
val onKeyguard = keyguardUpdateMonitor.isKeyguardVisible &&
!statusBarStateController.isDozing
- val userId = KeyguardUpdateMonitor.getCurrentUser()
+ val userId = selectedUserInteractor.getSelectedUserId()
val isFaceEnabled = keyguardUpdateMonitor.isFaceAuthEnabledForUser(userId)
val shouldListen = (onKeyguard || bouncerVisible) && isFaceEnabled
if (shouldListen != isListening) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
index 92c786fb569f..00fd9fbfffe3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
@@ -263,8 +263,7 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen
if (result.success) {
mCached = true;
mCache = result.bitmap;
- mMediaManager.updateMediaMetaData(
- true /* metaDataChanged */, true /* allowEnterAnimation */);
+ mMediaManager.updateMediaMetaData(true /* metaDataChanged */);
}
mLoader = null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 3adf3385e3cc..90fddd9ae22c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -92,6 +92,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.unfold.FoldAodAnimationController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import dagger.Lazy;
@@ -313,6 +314,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private final KeyguardUpdateMonitor mKeyguardUpdateManager;
private final LatencyTracker mLatencyTracker;
private final KeyguardSecurityModel mKeyguardSecurityModel;
+ private final SelectedUserInteractor mSelectedUserInteractor;
@Nullable private KeyguardBypassController mBypassController;
@Nullable private OccludingAppBiometricUI mOccludingAppBiometricUI;
@@ -370,7 +372,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
KeyguardTransitionInteractor keyguardTransitionInteractor,
@Main CoroutineDispatcher mainDispatcher,
Lazy<WindowManagerLockscreenVisibilityInteractor> wmLockscreenVisibilityInteractor,
- Lazy<KeyguardDismissActionInteractor> keyguardDismissActionInteractorLazy
+ Lazy<KeyguardDismissActionInteractor> keyguardDismissActionInteractorLazy,
+ SelectedUserInteractor selectedUserInteractor
) {
mContext = context;
mViewMediatorCallback = callback;
@@ -403,6 +406,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mMainDispatcher = mainDispatcher;
mWmLockscreenVisibilityInteractor = wmLockscreenVisibilityInteractor;
mKeyguardDismissActionInteractor = keyguardDismissActionInteractorLazy;
+ mSelectedUserInteractor = selectedUserInteractor;
}
KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@@ -961,7 +965,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN);
}
if (isShowing) {
- mMediaManager.updateMediaMetaData(false, animate && !isOccluded);
+ mMediaManager.updateMediaMetaData(false);
}
mNotificationShadeWindowController.setKeyguardOccluded(isOccluded);
@@ -1142,7 +1146,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
*/
public boolean isSecure() {
return mKeyguardSecurityModel.getSecurityMode(
- KeyguardUpdateMonitor.getCurrentUser()) != KeyguardSecurityModel.SecurityMode.None;
+ mSelectedUserInteractor.getSelectedUserId())
+ != KeyguardSecurityModel.SecurityMode.None;
}
/**
@@ -1690,7 +1695,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
*/
public boolean needsFullscreenBouncer() {
KeyguardSecurityModel.SecurityMode mode = mKeyguardSecurityModel.getSecurityMode(
- KeyguardUpdateMonitor.getCurrentUser());
+ mSelectedUserInteractor.getSelectedUserId());
return mode == KeyguardSecurityModel.SecurityMode.SimPin
|| mode == KeyguardSecurityModel.SecurityMode.SimPuk;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 2d14f6b3c508..57a8e6fe0d91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -221,7 +221,7 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu
@Override
public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
- mMediaManager.updateMediaMetaData(metaDataChanged, allowEnterAnimation);
+ mMediaManager.updateMediaMetaData(metaDataChanged);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLib.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLib.kt
index e9e52a2397e1..1670dd39ba24 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLib.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLib.kt
@@ -104,7 +104,7 @@ constructor(
val callback =
object : WifiPickerTracker.WifiPickerTrackerCallback {
override fun onWifiEntriesChanged() {
- val connectedEntry = wifiPickerTracker?.connectedWifiEntry
+ val connectedEntry = wifiPickerTracker.mergedOrPrimaryConnection
logOnWifiEntriesChanged(connectedEntry)
val secondaryNetworks =
@@ -217,6 +217,21 @@ constructor(
.stateIn(scope, SharingStarted.Eagerly, emptyList())
/**
+ * [WifiPickerTracker.getConnectedWifiEntry] stores a [MergedCarrierEntry] separately from the
+ * [WifiEntry] for the primary connection. Therefore, we have to prefer the carrier-merged entry
+ * if it exists, falling back on the connected entry if null
+ */
+ private val WifiPickerTracker?.mergedOrPrimaryConnection: WifiEntry?
+ get() {
+ val mergedEntry: MergedCarrierEntry? = this?.mergedCarrierEntry
+ return if (mergedEntry != null && mergedEntry.isDefaultNetwork) {
+ mergedEntry
+ } else {
+ this?.connectedWifiEntry
+ }
+ }
+
+ /**
* Converts WifiTrackerLib's [WifiEntry] into our internal model only if the entry is the
* primary network. Returns an inactive network if it's not primary.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index 1c88289706f3..c62451813699 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -37,11 +37,12 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.logging.KeyguardUpdateMonitorLogger;
import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
+import com.android.systemui.res.R;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import dagger.Lazy;
@@ -64,6 +65,7 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum
private final Context mContext;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final LockPatternUtils mLockPatternUtils;
+ private final SelectedUserInteractor mUserInteractor;
private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
new UpdateMonitorCallback();
private final Lazy<KeyguardUnlockAnimationController> mUnlockAnimationControllerLazy;
@@ -120,11 +122,13 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum
Lazy<KeyguardUnlockAnimationController> keyguardUnlockAnimationController,
KeyguardUpdateMonitorLogger logger,
DumpManager dumpManager,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags,
+ SelectedUserInteractor userInteractor) {
mContext = context;
mLogger = logger;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
+ mUserInteractor = userInteractor;
mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
mUnlockAnimationControllerLazy = keyguardUnlockAnimationController;
mFeatureFlags = featureFlags;
@@ -250,7 +254,7 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum
@VisibleForTesting
void update(boolean updateAlways) {
Trace.beginSection("KeyguardStateController#update");
- int user = KeyguardUpdateMonitor.getCurrentUser();
+ int user = mUserInteractor.getSelectedUserId();
boolean secure = mLockPatternUtils.isSecure(user);
boolean canDismissLockScreen = !secure || mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)
|| (Build.IS_DEBUGGABLE && DEBUG_AUTH_WITH_ADB && mDebugUnlocked);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.kt
index f88339a4d077..7829d6e7760a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.kt
@@ -27,7 +27,7 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.user.UserSwitchDialogController.DialogShower
import com.android.systemui.user.data.source.UserRecord
import com.android.systemui.user.domain.interactor.GuestUserInteractor
-import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper
import dagger.Lazy
import java.io.PrintWriter
@@ -41,7 +41,7 @@ class UserSwitcherController
@Inject
constructor(
@Application private val applicationContext: Context,
- private val userInteractorLazy: Lazy<UserInteractor>,
+ private val userSwitcherInteractorLazy: Lazy<UserSwitcherInteractor>,
private val guestUserInteractorLazy: Lazy<GuestUserInteractor>,
private val keyguardInteractorLazy: Lazy<KeyguardInteractor>,
private val activityStarter: ActivityStarter,
@@ -53,26 +53,29 @@ constructor(
fun onUserSwitched()
}
- private val userInteractor: UserInteractor by lazy { userInteractorLazy.get() }
+ private val mUserSwitcherInteractor: UserSwitcherInteractor by lazy {
+ userSwitcherInteractorLazy.get()
+ }
private val guestUserInteractor: GuestUserInteractor by lazy { guestUserInteractorLazy.get() }
private val keyguardInteractor: KeyguardInteractor by lazy { keyguardInteractorLazy.get() }
- private val callbackCompatMap = mutableMapOf<UserSwitchCallback, UserInteractor.UserCallback>()
+ private val callbackCompatMap =
+ mutableMapOf<UserSwitchCallback, UserSwitcherInteractor.UserCallback>()
/** The current list of [UserRecord]. */
val users: ArrayList<UserRecord>
- get() = userInteractor.userRecords.value
+ get() = mUserSwitcherInteractor.userRecords.value
/** Whether the user switcher experience should use the simple experience. */
val isSimpleUserSwitcher: Boolean
- get() = userInteractor.isSimpleUserSwitcher
+ get() = mUserSwitcherInteractor.isSimpleUserSwitcher
val isUserSwitcherEnabled: Boolean
- get() = userInteractor.isUserSwitcherEnabled
+ get() = mUserSwitcherInteractor.isUserSwitcherEnabled
/** The [UserRecord] of the current user or `null` when none. */
val currentUserRecord: UserRecord?
- get() = userInteractor.selectedUserRecord.value
+ get() = mUserSwitcherInteractor.selectedUserRecord.value
/** The name of the current user of the device or `null`, when none is selected. */
val currentUserName: String?
@@ -81,8 +84,8 @@ constructor(
LegacyUserUiHelper.getUserRecordName(
context = applicationContext,
record = it,
- isGuestUserAutoCreated = userInteractor.isGuestUserAutoCreated,
- isGuestUserResetting = userInteractor.isGuestUserResetting,
+ isGuestUserAutoCreated = mUserSwitcherInteractor.isGuestUserAutoCreated,
+ isGuestUserResetting = mUserSwitcherInteractor.isGuestUserResetting,
)
}
@@ -98,21 +101,21 @@ constructor(
* @param dialogShower An optional [DialogShower] in case we need to show dialogs.
*/
fun onUserSelected(userId: Int, dialogShower: DialogShower?) {
- userInteractor.selectUser(userId, dialogShower)
+ mUserSwitcherInteractor.selectUser(userId, dialogShower)
}
/** Whether the guest user is configured to always be present on the device. */
val isGuestUserAutoCreated: Boolean
- get() = userInteractor.isGuestUserAutoCreated
+ get() = mUserSwitcherInteractor.isGuestUserAutoCreated
/** Whether the guest user is currently being reset. */
val isGuestUserResetting: Boolean
- get() = userInteractor.isGuestUserResetting
+ get() = mUserSwitcherInteractor.isGuestUserResetting
/** Registers an adapter to notify when the users change. */
fun addAdapter(adapter: WeakReference<BaseUserSwitcherAdapter>) {
- userInteractor.addCallback(
- object : UserInteractor.UserCallback {
+ mUserSwitcherInteractor.addCallback(
+ object : UserSwitcherInteractor.UserCallback {
override fun isEvictable(): Boolean {
return adapter.get() == null
}
@@ -129,7 +132,7 @@ constructor(
record: UserRecord,
dialogShower: DialogShower?,
) {
- userInteractor.onRecordSelected(record, dialogShower)
+ mUserSwitcherInteractor.onRecordSelected(record, dialogShower)
}
/**
@@ -152,7 +155,7 @@ constructor(
* `UserHandle.USER_NULL`, then switch immediately to the newly created guest user.
*/
fun removeGuestUser(guestUserId: Int, targetUserId: Int) {
- userInteractor.removeGuestUser(
+ mUserSwitcherInteractor.removeGuestUser(
guestUserId = guestUserId,
targetUserId = targetUserId,
)
@@ -168,7 +171,7 @@ constructor(
* only if its ephemeral, else keep guest
*/
fun exitGuestUser(guestUserId: Int, targetUserId: Int, forceRemoveGuestOnExit: Boolean) {
- userInteractor.exitGuestUser(guestUserId, targetUserId, forceRemoveGuestOnExit)
+ mUserSwitcherInteractor.exitGuestUser(guestUserId, targetUserId, forceRemoveGuestOnExit)
}
/**
@@ -194,31 +197,31 @@ constructor(
* The pictures are only loaded if they have not been loaded yet.
*/
fun refreshUsers() {
- userInteractor.refreshUsers()
+ mUserSwitcherInteractor.refreshUsers()
}
/** Adds a subscriber to when user switches. */
fun addUserSwitchCallback(callback: UserSwitchCallback) {
val interactorCallback =
- object : UserInteractor.UserCallback {
+ object : UserSwitcherInteractor.UserCallback {
override fun onUserStateChanged() {
callback.onUserSwitched()
}
}
callbackCompatMap[callback] = interactorCallback
- userInteractor.addCallback(interactorCallback)
+ mUserSwitcherInteractor.addCallback(interactorCallback)
}
/** Removes a previously-added subscriber. */
fun removeUserSwitchCallback(callback: UserSwitchCallback) {
val interactorCallback = callbackCompatMap.remove(callback)
if (interactorCallback != null) {
- userInteractor.removeCallback(interactorCallback)
+ mUserSwitcherInteractor.removeCallback(interactorCallback)
}
}
fun dump(pw: PrintWriter, args: Array<out String>) {
- userInteractor.dump(pw)
+ mUserSwitcherInteractor.dump(pw)
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt
index dc7fadd5eb14..1238789389ee 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt
@@ -21,7 +21,6 @@ import android.graphics.drawable.Drawable
import android.os.Handler
import android.os.UserManager
import android.provider.Settings.Global.USER_SWITCHER_ENABLED
-import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
@@ -30,7 +29,6 @@ import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.qs.SettingObserver
import com.android.systemui.qs.footer.data.model.UserSwitcherStatusModel
import com.android.systemui.res.R
-import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.policy.UserInfoController
import com.android.systemui.statusbar.policy.UserSwitcherController
import com.android.systemui.util.settings.GlobalSettings
@@ -61,10 +59,10 @@ constructor(
@Background private val bgHandler: Handler,
@Background private val bgDispatcher: CoroutineDispatcher,
private val userManager: UserManager,
- private val userTracker: UserTracker,
private val userSwitcherController: UserSwitcherController,
private val userInfoController: UserInfoController,
private val globalSetting: GlobalSettings,
+ private val userRepository: UserRepository,
) : UserSwitcherRepository {
private val showUserSwitcherForSingleUser =
context.resources.getBoolean(R.bool.qs_show_user_switcher_for_single_user)
@@ -80,7 +78,7 @@ constructor(
globalSetting,
bgHandler,
USER_SWITCHER_ENABLED,
- userTracker.userId,
+ userRepository.getSelectedUserInfo().id,
) {
override fun handleValueChanged(value: Int, observedChange: Boolean) {
if (observedChange) {
@@ -147,7 +145,7 @@ constructor(
private suspend fun isGuestUser(): Boolean {
return withContext(bgDispatcher) {
- userManager.isGuestUser(KeyguardUpdateMonitor.getCurrentUser())
+ userManager.isGuestUser(userRepository.getSelectedUserInfo().id)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt
new file mode 100644
index 000000000000..0e693d0c8622
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt
@@ -0,0 +1,38 @@
+package com.android.systemui.user.domain.interactor
+
+import android.annotation.UserIdInt
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags.REFACTOR_GETCURRENTUSER
+import com.android.systemui.user.data.repository.UserRepository
+import javax.inject.Inject
+
+/** Encapsulates business logic to interact the selected user */
+@SysUISingleton
+class SelectedUserInteractor
+@Inject
+constructor(
+ private val repository: UserRepository,
+ private val flags: FeatureFlagsClassic,
+) {
+
+ /**
+ * Returns the ID of the currently-selected user.
+ *
+ * @param bypassFlag this will ignore the feature flag and get the data from the repository
+ * instead. This is used for refactored methods that were previously pointing to `userTracker`
+ * and therefore should not be routed back to KeyguardUpdateMonitor when flag is disabled.
+ * KeyguardUpdateMonitor.getCurrentUser() is deprecated and will be removed soon (together
+ * with this flag).
+ */
+ @UserIdInt
+ @JvmOverloads
+ fun getSelectedUserId(bypassFlag: Boolean = false): Int {
+ if (bypassFlag || flags.isEnabled(REFACTOR_GETCURRENTUSER)) {
+ return repository.getSelectedUserInfo().id
+ } else {
+ return KeyguardUpdateMonitor.getCurrentUser()
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt
index dbc3bf3a75a2..e0d205fc4b6a 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 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.
@@ -83,9 +83,9 @@ import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
-/** Encapsulates business logic to interact with user data and systems. */
+/** Encapsulates business logic to for the user switcher. */
@SysUISingleton
-class UserInteractor
+class UserSwitcherInteractor
@Inject
constructor(
@Application private val applicationContext: Context,
@@ -383,10 +383,6 @@ constructor(
pw.println("isGuestUserAutoCreated=$isGuestUserAutoCreated")
}
- fun onDeviceBootCompleted() {
- guestUserInteractor.onDeviceBootCompleted()
- }
-
/** Switches to the user or executes the action represented by the given record. */
fun onRecordSelected(
record: UserRecord,
@@ -535,12 +531,6 @@ constructor(
}
}
- /** Returns the ID of the currently-selected user. */
- @UserIdInt
- fun getSelectedUserId(): Int {
- return repository.getSelectedUserInfo().id
- }
-
private fun showDialog(request: ShowDialogRequestModel) {
_dialogShowRequests.value = request
}
@@ -664,7 +654,6 @@ constructor(
// Connect to the new secondary user's service (purely to ensure that a persistent
// SystemUI application is created for that user)
-
if (userId != Process.myUserHandle().identifier) {
applicationContext.startServiceAsUser(
intent,
@@ -826,6 +815,6 @@ constructor(
}
companion object {
- private const val TAG = "UserInteractor"
+ private const val TAG = "UserSwitcherInteractor"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt
index 0930cb8a3d7a..922dc0521a49 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt
@@ -33,7 +33,7 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.tiles.UserDetailView
import com.android.systemui.user.UserSwitchFullscreenDialog
-import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
import com.android.systemui.user.domain.model.ShowDialogRequestModel
import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
import dagger.Lazy
@@ -53,7 +53,7 @@ constructor(
private val falsingManager: Lazy<FalsingManager>,
private val broadcastSender: Lazy<BroadcastSender>,
private val dialogLaunchAnimator: Lazy<DialogLaunchAnimator>,
- private val interactor: Lazy<UserInteractor>,
+ private val interactor: Lazy<UserSwitcherInteractor>,
private val userDetailAdapterProvider: Provider<UserDetailView.Adapter>,
private val eventLogger: Lazy<UiEventLogger>,
private val activityStarter: Lazy<ActivityStarter>,
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModel.kt
index 78edad7c3af2..2c425b199b4e 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModel.kt
@@ -17,12 +17,10 @@
package com.android.systemui.user.ui.viewmodel
-import android.content.Context
import android.graphics.drawable.Drawable
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Text
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -33,8 +31,7 @@ import kotlinx.coroutines.flow.mapLatest
class StatusBarUserChipViewModel
@Inject
constructor(
- @Application private val context: Context,
- interactor: UserInteractor,
+ interactor: UserSwitcherInteractor,
) {
/** Whether the status bar chip ui should be available */
val chipEnabled: Boolean = interactor.isStatusBarUserChipEnabled
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
index 20f0fa8cf46b..4089889f4b1e 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
@@ -20,9 +20,8 @@ package com.android.systemui.user.ui.viewmodel
import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.ui.drawable.CircularDrawable
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.res.R
import com.android.systemui.user.domain.interactor.GuestUserInteractor
-import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper
import com.android.systemui.user.shared.model.UserActionModel
import com.android.systemui.user.shared.model.UserModel
@@ -38,17 +37,17 @@ import kotlinx.coroutines.flow.map
class UserSwitcherViewModel
@Inject
constructor(
- private val userInteractor: UserInteractor,
+ private val userSwitcherInteractor: UserSwitcherInteractor,
private val guestUserInteractor: GuestUserInteractor,
) {
/** The currently selected user. */
val selectedUser: Flow<UserViewModel> =
- userInteractor.selectedUser.map { user -> toViewModel(user) }
+ userSwitcherInteractor.selectedUser.map { user -> toViewModel(user) }
/** On-device users. */
val users: Flow<List<UserViewModel>> =
- userInteractor.users.map { models -> models.map { user -> toViewModel(user) } }
+ userSwitcherInteractor.users.map { models -> models.map { user -> toViewModel(user) } }
/** The maximum number of columns that the user selection grid should use. */
val maximumUserColumns: Flow<Int> = users.map { getMaxUserSwitcherItemColumns(it.size) }
@@ -61,7 +60,9 @@ constructor(
val isMenuVisible: Flow<Boolean> = _isMenuVisible
/** The user action menu. */
val menu: Flow<List<UserActionViewModel>> =
- userInteractor.actions.map { actions -> actions.map { action -> toViewModel(action) } }
+ userSwitcherInteractor.actions.map { actions ->
+ actions.map { action -> toViewModel(action) }
+ }
/** Whether the button to open the user action menu is visible. */
val isOpenMenuButtonVisible: Flow<Boolean> = menu.map { it.isNotEmpty() }
@@ -175,7 +176,7 @@ constructor(
isTablet = true,
),
onClicked = {
- userInteractor.executeAction(action = model)
+ userSwitcherInteractor.executeAction(action = model)
// We don't finish because we want to show a dialog over the full-screen UI and
// that dialog can be dismissed in case the user changes their mind and decides not
// to add a user.
@@ -195,7 +196,7 @@ constructor(
null
} else {
{
- userInteractor.selectUser(model.id)
+ userSwitcherInteractor.selectUser(model.id)
userSwitched.value = true
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java b/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java
index 9b06a37e681f..d56672564f6f 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java
@@ -191,7 +191,7 @@ public class AsyncSensorManager extends SensorManager
}
@Override
- protected boolean initDataInjectionImpl(boolean enable) {
+ protected boolean initDataInjectionImpl(boolean enable, @DataInjectionMode int mode) {
throw new UnsupportedOperationException("not implemented");
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/Events.java b/packages/SystemUI/src/com/android/systemui/volume/Events.java
index 2d1e622fbdce..50d15475434b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/Events.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/Events.java
@@ -234,6 +234,10 @@ public class Events {
VOLUME_DIALOG_SLIDER(150),
@UiEvent(doc = "The audio stream was set to silent via slider")
VOLUME_DIALOG_SLIDER_TO_ZERO(151),
+ @UiEvent(doc = "ODI captions was clicked")
+ VOLUME_DIALOG_ODI_CAPTIONS_CLICKED(1503),
+ @UiEvent(doc = "ODI captions tooltip dismiss was clicked")
+ VOLUME_DIALOG_ODI_CAPTIONS_TOOLTIP_CLICKED(1504),
@UiEvent(doc = "The audio volume was adjusted to silent via key")
VOLUME_KEY_TO_ZERO(152),
@UiEvent(doc = "The audio volume was adjusted to non-silent via key")
@@ -362,6 +366,10 @@ public class Events {
if (tag == EVENT_SETTINGS_CLICK) {
sLegacyLogger.action(MetricsEvent.ACTION_VOLUME_SETTINGS);
sUiEventLogger.log(VolumeDialogEvent.VOLUME_DIALOG_SETTINGS_CLICK);
+ } else if (tag == EVENT_ODI_CAPTIONS_CLICK) {
+ sUiEventLogger.log(VolumeDialogEvent.VOLUME_DIALOG_ODI_CAPTIONS_CLICKED);
+ } else if (tag == EVENT_ODI_CAPTIONS_TOOLTIP_CLICK) {
+ sUiEventLogger.log(VolumeDialogEvent.VOLUME_DIALOG_ODI_CAPTIONS_TOOLTIP_CLICKED);
}
return sb.toString();
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 929b91cf6993..0ff308e19dd6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -120,7 +120,6 @@ import com.android.internal.view.RotationPolicy;
import com.android.settingslib.Utils;
import com.android.systemui.Dumpable;
import com.android.systemui.Prefs;
-import com.android.systemui.res.R;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
@@ -129,6 +128,7 @@ import com.android.systemui.plugins.VolumeDialog;
import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.plugins.VolumeDialogController.State;
import com.android.systemui.plugins.VolumeDialogController.StreamState;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DevicePostureController;
@@ -287,7 +287,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
private boolean mIsAnimatingDismiss = false;
private boolean mHasSeenODICaptionsTooltip;
private ViewStub mODICaptionsTooltipViewStub;
- private View mODICaptionsTooltipView = null;
+ @VisibleForTesting View mODICaptionsTooltipView = null;
private final boolean mUseBackgroundBlur;
private Consumer<Boolean> mCrossWindowBlurEnabledListener;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
index 81fef7adb3ea..b31f630a4317 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
@@ -35,9 +35,12 @@ import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_K
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.FakeSettings
+import java.io.PrintWriter
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
@@ -45,26 +48,21 @@ import org.junit.Test
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
-import java.io.PrintWriter
@SmallTest
class ActiveUnlockConfigTest : SysuiTestCase() {
private lateinit var secureSettings: FakeSettings
- @Mock
- private lateinit var contentResolver: ContentResolver
- @Mock
- private lateinit var handler: Handler
- @Mock
- private lateinit var dumpManager: DumpManager
- @Mock
- private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock private lateinit var contentResolver: ContentResolver
+ @Mock private lateinit var handler: Handler
+ @Mock private lateinit var dumpManager: DumpManager
+ @Mock private lateinit var selectedUserInteractor: SelectedUserInteractor
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var mockPrintWriter: PrintWriter
- @Captor
- private lateinit var settingsObserverCaptor: ArgumentCaptor<ContentObserver>
+ @Captor private lateinit var settingsObserverCaptor: ArgumentCaptor<ContentObserver>
private lateinit var activeUnlockConfig: ActiveUnlockConfig
private var currentUser: Int = 0
@@ -73,14 +71,16 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
- currentUser = KeyguardUpdateMonitor.getCurrentUser()
+ whenever(selectedUserInteractor.getSelectedUserId()).thenReturn(currentUser)
secureSettings = FakeSettings()
- activeUnlockConfig = ActiveUnlockConfig(
+ activeUnlockConfig =
+ ActiveUnlockConfig(
handler,
secureSettings,
contentResolver,
+ selectedUserInteractor,
dumpManager
- )
+ )
}
@Test
@@ -92,8 +92,9 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
fun onWakeupSettingChanged() {
// GIVEN no active unlock settings enabled
assertFalse(
- activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE)
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE
+ )
)
// WHEN unlock on wake is allowed
@@ -102,16 +103,19 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
// THEN active unlock triggers allowed on: wake, unlock-intent, and biometric failure
assertTrue(
- activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE)
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE
+ )
)
assertTrue(
- activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT)
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
+ )
)
assertTrue(
- activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL)
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL
+ )
)
}
@@ -119,8 +123,9 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
fun onUnlockIntentSettingChanged() {
// GIVEN no active unlock settings enabled
assertFalse(
- activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT)
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
+ )
)
// WHEN unlock on biometric failed is allowed
@@ -128,12 +133,21 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT))
// THEN active unlock triggers allowed on: biometric failure ONLY
- assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE))
- assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT))
- assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL))
+ assertFalse(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE
+ )
+ )
+ assertTrue(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
+ )
+ )
+ assertTrue(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL
+ )
+ )
}
@Test
@@ -141,24 +155,39 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
// GIVEN no active unlock settings enabled and triggering unlock intent on biometric
// enrollment setting is disabled (empty string is disabled, null would use the default)
secureSettings.putStringForUser(
- ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED, "", currentUser)
- updateSetting(secureSettings.getUriFor(
- ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
- ))
- assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL))
+ ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
+ "",
+ currentUser
+ )
+ updateSetting(
+ secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED)
+ )
+ assertFalse(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL
+ )
+ )
// WHEN unlock on biometric failed is allowed
secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 1, currentUser)
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
// THEN active unlock triggers allowed on: biometric failure ONLY
- assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE))
- assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT))
- assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL))
+ assertFalse(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE
+ )
+ )
+ assertFalse(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
+ )
+ )
+ assertTrue(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL
+ )
+ )
}
@Test
@@ -168,16 +197,21 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
// WHEN face error timeout (3), allow trigger active unlock
- secureSettings.putStringForUser(
- ACTIVE_UNLOCK_ON_FACE_ERRORS, "3", currentUser)
+ secureSettings.putStringForUser(ACTIVE_UNLOCK_ON_FACE_ERRORS, "3", currentUser)
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ERRORS))
// THEN active unlock triggers allowed on error TIMEOUT
- assertTrue(activeUnlockConfig.shouldRequestActiveUnlockOnFaceError(
- BiometricFaceConstants.FACE_ERROR_TIMEOUT))
+ assertTrue(
+ activeUnlockConfig.shouldRequestActiveUnlockOnFaceError(
+ BiometricFaceConstants.FACE_ERROR_TIMEOUT
+ )
+ )
- assertFalse(activeUnlockConfig.shouldRequestActiveUnlockOnFaceError(
- BiometricFaceConstants.FACE_ERROR_CANCELED))
+ assertFalse(
+ activeUnlockConfig.shouldRequestActiveUnlockOnFaceError(
+ BiometricFaceConstants.FACE_ERROR_CANCELED
+ )
+ )
}
@Test
@@ -189,21 +223,34 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
// WHEN face acquiredMsg DARK_GLASSESand MOUTH_COVERING are allowed to trigger
secureSettings.putStringForUser(
ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO,
- "${BiometricFaceConstants.FACE_ACQUIRED_MOUTH_COVERING_DETECTED}" +
- "|${BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED}",
- currentUser)
+ "${BiometricFaceConstants.FACE_ACQUIRED_MOUTH_COVERING_DETECTED}" +
+ "|${BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED}",
+ currentUser
+ )
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO))
// THEN active unlock triggers allowed on acquired messages DARK_GLASSES & MOUTH_COVERING
- assertTrue(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
- BiometricFaceConstants.FACE_ACQUIRED_MOUTH_COVERING_DETECTED))
- assertTrue(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
- BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED))
-
- assertFalse(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
- BiometricFaceConstants.FACE_ACQUIRED_GOOD))
- assertFalse(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
- BiometricFaceConstants.FACE_ACQUIRED_NOT_DETECTED))
+ assertTrue(
+ activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
+ BiometricFaceConstants.FACE_ACQUIRED_MOUTH_COVERING_DETECTED
+ )
+ )
+ assertTrue(
+ activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
+ BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED
+ )
+ )
+
+ assertFalse(
+ activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
+ BiometricFaceConstants.FACE_ACQUIRED_GOOD
+ )
+ )
+ assertFalse(
+ activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
+ BiometricFaceConstants.FACE_ACQUIRED_NOT_DETECTED
+ )
+ )
}
@Test
@@ -221,14 +268,19 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
secureSettings.putStringForUser(
ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
- "${ActiveUnlockConfig.BiometricType.NONE.intValue}", currentUser)
- updateSetting(secureSettings.getUriFor(
- ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
- ))
+ "${ActiveUnlockConfig.BiometricType.NONE.intValue}",
+ currentUser
+ )
+ updateSetting(
+ secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED)
+ )
// THEN active unlock triggers allowed on unlock intent
- assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT))
+ assertTrue(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
+ )
+ )
}
@Test
@@ -245,33 +297,43 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
// WHEN unlock intent is allowed when ONLY fingerprint is enrolled or NO biometircs
// are enrolled
secureSettings.putStringForUser(
- ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
- "${ActiveUnlockConfig.BiometricType.ANY_FACE.intValue}" +
- "|${ActiveUnlockConfig.BiometricType.ANY_FINGERPRINT.intValue}",
- currentUser)
- updateSetting(secureSettings.getUriFor(
- ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
- ))
+ ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
+ "${ActiveUnlockConfig.BiometricType.ANY_FACE.intValue}" +
+ "|${ActiveUnlockConfig.BiometricType.ANY_FINGERPRINT.intValue}",
+ currentUser
+ )
+ updateSetting(
+ secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED)
+ )
// THEN active unlock triggers NOT allowed on unlock intent
- assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT))
+ assertFalse(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
+ )
+ )
// WHEN fingerprint ONLY enrolled
`when`(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(false)
`when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(true)
// THEN active unlock triggers allowed on unlock intent
- assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT))
+ assertTrue(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
+ )
+ )
// WHEN face ONLY enrolled
`when`(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(true)
`when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(false)
// THEN active unlock triggers allowed on unlock intent
- assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT))
+ assertTrue(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
+ )
+ )
}
@Test
@@ -280,7 +342,8 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
secureSettings.putIntForUser(
ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
PowerManager.WAKE_REASON_LIFT,
- currentUser)
+ currentUser
+ )
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
// THEN only WAKE_REASON_LIFT is considered an unlock intent
@@ -299,16 +362,18 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
secureSettings.putStringForUser(
ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
PowerManager.WAKE_REASON_LIFT.toString() +
- "|" +
- PowerManager.WAKE_REASON_TAP.toString(),
+ "|" +
+ PowerManager.WAKE_REASON_TAP.toString(),
currentUser
)
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
// THEN WAKE_REASON_LIFT and WAKE_REASON TAP are considered an unlock intent
for (wakeReason in 0..WAKE_REASON_BIOMETRIC) {
- if (wakeReason == PowerManager.WAKE_REASON_LIFT ||
- wakeReason == PowerManager.WAKE_REASON_TAP) {
+ if (
+ wakeReason == PowerManager.WAKE_REASON_LIFT ||
+ wakeReason == PowerManager.WAKE_REASON_TAP
+ ) {
assertTrue(activeUnlockConfig.isWakeupConsideredUnlockIntent(wakeReason))
} else {
assertFalse(activeUnlockConfig.isWakeupConsideredUnlockIntent(wakeReason))
@@ -316,26 +381,36 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
}
assertTrue(activeUnlockConfig.isWakeupConsideredUnlockIntent(PowerManager.WAKE_REASON_LIFT))
assertTrue(activeUnlockConfig.isWakeupConsideredUnlockIntent(PowerManager.WAKE_REASON_TAP))
- assertFalse(activeUnlockConfig.isWakeupConsideredUnlockIntent(
- PowerManager.WAKE_REASON_UNFOLD_DEVICE))
+ assertFalse(
+ activeUnlockConfig.isWakeupConsideredUnlockIntent(
+ PowerManager.WAKE_REASON_UNFOLD_DEVICE
+ )
+ )
}
@Test
fun isWakeupConsideredUnlockIntent_emptyValues() {
// GIVEN lift and tap are considered an unlock intent
- secureSettings.putStringForUser(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS, " ",
- currentUser)
+ secureSettings.putStringForUser(
+ ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
+ " ",
+ currentUser
+ )
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
// THEN no wake up gestures are considered an unlock intent
for (wakeReason in 0..WAKE_REASON_BIOMETRIC) {
assertFalse(activeUnlockConfig.isWakeupConsideredUnlockIntent(wakeReason))
}
- assertFalse(activeUnlockConfig.isWakeupConsideredUnlockIntent(
- PowerManager.WAKE_REASON_LIFT))
+ assertFalse(
+ activeUnlockConfig.isWakeupConsideredUnlockIntent(PowerManager.WAKE_REASON_LIFT)
+ )
assertFalse(activeUnlockConfig.isWakeupConsideredUnlockIntent(PowerManager.WAKE_REASON_TAP))
- assertFalse(activeUnlockConfig.isWakeupConsideredUnlockIntent(
- PowerManager.WAKE_REASON_UNFOLD_DEVICE))
+ assertFalse(
+ activeUnlockConfig.isWakeupConsideredUnlockIntent(
+ PowerManager.WAKE_REASON_UNFOLD_DEVICE
+ )
+ )
}
@Test
@@ -343,11 +418,12 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
verifyRegisterSettingObserver()
// GIVEN lift is considered an unlock intent
- secureSettings.putStringForUser(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
- PowerManager.WAKE_REASON_LIFT.toString(), currentUser)
- updateSetting(secureSettings.getUriFor(
- ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD
- ))
+ secureSettings.putStringForUser(
+ ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
+ PowerManager.WAKE_REASON_LIFT.toString(),
+ currentUser
+ )
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD))
// THEN only WAKE_REASON_LIFT is considered an unlock intent
for (wakeReason in 0..WAKE_REASON_BIOMETRIC) {
@@ -364,11 +440,12 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
verifyRegisterSettingObserver()
// GIVEN lift and tap are considered an unlock intent
- secureSettings.putStringForUser(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
- " ", currentUser)
- updateSetting(secureSettings.getUriFor(
- ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD
- ))
+ secureSettings.putStringForUser(
+ ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
+ " ",
+ currentUser
+ )
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD))
// THEN no wake up gestures are considered an unlock intent
for (wakeReason in 0..WAKE_REASON_BIOMETRIC) {
@@ -381,20 +458,21 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
verifyRegisterSettingObserver()
// GIVEN lift and tap are considered an unlock intent
- secureSettings.putStringForUser(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
+ secureSettings.putStringForUser(
+ ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD,
PowerManager.WAKE_REASON_LIFT.toString() +
- "|" +
- PowerManager.WAKE_REASON_TAP.toString(),
+ "|" +
+ PowerManager.WAKE_REASON_TAP.toString(),
currentUser
)
- updateSetting(secureSettings.getUriFor(
- ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD
- ))
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD))
// THEN WAKE_REASON_LIFT and WAKE_REASON TAP are considered an unlock intent
for (wakeReason in 0..WAKE_REASON_BIOMETRIC) {
- if (wakeReason == PowerManager.WAKE_REASON_LIFT ||
- wakeReason == PowerManager.WAKE_REASON_TAP) {
+ if (
+ wakeReason == PowerManager.WAKE_REASON_LIFT ||
+ wakeReason == PowerManager.WAKE_REASON_TAP
+ ) {
assertTrue(activeUnlockConfig.shouldWakeupForceDismissKeyguard(wakeReason))
} else {
assertFalse(activeUnlockConfig.shouldWakeupForceDismissKeyguard(wakeReason))
@@ -405,13 +483,16 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
@Test
fun dump_onUnlockIntentWhenBiometricEnrolled_invalidNum_noArrayOutOfBoundsException() {
// GIVEN an invalid input (-1)
- secureSettings.putStringForUser(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
- "-1", currentUser)
+ secureSettings.putStringForUser(
+ ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
+ "-1",
+ currentUser
+ )
// WHEN the setting updates
- updateSetting(secureSettings.getUriFor(
- ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
- ))
+ updateSetting(
+ secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED)
+ )
// THEN no exception thrown
activeUnlockConfig.dump(mockPrintWriter, emptyArray())
@@ -419,12 +500,7 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
private fun updateSetting(uri: Uri) {
verifyRegisterSettingObserver()
- settingsObserverCaptor.value.onChange(
- false,
- listOf(uri),
- 0,
- 0 /* flags */
- )
+ settingsObserverCaptor.value.onChange(false, listOf(uri), 0, 0 /* flags */)
}
private fun verifyRegisterSettingObserver() {
@@ -433,19 +509,21 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
verifyRegisterSettingObserver(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
verifyRegisterSettingObserver(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ERRORS))
verifyRegisterSettingObserver(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO))
- verifyRegisterSettingObserver(secureSettings.getUriFor(
- ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
- ))
- verifyRegisterSettingObserver(secureSettings.getUriFor(
- ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS
- ))
+ verifyRegisterSettingObserver(
+ secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED)
+ )
+ verifyRegisterSettingObserver(
+ secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS)
+ )
}
private fun verifyRegisterSettingObserver(uri: Uri) {
- verify(contentResolver).registerContentObserver(
+ verify(contentResolver)
+ .registerContentObserver(
eq(uri),
eq(false),
capture(settingsObserverCaptor),
- eq(UserHandle.USER_ALL))
+ eq(UserHandle.USER_ALL)
+ )
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
index d506584cdfac..cdd0eb0e265d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
@@ -50,6 +50,7 @@ import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import org.junit.After;
import org.junit.Before;
@@ -64,7 +65,7 @@ import org.mockito.MockitoAnnotations;
@SmallTest
public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase {
- private static final int TARGET_USER_ID = KeyguardUpdateMonitor.getCurrentUser();
+ private static final int TARGET_USER_ID = 0;
private AdminSecondaryLockScreenController mTestController;
private ComponentName mComponentName;
@@ -80,12 +81,15 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase {
private KeyguardSecurityCallback mKeyguardCallback;
@Mock
private KeyguardUpdateMonitor mUpdateMonitor;
+ @Mock
+ private SelectedUserInteractor mSelectedUserInteractor;
private SurfaceControlViewHost.SurfacePackage mSurfacePackage;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ when(mSelectedUserInteractor.getSelectedUserId()).thenReturn(TARGET_USER_ID);
mKeyguardSecurityContainer = spy(new KeyguardSecurityContainer(mContext));
mKeyguardSecurityContainer.setId(View.generateViewId());
@@ -106,7 +110,8 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase {
new Binder())).getSurfacePackage();
mTestController = new AdminSecondaryLockScreenController.Factory(
- mContext, mKeyguardSecurityContainer, mUpdateMonitor, mHandler)
+ mContext, mKeyguardSecurityContainer, mUpdateMonitor, mHandler,
+ mSelectedUserInteractor)
.create(mKeyguardCallback);
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
index 30fed0b0316f..c61b11ae7336 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
@@ -29,6 +29,7 @@ import com.android.internal.widget.LockPatternUtils
import com.android.systemui.SysuiTestCase
import com.android.systemui.shade.ShadeController
import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
@@ -59,6 +60,7 @@ class EmergencyButtonControllerTest : SysuiTestCase() {
@Mock lateinit var metricsLogger: MetricsLogger
@Mock lateinit var lockPatternUtils: LockPatternUtils
@Mock lateinit var packageManager: PackageManager
+ @Mock lateinit var mSelectedUserInteractor: SelectedUserInteractor
val fakeSystemClock = FakeSystemClock()
val mainExecutor = FakeExecutor(fakeSystemClock)
val backgroundExecutor = FakeExecutor(fakeSystemClock)
@@ -79,7 +81,8 @@ class EmergencyButtonControllerTest : SysuiTestCase() {
metricsLogger,
lockPatternUtils,
mainExecutor,
- backgroundExecutor
+ backgroundExecutor,
+ mSelectedUserInteractor,
)
context.setMockPackageManager(packageManager)
Mockito.`when`(emergencyButton.context).thenReturn(context)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
index 42f65f6f53b4..7f20d9aa9451 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
@@ -37,12 +37,13 @@ import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardAbsKeyInputView.KeyDownListener;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.res.R;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import org.junit.Before;
import org.junit.Test;
@@ -81,6 +82,8 @@ public class KeyguardAbsKeyInputViewControllerTest extends SysuiTestCase {
private EmergencyButtonController mEmergencyButtonController;
private FakeFeatureFlags mFeatureFlags;
+ @Mock
+ private SelectedUserInteractor mSelectedUserInteractor;
private KeyguardAbsKeyInputViewController mKeyguardAbsKeyInputViewController;
@Before
@@ -105,7 +108,7 @@ public class KeyguardAbsKeyInputViewControllerTest extends SysuiTestCase {
return new KeyguardAbsKeyInputViewController(mAbsKeyInputView,
mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
mKeyguardMessageAreaControllerFactory, mLatencyTracker, mFalsingCollector,
- mEmergencyButtonController, mFeatureFlags) {
+ mEmergencyButtonController, mFeatureFlags, mSelectedUserInteractor) {
@Override
void resetState() {
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt
index 91b544b8265c..634dac16d1ec 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt
@@ -25,6 +25,7 @@ import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUT
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.SessionTracker
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -34,8 +35,8 @@ import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidTestingRunner::class)
@SmallTest
@@ -50,6 +51,8 @@ class KeyguardBiometricLockoutLoggerTest : SysuiTestCase() {
lateinit var sessionTracker: SessionTracker
@Mock
lateinit var sessionId: InstanceId
+ @Mock
+ lateinit var mSelectedUserInteractor: SelectedUserInteractor
@Captor
lateinit var updateMonitorCallbackCaptor: ArgumentCaptor<KeyguardUpdateMonitorCallback>
@@ -65,7 +68,8 @@ class KeyguardBiometricLockoutLoggerTest : SysuiTestCase() {
keyguardBiometricLockoutLogger = KeyguardBiometricLockoutLogger(
uiEventLogger,
keyguardUpdateMonitor,
- sessionTracker)
+ sessionTracker,
+ mSelectedUserInteractor)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
index d8a2c5f76fce..3fbcf6d77f82 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
@@ -24,12 +24,13 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.DevicePostureController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.mockito.whenever
import org.junit.Before
@@ -62,6 +63,7 @@ class KeyguardPasswordViewControllerTest : SysuiTestCase() {
@Mock lateinit var mainExecutor: DelayableExecutor
@Mock lateinit var falsingCollector: FalsingCollector
@Mock lateinit var keyguardViewController: KeyguardViewController
+ @Mock lateinit var mSelectedUserInteractor: SelectedUserInteractor
@Mock private lateinit var mKeyguardMessageArea: BouncerKeyguardMessageArea
@Mock
private lateinit var mKeyguardMessageAreaController:
@@ -106,7 +108,8 @@ class KeyguardPasswordViewControllerTest : SysuiTestCase() {
falsingCollector,
keyguardViewController,
postureController,
- fakeFeatureFlags
+ fakeFeatureFlags,
+ mSelectedUserInteractor,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index dc1618d128b3..74c922561343 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -24,15 +24,16 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.DevicePostureController
import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED
import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -75,6 +76,9 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() {
KeyguardMessageAreaController.Factory
@Mock
+ private lateinit var mSelectedUserInteractor: SelectedUserInteractor
+
+ @Mock
private lateinit var mKeyguardMessageAreaController:
KeyguardMessageAreaController<BouncerKeyguardMessageArea>
@@ -108,7 +112,8 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() {
mEmergencyButtonController,
mKeyguardMessageAreaControllerFactory,
mPostureController,
- fakeFeatureFlags
+ fakeFeatureFlags,
+ mSelectedUserInteractor
)
mKeyguardPatternView.onAttachedToWindow()
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
index 4a24e4a1f40f..d41c2497b230 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
@@ -34,10 +34,10 @@ import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.classifier.FalsingCollectorFake;
-import com.android.systemui.classifier.SingleTapClassifier;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.res.R;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import org.junit.Before;
import org.junit.Test;
@@ -76,11 +76,11 @@ public class KeyguardPinBasedInputViewControllerTest extends SysuiTestCase {
private EmergencyButtonController mEmergencyButtonController;
private FalsingCollector mFalsingCollector = new FalsingCollectorFake();
@Mock
- private SingleTapClassifier mSingleTapClassifier;
- @Mock
private View mDeleteButton;
@Mock
private View mOkButton;
+ @Mock
+ private SelectedUserInteractor mSelectedUserInteractor;
private NumPadKey[] mButtons = new NumPadKey[]{};
private KeyguardPinBasedInputViewController mKeyguardPinViewController;
@@ -108,7 +108,8 @@ public class KeyguardPinBasedInputViewControllerTest extends SysuiTestCase {
mKeyguardPinViewController = new KeyguardPinBasedInputViewController(mPinBasedInputView,
mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
mKeyguardMessageAreaControllerFactory, mLatencyTracker, mLiftToactivateListener,
- mEmergencyButtonController, mFalsingCollector, featureFlags) {
+ mEmergencyButtonController, mFalsingCollector, featureFlags,
+ mSelectedUserInteractor) {
@Override
public void onResume(int reason) {
super.onResume(reason);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
index 9df4dd4df4d2..80d45bcc23dd 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -25,15 +25,16 @@ import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
import com.android.keyguard.KeyguardSecurityModel.SecurityMode
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.DevicePostureController
import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED
import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -84,6 +85,7 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
@Mock private val mEmergencyButtonController: EmergencyButtonController? = null
private val falsingCollector: FalsingCollector = FalsingCollectorFake()
@Mock lateinit var postureController: DevicePostureController
+ @Mock lateinit var mSelectedUserInteractor: SelectedUserInteractor
@Mock lateinit var featureFlags: FeatureFlags
@Mock lateinit var passwordTextView: PasswordTextView
@@ -133,7 +135,8 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
mEmergencyButtonController,
falsingCollector,
postureController,
- featureFlags
+ featureFlags,
+ mSelectedUserInteractor,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index 20d4eb907944..fda4133240c2 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -63,7 +63,7 @@ import com.android.systemui.statusbar.policy.DevicePostureController
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.statusbar.policy.UserSwitcherController
-import com.android.systemui.user.domain.interactor.UserInteractor
+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.argThat
@@ -136,7 +136,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
@Mock private lateinit var telephonyManager: TelephonyManager
@Mock private lateinit var viewMediatorCallback: ViewMediatorCallback
@Mock private lateinit var audioManager: AudioManager
- @Mock private lateinit var userInteractor: UserInteractor
+ @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
@Mock private lateinit var faceAuthAccessibilityDelegate: FaceAuthAccessibilityDelegate
@Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
@Mock private lateinit var postureController: DevicePostureController
@@ -215,10 +215,10 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
null,
keyguardViewController,
postureController,
- featureFlags
+ featureFlags,
+ mSelectedUserInteractor,
)
- whenever(userInteractor.getSelectedUserId()).thenReturn(TARGET_USER_ID)
sceneTestUtils = SceneTestUtils(this)
sceneInteractor = sceneTestUtils.sceneInteractor()
keyguardTransitionInteractor =
@@ -260,7 +260,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
mock(),
mock(),
{ JavaAdapter(sceneTestUtils.testScope.backgroundScope) },
- userInteractor,
+ mSelectedUserInteractor,
deviceProvisionedController,
faceAuthAccessibilityDelegate,
keyguardTransitionInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
index 4290b8b28687..94c3bde29597 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
@@ -24,11 +24,12 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.res.R
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.any
import org.junit.Before
import org.junit.Test
@@ -58,6 +59,7 @@ class KeyguardSimPinViewControllerTest : SysuiTestCase() {
@Mock private lateinit var telephonyManager: TelephonyManager
@Mock private lateinit var falsingCollector: FalsingCollector
@Mock private lateinit var emergencyButtonController: EmergencyButtonController
+ @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
@Mock
private lateinit var keyguardMessageAreaController:
KeyguardMessageAreaController<BouncerKeyguardMessageArea>
@@ -90,6 +92,7 @@ class KeyguardSimPinViewControllerTest : SysuiTestCase() {
falsingCollector,
emergencyButtonController,
fakeFeatureFlags,
+ mSelectedUserInteractor
)
underTest.init()
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
index 31ee6411f93b..7b1f302da6e8 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
@@ -24,11 +24,12 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.res.R
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.any
import org.junit.Before
import org.junit.Test
@@ -54,6 +55,7 @@ class KeyguardSimPukViewControllerTest : SysuiTestCase() {
@Mock private lateinit var telephonyManager: TelephonyManager
@Mock private lateinit var falsingCollector: FalsingCollector
@Mock private lateinit var emergencyButtonController: EmergencyButtonController
+ @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
@Mock
private lateinit var keyguardMessageAreaController:
KeyguardMessageAreaController<BouncerKeyguardMessageArea>
@@ -89,6 +91,7 @@ class KeyguardSimPukViewControllerTest : SysuiTestCase() {
falsingCollector,
emergencyButtonController,
fakeFeatureFlags,
+ mSelectedUserInteractor,
)
underTest.init()
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 47be236b1272..aabdcb7fa660 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -38,7 +38,6 @@ import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT
import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_STATE_CANCELLING_RESTARTING;
import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT;
import static com.android.keyguard.KeyguardUpdateMonitor.HAL_POWER_PRESS_TIMEOUT;
-import static com.android.keyguard.KeyguardUpdateMonitor.getCurrentUser;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
@@ -82,7 +81,6 @@ import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.hardware.SensorPrivacyManager;
import android.hardware.biometrics.BiometricAuthenticator;
@@ -157,8 +155,8 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.telephony.TelephonyListenerManager;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.settings.GlobalSettings;
-import com.android.systemui.util.settings.SecureSettings;
import org.junit.After;
import org.junit.Assert;
@@ -240,8 +238,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Mock
private BroadcastDispatcher mBroadcastDispatcher;
@Mock
- private SecureSettings mSecureSettings;
- @Mock
private TelephonyManager mTelephonyManager;
@Mock
private SensorPrivacyManager mSensorPrivacyManager;
@@ -278,18 +274,17 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Mock
private UsbPortStatus mUsbPortStatus;
@Mock
- private Uri mURI;
- @Mock
private TaskStackChangeListeners mTaskStackChangeListeners;
@Mock
private IActivityTaskManager mActivityTaskManager;
@Mock
private WakefulnessLifecycle mWakefulness;
+ @Mock
+ private SelectedUserInteractor mSelectedUserInteractor;
private List<FaceSensorPropertiesInternal> mFaceSensorProperties;
private List<FingerprintSensorPropertiesInternal> mFingerprintSensorProperties;
private final int mCurrentUserId = 100;
- private final UserInfo mCurrentUserInfo = new UserInfo(mCurrentUserId, "Test user", 0);
@Captor
private ArgumentCaptor<IBiometricEnabledOnKeyguardCallback>
@@ -337,8 +332,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
.startMocking();
ExtendedMockito.doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
.when(SubscriptionManager::getDefaultSubscriptionId);
- KeyguardUpdateMonitor.setCurrentUser(mCurrentUserId);
- when(mUserTracker.getUserId()).thenReturn(mCurrentUserId);
+ when(mSelectedUserInteractor.getSelectedUserId()).thenReturn(mCurrentUserId);
+ when(mSelectedUserInteractor.getSelectedUserId(anyBoolean())).thenReturn(mCurrentUserId);
mContext.getOrCreateTestableResources().addOverride(
com.android.systemui.res.R.integer.config_face_auth_supported_posture,
@@ -351,13 +346,11 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mContext.getOrCreateTestableResources().addOverride(com.android.systemui.res
.R.array.config_fingerprint_listen_on_occluding_activity_packages,
- new String[]{ PKG_ALLOWING_FP_LISTEN_ON_OCCLUDING_ACTIVITY });
+ new String[]{PKG_ALLOWING_FP_LISTEN_ON_OCCLUDING_ACTIVITY});
mTestableLooper = TestableLooper.get(this);
allowTestableLooperAsMainThread();
- when(mSecureSettings.getUriFor(anyString())).thenReturn(mURI);
-
final ContentResolver contentResolver = mContext.getContentResolver();
ExtendedMockito.spyOn(contentResolver);
doNothing().when(contentResolver)
@@ -1005,11 +998,14 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void trustAgentHasTrust() {
// WHEN user has trust
- mKeyguardUpdateMonitor.onTrustChanged(true, true, getCurrentUser(), 0, null);
+ mKeyguardUpdateMonitor.onTrustChanged(true, true,
+ mSelectedUserInteractor.getSelectedUserId(), 0, null);
// THEN user is considered as "having trust" and bouncer can be skipped
- Assert.assertTrue(mKeyguardUpdateMonitor.getUserHasTrust(getCurrentUser()));
- Assert.assertTrue(mKeyguardUpdateMonitor.getUserCanSkipBouncer(getCurrentUser()));
+ Assert.assertTrue(mKeyguardUpdateMonitor.getUserHasTrust(
+ mSelectedUserInteractor.getSelectedUserId()));
+ Assert.assertTrue(mKeyguardUpdateMonitor.getUserCanSkipBouncer(
+ mSelectedUserInteractor.getSelectedUserId()));
}
@Test
@@ -1027,15 +1023,19 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void trustAgentHasTrust_fingerprintLockout() {
// GIVEN user has trust
- mKeyguardUpdateMonitor.onTrustChanged(true, true, getCurrentUser(), 0, null);
- Assert.assertTrue(mKeyguardUpdateMonitor.getUserHasTrust(getCurrentUser()));
+ mKeyguardUpdateMonitor.onTrustChanged(true, true,
+ mSelectedUserInteractor.getSelectedUserId(), 0, null);
+ Assert.assertTrue(mKeyguardUpdateMonitor.getUserHasTrust(
+ mSelectedUserInteractor.getSelectedUserId()));
// WHEN fingerprint is lock out
fingerprintErrorTemporaryLockOut();
// THEN user is NOT considered as "having trust" and bouncer cannot be skipped
- Assert.assertFalse(mKeyguardUpdateMonitor.getUserHasTrust(getCurrentUser()));
- Assert.assertFalse(mKeyguardUpdateMonitor.getUserCanSkipBouncer(getCurrentUser()));
+ Assert.assertFalse(mKeyguardUpdateMonitor.getUserHasTrust(
+ mSelectedUserInteractor.getSelectedUserId()));
+ Assert.assertFalse(mKeyguardUpdateMonitor.getUserCanSkipBouncer(
+ mSelectedUserInteractor.getSelectedUserId()));
}
@Test
@@ -1217,7 +1217,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mTestableLooper.processAllMessages();
lockscreenBypassIsAllowed();
mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, true /* newlyUnlocked */,
- KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */,
+ mSelectedUserInteractor.getSelectedUserId(), 0 /* flags */,
new ArrayList<>());
keyguardIsVisible();
verifyFaceAuthenticateCall();
@@ -1249,7 +1249,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
mTestableLooper.processAllMessages();
mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, true /* newlyUnlocked */,
- KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */, new ArrayList<>());
+ mSelectedUserInteractor.getSelectedUserId(), 0 /* flags */, new ArrayList<>());
keyguardIsVisible();
verifyFaceAuthenticateNeverCalled();
}
@@ -1287,7 +1287,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
public void testOnFaceAuthenticated_skipsFaceWhenAuthenticated() {
// test whether face will be skipped if authenticated, so the value of isClass3Biometric
// doesn't matter here
- mKeyguardUpdateMonitor.onFaceAuthenticated(KeyguardUpdateMonitor.getCurrentUser(),
+ mKeyguardUpdateMonitor.onFaceAuthenticated(mSelectedUserInteractor.getSelectedUserId(),
true /* isClass3Biometric */);
setKeyguardBouncerVisibility(true);
mTestableLooper.processAllMessages();
@@ -1333,7 +1333,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testGetUserCanSkipBouncer_whenFace() {
- int user = KeyguardUpdateMonitor.getCurrentUser();
+ int user = mSelectedUserInteractor.getSelectedUserId();
mKeyguardUpdateMonitor.onFaceAuthenticated(user, true /* isClass3Biometric */);
assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isTrue();
}
@@ -1342,14 +1342,14 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
public void testGetUserCanSkipBouncer_whenFace_nonStrongAndDisallowed() {
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(false /* isClass3Biometric */))
.thenReturn(false);
- int user = KeyguardUpdateMonitor.getCurrentUser();
+ int user = mSelectedUserInteractor.getSelectedUserId();
mKeyguardUpdateMonitor.onFaceAuthenticated(user, false /* isClass3Biometric */);
assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isFalse();
}
@Test
public void testGetUserCanSkipBouncer_whenFingerprint() {
- int user = KeyguardUpdateMonitor.getCurrentUser();
+ int user = mSelectedUserInteractor.getSelectedUserId();
mKeyguardUpdateMonitor.onFingerprintAuthenticated(user, true /* isClass3Biometric */);
assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isTrue();
}
@@ -1358,7 +1358,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
public void testGetUserCanSkipBouncer_whenFingerprint_nonStrongAndDisallowed() {
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(false /* isClass3Biometric */))
.thenReturn(false);
- int user = KeyguardUpdateMonitor.getCurrentUser();
+ int user = mSelectedUserInteractor.getSelectedUserId();
mKeyguardUpdateMonitor.onFingerprintAuthenticated(user, false /* isClass3Biometric */);
assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isFalse();
}
@@ -1372,7 +1372,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
assertThat(mKeyguardUpdateMonitor.mUserFingerprintAuthenticated.size()).isEqualTo(1);
assertThat(mKeyguardUpdateMonitor.mUserFaceAuthenticated.size()).isEqualTo(1);
- mKeyguardUpdateMonitor.handleUserSwitching(10 /* user */, () -> {});
+ mKeyguardUpdateMonitor.handleUserSwitching(10 /* user */, () -> {
+ });
assertThat(mKeyguardUpdateMonitor.mUserFingerprintAuthenticated.size()).isEqualTo(0);
assertThat(mKeyguardUpdateMonitor.mUserFaceAuthenticated.size()).isEqualTo(0);
}
@@ -1446,7 +1447,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testGetUserCanSkipBouncer_whenTrust() {
- int user = KeyguardUpdateMonitor.getCurrentUser();
+ int user = mSelectedUserInteractor.getSelectedUserId();
mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, true /* newlyUnlocked */,
user, 0 /* flags */, new ArrayList<>());
assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isTrue();
@@ -1493,7 +1494,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testIsUserUnlocked() {
// mUserManager will report the user as unlocked on @Before
- assertThat(mKeyguardUpdateMonitor.isUserUnlocked(KeyguardUpdateMonitor.getCurrentUser()))
+ assertThat(
+ mKeyguardUpdateMonitor.isUserUnlocked(mSelectedUserInteractor.getSelectedUserId()))
.isTrue();
// Invalid user should not be unlocked.
int randomUser = 99;
@@ -1502,7 +1504,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testTrustUsuallyManaged_whenTrustChanges() {
- int user = KeyguardUpdateMonitor.getCurrentUser();
+ int user = mSelectedUserInteractor.getSelectedUserId();
when(mTrustManager.isTrustUsuallyManaged(eq(user))).thenReturn(true);
mKeyguardUpdateMonitor.onTrustManagedChanged(false /* managed */, user);
assertThat(mKeyguardUpdateMonitor.isTrustUsuallyManaged(user)).isTrue();
@@ -1510,7 +1512,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testTrustUsuallyManaged_resetWhenUserIsRemoved() {
- int user = KeyguardUpdateMonitor.getCurrentUser();
+ int user = mSelectedUserInteractor.getSelectedUserId();
when(mTrustManager.isTrustUsuallyManaged(eq(user))).thenReturn(true);
mKeyguardUpdateMonitor.onTrustManagedChanged(false /* managed */, user);
assertThat(mKeyguardUpdateMonitor.isTrustUsuallyManaged(user)).isTrue();
@@ -1521,9 +1523,9 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testSecondaryLockscreenRequirement() {
- KeyguardUpdateMonitor.setCurrentUser(UserHandle.myUserId());
+ when(mSelectedUserInteractor.getSelectedUserId()).thenReturn(UserHandle.myUserId());
when(mUserTracker.getUserId()).thenReturn(UserHandle.myUserId());
- int user = KeyguardUpdateMonitor.getCurrentUser();
+ int user = mSelectedUserInteractor.getSelectedUserId();
String packageName = "fake.test.package";
String cls = "FakeService";
ServiceInfo serviceInfo = new ServiceInfo();
@@ -1680,7 +1682,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mStatusBarStateListener.onStateChanged(StatusBarState.KEYGUARD);
// WHEN user loses smart unlock trust
- when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
+ when(mStrongAuthTracker.getStrongAuthForUser(mSelectedUserInteractor.getSelectedUserId()))
.thenReturn(SOME_AUTH_REQUIRED_AFTER_USER_REQUEST);
// THEN we should still listen for udfps
@@ -1724,7 +1726,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
// WHEN trust is enabled (ie: via smartlock)
mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, true /* newlyUnlocked */,
- KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */, new ArrayList<>());
+ mSelectedUserInteractor.getSelectedUserId(), 0 /* flags */, new ArrayList<>());
// THEN we shouldn't listen for udfps
assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(true)).isEqualTo(false);
@@ -1738,7 +1740,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
// WHEN face authenticated
mKeyguardUpdateMonitor.onFaceAuthenticated(
- KeyguardUpdateMonitor.getCurrentUser(), false);
+ mSelectedUserInteractor.getSelectedUserId(), false);
// THEN we shouldn't listen for udfps
assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(true)).isEqualTo(false);
@@ -1825,7 +1827,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
public void testShowTrustGrantedMessage_onTrustGranted() {
// WHEN trust is enabled (ie: via some trust agent) with a trustGranted string
mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, true /* newlyUnlocked */,
- KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */,
+ mSelectedUserInteractor.getSelectedUserId(), 0 /* flags */,
Arrays.asList("Unlocked by wearable"));
// THEN the showTrustGrantedMessage should be called with the first message
@@ -2256,7 +2258,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mKeyguardUpdateMonitor.onTrustChanged(
true /* enabled */,
true /* newlyUnlocked */,
- getCurrentUser() /* userId */,
+ mSelectedUserInteractor.getSelectedUserId() /* userId */,
TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD /* flags */,
null /* trustGrantedMessages */);
@@ -2281,7 +2283,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mKeyguardUpdateMonitor.onTrustChanged(
true /* enabled */,
true /* newlyUnlocked */,
- getCurrentUser() /* userId */,
+ mSelectedUserInteractor.getSelectedUserId() /* userId */,
TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD /* flags */,
null /* trustGrantedMessages */);
@@ -2333,7 +2335,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mKeyguardUpdateMonitor.onTrustChanged(
true /* enabled */,
true /* newlyUnlocked */,
- getCurrentUser() /* userId */,
+ mSelectedUserInteractor.getSelectedUserId() /* userId */,
TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD
| TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE /* flags */,
null /* trustGrantedMessages */);
@@ -2362,7 +2364,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mKeyguardUpdateMonitor.onTrustChanged(
true /* enabled */,
true /* newlyUnlocked */,
- getCurrentUser() /* userId, not the current userId */,
+ mSelectedUserInteractor.getSelectedUserId() /* userId, not the current userId */,
TrustAgentService.FLAG_GRANT_TRUST_INITIATED_BY_USER /* flags */,
null /* trustGrantedMessages */);
@@ -2388,7 +2390,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mKeyguardUpdateMonitor.onTrustChanged(
true /* enabled */,
true /* newlyUnlocked */,
- getCurrentUser() /* userId, not the current userId */,
+ mSelectedUserInteractor.getSelectedUserId() /* userId, not the current userId */,
TrustAgentService.FLAG_GRANT_TRUST_INITIATED_BY_USER
| TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE /* flags */,
null /* trustGrantedMessages */);
@@ -2423,7 +2425,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
// WHEN strong auth changes and device is in user lockdown
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
userDeviceLockDown();
- mKeyguardUpdateMonitor.notifyStrongAuthAllowedChanged(getCurrentUser());
+ mKeyguardUpdateMonitor.notifyStrongAuthAllowedChanged(
+ mSelectedUserInteractor.getSelectedUserId());
mTestableLooper.processAllMessages();
// THEN face and fingerprint listening are cancelled
@@ -2451,7 +2454,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
// WHEN non-strong biometric allowed changes
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
- mKeyguardUpdateMonitor.notifyNonStrongBiometricAllowedChanged(getCurrentUser());
+ mKeyguardUpdateMonitor.notifyNonStrongBiometricAllowedChanged(
+ mSelectedUserInteractor.getSelectedUserId());
mTestableLooper.processAllMessages();
// THEN face and fingerprint listening are cancelled
@@ -2516,13 +2520,15 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
keyguardIsVisible();
keyguardNotGoingAway();
statusBarShadeIsNotLocked();
- when(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())).thenReturn(true);
+ when(mLockPatternUtils.isSecure(mSelectedUserInteractor.getSelectedUserId())).thenReturn(
+ true);
// WHEN the assistant is visible
mKeyguardUpdateMonitor.setAssistantVisible(true);
// THEN request unlock with keyguard dismissal
- verify(mTrustManager).reportUserRequestedUnlock(eq(KeyguardUpdateMonitor.getCurrentUser()),
+ verify(mTrustManager).reportUserRequestedUnlock(
+ eq(mSelectedUserInteractor.getSelectedUserId()),
eq(true));
}
@@ -2531,7 +2537,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
throws RemoteException {
// GIVEN shouldTriggerActiveUnlock
bouncerFullyVisible();
- when(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())).thenReturn(true);
+ when(mLockPatternUtils.isSecure(mSelectedUserInteractor.getSelectedUserId())).thenReturn(
+ true);
// GIVEN active unlock triggers on biometric failures
when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -2542,7 +2549,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback.onAuthenticationFailed();
// ALWAYS request unlock with a keyguard dismissal
- verify(mTrustManager).reportUserRequestedUnlock(eq(KeyguardUpdateMonitor.getCurrentUser()),
+ verify(mTrustManager).reportUserRequestedUnlock(
+ eq(mSelectedUserInteractor.getSelectedUserId()),
eq(true));
}
@@ -2554,7 +2562,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
keyguardIsVisible();
keyguardNotGoingAway();
statusBarShadeIsNotLocked();
- when(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())).thenReturn(true);
+ when(mLockPatternUtils.isSecure(mSelectedUserInteractor.getSelectedUserId())).thenReturn(
+ true);
// GIVEN active unlock triggers on biometric failures
when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -2566,7 +2575,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mKeyguardUpdateMonitor.mFaceAuthenticationCallback.onAuthenticationFailed();
// THEN request unlock with NO keyguard dismissal
- verify(mTrustManager).reportUserRequestedUnlock(eq(KeyguardUpdateMonitor.getCurrentUser()),
+ verify(mTrustManager).reportUserRequestedUnlock(
+ eq(mSelectedUserInteractor.getSelectedUserId()),
eq(false));
}
@@ -2578,7 +2588,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
keyguardIsVisible();
keyguardNotGoingAway();
statusBarShadeIsNotLocked();
- when(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())).thenReturn(true);
+ when(mLockPatternUtils.isSecure(mSelectedUserInteractor.getSelectedUserId())).thenReturn(
+ true);
// GIVEN active unlock triggers on biometric failures
when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -2590,7 +2601,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mKeyguardUpdateMonitor.mFaceAuthenticationCallback.onAuthenticationFailed();
// THEN request unlock with a keyguard dismissal
- verify(mTrustManager).reportUserRequestedUnlock(eq(KeyguardUpdateMonitor.getCurrentUser()),
+ verify(mTrustManager).reportUserRequestedUnlock(
+ eq(mSelectedUserInteractor.getSelectedUserId()),
eq(true));
}
@@ -2600,7 +2612,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
// GIVEN shouldTriggerActiveUnlock
when(mAuthController.isUdfpsFingerDown()).thenReturn(false);
lockscreenBypassIsNotAllowed();
- when(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())).thenReturn(true);
+ when(mLockPatternUtils.isSecure(mSelectedUserInteractor.getSelectedUserId())).thenReturn(
+ true);
// GIVEN active unlock triggers on biometric failures
when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -2612,7 +2625,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mKeyguardUpdateMonitor.mFaceAuthenticationCallback.onAuthenticationFailed();
// THEN request unlock with a keyguard dismissal
- verify(mTrustManager).reportUserRequestedUnlock(eq(KeyguardUpdateMonitor.getCurrentUser()),
+ verify(mTrustManager).reportUserRequestedUnlock(
+ eq(mSelectedUserInteractor.getSelectedUserId()),
eq(true));
}
@@ -2701,7 +2715,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
throws RemoteException {
// GIVEN shouldTriggerActiveUnlock
keyguardIsVisible();
- when(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())).thenReturn(true);
+ when(mLockPatternUtils.isSecure(mSelectedUserInteractor.getSelectedUserId())).thenReturn(
+ true);
// GIVEN active unlock triggers on wakeup
when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -2717,7 +2732,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mTestableLooper.processAllMessages();
// THEN request unlock with a keyguard dismissal
- verify(mTrustManager).reportUserRequestedUnlock(eq(KeyguardUpdateMonitor.getCurrentUser()),
+ verify(mTrustManager).reportUserRequestedUnlock(
+ eq(mSelectedUserInteractor.getSelectedUserId()),
eq(true));
}
@@ -2726,7 +2742,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
throws RemoteException {
// GIVEN shouldTriggerActiveUnlock on wake from UNFOLD_DEVICE
keyguardIsVisible();
- when(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())).thenReturn(true);
+ when(mLockPatternUtils.isSecure(mSelectedUserInteractor.getSelectedUserId())).thenReturn(
+ true);
// GIVEN active unlock triggers on wakeup
when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -2742,7 +2759,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mTestableLooper.processAllMessages();
// THEN request unlock WITHOUT a keyguard dismissal
- verify(mTrustManager).reportUserRequestedUnlock(eq(KeyguardUpdateMonitor.getCurrentUser()),
+ verify(mTrustManager).reportUserRequestedUnlock(
+ eq(mSelectedUserInteractor.getSelectedUserId()),
eq(false));
}
@@ -2751,7 +2769,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
throws RemoteException {
// GIVEN shouldTriggerActiveUnlock
keyguardIsVisible();
- when(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())).thenReturn(true);
+ when(mLockPatternUtils.isSecure(mSelectedUserInteractor.getSelectedUserId())).thenReturn(
+ true);
// GIVEN active unlock triggers on wakeup
when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -2767,7 +2786,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mTestableLooper.processAllMessages();
// THEN request unlock with a keyguard dismissal
- verify(mTrustManager).reportUserRequestedUnlock(eq(KeyguardUpdateMonitor.getCurrentUser()),
+ verify(mTrustManager).reportUserRequestedUnlock(
+ eq(mSelectedUserInteractor.getSelectedUserId()),
eq(true));
}
@@ -2777,7 +2797,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
throws RemoteException {
// GIVEN shouldTriggerActiveUnlock on wake from UNFOLD_DEVICE
keyguardIsVisible();
- when(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())).thenReturn(true);
+ when(mLockPatternUtils.isSecure(mSelectedUserInteractor.getSelectedUserId())).thenReturn(
+ true);
// GIVEN active unlock triggers on wakeup
when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -2793,7 +2814,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mTestableLooper.processAllMessages();
// THEN request unlock WITHOUT a keyguard dismissal
- verify(mTrustManager).reportUserRequestedUnlock(eq(KeyguardUpdateMonitor.getCurrentUser()),
+ verify(mTrustManager).reportUserRequestedUnlock(
+ eq(mSelectedUserInteractor.getSelectedUserId()),
eq(false));
}
@@ -2857,27 +2879,29 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
assertThat(captor.getValue().getWakeReason())
.isEqualTo(PowerManager.WAKE_REASON_POWER_BUTTON);
}
+
@Test
public void testFingerprintSensorProperties() throws RemoteException {
mFingerprintAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(
new ArrayList<>());
assertThat(mKeyguardUpdateMonitor.isUnlockWithFingerprintPossible(
- KeyguardUpdateMonitor.getCurrentUser())).isFalse();
+ mSelectedUserInteractor.getSelectedUserId())).isFalse();
mFingerprintAuthenticatorsRegisteredCallback
.onAllAuthenticatorsRegistered(mFingerprintSensorProperties);
verifyFingerprintAuthenticateCall();
assertThat(mKeyguardUpdateMonitor.isUnlockWithFingerprintPossible(
- KeyguardUpdateMonitor.getCurrentUser())).isTrue();
+ mSelectedUserInteractor.getSelectedUserId())).isTrue();
}
+
@Test
public void testFaceSensorProperties() throws RemoteException {
mFaceAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(new ArrayList<>());
assertThat(mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(
- KeyguardUpdateMonitor.getCurrentUser())).isFalse();
+ mSelectedUserInteractor.getSelectedUserId())).isFalse();
mFaceAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(mFaceSensorProperties);
biometricsEnabledForCurrentUser();
@@ -2885,7 +2909,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
verifyFaceAuthenticateNeverCalled();
verifyFaceDetectNeverCalled();
assertThat(mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(
- KeyguardUpdateMonitor.getCurrentUser())).isTrue();
+ mSelectedUserInteractor.getSelectedUserId())).isTrue();
}
@Test
@@ -2917,13 +2941,13 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mKeyguardUpdateMonitor.onTrustChanged(
true /* enabled */,
true /* newlyUnlocked */,
- getCurrentUser() /* userId */,
+ mSelectedUserInteractor.getSelectedUserId() /* userId */,
TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD /* flags */,
null /* trustGrantedMessages */);
// THEN onTrustChanged is called FIRST
final InOrder inOrder = Mockito.inOrder(callback);
- inOrder.verify(callback).onTrustChanged(eq(getCurrentUser()));
+ inOrder.verify(callback).onTrustChanged(eq(mSelectedUserInteractor.getSelectedUserId()));
// AND THEN onTrustGrantedForCurrentUser callback called
inOrder.verify(callback).onTrustGrantedForCurrentUser(
@@ -3144,7 +3168,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
private void mockCanBypassLockscreen(boolean canBypass) {
// force update the isFaceEnrolled cache:
- mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(getCurrentUser());
+ mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(
+ mSelectedUserInteractor.getSelectedUserId());
mKeyguardUpdateMonitor.setKeyguardBypassController(mKeyguardBypassController);
when(mKeyguardBypassController.canBypass()).thenReturn(canBypass);
@@ -3235,22 +3260,23 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
private void biometricsNotDisabledThroughDevicePolicyManager() {
when(mDevicePolicyManager.getKeyguardDisabledFeatures(null,
- KeyguardUpdateMonitor.getCurrentUser())).thenReturn(0);
+ mSelectedUserInteractor.getSelectedUserId())).thenReturn(0);
}
private void biometricsEnabledForCurrentUser() throws RemoteException {
- mBiometricEnabledOnKeyguardCallback.onChanged(true, KeyguardUpdateMonitor.getCurrentUser());
+ mBiometricEnabledOnKeyguardCallback.onChanged(true,
+ mSelectedUserInteractor.getSelectedUserId());
}
private void biometricsDisabledForCurrentUser() throws RemoteException {
mBiometricEnabledOnKeyguardCallback.onChanged(
false,
- KeyguardUpdateMonitor.getCurrentUser()
+ mSelectedUserInteractor.getSelectedUserId()
);
}
private void primaryAuthRequiredEncrypted() {
- when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
+ when(mStrongAuthTracker.getStrongAuthForUser(mSelectedUserInteractor.getSelectedUserId()))
.thenReturn(STRONG_AUTH_REQUIRED_AFTER_BOOT);
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
}
@@ -3261,7 +3287,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
private void primaryAuthNotRequiredByStrongAuthTracker() {
- when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
+ when(mStrongAuthTracker.getStrongAuthForUser(mSelectedUserInteractor.getSelectedUserId()))
.thenReturn(0);
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
}
@@ -3270,7 +3296,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mKeyguardUpdateMonitor.onTrustChanged(
false,
false,
- KeyguardUpdateMonitor.getCurrentUser(),
+ mSelectedUserInteractor.getSelectedUserId(),
-1,
new ArrayList<>()
);
@@ -3435,7 +3461,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
protected TestableKeyguardUpdateMonitor(Context context) {
super(context, mUserTracker,
TestableLooper.get(KeyguardUpdateMonitorTest.this).getLooper(),
- mBroadcastDispatcher, mSecureSettings, mDumpManager,
+ mBroadcastDispatcher, mDumpManager,
mBackgroundExecutor, mMainExecutor,
mStatusBarStateController, mLockPatternUtils,
mAuthController, mTelephonyListenerManager,
@@ -3447,7 +3473,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mFaceWakeUpTriggersConfig, mDevicePostureController,
Optional.of(mInteractiveToAuthProvider),
mTaskStackChangeListeners, mActivityTaskManager, mDisplayTracker,
- mWakefulness);
+ mWakefulness, mSelectedUserInteractor);
setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
index 5346db1aa067..daa6070b5403 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
@@ -19,8 +19,7 @@ package com.android.systemui.accessibility.floatingmenu;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
-import static com.android.systemui.Flags.FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG;
-import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
+import static com.android.systemui.accessibility.utils.FlagUtils.setFlagDefaults;
import static com.google.common.truth.Truth.assertThat;
@@ -90,7 +89,7 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
- setFlagDefault(mSetFlagsRule, FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG);
+ setFlagDefaults(mSetFlagsRule);
MockitoAnnotations.initMocks(this);
mContextWrapper = new ContextWrapper(mContext) {
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java
index 3b2ea0f5ce86..56664356f964 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java
@@ -16,8 +16,7 @@
package com.android.systemui.accessibility.floatingmenu;
-import static com.android.systemui.Flags.FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG;
-import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
+import static com.android.systemui.accessibility.utils.FlagUtils.setFlagDefaults;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
@@ -59,7 +58,7 @@ public class DismissAnimationControllerTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
- setFlagDefault(mSetFlagsRule, FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG);
+ setFlagDefaults(mSetFlagsRule);
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
mock(SecureSettings.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
index 76a3153e648a..ec6ec63d213d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
@@ -16,8 +16,7 @@
package com.android.systemui.accessibility.floatingmenu;
-import static com.android.systemui.Flags.FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG;
-import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
+import static com.android.systemui.accessibility.utils.FlagUtils.setFlagDefaults;
import static com.google.common.truth.Truth.assertThat;
@@ -78,7 +77,7 @@ public class MenuAnimationControllerTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
- setFlagDefault(mSetFlagsRule, FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG);
+ setFlagDefaults(mSetFlagsRule);
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
stubWindowManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java
index 83bcd8d50407..e8192c43f6f8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java
@@ -16,8 +16,7 @@
package com.android.systemui.accessibility.floatingmenu;
-import static com.android.systemui.Flags.FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG;
-import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
+import static com.android.systemui.accessibility.utils.FlagUtils.setFlagDefaults;
import static com.google.common.truth.Truth.assertThat;
@@ -29,8 +28,8 @@ import android.widget.TextView;
import androidx.test.filters.SmallTest;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.res.R;
import org.junit.Before;
import org.junit.Test;
@@ -46,7 +45,7 @@ public class MenuEduTooltipViewTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
- setFlagDefault(mSetFlagsRule, FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG);
+ setFlagDefaults(mSetFlagsRule);
final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
mMenuViewAppearance = new MenuViewAppearance(mContext, windowManager);
mMenuEduTooltipView = new MenuEduTooltipView(mContext, mMenuViewAppearance);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
index e01f1b76df79..62cb9a01ac84 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
@@ -19,8 +19,7 @@ package com.android.systemui.accessibility.floatingmenu;
import static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS;
import static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS;
-import static com.android.systemui.Flags.FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG;
-import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
+import static com.android.systemui.accessibility.utils.FlagUtils.setFlagDefaults;
import static com.google.common.truth.Truth.assertThat;
@@ -40,8 +39,8 @@ import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate;
import androidx.test.filters.SmallTest;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.res.R;
import com.android.systemui.util.settings.SecureSettings;
import org.junit.Before;
@@ -75,7 +74,7 @@ public class MenuItemAccessibilityDelegateTest extends SysuiTestCase {
@Before
public void setUp() {
- setFlagDefault(mSetFlagsRule, FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG);
+ setFlagDefaults(mSetFlagsRule);
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
stubWindowManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
index a88ee108141b..32487534423d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
@@ -18,8 +18,7 @@ package com.android.systemui.accessibility.floatingmenu;
import static android.view.View.OVER_SCROLL_NEVER;
-import static com.android.systemui.Flags.FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG;
-import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
+import static com.android.systemui.accessibility.utils.FlagUtils.setFlagDefaults;
import static com.google.common.truth.Truth.assertThat;
@@ -83,7 +82,7 @@ public class MenuListViewTouchHandlerTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
- setFlagDefault(mSetFlagsRule, FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG);
+ setFlagDefaults(mSetFlagsRule);
final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
mock(SecureSettings.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
index 41e5c209e344..03a4ba78a208 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
@@ -19,8 +19,7 @@ package com.android.systemui.accessibility.floatingmenu;
import static android.view.WindowInsets.Type.displayCutout;
import static android.view.WindowInsets.Type.systemBars;
-import static com.android.systemui.Flags.FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG;
-import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
+import static com.android.systemui.accessibility.utils.FlagUtils.setFlagDefaults;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
@@ -76,7 +75,7 @@ public class MenuViewLayerControllerTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
- setFlagDefault(mSetFlagsRule, FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG);
+ setFlagDefaults(mSetFlagsRule);
final WindowManager wm = mContext.getSystemService(WindowManager.class);
doAnswer(invocation -> wm.getMaximumWindowMetrics()).when(
mWindowManager).getMaximumWindowMetrics();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
index b0776c9a5b58..df7d1b7a4aad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
@@ -22,9 +22,8 @@ import static android.view.WindowInsets.Type.displayCutout;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.systemBars;
-import static com.android.systemui.Flags.FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG;
import static com.android.systemui.accessibility.floatingmenu.MenuViewLayer.LayerIndex;
-import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
+import static com.android.systemui.accessibility.utils.FlagUtils.setFlagDefaults;
import static com.google.common.truth.Truth.assertThat;
@@ -113,7 +112,7 @@ public class MenuViewLayerTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
- setFlagDefault(mSetFlagsRule, FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG);
+ setFlagDefaults(mSetFlagsRule);
final Rect mDisplayBounds = new Rect();
mDisplayBounds.set(/* left= */ 0, /* top= */ 0, DISPLAY_WINDOW_WIDTH,
DISPLAY_WINDOW_HEIGHT);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
index ac2bfaf8eddf..b9fd5d0f5e84 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
@@ -18,8 +18,7 @@ package com.android.systemui.accessibility.floatingmenu;
import static android.app.UiModeManager.MODE_NIGHT_YES;
-import static com.android.systemui.Flags.FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG;
-import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
+import static com.android.systemui.accessibility.utils.FlagUtils.setFlagDefaults;
import static com.google.common.truth.Truth.assertThat;
@@ -70,7 +69,7 @@ public class MenuViewTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
- setFlagDefault(mSetFlagsRule, FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG);
+ setFlagDefaults(mSetFlagsRule);
mUiModeManager = mContext.getSystemService(UiModeManager.class);
mNightMode = mUiModeManager.getNightMode();
mUiModeManager.setNightMode(MODE_NIGHT_YES);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/utils/FlagUtils.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/utils/FlagUtils.java
new file mode 100644
index 000000000000..297554956ed8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/utils/FlagUtils.java
@@ -0,0 +1,35 @@
+/*
+ * 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.accessibility.utils;
+
+import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
+
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import com.android.systemui.Flags;
+
+public class FlagUtils {
+ /**
+ * Populates a setFlagsRule with every SystemUI a11y feature flag.
+ * This function should be updated when new flags are added.
+ *
+ * @param setFlagsRule set flags rule from the test environment.
+ */
+ public static void setFlagDefaults(SetFlagsRule setFlagsRule) {
+ setFlagDefault(setFlagsRule, Flags.FLAG_FLOATING_MENU_OVERLAPS_NAV_BARS_FLAG);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index ebe13feb0f6f..c5f16aa97f18 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -36,7 +36,6 @@ import android.view.accessibility.AccessibilityManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
@@ -46,12 +45,14 @@ import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.res.R
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.phone.SystemUIDialogManager
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.settings.SecureSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -111,6 +112,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
@Mock private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
@Mock private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
@Mock private lateinit var udfpsUtils: UdfpsUtils
+ @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
@Mock private lateinit var udfpsKeyguardAccessibilityDelegate:
UdfpsKeyguardAccessibilityDelegate
@Mock private lateinit var udfpsKeyguardViewModels: Provider<UdfpsKeyguardViewModels>
@@ -163,6 +165,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
isDebuggable,
udfpsKeyguardAccessibilityDelegate,
udfpsKeyguardViewModels,
+ mSelectedUserInteractor,
)
block()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index dcb53984ad87..f32e1a5bcd6a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -102,6 +102,7 @@ import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.concurrency.FakeExecution;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -214,6 +215,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
private UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate;
@Mock
private Provider<UdfpsKeyguardViewModels> mUdfpsKeyguardViewModels;
+ @Mock
+ private SelectedUserInteractor mSelectedUserInteractor;
// Capture listeners so that they can be used to send events
@Captor
@@ -326,7 +329,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
mInputManager,
mock(KeyguardFaceAuthInteractor.class),
mUdfpsKeyguardAccessibilityDelegate,
- mUdfpsKeyguardViewModels
+ mUdfpsKeyguardViewModels,
+ mSelectedUserInteractor
);
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
index e512adcc0542..2c4e1362bed3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
@@ -40,6 +40,7 @@ import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.concurrency.DelayableExecutor;
import org.junit.Before;
@@ -69,6 +70,7 @@ public class UdfpsKeyguardViewLegacyControllerBaseTest extends SysuiTestCase {
protected @Mock PrimaryBouncerInteractor mPrimaryBouncerInteractor;
protected @Mock AlternateBouncerInteractor mAlternateBouncerInteractor;
protected @Mock UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate;
+ protected @Mock SelectedUserInteractor mSelectedUserInteractor;
protected FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
@@ -142,7 +144,8 @@ public class UdfpsKeyguardViewLegacyControllerBaseTest extends SysuiTestCase {
mFeatureFlags,
mPrimaryBouncerInteractor,
mAlternateBouncerInteractor,
- mUdfpsKeyguardAccessibilityDelegate);
+ mUdfpsKeyguardAccessibilityDelegate,
+ mSelectedUserInteractor);
return controller;
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
index 02ee53879ea0..97dada27f8c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
@@ -96,6 +96,7 @@ class UdfpsKeyguardViewLegacyControllerWithCoroutinesTest :
mKeyguardUpdateMonitor,
FakeTrustRepository(),
testScope.backgroundScope,
+ mSelectedUserInteractor,
)
mAlternateBouncerInteractor =
AlternateBouncerInteractor(
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 6b9c34bbd237..bbf471c34cc7 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
@@ -24,6 +24,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
@@ -55,6 +56,7 @@ class UdfpsOverlayInteractorTest : SysuiTestCase() {
@Mock private lateinit var udfpsOverlayParams: UdfpsOverlayParams
@Mock private lateinit var overlayBounds: Rect
+ @Mock private lateinit var selectedUserInteractor: SelectedUserInteractor
private lateinit var underTest: UdfpsOverlayInteractor
@@ -104,7 +106,12 @@ class UdfpsOverlayInteractorTest : SysuiTestCase() {
}
private fun createUdpfsOverlayInteractor() {
- underTest = UdfpsOverlayInteractor(authController, testScope.backgroundScope)
+ underTest =
+ UdfpsOverlayInteractor(
+ authController,
+ selectedUserInteractor,
+ testScope.backgroundScope
+ )
testScope.runCurrent()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
index cc4eca546e17..b48bc1d28f86 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
@@ -48,6 +48,7 @@ import com.android.systemui.res.R.string.kg_too_many_failed_attempts_countdown
import com.android.systemui.res.R.string.kg_trust_agent_disabled
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.KotlinArgumentCaptor
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -86,6 +87,7 @@ class BouncerMessageInteractorTest : SysuiTestCase() {
@Mock private lateinit var countDownTimerUtil: CountDownTimerUtil
@Mock private lateinit var systemPropertiesHelper: SystemPropertiesHelper
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
private lateinit var testScope: TestScope
@@ -119,6 +121,7 @@ class BouncerMessageInteractorTest : SysuiTestCase() {
keyguardUpdateMonitor,
fakeTrustRepository,
testScope.backgroundScope,
+ mSelectedUserInteractor,
)
underTest =
BouncerMessageInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
index 9a5b4585e0ae..9373ada75003 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
@@ -25,7 +25,6 @@ import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.DejankUtils
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN
@@ -37,7 +36,9 @@ import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
import com.android.systemui.utils.os.FakeHandler
@@ -70,6 +71,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() {
@Mock private lateinit var falsingCollector: FalsingCollector
@Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
private lateinit var mainHandler: FakeHandler
private lateinit var underTest: PrimaryBouncerInteractor
private lateinit var resources: TestableResources
@@ -100,6 +102,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() {
keyguardUpdateMonitor,
trustRepository,
testScope.backgroundScope,
+ mSelectedUserInteractor,
)
whenever(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null)
whenever(repository.primaryBouncerShow.value).thenReturn(false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
index 2018e614ba00..d1b120e0948b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
@@ -28,8 +28,8 @@ import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.TrustRepository
-import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.utils.os.FakeHandler
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.TestScope
@@ -51,8 +51,8 @@ class PrimaryBouncerInteractorWithCoroutinesTest : SysuiTestCase() {
@Mock private lateinit var primaryBouncerCallbackInteractor: PrimaryBouncerCallbackInteractor
@Mock private lateinit var falsingCollector: FalsingCollector
@Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry
- @Mock private lateinit var keyguardBypassController: KeyguardBypassController
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
private val mainHandler = FakeHandler(Looper.getMainLooper())
private lateinit var underTest: PrimaryBouncerInteractor
@@ -74,6 +74,7 @@ class PrimaryBouncerInteractorWithCoroutinesTest : SysuiTestCase() {
keyguardUpdateMonitor,
Mockito.mock(TrustRepository::class.java),
TestScope().backgroundScope,
+ mSelectedUserInteractor,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
index 802f8e6043fd..b75f3e0876ff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
@@ -32,8 +32,8 @@ import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.TrustRepository
-import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.utils.os.FakeHandler
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.launchIn
@@ -60,7 +60,7 @@ class KeyguardBouncerViewModelTest : SysuiTestCase() {
@Mock private lateinit var primaryBouncerCallbackInteractor: PrimaryBouncerCallbackInteractor
@Mock private lateinit var falsingCollector: FalsingCollector
@Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry
- @Mock private lateinit var keyguardBypassController: KeyguardBypassController
+ @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
private val mainHandler = FakeHandler(Looper.getMainLooper())
val repository = FakeKeyguardBouncerRepository()
@@ -82,6 +82,7 @@ class KeyguardBouncerViewModelTest : SysuiTestCase() {
keyguardUpdateMonitor,
Mockito.mock(TrustRepository::class.java),
TestScope().backgroundScope,
+ mSelectedUserInteractor,
)
underTest = KeyguardBouncerViewModel(bouncerView, bouncerInteractor)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
index 037c1badf0ac..6d3cc4c536f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
@@ -28,11 +28,11 @@ import androidx.test.filters.SmallTest
import com.android.systemui.ActivityIntentHelper
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.KotlinArgumentCaptor
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
@@ -46,8 +46,8 @@ import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(JUnit4::class)
@@ -74,7 +74,7 @@ class CameraGestureHelperTest : SysuiTestCase() {
@Mock
lateinit var contentResolver: ContentResolver
@Mock
- lateinit var userTracker: UserTracker
+ lateinit var mSelectedUserInteractor: SelectedUserInteractor
private lateinit var underTest: CameraGestureHelper
@@ -103,7 +103,7 @@ class CameraGestureHelperTest : SysuiTestCase() {
cameraIntents = cameraIntents,
contentResolver = contentResolver,
uiExecutor = MoreExecutors.directExecutor(),
- userTracker = userTracker,
+ selectedUserInteractor = mSelectedUserInteractor,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
index 442bf918ad8c..80fe9e762832 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
@@ -40,6 +40,7 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.sensors.ThresholdSensor;
@@ -75,6 +76,8 @@ public class FalsingCollectorImplTest extends SysuiTestCase {
private ShadeExpansionStateManager mShadeExpansionStateManager;
@Mock
private BatteryController mBatteryController;
+ @Mock
+ private SelectedUserInteractor mSelectedUserInteractor;
private final DockManagerFake mDockManager = new DockManagerFake();
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private final FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock);
@@ -90,7 +93,7 @@ public class FalsingCollectorImplTest extends SysuiTestCase {
mKeyguardUpdateMonitor, mHistoryTracker, mProximitySensor,
mStatusBarStateController, mKeyguardStateController, mShadeExpansionStateManager,
mBatteryController,
- mDockManager, mFakeExecutor, mFakeSystemClock);
+ mDockManager, mFakeExecutor, mFakeSystemClock, () -> mSelectedUserInteractor);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
index 15e5e1c7b8db..80b281e458d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
@@ -37,6 +37,7 @@ import com.android.internal.colorextraction.types.Tonal;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import org.junit.Before;
import org.junit.Test;
@@ -63,6 +64,8 @@ public class SysuiColorExtractorTests extends SysuiTestCase {
private WallpaperManager mWallpaperManager;
@Mock
private DumpManager mDumpManager;
+ @Mock
+ private SelectedUserInteractor mSelectedUserInteractor;
private ColorExtractor.GradientColors mColors;
private SysuiColorExtractor mColorExtractor;
@@ -83,7 +86,8 @@ public class SysuiColorExtractorTests extends SysuiTestCase {
mock(ConfigurationController.class),
mWallpaperManager,
mDumpManager,
- true /* immediately */);
+ true /* immediately */,
+ () -> mSelectedUserInteractor);
}
@Test
@@ -125,7 +129,8 @@ public class SysuiColorExtractorTests extends SysuiTestCase {
configurationController,
mWallpaperManager,
mDumpManager,
- true /* immediately */);
+ true /* immediately */,
+ () -> mSelectedUserInteractor);
verify(configurationController).addCallback(eq(sysuiColorExtractor));
reset(tonal);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
index 3df9cbb29e4a..91409a376556 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
@@ -11,11 +11,14 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.communal.data.model.CommunalWidgetMetadata
+import com.android.systemui.communal.shared.CommunalContentSize
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.FakeLogBuffer
+import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.kotlinArgumentCaptor
@@ -59,9 +62,12 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
@Mock private lateinit var stopwatchProviderInfo: AppWidgetProviderInfo
+ private lateinit var communalRepository: FakeCommunalRepository
+
private lateinit var logBuffer: LogBuffer
private val testDispatcher = StandardTestDispatcher()
+
private val testScope = TestScope(testDispatcher)
@Before
@@ -71,6 +77,14 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
logBuffer = FakeLogBuffer.Factory.create()
featureFlagEnabled(true)
+ communalRepository = FakeCommunalRepository()
+ communalRepository.setIsCommunalEnabled(true)
+
+ overrideResource(
+ R.array.config_communalWidgetAllowlist,
+ arrayOf(componentName1, componentName2)
+ )
+
whenever(stopwatchProviderInfo.loadLabel(any())).thenReturn("Stopwatch")
whenever(userTracker.userHandle).thenReturn(userHandle)
}
@@ -219,11 +233,36 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
Mockito.verify(appWidgetHost).stopListening()
}
+ @Test
+ fun getCommunalWidgetAllowList_onInit() {
+ testScope.runTest {
+ val repository = initCommunalWidgetRepository()
+ val communalWidgetAllowlist = repository.communalWidgetAllowlist
+ assertThat(
+ listOf(
+ CommunalWidgetMetadata(
+ componentName = componentName1,
+ priority = 2,
+ sizes = listOf(CommunalContentSize.HALF)
+ ),
+ CommunalWidgetMetadata(
+ componentName = componentName2,
+ priority = 1,
+ sizes = listOf(CommunalContentSize.HALF)
+ )
+ )
+ )
+ .containsExactly(*communalWidgetAllowlist.toTypedArray())
+ }
+ }
+
private fun initCommunalWidgetRepository(): CommunalWidgetRepositoryImpl {
return CommunalWidgetRepositoryImpl(
+ context,
appWidgetManager,
appWidgetHost,
broadcastDispatcher,
+ communalRepository,
packageManager,
userManager,
userTracker,
@@ -282,4 +321,9 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
private fun installedProviders(providers: List<AppWidgetProviderInfo>) {
whenever(appWidgetManager.installedProviders).thenReturn(providers)
}
+
+ companion object {
+ const val componentName1 = "component name 1"
+ const val componentName2 = "component name 2"
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt
new file mode 100644
index 000000000000..9b8e581d1ba4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt
@@ -0,0 +1,195 @@
+/*
+ * 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.deviceentry.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.keyguard.logging.BiometricUnlockLogger
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
+import com.android.systemui.keyevent.data.repository.FakeKeyEventRepository
+import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.power.shared.model.WakefulnessState
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceEntryHapticsInteractorTest : SysuiTestCase() {
+
+ private lateinit var repository: DeviceEntryHapticsRepository
+ private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+ private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
+ private lateinit var keyEventRepository: FakeKeyEventRepository
+ private lateinit var powerRepository: FakePowerRepository
+ private lateinit var systemClock: FakeSystemClock
+ private lateinit var underTest: DeviceEntryHapticsInteractor
+
+ @Before
+ fun setUp() {
+ repository = DeviceEntryHapticsRepositoryImpl()
+ fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+ biometricSettingsRepository = FakeBiometricSettingsRepository()
+ keyEventRepository = FakeKeyEventRepository()
+ powerRepository = FakePowerRepository()
+ systemClock = FakeSystemClock()
+ underTest =
+ DeviceEntryHapticsInteractor(
+ repository = repository,
+ fingerprintPropertyRepository = fingerprintPropertyRepository,
+ biometricSettingsRepository = biometricSettingsRepository,
+ keyEventInteractor = KeyEventInteractor(keyEventRepository),
+ powerInteractor =
+ PowerInteractor(
+ powerRepository,
+ mock(FalsingCollector::class.java),
+ mock(ScreenOffAnimationController::class.java),
+ mock(StatusBarStateController::class.java),
+ ),
+ systemClock = systemClock,
+ logger = mock(BiometricUnlockLogger::class.java),
+ )
+ }
+
+ @Test
+ fun nonPowerButtonFPS_vibrateSuccess() = runTest {
+ val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+ setFingerprintSensorType(FingerprintSensorType.UDFPS_ULTRASONIC)
+ underTest.vibrateSuccess()
+ assertThat(playSuccessHaptic).isTrue()
+ }
+
+ @Test
+ fun powerButtonFPS_vibrateSuccess() = runTest {
+ val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+ setPowerButtonFingerprintProperty()
+ setFingerprintEnrolled()
+ keyEventRepository.setPowerButtonDown(false)
+
+ // It's been 10 seconds since the last power button wakeup
+ setAwakeFromPowerButton()
+ runCurrent()
+ systemClock.setUptimeMillis(systemClock.uptimeMillis() + 10000)
+
+ underTest.vibrateSuccess()
+ assertThat(playSuccessHaptic).isTrue()
+ }
+
+ @Test
+ fun powerButtonFPS_powerDown_doNotVibrateSuccess() = runTest {
+ val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+ setPowerButtonFingerprintProperty()
+ setFingerprintEnrolled()
+ keyEventRepository.setPowerButtonDown(true) // power button is currently DOWN
+
+ // It's been 10 seconds since the last power button wakeup
+ setAwakeFromPowerButton()
+ runCurrent()
+ systemClock.setUptimeMillis(systemClock.uptimeMillis() + 10000)
+
+ underTest.vibrateSuccess()
+ assertThat(playSuccessHaptic).isFalse()
+ }
+
+ @Test
+ fun powerButtonFPS_powerButtonRecentlyPressed_doNotVibrateSuccess() = runTest {
+ val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+ setPowerButtonFingerprintProperty()
+ setFingerprintEnrolled()
+ keyEventRepository.setPowerButtonDown(false)
+
+ // It's only been 50ms since the last power button wakeup
+ setAwakeFromPowerButton()
+ runCurrent()
+ systemClock.setUptimeMillis(systemClock.uptimeMillis() + 50)
+
+ underTest.vibrateSuccess()
+ assertThat(playSuccessHaptic).isFalse()
+ }
+
+ @Test
+ fun nonPowerButtonFPS_vibrateError() = runTest {
+ val playErrorHaptic by collectLastValue(underTest.playErrorHaptic)
+ setFingerprintSensorType(FingerprintSensorType.UDFPS_ULTRASONIC)
+ underTest.vibrateError()
+ assertThat(playErrorHaptic).isTrue()
+ }
+
+ @Test
+ fun powerButtonFPS_vibrateError() = runTest {
+ val playErrorHaptic by collectLastValue(underTest.playErrorHaptic)
+ setPowerButtonFingerprintProperty()
+ setFingerprintEnrolled()
+ underTest.vibrateError()
+ assertThat(playErrorHaptic).isTrue()
+ }
+
+ @Test
+ fun powerButtonFPS_powerDown_doNotVibrateError() = runTest {
+ val playErrorHaptic by collectLastValue(underTest.playErrorHaptic)
+ setPowerButtonFingerprintProperty()
+ setFingerprintEnrolled()
+ keyEventRepository.setPowerButtonDown(true)
+ underTest.vibrateError()
+ assertThat(playErrorHaptic).isFalse()
+ }
+
+ private fun setFingerprintSensorType(fingerprintSensorType: FingerprintSensorType) {
+ fingerprintPropertyRepository.setProperties(
+ sensorId = 0,
+ strength = SensorStrength.STRONG,
+ sensorType = fingerprintSensorType,
+ sensorLocations = mapOf(),
+ )
+ }
+
+ private fun setPowerButtonFingerprintProperty() {
+ setFingerprintSensorType(FingerprintSensorType.POWER_BUTTON)
+ }
+
+ private fun setFingerprintEnrolled() {
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ }
+
+ private fun setAwakeFromPowerButton() {
+ powerRepository.updateWakefulness(
+ WakefulnessState.AWAKE,
+ WakeSleepReason.POWER_BUTTON,
+ WakeSleepReason.POWER_BUTTON,
+ powerButtonLaunchGestureTriggered = false,
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
index 7e1edd2ac193..ba578a39b558 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
@@ -51,6 +51,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.UdfpsController;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.wakelock.WakeLockFake;
import com.android.systemui.utils.os.FakeHandler;
@@ -85,6 +86,8 @@ public class DozeScreenStateTest extends SysuiTestCase {
private DozeLog mDozeLog;
@Mock
private DozeScreenBrightness mDozeScreenBrightness;
+ @Mock
+ private SelectedUserInteractor mSelectedUserInteractor;
@Before
public void setUp() throws Exception {
@@ -100,7 +103,7 @@ public class DozeScreenStateTest extends SysuiTestCase {
mWakeLock = new WakeLockFake();
mScreen = new DozeScreenState(mServiceFake, mHandlerFake, mDozeHost, mDozeParameters,
mWakeLock, mAuthController, mUdfpsControllerProvider, mDozeLog,
- mDozeScreenBrightness);
+ mDozeScreenBrightness, mSelectedUserInteractor);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
index a88a8e590e76..3cc04511d57b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -52,9 +52,9 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.doze.DozeSensors.TriggerSensor;
import com.android.systemui.plugins.SensorManagerPlugin;
-import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.policy.DevicePostureController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.settings.FakeSettings;
@@ -102,7 +102,7 @@ public class DozeSensorsTest extends SysuiTestCase {
@Mock
private DevicePostureController mDevicePostureController;
@Mock
- private UserTracker mUserTracker;
+ private SelectedUserInteractor mSelectedUserInteractor;
@Mock
private ProximitySensor mProximitySensor;
@@ -122,7 +122,8 @@ public class DozeSensorsTest extends SysuiTestCase {
public void setUp() {
MockitoAnnotations.initMocks(this);
mTestableLooper = TestableLooper.get(this);
- when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser());
+ when(mSelectedUserInteractor.getSelectedUserId())
+ .thenReturn(ActivityManager.getCurrentUser());
when(mAmbientDisplayConfiguration.tapSensorTypeMapping())
.thenReturn(new String[]{"tapSensor"});
when(mAmbientDisplayConfiguration.getWakeLockScreenDebounce()).thenReturn(5000L);
@@ -435,7 +436,7 @@ public class DozeSensorsTest extends SysuiTestCase {
DozeSensors dozeSensors = new DozeSensors(mResources, mSensorManager, mDozeParameters,
mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback, mDozeLog,
mProximitySensor, mFakeSettings, mAuthController,
- mDevicePostureController, mUserTracker);
+ mDevicePostureController, mSelectedUserInteractor);
for (TriggerSensor sensor : dozeSensors.mTriggerSensors) {
assertFalse(sensor.mIgnoresSetting);
@@ -480,7 +481,7 @@ public class DozeSensorsTest extends SysuiTestCase {
DozeSensors dozeSensors = new DozeSensors(mResources, mSensorManager, mDozeParameters,
mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback, mDozeLog,
mProximitySensor, mFakeSettings, mAuthController,
- mDevicePostureController, mUserTracker);
+ mDevicePostureController, mSelectedUserInteractor);
for (TriggerSensor sensor : dozeSensors.mTriggerSensors) {
// THEN lift to wake's TriggerSensor enabledBySettings is false
@@ -499,7 +500,7 @@ public class DozeSensorsTest extends SysuiTestCase {
DozeSensors dozeSensors = new DozeSensors(mResources, mSensorManager, mDozeParameters,
mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback, mDozeLog,
mProximitySensor, mFakeSettings, mAuthController,
- mDevicePostureController, mUserTracker);
+ mDevicePostureController, mSelectedUserInteractor);
for (TriggerSensor sensor : dozeSensors.mTriggerSensors) {
// THEN lift to wake's TriggerSensor enabledBySettings is true
@@ -514,7 +515,7 @@ public class DozeSensorsTest extends SysuiTestCase {
super(mResources, mSensorManager, mDozeParameters,
mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback, mDozeLog,
mProximitySensor, mFakeSettings, mAuthController,
- mDevicePostureController, mUserTracker);
+ mDevicePostureController, mSelectedUserInteractor);
for (TriggerSensor sensor : mTriggerSensors) {
if (sensor instanceof PluginSensor
&& ((PluginSensor) sensor).mPluginSensor.getType()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index 494e230947b3..3a6b07539a43 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -53,6 +53,7 @@ import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.concurrency.FakeThreadFactory;
import com.android.systemui.util.sensors.AsyncSensorManager;
@@ -101,6 +102,8 @@ public class DozeTriggersTest extends SysuiTestCase {
@Mock
private UserTracker mUserTracker;
@Mock
+ private SelectedUserInteractor mSelectedUserInteractor;
+ @Mock
private SessionTracker mSessionTracker;
private DozeTriggers mTriggers;
@@ -134,7 +137,7 @@ public class DozeTriggersTest extends SysuiTestCase {
asyncSensorManager, wakeLock, mDockManager, mProximitySensor,
mProximityCheck, mDozeLog, mBroadcastDispatcher, new FakeSettings(),
mAuthController, mUiEventLogger, mSessionTracker, mKeyguardStateController,
- mDevicePostureController, mUserTracker);
+ mDevicePostureController, mUserTracker, mSelectedUserInteractor);
mTriggers.setDozeMachine(mMachine);
waitForSensorManager();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index 2d3f69d95204..00009f778ac7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -76,6 +76,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.window.StatusBarWindowController;
import com.android.systemui.telephony.TelephonyListenerManager;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.RingerModeLiveData;
import com.android.systemui.util.RingerModeTracker;
import com.android.systemui.util.settings.FakeGlobalSettings;
@@ -134,6 +135,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
@Mock private ShadeController mShadeController;
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock private DialogLaunchAnimator mDialogLaunchAnimator;
+ @Mock private SelectedUserInteractor mSelectedUserInteractor;
@Mock private OnBackInvokedDispatcher mOnBackInvokedDispatcher;
@Captor private ArgumentCaptor<OnBackInvokedCallback> mOnBackInvokedCallback;
@@ -186,12 +188,14 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
mPackageManager,
mShadeController,
mKeyguardUpdateMonitor,
- mDialogLaunchAnimator);
+ mDialogLaunchAnimator,
+ mSelectedUserInteractor);
mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting();
ColorExtractor.GradientColors backdropColors = new ColorExtractor.GradientColors();
backdropColors.setMainColor(Color.BLACK);
when(mColorExtractor.getNeutralColors()).thenReturn(backdropColors);
+ when(mSelectedUserInteractor.getSelectedUserId()).thenReturn(0);
}
@Test
@@ -568,7 +572,8 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
@Test
public void testOnLockScreen_disableSmartLock() {
mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
- int user = KeyguardUpdateMonitor.getCurrentUser();
+ int expectedUser = 100;
+ doReturn(expectedUser).when(mSelectedUserInteractor).getSelectedUserId();
doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
@@ -586,7 +591,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
mGlobalActionsDialogLite.showOrHideDialog(true, true, null /* view */);
// Then smart lock will be disabled
- verify(mLockPatternUtils).requireCredentialEntry(eq(user));
+ verify(mLockPatternUtils).requireCredentialEntry(eq(expectedUser));
// hide dialog again
mGlobalActionsDialogLite.showOrHideDialog(true, true, null /* view */);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt
index 0ee348e0d23d..7750d25de753 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt
@@ -28,6 +28,8 @@ import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
+import kotlin.math.max
+import kotlin.test.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -149,26 +151,52 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() {
}
@Test
+ fun playHapticAtProgress_beforeNextDragThreshold_playsLowTicksOnce() {
+ // GIVEN max velocity and a slider progress at half progress
+ val firstProgress = 0.5f
+ val firstTicks = generateTicksComposition(config.maxVelocityToScale, firstProgress)
+
+ // Given a second slider progress event smaller than the progress threshold
+ val secondProgress = firstProgress + max(0f, config.deltaProgressForDragThreshold - 0.01f)
+
+ // GIVEN system running for 1s
+ clock.advanceTime(1000)
+
+ // WHEN two calls to play occur with the required threshold separation (time and progress)
+ sliderHapticFeedbackProvider.onProgress(firstProgress)
+ clock.advanceTime(dragTextureThresholdMillis.toLong())
+ sliderHapticFeedbackProvider.onProgress(secondProgress)
+
+ // THEN Only the first compositions plays
+ verify(vibratorHelper, times(1))
+ .vibrate(eq(firstTicks), any(VibrationAttributes::class.java))
+ verify(vibratorHelper, times(1))
+ .vibrate(any(VibrationEffect::class.java), any(VibrationAttributes::class.java))
+ }
+
+ @Test
fun playHapticAtProgress_afterNextDragThreshold_playsLowTicksTwice() {
- // GIVEN max velocity and slider progress
- val progress = 1f
- val expectedScale = scaleAtProgressChange(config.maxVelocityToScale.toFloat(), progress)
- val ticks = VibrationEffect.startComposition()
- repeat(config.numberOfLowTicks) {
- ticks.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, expectedScale)
- }
+ // GIVEN max velocity and a slider progress at half progress
+ val firstProgress = 0.5f
+ val firstTicks = generateTicksComposition(config.maxVelocityToScale, firstProgress)
+
+ // Given a second slider progress event beyond progress threshold
+ val secondProgress = firstProgress + config.deltaProgressForDragThreshold + 0.01f
+ val secondTicks = generateTicksComposition(config.maxVelocityToScale, secondProgress)
// GIVEN system running for 1s
clock.advanceTime(1000)
- // WHEN two calls to play occur with the required threshold separation
- sliderHapticFeedbackProvider.onProgress(progress)
+ // WHEN two calls to play occur with the required threshold separation (time and progress)
+ sliderHapticFeedbackProvider.onProgress(firstProgress)
clock.advanceTime(dragTextureThresholdMillis.toLong())
- sliderHapticFeedbackProvider.onProgress(progress)
+ sliderHapticFeedbackProvider.onProgress(secondProgress)
- // THEN the correct composition plays two times
- verify(vibratorHelper, times(2))
- .vibrate(eq(ticks.compose()), any(VibrationAttributes::class.java))
+ // THEN the correct compositions play
+ verify(vibratorHelper, times(1))
+ .vibrate(eq(firstTicks), any(VibrationAttributes::class.java))
+ verify(vibratorHelper, times(1))
+ .vibrate(eq(secondTicks), any(VibrationAttributes::class.java))
}
@Test
@@ -229,6 +257,38 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() {
.vibrate(eq(bookendVibration), any(VibrationAttributes::class.java))
}
+ fun dragTextureLastProgress_afterDragTextureHaptics_keepsLastDragTextureProgress() {
+ // GIVEN max velocity and a slider progress at half progress
+ val progress = 0.5f
+
+ // GIVEN system running for 1s
+ clock.advanceTime(1000)
+
+ // WHEN a drag texture plays
+ sliderHapticFeedbackProvider.onProgress(progress)
+
+ // THEN the dragTextureLastProgress remembers the latest progress
+ assertEquals(progress, sliderHapticFeedbackProvider.dragTextureLastProgress)
+ }
+
+ @Test
+ fun dragTextureLastProgress_afterDragTextureHaptics_resetsOnHandleReleased() {
+ // GIVEN max velocity and a slider progress at half progress
+ val progress = 0.5f
+
+ // GIVEN system running for 1s
+ clock.advanceTime(1000)
+
+ // WHEN a drag texture plays
+ sliderHapticFeedbackProvider.onProgress(progress)
+
+ // WHEN the handle is released
+ sliderHapticFeedbackProvider.onHandleReleasedFromTouch()
+
+ // THEN the dragTextureLastProgress tracker is reset
+ assertEquals(-1f, sliderHapticFeedbackProvider.dragTextureLastProgress)
+ }
+
private fun scaleAtBookends(velocity: Float): Float {
val range = config.upperBookendScale - config.lowerBookendScale
val interpolatedVelocity =
@@ -244,4 +304,15 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() {
val bump = interpolatedVelocity * config.additionalVelocityMaxBump
return interpolatedProgress * range + config.progressBasedDragMinScale + bump
}
+
+ private fun generateTicksComposition(velocity: Float, progress: Float): VibrationEffect {
+ val ticks = VibrationEffect.startComposition()
+ repeat(config.numberOfLowTicks) {
+ ticks.addPrimitive(
+ VibrationEffect.Composition.PRIMITIVE_LOW_TICK,
+ scaleAtProgressChange(velocity, progress)
+ )
+ }
+ return ticks.compose()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index a6468234b9b6..2b280c05ba1b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -117,6 +117,7 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.DeviceConfigProxyFake;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -187,6 +188,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
private @Mock ShadeExpansionStateManager mShadeExpansionStateManager;
private @Mock ShadeInteractor mShadeInteractor;
private @Mock ShadeWindowLogger mShadeWindowLogger;
+ private @Mock SelectedUserInteractor mSelectedUserInteractor;
private @Captor ArgumentCaptor<KeyguardStateController.Callback>
mKeyguardStateControllerCallback;
private @Captor ArgumentCaptor<KeyguardUpdateMonitorCallback>
@@ -213,7 +215,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
private @Mock SystemPropertiesHelper mSystemPropertiesHelper;
private FakeFeatureFlags mFeatureFlags;
- private int mInitialUserId;
+ private final int mDefaultUserId = 100;
@Before
public void setUp() throws Exception {
@@ -233,6 +235,8 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
.thenReturn(mock(Flow.class));
when(mDreamingToLockscreenTransitionViewModel.getTransitionEnded())
.thenReturn(mock(Flow.class));
+ when(mSelectedUserInteractor.getSelectedUserId()).thenReturn(mDefaultUserId);
+ when(mSelectedUserInteractor.getSelectedUserId(anyBoolean())).thenReturn(mDefaultUserId);
mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(
mContext,
new FakeWindowRootViewComponent.Factory(mock(WindowRootView.class)),
@@ -251,9 +255,11 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
mAuthController,
mShadeExpansionStateManager,
() -> mShadeInteractor,
- mShadeWindowLogger);
+ mShadeWindowLogger,
+ () -> mSelectedUserInteractor);
mFeatureFlags = new FakeFeatureFlags();
mFeatureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false);
+ mFeatureFlags.set(Flags.REFACTOR_GETCURRENTUSER, true);
DejankUtils.setImmediate(true);
@@ -266,12 +272,6 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
}).when(mKeyguardStateController).notifyKeyguardGoingAway(anyBoolean());
createAndStartViewMediator();
- mInitialUserId = KeyguardUpdateMonitor.getCurrentUser();
- }
-
- @After
- public void teardown() {
- KeyguardUpdateMonitor.setCurrentUser(mInitialUserId);
}
/**
@@ -451,7 +451,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
mViewMediator.setKeyguardEnabled(false);
TestableLooper.get(this).processAllMessages();
- mViewMediator.mViewMediatorCallback.keyguardDonePending(mUpdateMonitor.getCurrentUser());
+ mViewMediator.mViewMediatorCallback.keyguardDonePending(mDefaultUserId);
mViewMediator.mViewMediatorCallback.readyForKeyguardDone();
final ArgumentCaptor<Runnable> animationRunnableCaptor =
ArgumentCaptor.forClass(Runnable.class);
@@ -617,8 +617,8 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
public void lockAfterScreenTimeoutUsesValueFromSettings() {
int currentUserId = 99;
int userSpecificTimeout = 5999;
- KeyguardUpdateMonitor.setCurrentUser(currentUserId);
+ when(mSelectedUserInteractor.getSelectedUserId()).thenReturn(currentUserId);
when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(false);
when(mDevicePolicyManager.getMaximumTimeToLock(null, currentUserId)).thenReturn(0L);
when(mSecureSettings.getIntForUser(LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
@@ -718,7 +718,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
startMockKeyguardExitAnimation();
assertTrue(mViewMediator.isAnimatingBetweenKeyguardAndSurfaceBehind());
- mViewMediator.mViewMediatorCallback.keyguardDonePending(mUpdateMonitor.getCurrentUser());
+ mViewMediator.mViewMediatorCallback.keyguardDonePending(mDefaultUserId);
mViewMediator.mViewMediatorCallback.readyForKeyguardDone();
TestableLooper.get(this).processAllMessages();
verify(mKeyguardUnlockAnimationController).notifyFinishedKeyguardExitAnimation(false);
@@ -782,7 +782,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
// Verify keyguard told of authentication
verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(anyBoolean());
- mViewMediator.mViewMediatorCallback.keyguardDonePending(mUpdateMonitor.getCurrentUser());
+ mViewMediator.mViewMediatorCallback.keyguardDonePending(mDefaultUserId);
mViewMediator.mViewMediatorCallback.readyForKeyguardDone();
final ArgumentCaptor<Runnable> animationRunnableCaptor =
ArgumentCaptor.forClass(Runnable.class);
@@ -814,7 +814,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
// Verify keyguard told of authentication
verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(anyBoolean());
clearInvocations(mStatusBarKeyguardViewManager);
- mViewMediator.mViewMediatorCallback.keyguardDonePending(mUpdateMonitor.getCurrentUser());
+ mViewMediator.mViewMediatorCallback.keyguardDonePending(mDefaultUserId);
mViewMediator.mViewMediatorCallback.readyForKeyguardDone();
final ArgumentCaptor<Runnable> animationRunnableCaptor =
ArgumentCaptor.forClass(Runnable.class);
@@ -844,7 +844,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
// Verify keyguard told of authentication
verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(anyBoolean());
- mViewMediator.mViewMediatorCallback.keyguardDonePending(mUpdateMonitor.getCurrentUser());
+ mViewMediator.mViewMediatorCallback.keyguardDonePending(mDefaultUserId);
mViewMediator.mViewMediatorCallback.readyForKeyguardDone();
final ArgumentCaptor<Runnable> animationRunnableCaptor =
ArgumentCaptor.forClass(Runnable.class);
@@ -1111,7 +1111,8 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
mDispatcher,
() -> mDreamingToLockscreenTransitionViewModel,
mSystemPropertiesHelper,
- () -> mock(WindowManagerLockscreenVisibilityManager.class));
+ () -> mock(WindowManagerLockscreenVisibilityManager.class),
+ mSelectedUserInteractor);
mViewMediator.start();
mViewMediator.registerCentralSurfaces(mCentralSurfaces, null, null, null, null, null);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index b9119e10b720..ef03fdf66fd0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -30,14 +30,10 @@ import com.android.systemui.doze.DozeMachine
import com.android.systemui.doze.DozeTransitionCallback
import com.android.systemui.doze.DozeTransitionListener
import com.android.systemui.dreams.DreamOverlayCallbackController
-import com.android.systemui.keyguard.ScreenLifecycle
-import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.statusbar.phone.BiometricUnlockController
-import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
@@ -47,7 +43,6 @@ import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
@@ -68,13 +63,10 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
@Mock private lateinit var statusBarStateController: StatusBarStateController
@Mock private lateinit var keyguardStateController: KeyguardStateController
- @Mock private lateinit var screenLifecycle: ScreenLifecycle
- @Mock private lateinit var biometricUnlockController: BiometricUnlockController
@Mock private lateinit var dozeTransitionListener: DozeTransitionListener
@Mock private lateinit var authController: AuthController
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var dreamOverlayCallbackController: DreamOverlayCallbackController
- @Mock private lateinit var dozeParameters: DozeParameters
private val mainDispatcher = StandardTestDispatcher()
private val testDispatcher = StandardTestDispatcher()
private val testScope = TestScope(testDispatcher)
@@ -89,12 +81,9 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
underTest =
KeyguardRepositoryImpl(
statusBarStateController,
- screenLifecycle,
- biometricUnlockController,
keyguardStateController,
keyguardUpdateMonitor,
dozeTransitionListener,
- dozeParameters,
authController,
dreamOverlayCallbackController,
mainDispatcher,
@@ -201,26 +190,6 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
}
@Test
- fun isAodAvailable() = runTest {
- val flow = underTest.isAodAvailable
- val isAodAvailable = collectLastValue(flow)
- runCurrent()
-
- val callback =
- withArgCaptor<DozeParameters.Callback> { verify(dozeParameters).addCallback(capture()) }
-
- whenever(dozeParameters.getAlwaysOn()).thenReturn(false)
- callback.onAlwaysOnChange()
- assertThat(isAodAvailable()).isEqualTo(false)
-
- whenever(dozeParameters.getAlwaysOn()).thenReturn(true)
- callback.onAlwaysOnChange()
- assertThat(isAodAvailable()).isEqualTo(true)
-
- flow.onCompletion { verify(dozeParameters).removeCallback(callback) }
- }
-
- @Test
fun isKeyguardOccluded() =
testScope.runTest {
whenever(keyguardStateController.isOccluded).thenReturn(false)
@@ -386,53 +355,6 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
}
@Test
- fun biometricUnlockState() =
- testScope.runTest {
- val values = mutableListOf<BiometricUnlockModel>()
- val job = underTest.biometricUnlockState.onEach(values::add).launchIn(this)
-
- runCurrent()
- val captor = argumentCaptor<BiometricUnlockController.BiometricUnlockEventsListener>()
- verify(biometricUnlockController).addListener(captor.capture())
-
- listOf(
- BiometricUnlockController.MODE_NONE,
- BiometricUnlockController.MODE_WAKE_AND_UNLOCK,
- BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING,
- BiometricUnlockController.MODE_SHOW_BOUNCER,
- BiometricUnlockController.MODE_ONLY_WAKE,
- BiometricUnlockController.MODE_UNLOCK_COLLAPSING,
- BiometricUnlockController.MODE_DISMISS_BOUNCER,
- BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM,
- )
- .forEach {
- whenever(biometricUnlockController.mode).thenReturn(it)
- captor.value.onModeChanged(it)
- runCurrent()
- }
-
- assertThat(values)
- .isEqualTo(
- listOf(
- // Initial value will be NONE, followed by onModeChanged() call
- BiometricUnlockModel.NONE,
- BiometricUnlockModel.NONE,
- BiometricUnlockModel.WAKE_AND_UNLOCK,
- BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING,
- BiometricUnlockModel.SHOW_BOUNCER,
- BiometricUnlockModel.ONLY_WAKE,
- BiometricUnlockModel.UNLOCK_COLLAPSING,
- BiometricUnlockModel.DISMISS_BOUNCER,
- BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM,
- )
- )
-
- job.cancel()
- runCurrent()
- verify(biometricUnlockController).removeListener(captor.value)
- }
-
- @Test
fun dozeTransitionModel() =
testScope.runTest {
// For the initial state
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
index 5bd747fc86e4..8dea57c2eb34 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
@@ -20,10 +20,14 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.coroutines.collectValues
import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.mock
import dagger.Lazy
import junit.framework.Assert.assertEquals
@@ -42,6 +46,12 @@ import org.junit.runner.RunWith
class FromPrimaryBouncerTransitionInteractorTest : KeyguardTransitionInteractorTestCase() {
private lateinit var underTest: FromPrimaryBouncerTransitionInteractor
+ private val mSelectedUserInteractor =
+ SelectedUserInteractor(
+ FakeUserRepository(),
+ FakeFeatureFlagsClassic().apply { set(Flags.REFACTOR_GETCURRENTUSER, true) }
+ )
+
// Override the fromPrimaryBouncerTransitionInteractor provider from the superclass so our
// underTest interactor is provided to any classes that need it.
override var fromPrimaryBouncerTransitionInteractorLazy:
@@ -63,6 +73,7 @@ class FromPrimaryBouncerTransitionInteractorTest : KeyguardTransitionInteractorT
flags = FakeFeatureFlags(),
keyguardSecurityModel = mock(),
powerInteractor = PowerInteractorFactory.create().powerInteractor,
+ selectedUserInteractor = mSelectedUserInteractor
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
index d6e19cbcb826..e87adf5e424b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
@@ -64,8 +64,6 @@ class KeyguardDismissActionInteractorTest : SysuiTestCase() {
KeyguardDismissInteractorFactory.create(
context = context,
testScope = testScope,
- broadcastDispatcher = fakeBroadcastDispatcher,
- dispatcher = dispatcher,
)
keyguardRepository = dismissInteractorWithDependencies.keyguardRepository
transitionRepository = FakeKeyguardTransitionRepository()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt
index a5cfbbfda196..ecb46bdd06c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt
@@ -58,8 +58,6 @@ class KeyguardDismissInteractorTest : SysuiTestCase() {
KeyguardDismissInteractorFactory.create(
context = context,
testScope = testScope,
- broadcastDispatcher = fakeBroadcastDispatcher,
- dispatcher = dispatcher,
)
underTest = underTestDependencies.interactor
underTestDependencies.userRepository.setUserInfos(listOf(userInfo))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
index 6ad2d731f375..e45f56a44b67 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
@@ -62,6 +62,7 @@ import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
@@ -99,6 +100,7 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() {
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var faceWakeUpTriggersConfig: FaceWakeUpTriggersConfig
+ @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
@Before
fun setup() {
@@ -145,6 +147,7 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() {
keyguardUpdateMonitor,
FakeTrustRepository(),
testScope.backgroundScope,
+ mSelectedUserInteractor,
),
AlternateBouncerInteractor(
mock(StatusBarStateController::class.java),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index 5d5ece0f8dcb..f3930a37c486 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -42,6 +42,7 @@ import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.se
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.shade.domain.model.ShadeModel
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.mockito.withArgCaptor
@@ -86,6 +87,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
// Used to verify transition requests for test output
@Mock private lateinit var keyguardSecurityModel: KeyguardSecurityModel
+ @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
private lateinit var fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor
private lateinit var fromDreamingTransitionInteractor: FromDreamingTransitionInteractor
@@ -158,6 +160,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
flags = featureFlags,
keyguardSecurityModel = keyguardSecurityModel,
powerInteractor = powerInteractor,
+ selectedUserInteractor = mSelectedUserInteractor,
)
.apply { start() }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
index 02c98cd9a787..f9362a773fc5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
@@ -46,6 +46,7 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.ActivityStarter.OnDismissAction
import com.android.systemui.power.data.repository.FakePowerRepository
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -90,6 +91,7 @@ class OccludingAppDeviceEntryInteractorTest : SysuiTestCase() {
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var mockedContext: Context
@Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
@Before
fun setup() {
@@ -145,6 +147,7 @@ class OccludingAppDeviceEntryInteractorTest : SysuiTestCase() {
keyguardUpdateMonitor,
trustRepository,
testScope.backgroundScope,
+ mSelectedUserInteractor
),
AlternateBouncerInteractor(
statusBarStateController = mock(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
index 34360d2ddd5c..6cdf4efd67da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
@@ -4,7 +4,9 @@ import android.content.ComponentName
import android.os.UserHandle
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
+import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_APP_SELECTOR_DISPLAYED as STATE_APP_SELECTOR_DISPLAYED
import com.android.systemui.SysuiTestCase
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.mediaprojection.appselector.data.RecentTask
import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider
import com.android.systemui.mediaprojection.appselector.data.RecentTaskThumbnailLoader
@@ -20,6 +22,7 @@ import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mockito.never
import org.mockito.Mockito.verify
@RunWith(AndroidTestingRunner::class)
@@ -37,10 +40,11 @@ class MediaProjectionAppSelectorControllerTest : SysuiTestCase() {
private val view: MediaProjectionAppSelectorView = mock()
private val policyResolver: ScreenCaptureDevicePolicyResolver = mock()
+ private val logger = mock<MediaProjectionMetricsLogger>()
private val thumbnailLoader = FakeThumbnailLoader()
- private val controller =
+ private fun createController(isFirstStart: Boolean = true) =
MediaProjectionAppSelectorController(
taskListProvider,
view,
@@ -50,6 +54,8 @@ class MediaProjectionAppSelectorControllerTest : SysuiTestCase() {
appSelectorComponentName,
callerPackageName,
thumbnailLoader,
+ isFirstStart,
+ logger
)
@Before
@@ -61,7 +67,7 @@ class MediaProjectionAppSelectorControllerTest : SysuiTestCase() {
fun initNoRecentTasks_bindsEmptyList() {
taskListProvider.tasks = emptyList()
- controller.init()
+ createController().init()
verify(view).bind(emptyList())
}
@@ -70,7 +76,7 @@ class MediaProjectionAppSelectorControllerTest : SysuiTestCase() {
fun initOneRecentTask_bindsList() {
taskListProvider.tasks = listOf(createRecentTask(taskId = 1))
- controller.init()
+ createController().init()
verify(view).bind(listOf(createRecentTask(taskId = 1)))
}
@@ -86,7 +92,7 @@ class MediaProjectionAppSelectorControllerTest : SysuiTestCase() {
)
taskListProvider.tasks = tasks
- controller.init()
+ createController().init()
assertThat(thumbnailLoader.capturedTaskIds).containsExactly(2, 3)
}
@@ -101,7 +107,7 @@ class MediaProjectionAppSelectorControllerTest : SysuiTestCase() {
)
taskListProvider.tasks = tasks
- controller.init()
+ createController().init()
verify(view)
.bind(
@@ -124,7 +130,7 @@ class MediaProjectionAppSelectorControllerTest : SysuiTestCase() {
)
taskListProvider.tasks = tasks
- controller.init()
+ createController().init()
verify(view)
.bind(
@@ -147,7 +153,7 @@ class MediaProjectionAppSelectorControllerTest : SysuiTestCase() {
)
taskListProvider.tasks = tasks
- controller.init()
+ createController().init()
verify(view)
.bind(
@@ -172,7 +178,7 @@ class MediaProjectionAppSelectorControllerTest : SysuiTestCase() {
)
taskListProvider.tasks = tasks
- controller.init()
+ createController().init()
verify(view)
.bind(
@@ -199,11 +205,29 @@ class MediaProjectionAppSelectorControllerTest : SysuiTestCase() {
taskListProvider.tasks = tasks
givenCaptureAllowed(isAllow = false)
- controller.init()
+ createController().init()
verify(view).bind(emptyList())
}
+ @Test
+ fun init_firstStart_logsAppSelectorDisplayed() {
+ val controller = createController(isFirstStart = true)
+
+ controller.init()
+
+ verify(logger).notifyPermissionProgress(STATE_APP_SELECTOR_DISPLAYED)
+ }
+
+ @Test
+ fun init_notFirstStart_doesNotLogAppSelectorDisplayed() {
+ val controller = createController(isFirstStart = false)
+
+ controller.init()
+
+ verify(logger, never()).notifyPermissionProgress(STATE_APP_SELECTOR_DISPLAYED)
+ }
+
private fun givenCaptureAllowed(isAllow: Boolean) {
whenever(policyResolver.isScreenCaptureAllowed(any(), any())).thenReturn(isAllow)
}
@@ -216,6 +240,7 @@ class MediaProjectionAppSelectorControllerTest : SysuiTestCase() {
): RecentTask {
return RecentTask(
taskId = taskId,
+ displayId = 0,
topActivityComponent = topActivityComponent,
baseIntentComponent = ComponentName("com", "Test"),
userId = userId,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
index 2c7ee56e9408..d75553fe57ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
@@ -128,6 +128,7 @@ class ShellRecentTaskListProviderTest : SysuiTestCase() {
private fun createRecentTask(taskId: Int): RecentTask =
RecentTask(
taskId = taskId,
+ displayId = 0,
userId = 0,
topActivityComponent = null,
baseIntentComponent = null,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
index 9b1f8303f1ec..0e6e4fa659da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
@@ -36,7 +36,6 @@ import com.android.systemui.qs.footer.FooterActionsTestUtils
import com.android.systemui.qs.footer.domain.model.SecurityButtonConfig
import com.android.systemui.res.R
import com.android.systemui.security.data.model.SecurityModel
-import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.statusbar.policy.FakeSecurityController
import com.android.systemui.statusbar.policy.FakeUserInfoController
import com.android.systemui.statusbar.policy.FakeUserInfoController.FakeInfo
@@ -130,7 +129,6 @@ class FooterActionsViewModelTest : SysuiTestCase() {
val userInfoController = FakeUserInfoController(FakeInfo(picture = picture))
val settings = FakeGlobalSettings()
val userId = 42
- val userTracker = FakeUserTracker(userId)
val userSwitcherControllerWrapper =
MockUserSwitcherControllerWrapper(currentUserName = "foo")
@@ -148,7 +146,6 @@ class FooterActionsViewModelTest : SysuiTestCase() {
utils.footerActionsInteractor(
userSwitcherRepository =
utils.userSwitcherRepository(
- userTracker = userTracker,
settings = settings,
userManager = userManager,
userInfoController = userInfoController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index 7b13de657657..e1345d28dbd0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -42,6 +42,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Assume.assumeTrue
@@ -292,6 +293,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
initialSceneKey = SceneKey.Lockscreen,
authenticationMethod = AuthenticationMethodModel.Pin,
isDeviceUnlocked = false,
+ startsAwake = false
)
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
underTest.start()
@@ -299,6 +301,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
utils.deviceEntryRepository.setUnlocked(true)
runCurrent()
powerInteractor.setAwakeForTest()
+ runCurrent()
assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
}
@@ -403,42 +406,43 @@ class SceneContainerStartableTest : SysuiTestCase() {
initialSceneKey = SceneKey.Lockscreen,
authenticationMethod = AuthenticationMethodModel.Pin,
isDeviceUnlocked = false,
+ startsAwake = false,
)
underTest.start()
runCurrent()
verify(falsingCollector, never()).onScreenTurningOn()
verify(falsingCollector, never()).onScreenOnFromTouch()
- verify(falsingCollector, never()).onScreenOff()
+ verify(falsingCollector, times(1)).onScreenOff()
powerInteractor.setAwakeForTest(reason = PowerManager.WAKE_REASON_POWER_BUTTON)
runCurrent()
verify(falsingCollector, times(1)).onScreenTurningOn()
verify(falsingCollector, never()).onScreenOnFromTouch()
- verify(falsingCollector, never()).onScreenOff()
+ verify(falsingCollector, times(1)).onScreenOff()
powerInteractor.setAsleepForTest()
runCurrent()
verify(falsingCollector, times(1)).onScreenTurningOn()
verify(falsingCollector, never()).onScreenOnFromTouch()
- verify(falsingCollector, times(1)).onScreenOff()
+ verify(falsingCollector, times(2)).onScreenOff()
powerInteractor.setAwakeForTest(reason = PowerManager.WAKE_REASON_TAP)
runCurrent()
verify(falsingCollector, times(1)).onScreenTurningOn()
verify(falsingCollector, times(1)).onScreenOnFromTouch()
- verify(falsingCollector, times(1)).onScreenOff()
+ verify(falsingCollector, times(2)).onScreenOff()
powerInteractor.setAsleepForTest()
runCurrent()
verify(falsingCollector, times(1)).onScreenTurningOn()
verify(falsingCollector, times(1)).onScreenOnFromTouch()
- verify(falsingCollector, times(2)).onScreenOff()
+ verify(falsingCollector, times(3)).onScreenOff()
powerInteractor.setAwakeForTest(reason = PowerManager.WAKE_REASON_POWER_BUTTON)
runCurrent()
verify(falsingCollector, times(2)).onScreenTurningOn()
verify(falsingCollector, times(1)).onScreenOnFromTouch()
- verify(falsingCollector, times(2)).onScreenOff()
+ verify(falsingCollector, times(3)).onScreenOff()
}
@Test
@@ -509,11 +513,12 @@ class SceneContainerStartableTest : SysuiTestCase() {
verify(falsingCollector, times(2)).onBouncerHidden()
}
- private fun prepareState(
+ private fun TestScope.prepareState(
isDeviceUnlocked: Boolean = false,
isBypassEnabled: Boolean = false,
initialSceneKey: SceneKey? = null,
authenticationMethod: AuthenticationMethodModel? = null,
+ startsAwake: Boolean = true,
): MutableStateFlow<ObservableTransitionState> {
assumeTrue(Flags.SCENE_CONTAINER_ENABLED)
sceneContainerFlags.enabled = true
@@ -537,6 +542,13 @@ class SceneContainerStartableTest : SysuiTestCase() {
authenticationMethod != AuthenticationMethodModel.None
)
}
+ if (startsAwake) {
+ powerInteractor.setAwakeForTest()
+ } else {
+ powerInteractor.setAsleepForTest()
+ }
+ runCurrent()
+
return transitionStateFlow
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
index 6e6833dc0944..90d2e78a411f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
@@ -17,8 +17,10 @@
package com.android.systemui.screenrecord;
import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
@@ -37,6 +39,8 @@ import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger;
+import com.android.systemui.mediaprojection.SessionCreationSource;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog;
import com.android.systemui.plugins.ActivityStarter;
@@ -76,6 +80,8 @@ public class RecordingControllerTest extends SysuiTestCase {
private ActivityStarter mActivityStarter;
@Mock
private UserTracker mUserTracker;
+ @Mock
+ private MediaProjectionMetricsLogger mMediaProjectionMetricsLogger;
private FakeFeatureFlags mFeatureFlags;
private RecordingController mController;
@@ -86,8 +92,15 @@ public class RecordingControllerTest extends SysuiTestCase {
public void setUp() {
MockitoAnnotations.initMocks(this);
mFeatureFlags = new FakeFeatureFlags();
- mController = new RecordingController(mMainExecutor, mBroadcastDispatcher, mContext,
- mFeatureFlags, mUserContextProvider, () -> mDevicePolicyResolver, mUserTracker);
+ mController = new RecordingController(
+ mMainExecutor,
+ mBroadcastDispatcher,
+ mContext,
+ mFeatureFlags,
+ mUserContextProvider,
+ () -> mDevicePolicyResolver,
+ mUserTracker,
+ mMediaProjectionMetricsLogger);
mController.addCallback(mCallback);
}
@@ -269,4 +282,21 @@ public class RecordingControllerTest extends SysuiTestCase {
assertThat(dialog).isInstanceOf(ScreenRecordPermissionDialog.class);
}
+
+ @Test
+ public void testPoliciesFlagEnabled_screenCapturingAllowed_logsProjectionInitiated() {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+
+ mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING, true);
+ mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, true);
+ when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(false);
+
+ mController.createScreenRecordDialog(mContext, mFeatureFlags,
+ mDialogLaunchAnimator, mActivityStarter, /* onStartRecordingClicked= */ null);
+
+ verify(mMediaProjectionMetricsLogger)
+ .notifyProjectionInitiated(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index eb006100d535..be82bc314277 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -90,7 +90,8 @@ import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository;
-import com.android.systemui.user.domain.interactor.UserInteractor;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.android.systemui.user.domain.interactor.UserSwitcherInteractor;
import com.google.common.util.concurrent.MoreExecutors;
@@ -131,6 +132,7 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
@Mock private AuthController mAuthController;
@Mock private ShadeExpansionStateManager mShadeExpansionStateManager;
@Mock private ShadeWindowLogger mShadeWindowLogger;
+ @Mock private SelectedUserInteractor mSelectedUserInteractor;
@Captor private ArgumentCaptor<WindowManager.LayoutParams> mLayoutParameters;
@Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListener;
private final Executor mBackgroundExecutor = MoreExecutors.directExecutor();
@@ -216,6 +218,7 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
keyguardInteractor,
featureFlags,
mKeyguardSecurityModel,
+ mSelectedUserInteractor,
powerInteractor);
mShadeInteractor =
@@ -230,7 +233,7 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
keyguardTransitionInteractor,
powerInteractor,
new FakeUserSetupRepository(),
- mock(UserInteractor.class),
+ mock(UserSwitcherInteractor.class),
new SharedNotificationContainerInteractor(
configurationRepository,
mContext,
@@ -255,7 +258,8 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
mAuthController,
mShadeExpansionStateManager,
() -> mShadeInteractor,
- mShadeWindowLogger) {
+ mShadeWindowLogger,
+ () -> mSelectedUserInteractor) {
@Override
protected boolean isDebuggable() {
return 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 677d9db0b97c..4f3216da7370 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -81,6 +81,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController
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.mockito.any
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
@@ -142,6 +143,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
Optional<UnfoldTransitionProgressProvider>
@Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
@Mock lateinit var dragDownHelper: DragDownHelper
+ @Mock lateinit var mSelectedUserInteractor: SelectedUserInteractor
@Mock
lateinit var primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel
@Mock lateinit var sysUIKeyEventHandler: SysUIKeyEventHandler
@@ -200,8 +202,6 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
centralSurfaces,
dozeServiceHost,
dozeScrimController,
- backActionInteractor,
- powerInteractor,
notificationShadeWindowController,
unfoldTransitionProgressProvider,
keyguardUnlockAnimationController,
@@ -243,6 +243,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
mock(KeyguardUpdateMonitor::class.java),
FakeTrustRepository(),
testScope.backgroundScope,
+ mSelectedUserInteractor,
),
facePropertyRepository = FakeFacePropertyRepository(),
deviceEntryFingerprintAuthRepository =
@@ -254,6 +255,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
sysUIKeyEventHandler,
primaryBouncerInteractor,
alternateBouncerInteractor,
+ mSelectedUserInteractor,
)
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 a4a2ca0a6a21..4d3eab45d001 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -79,6 +79,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController
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.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -144,6 +145,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
@Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
@Mock lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
@Mock lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
+ @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
@Mock
private lateinit var primaryBouncerToGoneTransitionViewModel:
PrimaryBouncerToGoneTransitionViewModel
@@ -202,8 +204,6 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
centralSurfaces,
dozeServiceHost,
dozeScrimController,
- backActionInteractor,
- powerInteractor,
notificationShadeWindowController,
unfoldTransitionProgressProvider,
keyguardUnlockAnimationController,
@@ -245,6 +245,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
Mockito.mock(KeyguardUpdateMonitor::class.java),
FakeTrustRepository(),
testScope.backgroundScope,
+ mSelectedUserInteractor,
),
facePropertyRepository = FakeFacePropertyRepository(),
deviceEntryFingerprintAuthRepository =
@@ -256,6 +257,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
Mockito.mock(SysUIKeyEventHandler::class.java),
primaryBouncerInteractor,
alternateBouncerInteractor,
+ mSelectedUserInteractor,
)
controller.setupExpandedStatusBar()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index 65174bab7f63..0fcfaf960737 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -95,15 +95,17 @@ import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository;
-import com.android.systemui.user.domain.interactor.UserInteractor;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.android.systemui.user.domain.interactor.UserSwitcherInteractor;
import com.android.systemui.util.kotlin.JavaAdapter;
+import dagger.Lazy;
+
import org.junit.After;
import org.junit.Before;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import dagger.Lazy;
import kotlinx.coroutines.test.TestScope;
public class QuickSettingsControllerBaseTest extends SysuiTestCase {
@@ -162,7 +164,8 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase {
@Mock protected DumpManager mDumpManager;
@Mock protected UiEventLogger mUiEventLogger;
@Mock protected CastController mCastController;
- @Mock protected UserInteractor mUserInteractor;
+ @Mock protected UserSwitcherInteractor mUserSwitcherInteractor;
+ @Mock protected SelectedUserInteractor mSelectedUserInteractor;
protected FakeDisableFlagsRepository mDisableFlagsRepository =
new FakeDisableFlagsRepository();
protected FakeKeyguardRepository mKeyguardRepository = new FakeKeyguardRepository();
@@ -249,6 +252,7 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase {
keyguardInteractor,
featureFlags,
mock(KeyguardSecurityModel.class),
+ mSelectedUserInteractor,
powerInteractor);
ResourcesSplitShadeStateController splitShadeStateController =
@@ -266,7 +270,7 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase {
keyguardTransitionInteractor,
powerInteractor,
new FakeUserSetupRepository(),
- mUserInteractor,
+ mUserSwitcherInteractor,
new SharedNotificationContainerInteractor(
configurationRepository,
mContext,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
index 5ca45f3df472..7cb6d931ea8b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
@@ -398,6 +398,12 @@ public class QuickSettingsControllerTest extends QuickSettingsControllerBaseTest
.isEqualTo(mQsController.getScrimCornerRadius());
}
+ @Test
+ public void disallowTouches_nullQs_false() {
+ mQsController.setQs(null);
+ assertThat(mQsController.disallowTouches()).isFalse();
+ }
+
private void lockScreen() {
mQsController.setBarState(KEYGUARD);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/MediaArtworkProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/MediaArtworkProcessorTest.kt
deleted file mode 100644
index e4da53a1a0a4..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/MediaArtworkProcessorTest.kt
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar
-
-import com.google.common.truth.Truth.assertThat
-
-import android.graphics.Bitmap
-import android.graphics.Canvas
-import android.graphics.Color
-import android.graphics.Point
-import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import org.junit.After
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-
-private const val WIDTH = 200
-private const val HEIGHT = 200
-
-@RunWith(AndroidTestingRunner::class)
-@SmallTest
-class MediaArtworkProcessorTest : SysuiTestCase() {
-
- private var screenWidth = 0
- private var screenHeight = 0
-
- private lateinit var processor: MediaArtworkProcessor
-
- @Before
- fun setUp() {
- processor = MediaArtworkProcessor()
-
- val point = Point()
- checkNotNull(context.display).getSize(point)
- screenWidth = point.x
- screenHeight = point.y
- }
-
- @After
- fun tearDown() {
- processor.clearCache()
- }
-
- @Test
- fun testProcessArtwork() {
- // GIVEN some "artwork", which is just a solid blue image
- val artwork = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.ARGB_8888)
- Canvas(artwork).drawColor(Color.BLUE)
- // WHEN the background is created from the artwork
- val background = processor.processArtwork(context, artwork)!!
- // THEN the background has the size of the screen that has been downsamples
- assertThat(background.height).isLessThan(screenHeight)
- assertThat(background.width).isLessThan(screenWidth)
- assertThat(background.config).isEqualTo(Bitmap.Config.ARGB_8888)
- }
-
- @Test
- fun testCache() {
- // GIVEN a solid blue image
- val artwork = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.ARGB_8888)
- Canvas(artwork).drawColor(Color.BLUE)
- // WHEN the background is processed twice
- val background1 = processor.processArtwork(context, artwork)!!
- val background2 = processor.processArtwork(context, artwork)!!
- // THEN the two bitmaps are the same
- // Note: This is currently broken and trying to use caching causes issues
- assertThat(background1).isNotSameInstanceAs(background2)
- }
-
- @Test
- fun testConfig() {
- // GIVEN some which is not ARGB_8888
- val artwork = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.ALPHA_8)
- Canvas(artwork).drawColor(Color.BLUE)
- // WHEN the background is created from the artwork
- val background = processor.processArtwork(context, artwork)!!
- // THEN the background has Config ARGB_8888
- assertThat(background.config).isEqualTo(Bitmap.Config.ARGB_8888)
- }
-
- @Test
- fun testRecycledArtwork() {
- // GIVEN some "artwork", which is just a solid blue image
- val artwork = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.ARGB_8888)
- Canvas(artwork).drawColor(Color.BLUE)
- // AND the artwork is recycled
- artwork.recycle()
- // WHEN the background is created from the artwork
- val background = processor.processArtwork(context, artwork)
- // THEN the processed bitmap is null
- assertThat(background).isNull()
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt
index 9d6ea857fefc..cfcf4257ce28 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt
@@ -48,9 +48,7 @@ class NotificationMediaManagerTest : SysuiTestCase() {
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- doCallRealMethod()
- .whenever(notificationMediaManager)
- .updateMediaMetaData(anyBoolean(), anyBoolean())
+ doCallRealMethod().whenever(notificationMediaManager).updateMediaMetaData(anyBoolean())
doReturn(mockBackDropView).whenever(notificationMediaManager).backDropView
}
@@ -62,7 +60,7 @@ class NotificationMediaManagerTest : SysuiTestCase() {
notificationMediaManager.mIsLockscreenLiveWallpaperEnabled = true
for (metaDataChanged in listOf(true, false)) {
for (allowEnterAnimation in listOf(true, false)) {
- notificationMediaManager.updateMediaMetaData(metaDataChanged, allowEnterAnimation)
+ notificationMediaManager.updateMediaMetaData(metaDataChanged)
verify(notificationMediaManager, never()).mediaMetadata
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java
deleted file mode 100644
index aeb5b037be0c..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2017 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 static com.google.common.truth.Truth.assertThat;
-
-import android.annotation.Nullable;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import androidx.palette.graphics.Palette;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.After;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class MediaNotificationProcessorTest extends SysuiTestCase {
-
- private static final int BITMAP_WIDTH = 10;
- private static final int BITMAP_HEIGHT = 10;
-
- /**
- * Color tolerance is borrowed from the AndroidX test utilities for Palette.
- */
- private static final int COLOR_TOLERANCE = 8;
-
- @Nullable private Bitmap mArtwork;
-
- @After
- public void tearDown() {
- if (mArtwork != null) {
- mArtwork.recycle();
- mArtwork = null;
- }
- }
-
- @Test
- public void findBackgroundSwatch_white() {
- // Given artwork that is completely white.
- mArtwork = Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(mArtwork);
- canvas.drawColor(Color.WHITE);
- // WHEN the background swatch is computed
- Palette.Swatch swatch = MediaNotificationProcessor.findBackgroundSwatch(mArtwork);
- // THEN the swatch color is white
- assertCloseColors(swatch.getRgb(), Color.WHITE);
- }
-
- @Test
- public void findBackgroundSwatch_red() {
- // Given artwork that is completely red.
- mArtwork = Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(mArtwork);
- canvas.drawColor(Color.RED);
- // WHEN the background swatch is computed
- Palette.Swatch swatch = MediaNotificationProcessor.findBackgroundSwatch(mArtwork);
- // THEN the swatch color is red
- assertCloseColors(swatch.getRgb(), Color.RED);
- }
-
- static void assertCloseColors(int expected, int actual) {
- assertThat((float) Color.red(expected)).isWithin(COLOR_TOLERANCE).of(Color.red(actual));
- assertThat((float) Color.green(expected)).isWithin(COLOR_TOLERANCE).of(Color.green(actual));
- assertThat((float) Color.blue(expected)).isWithin(COLOR_TOLERANCE).of(Color.blue(actual));
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
index 27be4c802aec..df547ae5883e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
@@ -33,6 +33,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.OnBefo
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
@@ -53,6 +54,7 @@ class SensitiveContentCoordinatorTest : SysuiTestCase() {
val keyguardUpdateMonitor: KeyguardUpdateMonitor = mock()
val statusBarStateController: StatusBarStateController = mock()
val keyguardStateController: KeyguardStateController = mock()
+ val mSelectedUserInteractor: SelectedUserInteractor = mock()
val coordinator: SensitiveContentCoordinator =
DaggerTestSensitiveContentCoordinatorComponent
@@ -62,7 +64,8 @@ class SensitiveContentCoordinatorTest : SysuiTestCase() {
lockscreenUserManager,
keyguardUpdateMonitor,
statusBarStateController,
- keyguardStateController)
+ keyguardStateController,
+ mSelectedUserInteractor)
.coordinator
@Test
@@ -263,7 +266,8 @@ interface TestSensitiveContentCoordinatorComponent {
@BindsInstance lockscreenUserManager: NotificationLockscreenUserManager,
@BindsInstance keyguardUpdateMonitor: KeyguardUpdateMonitor,
@BindsInstance statusBarStateController: StatusBarStateController,
- @BindsInstance keyguardStateController: KeyguardStateController
+ @BindsInstance keyguardStateController: KeyguardStateController,
+ @BindsInstance selectedUserInteractor: SelectedUserInteractor,
): TestSensitiveContentCoordinatorComponent
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index ac11ff29b83a..0a7dc4e05633 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -192,10 +192,26 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
val isOnLockscreen by collectLastValue(underTest.isOnLockscreen)
keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(to = KeyguardState.GONE, transitionState = TransitionState.FINISHED)
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ value = 1f,
+ transitionState = TransitionState.FINISHED
+ )
)
assertThat(isOnLockscreen).isFalse()
+ // While progressing from lockscreen, should still be true
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ value = 0.8f,
+ transitionState = TransitionState.RUNNING
+ )
+ )
+ assertThat(isOnLockscreen).isTrue()
+
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
to = KeyguardState.LOCKSCREEN,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 700de5305778..cfd220b45f1a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -18,7 +18,9 @@ package com.android.systemui.statusbar.phone;
import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
+
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -38,7 +40,6 @@ import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.testing.TestableResources;
-import android.view.HapticFeedbackConstants;
import android.view.ViewRootImpl;
import com.android.internal.logging.MetricsLogger;
@@ -47,17 +48,19 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.logging.BiometricUnlockLogger;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.keyguard.domain.interactor.BiometricUnlockInteractor;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
@@ -107,8 +110,6 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
@Mock
private WakefulnessLifecycle mWakefulnessLifecycle;
@Mock
- private ScreenLifecycle mScreenLifecycle;
- @Mock
private StatusBarStateController mStatusBarStateController;
@Mock
private SessionTracker mSessionTracker;
@@ -122,6 +123,12 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
private BiometricUnlockLogger mLogger;
@Mock
private ViewRootImpl mViewRootImpl;
+ @Mock
+ private DeviceEntryHapticsInteractor mDeviceEntryHapticsInteractor;
+ @Mock
+ private SelectedUserInteractor mSelectedUserInteractor;
+ @Mock
+ private BiometricUnlockInteractor mBiometricUnlockInteractor;
private final FakeSystemClock mSystemClock = new FakeSystemClock();
private FakeFeatureFlags mFeatureFlags;
private BiometricUnlockController mBiometricUnlockController;
@@ -158,7 +165,10 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
mAuthController, mStatusBarStateController,
mSessionTracker, mLatencyTracker, mScreenOffAnimationController, mVibratorHelper,
mSystemClock,
- mFeatureFlags
+ mFeatureFlags,
+ mDeviceEntryHapticsInteractor,
+ () -> mSelectedUserInteractor,
+ mBiometricUnlockInteractor
);
biometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager);
biometricUnlockController.addListener(mBiometricUnlockEventsListener);
@@ -462,145 +472,23 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
}
@Test
- public void onSideFingerprintSuccess_recentPowerButtonPress_noHaptic() {
- // GIVEN side fingerprint enrolled, last wake reason was power button
- when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
- when(mWakefulnessLifecycle.getLastWakeReason())
- .thenReturn(PowerManager.WAKE_REASON_POWER_BUTTON);
-
- // GIVEN last wake time just occurred
- when(mWakefulnessLifecycle.getLastWakeTime()).thenReturn(mSystemClock.uptimeMillis());
-
- // WHEN biometric fingerprint succeeds
- givenFingerprintModeUnlockCollapsing();
- mBiometricUnlockController.startWakeAndUnlock(BiometricSourceType.FINGERPRINT,
- true);
-
- // THEN DO NOT vibrate the device
- verify(mVibratorHelper, never()).vibrateAuthSuccess(anyString());
- }
-
- @Test
- public void onSideFingerprintSuccess_oldPowerButtonPress_playHaptic() {
- // GIVEN side fingerprint enrolled, last wake reason was power button
- when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
- when(mWakefulnessLifecycle.getLastWakeReason())
- .thenReturn(PowerManager.WAKE_REASON_POWER_BUTTON);
-
- // GIVEN last wake time was 500ms ago
- when(mWakefulnessLifecycle.getLastWakeTime()).thenReturn(mSystemClock.uptimeMillis());
- mSystemClock.advanceTime(500);
-
- // WHEN biometric fingerprint succeeds
- givenFingerprintModeUnlockCollapsing();
- mBiometricUnlockController.startWakeAndUnlock(BiometricSourceType.FINGERPRINT,
- true);
-
- // THEN vibrate the device
- verify(mVibratorHelper).vibrateAuthSuccess(anyString());
- }
-
- @Test
- public void onSideFingerprintSuccess_oldPowerButtonPress_playOneWayHaptic() {
- // GIVEN oneway haptics is enabled
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
- // GIVEN side fingerprint enrolled, last wake reason was power button
- when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
- when(mWakefulnessLifecycle.getLastWakeReason())
- .thenReturn(PowerManager.WAKE_REASON_POWER_BUTTON);
-
- // GIVEN last wake time was 500ms ago
- when(mWakefulnessLifecycle.getLastWakeTime()).thenReturn(mSystemClock.uptimeMillis());
- mSystemClock.advanceTime(500);
-
- // WHEN biometric fingerprint succeeds
- givenFingerprintModeUnlockCollapsing();
- mBiometricUnlockController.startWakeAndUnlock(BiometricSourceType.FINGERPRINT,
- true);
-
- // THEN vibrate the device
- verify(mVibratorHelper).performHapticFeedback(
- any(),
- eq(HapticFeedbackConstants.CONFIRM)
- );
- }
-
- @Test
- public void onSideFingerprintSuccess_recentGestureWakeUp_playHaptic() {
- // GIVEN side fingerprint enrolled, wakeup just happened
- when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
- when(mWakefulnessLifecycle.getLastWakeTime()).thenReturn(mSystemClock.uptimeMillis());
-
- // GIVEN last wake reason was from a gesture
- when(mWakefulnessLifecycle.getLastWakeReason())
- .thenReturn(PowerManager.WAKE_REASON_GESTURE);
-
+ public void onFingerprintSuccess_requestSuccessHaptic() {
// WHEN biometric fingerprint succeeds
givenFingerprintModeUnlockCollapsing();
mBiometricUnlockController.startWakeAndUnlock(BiometricSourceType.FINGERPRINT,
true);
- // THEN vibrate the device
- verify(mVibratorHelper).vibrateAuthSuccess(anyString());
- }
-
- @Test
- public void onSideFingerprintSuccess_recentGestureWakeUp_playOnewayHaptic() {
- //GIVEN oneway haptics is enabled
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
- // GIVEN side fingerprint enrolled, wakeup just happened
- when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
- when(mWakefulnessLifecycle.getLastWakeTime()).thenReturn(mSystemClock.uptimeMillis());
-
- // GIVEN last wake reason was from a gesture
- when(mWakefulnessLifecycle.getLastWakeReason())
- .thenReturn(PowerManager.WAKE_REASON_GESTURE);
-
- // WHEN biometric fingerprint succeeds
- givenFingerprintModeUnlockCollapsing();
- mBiometricUnlockController.startWakeAndUnlock(BiometricSourceType.FINGERPRINT,
- true);
-
- // THEN vibrate the device
- verify(mVibratorHelper).performHapticFeedback(
- any(),
- eq(HapticFeedbackConstants.CONFIRM)
- );
- }
-
- @Test
- public void onSideFingerprintFail_alwaysPlaysHaptic() {
- // GIVEN side fingerprint enrolled, last wake reason was recent power button
- when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
- when(mWakefulnessLifecycle.getLastWakeReason())
- .thenReturn(PowerManager.WAKE_REASON_POWER_BUTTON);
- when(mWakefulnessLifecycle.getLastWakeTime()).thenReturn(mSystemClock.uptimeMillis());
-
- // WHEN biometric fingerprint fails
- mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
-
// THEN always vibrate the device
- verify(mVibratorHelper).vibrateAuthError(anyString());
+ verify(mDeviceEntryHapticsInteractor).vibrateSuccess();
}
@Test
- public void onSideFingerprintFail_alwaysPlaysOneWayHaptic() {
- // GIVEN oneway haptics is enabled
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
- // GIVEN side fingerprint enrolled, last wake reason was recent power button
- when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
- when(mWakefulnessLifecycle.getLastWakeReason())
- .thenReturn(PowerManager.WAKE_REASON_POWER_BUTTON);
- when(mWakefulnessLifecycle.getLastWakeTime()).thenReturn(mSystemClock.uptimeMillis());
-
+ public void onFingerprintFail_requestErrorHaptic() {
// WHEN biometric fingerprint fails
mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
// THEN always vibrate the device
- verify(mVibratorHelper).performHapticFeedback(
- any(),
- eq(HapticFeedbackConstants.REJECT)
- );
+ verify(mDeviceEntryHapticsInteractor).vibrateError();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
index f5b7ca804fbc..6fecbb08f040 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
@@ -23,7 +23,6 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -44,6 +43,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.doze.AlwaysOnDisplayPolicy;
import com.android.systemui.doze.DozeScreenState;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.domain.interactor.DozeInteractor;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -85,6 +85,7 @@ public class DozeParametersTest extends SysuiTestCase {
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private ConfigurationController mConfigurationController;
@Mock private UserTracker mUserTracker;
+ @Mock private DozeInteractor mDozeInteractor;
@Captor private ArgumentCaptor<BatteryStateChangeCallback> mBatteryStateChangeCallback;
/**
@@ -128,7 +129,8 @@ public class DozeParametersTest extends SysuiTestCase {
mKeyguardUpdateMonitor,
mConfigurationController,
mStatusBarStateController,
- mUserTracker
+ mUserTracker,
+ mDozeInteractor
);
verify(mBatteryController).addCallback(mBatteryStateChangeCallback.capture());
@@ -186,9 +188,7 @@ public class DozeParametersTest extends SysuiTestCase {
@Test
public void testGetAlwaysOn_whenBatterySaverCallback() {
- DozeParameters.Callback callback = mock(DozeParameters.Callback.class);
- mDozeParameters.addCallback(callback);
-
+ reset(mDozeInteractor);
when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
when(mBatteryController.isAodPowerSave()).thenReturn(true);
@@ -196,16 +196,16 @@ public class DozeParametersTest extends SysuiTestCase {
mDozeParameters.onTuningChanged(Settings.Secure.DOZE_ALWAYS_ON, "1");
mBatteryStateChangeCallback.getValue().onPowerSaveChanged(true);
- verify(callback, times(2)).onAlwaysOnChange();
+ verify(mDozeInteractor, times(2)).setAodAvailable(anyBoolean());
verify(mScreenOffAnimationController, times(2)).onAlwaysOnChanged(false);
assertThat(mDozeParameters.getAlwaysOn()).isFalse();
reset(mScreenOffAnimationController);
- reset(callback);
+ reset(mDozeInteractor);
when(mBatteryController.isAodPowerSave()).thenReturn(false);
mBatteryStateChangeCallback.getValue().onPowerSaveChanged(true);
- verify(callback).onAlwaysOnChange();
+ verify(mDozeInteractor).setAodAvailable(anyBoolean());
verify(mScreenOffAnimationController).onAlwaysOnChanged(true);
assertThat(mDozeParameters.getAlwaysOn()).isTrue();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 45e9224aa253..46b3996c4337 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -93,6 +93,7 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.google.common.truth.Truth;
@@ -147,6 +148,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
@Mock private WindowInsetsController mWindowInsetsController;
@Mock private TaskbarDelegate mTaskbarDelegate;
@Mock private StatusBarKeyguardViewManager.KeyguardViewManagerCallback mCallback;
+ @Mock private SelectedUserInteractor mSelectedUserInteractor;
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback
@@ -212,7 +214,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mock(KeyguardTransitionInteractor.class),
StandardTestDispatcher(null, null),
() -> mock(WindowManagerLockscreenVisibilityInteractor.class),
- () -> mock(KeyguardDismissActionInteractor.class)) {
+ () -> mock(KeyguardDismissActionInteractor.class),
+ mSelectedUserInteractor) {
@Override
public ViewRootImpl getViewRootImpl() {
return mViewRootImpl;
@@ -715,7 +718,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mock(KeyguardTransitionInteractor.class),
StandardTestDispatcher(null, null),
() -> mock(WindowManagerLockscreenVisibilityInteractor.class),
- () -> mock(KeyguardDismissActionInteractor.class)) {
+ () -> mock(KeyguardDismissActionInteractor.class),
+ mSelectedUserInteractor) {
@Override
public ViewRootImpl getViewRootImpl() {
return mViewRootImpl;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLibTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLibTest.kt
index c2f56654e00a..31263627213d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLibTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLibTest.kt
@@ -201,11 +201,11 @@ class WifiRepositoryViaTrackerLibTest : SysuiTestCase() {
testScope.runTest {
val latest by collectLastValue(underTest.isWifiDefault)
- val wifiEntry =
+ val mergedEntry =
mock<MergedCarrierEntry>().apply {
whenever(this.isDefaultNetwork).thenReturn(true)
}
- whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
+ whenever(wifiPickerTracker.mergedCarrierEntry).thenReturn(mergedEntry)
getCallback().onWifiEntriesChanged()
assertThat(latest).isTrue()
@@ -229,11 +229,11 @@ class WifiRepositoryViaTrackerLibTest : SysuiTestCase() {
testScope.runTest {
val latest by collectLastValue(underTest.isWifiDefault)
- val wifiEntry =
+ val mergedEntry =
mock<MergedCarrierEntry>().apply {
whenever(this.isDefaultNetwork).thenReturn(false)
}
- whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
+ whenever(wifiPickerTracker.mergedCarrierEntry).thenReturn(mergedEntry)
getCallback().onWifiEntriesChanged()
assertThat(latest).isFalse()
@@ -526,13 +526,14 @@ class WifiRepositoryViaTrackerLibTest : SysuiTestCase() {
testScope.runTest {
val latest by collectLastValue(underTest.wifiNetwork)
- val wifiEntry =
+ val mergedEntry =
mock<MergedCarrierEntry>().apply {
whenever(this.isPrimaryNetwork).thenReturn(true)
whenever(this.level).thenReturn(3)
whenever(this.subscriptionId).thenReturn(567)
+ whenever(this.isDefaultNetwork).thenReturn(true)
}
- whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
+ whenever(wifiPickerTracker.mergedCarrierEntry).thenReturn(mergedEntry)
getCallback().onWifiEntriesChanged()
assertThat(latest is WifiNetworkModel.CarrierMerged).isTrue()
@@ -546,11 +547,12 @@ class WifiRepositoryViaTrackerLibTest : SysuiTestCase() {
testScope.runTest {
val latest by collectLastValue(underTest.wifiNetwork)
- val wifiEntry =
+ val mergedEntry =
mock<MergedCarrierEntry>().apply {
whenever(this.isPrimaryNetwork).thenReturn(true)
+ whenever(this.isDefaultNetwork).thenReturn(true)
}
- whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
+ whenever(wifiPickerTracker.mergedCarrierEntry).thenReturn(mergedEntry)
whenever(wifiManager.maxSignalLevel).thenReturn(5)
getCallback().onWifiEntriesChanged()
@@ -566,12 +568,13 @@ class WifiRepositoryViaTrackerLibTest : SysuiTestCase() {
testScope.runTest {
val latest by collectLastValue(underTest.wifiNetwork)
- val wifiEntry =
+ val mergedEntry =
mock<MergedCarrierEntry>().apply {
whenever(this.isPrimaryNetwork).thenReturn(true)
whenever(this.subscriptionId).thenReturn(INVALID_SUBSCRIPTION_ID)
+ whenever(this.isDefaultNetwork).thenReturn(true)
}
- whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
+ whenever(wifiPickerTracker.mergedCarrierEntry).thenReturn(mergedEntry)
getCallback().onWifiEntriesChanged()
@@ -628,11 +631,12 @@ class WifiRepositoryViaTrackerLibTest : SysuiTestCase() {
testScope.runTest {
val latest by collectLastValue(underTest.wifiNetwork)
- val wifiEntry =
+ val mergedEntry =
mock<MergedCarrierEntry>().apply {
whenever(this.isPrimaryNetwork).thenReturn(false)
}
- whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
+ whenever(wifiPickerTracker.mergedCarrierEntry).thenReturn(mergedEntry)
+ whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(null)
getCallback().onWifiEntriesChanged()
assertThat(latest).isEqualTo(WifiNetworkModel.Inactive)
@@ -717,12 +721,14 @@ class WifiRepositoryViaTrackerLibTest : SysuiTestCase() {
testScope.runTest {
val latest by collectLastValue(underTest.wifiNetwork)
- val wifiEntry =
+ val mergedEntry =
mock<MergedCarrierEntry>().apply {
whenever(this.isPrimaryNetwork).thenReturn(true)
whenever(this.level).thenReturn(3)
+ whenever(this.isDefaultNetwork).thenReturn(true)
}
- whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
+ whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(null)
+ whenever(wifiPickerTracker.mergedCarrierEntry).thenReturn(mergedEntry)
getCallback().onWifiEntriesChanged()
assertThat(latest is WifiNetworkModel.CarrierMerged).isTrue()
@@ -730,6 +736,7 @@ class WifiRepositoryViaTrackerLibTest : SysuiTestCase() {
// WHEN we lose our current network
whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(null)
+ whenever(wifiPickerTracker.mergedCarrierEntry).thenReturn(null)
getCallback().onWifiEntriesChanged()
// THEN we update to no network
@@ -767,6 +774,56 @@ class WifiRepositoryViaTrackerLibTest : SysuiTestCase() {
}
@Test
+ fun wifiNetwork_carrierMerged_default_usesCarrierMergedInfo() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.wifiNetwork)
+
+ val mergedEntry =
+ mock<MergedCarrierEntry>().apply {
+ whenever(this.isPrimaryNetwork).thenReturn(true)
+ whenever(this.level).thenReturn(3)
+ whenever(this.isDefaultNetwork).thenReturn(true)
+ }
+ val wifiEntry =
+ mock<WifiEntry>().apply {
+ whenever(this.isPrimaryNetwork).thenReturn(true)
+ whenever(this.level).thenReturn(1)
+ whenever(this.title).thenReturn(TITLE)
+ }
+ whenever(wifiPickerTracker.mergedCarrierEntry).thenReturn(mergedEntry)
+ whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
+
+ getCallback().onWifiEntriesChanged()
+
+ assertThat(latest is WifiNetworkModel.CarrierMerged).isTrue()
+ }
+
+ @Test
+ fun wifiNetwork_carrierMerged_notDefault_usesConnectedInfo() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.wifiNetwork)
+
+ val mergedEntry =
+ mock<MergedCarrierEntry>().apply {
+ whenever(this.isPrimaryNetwork).thenReturn(true)
+ whenever(this.level).thenReturn(3)
+ whenever(this.isDefaultNetwork).thenReturn(false)
+ }
+ val wifiEntry =
+ mock<WifiEntry>().apply {
+ whenever(this.isPrimaryNetwork).thenReturn(true)
+ whenever(this.level).thenReturn(1)
+ whenever(this.title).thenReturn(TITLE)
+ }
+ whenever(wifiPickerTracker.mergedCarrierEntry).thenReturn(mergedEntry)
+ whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
+
+ getCallback().onWifiEntriesChanged()
+
+ assertThat(latest is WifiNetworkModel.Active).isTrue()
+ }
+
+ @Test
fun secondaryNetworks_activeEntriesEmpty_isEmpty() =
testScope.runTest {
featureFlags.set(Flags.WIFI_SECONDARY_NETWORKS, true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
index cae892fc2213..e6b09e36cae9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
@@ -38,6 +38,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import dagger.Lazy;
@@ -67,6 +68,8 @@ public class KeyguardStateControllerTest extends SysuiTestCase {
@Mock
private Lazy<KeyguardUnlockAnimationController> mKeyguardUnlockAnimationControllerLazy;
@Mock
+ private SelectedUserInteractor mSelectedUserInteractor;
+ @Mock
private KeyguardUpdateMonitorLogger mLogger;
@Mock
private FeatureFlags mFeatureFlags;
@@ -84,7 +87,8 @@ public class KeyguardStateControllerTest extends SysuiTestCase {
mKeyguardUnlockAnimationControllerLazy,
mLogger,
mDumpManager,
- mFeatureFlags);
+ mFeatureFlags,
+ mSelectedUserInteractor);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt
new file mode 100644
index 000000000000..60fe7d2248e4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt
@@ -0,0 +1,50 @@
+package com.android.systemui.user.domain.interactor
+
+import android.content.pm.UserInfo
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+class SelectedUserInteractorTest : SysuiTestCase() {
+
+ private lateinit var underTest: SelectedUserInteractor
+
+ private val userRepository = FakeUserRepository()
+
+ @Before
+ fun setUp() {
+ userRepository.setUserInfos(USER_INFOS)
+ underTest =
+ SelectedUserInteractor(
+ userRepository,
+ FakeFeatureFlagsClassic().apply { set(Flags.REFACTOR_GETCURRENTUSER, true) }
+ )
+ }
+
+ @Test
+ fun getSelectedUserIdReturnsId() {
+ runBlocking { userRepository.setSelectedUserInfo(USER_INFOS[0]) }
+
+ val actualId = underTest.getSelectedUserId()
+
+ assertThat(actualId).isEqualTo(USER_INFOS[0].id)
+ }
+
+ companion object {
+ private val USER_INFOS =
+ listOf(
+ UserInfo(100, "First user", 0),
+ UserInfo(101, "Second user", 0),
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
index c56266dde752..1968d75a7964 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
@@ -88,7 +88,7 @@ import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(JUnit4::class)
-class UserInteractorTest : SysuiTestCase() {
+class UserSwitcherInteractorTest : SysuiTestCase() {
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var manager: UserManager
@@ -102,7 +102,7 @@ class UserInteractorTest : SysuiTestCase() {
@Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
- private lateinit var underTest: UserInteractor
+ private lateinit var underTest: UserSwitcherInteractor
private lateinit var spyContext: Context
private lateinit var testScope: TestScope
@@ -665,8 +665,8 @@ class UserInteractorTest : SysuiTestCase() {
userRepository.setUserInfos(userInfos)
userRepository.setSelectedUserInfo(userInfos[0])
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
- val callback1: UserInteractor.UserCallback = mock()
- val callback2: UserInteractor.UserCallback = mock()
+ val callback1: UserSwitcherInteractor.UserCallback = mock()
+ val callback2: UserSwitcherInteractor.UserCallback = mock()
underTest.addCallback(callback1)
underTest.addCallback(callback2)
runCurrent()
@@ -1117,7 +1117,7 @@ class UserInteractorTest : SysuiTestCase() {
userRepository.setSelectedUserInfo(userInfo)
}
underTest =
- UserInteractor(
+ UserSwitcherInteractor(
applicationContext = spyContext,
repository = userRepository,
activityStarter = activityStarter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
index a8db368d4150..7041eab9d247 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
@@ -42,7 +42,7 @@ import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.domain.interactor.GuestUserInteractor
import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode
import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
-import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -243,9 +243,8 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() {
userRepository.setSelectedUserInfo(USER_0)
}
return StatusBarUserChipViewModel(
- context = context,
interactor =
- UserInteractor(
+ UserSwitcherInteractor(
applicationContext = context,
repository = userRepository,
activityStarter = activityStarter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
index c236b12d723f..686f492fde50 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
@@ -42,7 +42,7 @@ import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.domain.interactor.GuestUserInteractor
import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode
import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
-import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper
import com.android.systemui.user.shared.model.UserActionModel
import com.android.systemui.util.mockito.any
@@ -157,8 +157,8 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
underTest =
UserSwitcherViewModel(
- userInteractor =
- UserInteractor(
+ userSwitcherInteractor =
+ UserSwitcherInteractor(
applicationContext = context,
repository = userRepository,
activityStarter = activityStarter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/EventsTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/EventsTest.java
index a853f1d84176..c69f5c8198eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/EventsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/EventsTest.java
@@ -209,6 +209,11 @@ public class EventsTest extends SysuiTestCase {
new int[]{MetricsEvent.POWER_OVERHEAT_ALARM,
MetricsEvent.RESERVED_FOR_LOGBUILDER_HISTOGRAM},
Events.VolumeDialogEvent.USB_OVERHEAT_ALARM_DISMISSED},
+ {Events.EVENT_ODI_CAPTIONS_CLICK, null, "writeEvent odi_captions_click", null,
+ Events.VolumeDialogEvent.VOLUME_DIALOG_ODI_CAPTIONS_CLICKED},
+ {Events.EVENT_ODI_CAPTIONS_TOOLTIP_CLICK, null,
+ "writeEvent odi_captions_tooltip_click", null,
+ Events.VolumeDialogEvent.VOLUME_DIALOG_ODI_CAPTIONS_TOOLTIP_CLICKED}
});
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index b8f747b8e961..c4c7472ba39c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -53,11 +53,12 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
+import androidx.test.core.view.MotionEventBuilder;
import androidx.test.filters.SmallTest;
import com.android.internal.jank.InteractionJankMonitor;
+import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.Prefs;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.AnimatorTestRule;
import com.android.systemui.dump.DumpManager;
@@ -66,6 +67,7 @@ import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.plugins.VolumeDialogController.State;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DevicePostureController;
@@ -76,6 +78,8 @@ import com.android.systemui.util.settings.SecureSettings;
import dagger.Lazy;
+import junit.framework.Assert;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -699,6 +703,60 @@ public class VolumeDialogImplTest extends SysuiTestCase {
}
}
+ /**
+ * The click should be a single tap, thus we inject a down and an up event.
+ */
+ @Test
+ public void clickCaptionsButton_logsUiEvent() {
+ UiEventLoggerFake logger = new UiEventLoggerFake();
+ Events.sUiEventLogger = logger;
+ MotionEvent down = MotionEventBuilder.newBuilder()
+ .setAction(MotionEvent.ACTION_DOWN).build();
+ MotionEvent up = MotionEventBuilder.newBuilder()
+ .setAction(MotionEvent.ACTION_UP).build();
+
+ mODICaptionsIcon.onTouchEvent(down);
+ mODICaptionsIcon.onTouchEvent(up);
+ mTestableLooper.moveTimeForward(300); // to confirm it was only a single tap
+ mTestableLooper.processAllMessages();
+
+ boolean foundCaptionLog = false;
+ for (UiEventLoggerFake.FakeUiEvent event : logger.getLogs()) {
+ if (event.eventId
+ == Events.VolumeDialogEvent.VOLUME_DIALOG_ODI_CAPTIONS_CLICKED.getId()) {
+ foundCaptionLog = true;
+ break;
+ }
+ }
+ Assert.assertTrue("Did not log the captions button click.", foundCaptionLog);
+ }
+
+ /**
+ * Pressing the small x button at top right dismisses the captions tooltip.
+ */
+ @Test
+ public void dismissCaptionsTooltip_logsUiEvent() {
+ UiEventLoggerFake logger = new UiEventLoggerFake();
+ Events.sUiEventLogger = logger;
+ mDialog.showCaptionsTooltip();
+ assumeNotNull(mDialog.mODICaptionsTooltipView);
+ View dismissButton = mDialog.mODICaptionsTooltipView.findViewById(R.id.dismiss);
+
+ dismissButton.performClick();
+
+ boolean foundCaptionLog = false;
+ for (UiEventLoggerFake.FakeUiEvent event : logger.getLogs()) {
+ if (event.eventId
+ == Events.VolumeDialogEvent.VOLUME_DIALOG_ODI_CAPTIONS_TOOLTIP_CLICKED.getId()
+ ) {
+ foundCaptionLog = true;
+ break;
+ }
+ }
+ Assert.assertTrue("Did not log the captions tooltip dismiss button click.",
+ foundCaptionLog);
+ }
+
@After
public void teardown() {
// Detailed logs to track down timeout issues in b/299491332
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index c8327029026d..a42fa4129a5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -156,7 +156,8 @@ import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository;
-import com.android.systemui.user.domain.interactor.UserInteractor;
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.android.systemui.user.domain.interactor.UserSwitcherInteractor;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.bubbles.Bubble;
@@ -324,6 +325,8 @@ public class BubblesTest extends SysuiTestCase {
@Mock
private ShadeWindowLogger mShadeWindowLogger;
@Mock
+ private SelectedUserInteractor mSelectedUserInteractor;
+ @Mock
private NotifPipelineFlags mNotifPipelineFlags;
@Mock
private Icon mAppBubbleIcon;
@@ -433,6 +436,7 @@ public class BubblesTest extends SysuiTestCase {
keyguardInteractor,
featureFlags,
mock(KeyguardSecurityModel.class),
+ mSelectedUserInteractor,
powerInteractor);
ResourcesSplitShadeStateController splitShadeStateController =
@@ -450,7 +454,7 @@ public class BubblesTest extends SysuiTestCase {
keyguardTransitionInteractor,
powerInteractor,
new FakeUserSetupRepository(),
- mock(UserInteractor.class),
+ mock(UserSwitcherInteractor.class),
new SharedNotificationContainerInteractor(
configurationRepository,
mContext,
@@ -476,7 +480,8 @@ public class BubblesTest extends SysuiTestCase {
mAuthController,
mShadeExpansionStateManager,
() -> mShadeInteractor,
- mShadeWindowLogger
+ mShadeWindowLogger,
+ () -> mSelectedUserInteractor
);
mNotificationShadeWindowController.fetchWindowRootView();
mNotificationShadeWindowController.attach();
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
index 1a8c5830e453..30132f7747b7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
@@ -1,5 +1,6 @@
package com.android.systemui.communal.data.repository
+import com.android.systemui.communal.data.model.CommunalWidgetMetadata
import com.android.systemui.communal.shared.CommunalAppWidgetInfo
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -8,6 +9,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
class FakeCommunalWidgetRepository : CommunalWidgetRepository {
private val _stopwatchAppWidgetInfo = MutableStateFlow<CommunalAppWidgetInfo?>(null)
override val stopwatchAppWidgetInfo: Flow<CommunalAppWidgetInfo?> = _stopwatchAppWidgetInfo
+ override var communalWidgetAllowlist: List<CommunalWidgetMetadata> = emptyList()
fun setStopwatchAppWidgetInfo(appWidgetInfo: CommunalAppWidgetInfo) {
_stopwatchAppWidgetInfo.value = appWidgetInfo
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 9de7ad8ea780..fae49b120d7d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -29,12 +29,12 @@ import com.android.systemui.keyguard.shared.model.KeyguardRootViewVisibilityStat
import com.android.systemui.keyguard.shared.model.StatusBarState
import dagger.Binds
import dagger.Module
+import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
-import javax.inject.Inject
/** Fake implementation of [KeyguardRepository] */
@SysUISingleton
@@ -182,8 +182,8 @@ class FakeKeyguardRepository @Inject constructor() : KeyguardRepository {
_lastDozeTapToWakePosition.value = position
}
- fun setAodAvailable(isAodAvailable: Boolean) {
- _isAodAvailable.value = isAodAvailable
+ override fun setAodAvailable(value: Boolean) {
+ _isAodAvailable.value = value
}
fun setDreaming(isDreaming: Boolean) {
@@ -202,8 +202,8 @@ class FakeKeyguardRepository @Inject constructor() : KeyguardRepository {
_dozeAmount.value = dozeAmount
}
- fun setBiometricUnlockState(state: BiometricUnlockModel) {
- _biometricUnlockState.tryEmit(state)
+ override fun setBiometricUnlockState(value: BiometricUnlockModel) {
+ _biometricUnlockState.tryEmit(value)
}
fun setBiometricUnlockSource(source: BiometricUnlockSource?) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
index 8e96b522e997..fc34903e8a1d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
@@ -16,11 +16,8 @@
package com.android.systemui.keyguard.domain.interactor
-import android.app.ActivityManager
import android.content.Context
import android.os.Handler
-import android.os.UserManager
-import com.android.internal.logging.UiEventLogger
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
@@ -28,7 +25,6 @@ import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.bouncer.ui.BouncerView
-import com.android.systemui.broadcast.FakeBroadcastDispatcher
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
@@ -36,21 +32,13 @@ import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
-import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.data.repository.FakePowerRepository
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
-import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
import com.android.systemui.user.data.repository.FakeUserRepository
-import com.android.systemui.user.domain.interactor.GuestUserInteractor
-import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode
-import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
-import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.time.FakeSystemClock
-import com.android.systemui.utils.UserRestrictionChecker
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.test.TestScope
import org.mockito.Mockito.mock
@@ -64,8 +52,6 @@ object KeyguardDismissInteractorFactory {
fun create(
context: Context,
testScope: TestScope,
- broadcastDispatcher: FakeBroadcastDispatcher,
- dispatcher: CoroutineDispatcher,
trustRepository: FakeTrustRepository = FakeTrustRepository(),
keyguardRepository: FakeKeyguardRepository = FakeKeyguardRepository(),
bouncerRepository: FakeKeyguardBouncerRepository = FakeKeyguardBouncerRepository(),
@@ -74,6 +60,7 @@ object KeyguardDismissInteractorFactory {
FakeFeatureFlagsClassic().apply {
set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, true)
set(Flags.FULL_SCREEN_USER_SWITCHER, false)
+ set(Flags.REFACTOR_GETCURRENTUSER, true)
},
powerRepository: FakePowerRepository = FakePowerRepository(),
userRepository: FakeUserRepository = FakeUserRepository(),
@@ -92,6 +79,7 @@ object KeyguardDismissInteractorFactory {
keyguardUpdateMonitor,
trustRepository,
testScope.backgroundScope,
+ mock(SelectedUserInteractor::class.java),
)
val alternateBouncerInteractor =
AlternateBouncerInteractor(
@@ -103,38 +91,11 @@ object KeyguardDismissInteractorFactory {
keyguardUpdateMonitor,
)
val powerInteractorWithDeps =
- PowerInteractorFactory.create(
- repository = powerRepository,
- )
- val userInteractor =
- UserInteractor(
- applicationContext = context,
- repository = userRepository,
- mock(ActivityStarter::class.java),
- keyguardInteractor =
- KeyguardInteractorFactory.create(
- repository = keyguardRepository,
- bouncerRepository = bouncerRepository,
- featureFlags = featureFlags,
- )
- .keyguardInteractor,
- featureFlags = featureFlags,
- manager = mock(UserManager::class.java),
- headlessSystemUserMode = mock(HeadlessSystemUserMode::class.java),
- applicationScope = testScope.backgroundScope,
- telephonyInteractor =
- TelephonyInteractor(
- repository = FakeTelephonyRepository(),
- ),
- broadcastDispatcher = broadcastDispatcher,
- keyguardUpdateMonitor = keyguardUpdateMonitor,
- backgroundDispatcher = dispatcher,
- activityManager = mock(ActivityManager::class.java),
- refreshUsersScheduler = mock(RefreshUsersScheduler::class.java),
- guestUserInteractor = mock(GuestUserInteractor::class.java),
- uiEventLogger = mock(UiEventLogger::class.java),
- userRestrictionChecker = mock(UserRestrictionChecker::class.java),
+ PowerInteractorFactory.create(
+ repository = powerRepository,
)
+ val selectedUserInteractor =
+ SelectedUserInteractor(repository = userRepository, flags = featureFlags)
return WithDependencies(
trustRepository = trustRepository,
keyguardRepository = keyguardRepository,
@@ -149,7 +110,7 @@ object KeyguardDismissInteractorFactory {
primaryBouncerInteractor,
alternateBouncerInteractor,
powerInteractorWithDeps.powerInteractor,
- userInteractor,
+ selectedUserInteractor,
),
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt
index 911eafae5c27..cddb007222d5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt
@@ -41,17 +41,17 @@ import com.android.systemui.qs.footer.domain.interactor.FooterActionsInteractorI
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
import com.android.systemui.security.data.repository.SecurityRepository
import com.android.systemui.security.data.repository.SecurityRepositoryImpl
-import com.android.systemui.settings.FakeUserTracker
-import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.FakeSecurityController
import com.android.systemui.statusbar.policy.FakeUserInfoController
import com.android.systemui.statusbar.policy.SecurityController
import com.android.systemui.statusbar.policy.UserInfoController
import com.android.systemui.statusbar.policy.UserSwitcherController
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.user.data.repository.UserSwitcherRepository
import com.android.systemui.user.data.repository.UserSwitcherRepositoryImpl
-import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.settings.FakeGlobalSettings
import com.android.systemui.util.settings.GlobalSettings
@@ -102,7 +102,7 @@ class FooterActionsTestUtils(
deviceProvisionedController: DeviceProvisionedController = mock(),
qsSecurityFooterUtils: QSSecurityFooterUtils = mock(),
fgsManagerController: FgsManagerController = mock(),
- userInteractor: UserInteractor = mock(),
+ userSwitcherInteractor: UserSwitcherInteractor = mock(),
securityRepository: SecurityRepository = securityRepository(),
foregroundServicesRepository: ForegroundServicesRepository = foregroundServicesRepository(),
userSwitcherRepository: UserSwitcherRepository = userSwitcherRepository(),
@@ -116,7 +116,7 @@ class FooterActionsTestUtils(
deviceProvisionedController,
qsSecurityFooterUtils,
fgsManagerController,
- userInteractor,
+ userSwitcherInteractor,
securityRepository,
foregroundServicesRepository,
userSwitcherRepository,
@@ -149,7 +149,7 @@ class FooterActionsTestUtils(
bgHandler: Handler = Handler(testableLooper.looper),
bgDispatcher: CoroutineDispatcher = StandardTestDispatcher(scheduler),
userManager: UserManager = mock(),
- userTracker: UserTracker = FakeUserTracker(),
+ userRepository: UserRepository = FakeUserRepository(),
userSwitcherController: UserSwitcherController = mock(),
userInfoController: UserInfoController = FakeUserInfoController(),
settings: GlobalSettings = FakeGlobalSettings(),
@@ -159,10 +159,10 @@ class FooterActionsTestUtils(
bgHandler,
bgDispatcher,
userManager,
- userTracker,
userSwitcherController,
userInfoController,
settings,
+ userRepository,
)
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/sensors/FakeSensorManager.java b/packages/SystemUI/tests/utils/src/com/android/systemui/util/sensors/FakeSensorManager.java
index 197873f15d0d..288dcfe2825d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/sensors/FakeSensorManager.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/sensors/FakeSensorManager.java
@@ -186,7 +186,7 @@ public class FakeSensorManager extends SensorManager {
}
@Override
- protected boolean initDataInjectionImpl(boolean enable) {
+ protected boolean initDataInjectionImpl(boolean enable, @DataInjectionMode int mode) {
return false;
}
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index f09cb19d0c1d..1a735f89a2bc 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -43,6 +43,13 @@ flag {
}
flag {
+ name: "disable_continuous_shortcut_on_force_stop"
+ namespace: "accessibility"
+ description: "When a package is force stopped, remove the button shortcuts of any continuously-running shortcuts."
+ bug: "198018180"
+}
+
+flag {
name: "deprecate_package_list_observer"
namespace: "accessibility"
description: "Stops using the deprecated PackageListObserver."
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index cd83f8f4d9d8..5af80da894cc 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -68,7 +68,7 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
*
* @see #setUserAndEnabledFeatures(int, int)
*/
- static final int FLAG_FEATURE_SCREEN_MAGNIFIER = 0x00000001;
+ static final int FLAG_FEATURE_MAGNIFICATION_SINGLE_FINGER_TRIPLE_TAP = 0x00000001;
/**
* Flag for enabling the touch exploration feature.
@@ -100,7 +100,7 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
/**
* Flag for enabling the feature to control the screen magnifier. If
- * {@link #FLAG_FEATURE_SCREEN_MAGNIFIER} is set this flag is ignored
+ * {@link #FLAG_FEATURE_MAGNIFICATION_SINGLE_FINGER_TRIPLE_TAP} is set this flag is ignored
* as the screen magnifier feature performs a super set of the work
* performed by this feature.
*
@@ -149,7 +149,7 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
FLAG_FEATURE_INJECT_MOTION_EVENTS
| FLAG_FEATURE_AUTOCLICK
| FLAG_FEATURE_TOUCH_EXPLORATION
- | FLAG_FEATURE_SCREEN_MAGNIFIER
+ | FLAG_FEATURE_MAGNIFICATION_SINGLE_FINGER_TRIPLE_TAP
| FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER
| FLAG_SERVICE_HANDLES_DOUBLE_TAP
| FLAG_REQUEST_MULTI_FINGER_GESTURES
@@ -530,7 +530,7 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
}
if ((mEnabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0
- || ((mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0)
+ || ((mEnabledFeatures & FLAG_FEATURE_MAGNIFICATION_SINGLE_FINGER_TRIPLE_TAP) != 0)
|| ((mEnabledFeatures & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0)) {
final MagnificationGestureHandler magnificationGestureHandler =
createMagnificationGestureHandler(displayId,
@@ -648,7 +648,7 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
private MagnificationGestureHandler createMagnificationGestureHandler(
int displayId, Context displayContext) {
final boolean detectControlGestures = (mEnabledFeatures
- & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0;
+ & FLAG_FEATURE_MAGNIFICATION_SINGLE_FINGER_TRIPLE_TAP) != 0;
final boolean triggerable = (mEnabledFeatures
& FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0;
MagnificationGestureHandler magnificationGestureHandler;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 8c1d444006bd..e65a185adfd2 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.accessibility;
+import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_MANAGER;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_MANAGER_CLIENT;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT;
@@ -182,6 +183,7 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
@@ -650,6 +652,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
+ /**
+ * Returns the lock object for any synchronized test blocks.
+ * Should not be used outside of testing.
+ * @return lock object.
+ */
+ @VisibleForTesting
+ Object getLock() {
+ return mLock;
+ }
+
AccessibilityUserState getCurrentUserState() {
synchronized (mLock) {
return getCurrentUserStateLocked();
@@ -746,6 +758,62 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
+ /**
+ * Handles a package or packages being force stopped.
+ * Will disable any relevant services,
+ * and remove any button targets of continuous services,
+ * denoted by {@link AccessibilityServiceInfo#FLAG_REQUEST_ACCESSIBILITY_BUTTON}.
+ * If the result is {@code true},
+ * then {@link AccessibilityManagerService#onUserStateChangedLocked(
+ * AccessibilityUserState, boolean)} should be called afterwards.
+ *
+ * @param packages list of packages that have stopped.
+ * @param userState user state to be read & modified.
+ * @return {@code true} if a service was enabled or a button target was removed,
+ * {@code false} otherwise.
+ */
+ @VisibleForTesting
+ boolean onPackagesForceStoppedLocked(
+ String[] packages, AccessibilityUserState userState) {
+ final List<String> continuousServicePackages =
+ userState.mInstalledServices.stream().filter(service ->
+ (service.flags & FLAG_REQUEST_ACCESSIBILITY_BUTTON)
+ == FLAG_REQUEST_ACCESSIBILITY_BUTTON
+ ).map(service -> service.getComponentName().flattenToString()).toList();
+
+ boolean enabledServicesChanged = false;
+ final Iterator<ComponentName> it = userState.mEnabledServices.iterator();
+ while (it.hasNext()) {
+ final ComponentName comp = it.next();
+ final String compPkg = comp.getPackageName();
+ for (String pkg : packages) {
+ if (compPkg.equals(pkg)) {
+ it.remove();
+ userState.getBindingServicesLocked().remove(comp);
+ userState.getCrashedServicesLocked().remove(comp);
+ enabledServicesChanged = true;
+ }
+ }
+ }
+ if (enabledServicesChanged) {
+ persistComponentNamesToSettingLocked(
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+ userState.mEnabledServices, userState.mUserId);
+ }
+
+ boolean buttonTargetsChanged = userState.mAccessibilityButtonTargets.removeIf(
+ target -> continuousServicePackages.stream().anyMatch(
+ pkg -> Objects.equals(target, pkg)));
+ if (buttonTargetsChanged) {
+ persistColonDelimitedSetToSettingLocked(
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
+ userState.mUserId,
+ userState.mAccessibilityButtonTargets, str -> str);
+ }
+
+ return enabledServicesChanged || buttonTargetsChanged;
+ }
+
@VisibleForTesting
PackageMonitor getPackageMonitor() {
return mPackageMonitor;
@@ -850,6 +918,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
+ /**
+ * Handles instances in which a package or packages have forcibly stopped.
+ *
+ * @param intent intent containing package event information.
+ * @param uid linux process user id (different from Android user id).
+ * @param packages array of package names that have stopped.
+ * @param doit whether to try and handle the stop or just log the trace.
+ *
+ * @return {@code true} if package should be restarted, {@code false} otherwise.
+ */
@Override
public boolean onHandleForceStop(Intent intent, String[] packages,
int uid, boolean doit) {
@@ -867,26 +945,36 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return false;
}
final AccessibilityUserState userState = getUserStateLocked(userId);
- final Iterator<ComponentName> it = userState.mEnabledServices.iterator();
- while (it.hasNext()) {
- final ComponentName comp = it.next();
- final String compPkg = comp.getPackageName();
- for (String pkg : packages) {
- if (compPkg.equals(pkg)) {
- if (!doit) {
- return true;
+
+ if (Flags.disableContinuousShortcutOnForceStop()) {
+ if (doit && onPackagesForceStoppedLocked(packages, userState)) {
+ onUserStateChangedLocked(userState);
+ return false;
+ } else {
+ return true;
+ }
+ } else {
+ final Iterator<ComponentName> it = userState.mEnabledServices.iterator();
+ while (it.hasNext()) {
+ final ComponentName comp = it.next();
+ final String compPkg = comp.getPackageName();
+ for (String pkg : packages) {
+ if (compPkg.equals(pkg)) {
+ if (!doit) {
+ return true;
+ }
+ it.remove();
+ userState.getBindingServicesLocked().remove(comp);
+ userState.getCrashedServicesLocked().remove(comp);
+ persistComponentNamesToSettingLocked(
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+ userState.mEnabledServices, userId);
+ onUserStateChangedLocked(userState);
}
- it.remove();
- userState.getBindingServicesLocked().remove(comp);
- userState.getCrashedServicesLocked().remove(comp);
- persistComponentNamesToSettingLocked(
- Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
- userState.mEnabledServices, userId);
- onUserStateChangedLocked(userState);
}
}
+ return false;
}
- return false;
}
}
};
@@ -2452,7 +2540,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
* @param userId The user id.
* @param outComponentNames The output component names.
*/
- private void readComponentNamesFromSettingLocked(String settingName, int userId,
+ @VisibleForTesting
+ void readComponentNamesFromSettingLocked(String settingName, int userId,
Set<ComponentName> outComponentNames) {
readColonDelimitedSettingToSet(settingName, userId,
str -> ComponentName.unflattenFromString(str), outComponentNames);
@@ -2481,7 +2570,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
componentName -> componentName.flattenToShortString());
}
- private <T> void readColonDelimitedSettingToSet(String settingName, int userId,
+ /**
+ * Reads a colon delimited setting,
+ * passes the values through a function,
+ * then stores the values in a provided set.
+ *
+ * @param settingName Name of setting.
+ * @param userId user id corresponding to setting.
+ * @param toItem function mapping values to the output set.
+ * @param outSet output set to write to.
+ * @param <T> type of output set.
+ */
+ @VisibleForTesting
+ <T> void readColonDelimitedSettingToSet(String settingName, int userId,
Function<String, T> toItem, Set<T> outSet) {
final String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
settingName, userId);
@@ -2693,8 +2794,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
AccessibilityInputFilter inputFilter = null;
synchronized (mLock) {
int flags = 0;
- if (userState.isDisplayMagnificationEnabledLocked()) {
- flags |= AccessibilityInputFilter.FLAG_FEATURE_SCREEN_MAGNIFIER;
+ if (userState.isMagnificationSingleFingerTripleTapEnabledLocked()) {
+ flags |= AccessibilityInputFilter
+ .FLAG_FEATURE_MAGNIFICATION_SINGLE_FINGER_TRIPLE_TAP;
}
if (userState.isShortcutMagnificationEnabledLocked()) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER;
@@ -3047,12 +3149,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
private boolean readMagnificationEnabledSettingsLocked(AccessibilityUserState userState) {
- final boolean displayMagnificationEnabled = Settings.Secure.getIntForUser(
+ final boolean magnificationSingleFingerTripleTapEnabled = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
0, userState.mUserId) == 1;
- if ((displayMagnificationEnabled != userState.isDisplayMagnificationEnabledLocked())) {
- userState.setDisplayMagnificationEnabledLocked(displayMagnificationEnabled);
+ if ((magnificationSingleFingerTripleTapEnabled
+ != userState.isMagnificationSingleFingerTripleTapEnabledLocked())) {
+ userState.setMagnificationSingleFingerTripleTapEnabledLocked(
+ magnificationSingleFingerTripleTapEnabled);
return true;
}
return false;
@@ -3292,7 +3396,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
// We would skip overlay display because it uses overlay window to simulate secondary
// displays in one display. It's not a real display and there's no input events for it.
final ArrayList<Display> displays = getValidDisplayList();
- if (userState.isDisplayMagnificationEnabledLocked()
+ if (userState.isMagnificationSingleFingerTripleTapEnabledLocked()
|| userState.isShortcutMagnificationEnabledLocked()) {
for (int i = 0; i < displays.size(); i++) {
final Display display = displays.get(i);
@@ -3321,7 +3425,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return;
}
final boolean connect = (userState.isShortcutMagnificationEnabledLocked()
- || userState.isDisplayMagnificationEnabledLocked())
+ || userState.isMagnificationSingleFingerTripleTapEnabledLocked())
&& (userState.getMagnificationCapabilitiesLocked()
!= Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN)
|| userHasMagnificationServicesLocked(userState);
@@ -3472,7 +3576,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return true;
}
final boolean requestA11yButton = (serviceInfo.flags
- & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
+ & FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
if (requestA11yButton && !userState.mEnabledServices.contains(componentName)) {
// An a11y service targeting sdk version > Q and request A11y button and is assigned
// to a11y btn should be in the enabled list.
@@ -3773,7 +3877,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
final int targetSdk = installedServiceInfo.getResolveInfo()
.serviceInfo.applicationInfo.targetSdkVersion;
final boolean requestA11yButton = (installedServiceInfo.flags
- & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
+ & FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
// Turns on / off the accessibility service
if ((targetSdk <= Build.VERSION_CODES.Q && shortcutType == ACCESSIBILITY_SHORTCUT_KEY)
|| (targetSdk > Build.VERSION_CODES.Q && !requestA11yButton)) {
@@ -5006,7 +5110,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
updateWindowMagnificationConnectionIfNeeded(userState);
// Remove magnification button UI when the magnification capability is not all mode or
// magnification is disabled.
- if (!(userState.isDisplayMagnificationEnabledLocked()
+ if (!(userState.isMagnificationSingleFingerTripleTapEnabledLocked()
|| userState.isShortcutMagnificationEnabledLocked())
|| userState.getMagnificationCapabilitiesLocked()
!= Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index 693526adc8d6..b4efec1a4b38 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -109,7 +109,7 @@ class AccessibilityUserState {
private boolean mBindInstantServiceAllowed;
private boolean mIsAudioDescriptionByDefaultRequested;
private boolean mIsAutoclickEnabled;
- private boolean mIsDisplayMagnificationEnabled;
+ private boolean mIsMagnificationSingleFingerTripleTapEnabled;
private boolean mIsFilterKeyEventsEnabled;
private boolean mIsPerformGesturesEnabled;
private boolean mAccessibilityFocusOnlyInActiveWindow;
@@ -211,7 +211,7 @@ class AccessibilityUserState {
mRequestMultiFingerGestures = false;
mRequestTwoFingerPassthrough = false;
mSendMotionEventsEnabled = false;
- mIsDisplayMagnificationEnabled = false;
+ mIsMagnificationSingleFingerTripleTapEnabled = false;
mIsAutoclickEnabled = false;
mUserNonInteractiveUiTimeout = 0;
mUserInteractiveUiTimeout = 0;
@@ -520,7 +520,7 @@ class AccessibilityUserState {
.append(String.valueOf(mRequestTwoFingerPassthrough));
pw.append(", sendMotionEventsEnabled").append(String.valueOf(mSendMotionEventsEnabled));
pw.append(", displayMagnificationEnabled=").append(String.valueOf(
- mIsDisplayMagnificationEnabled));
+ mIsMagnificationSingleFingerTripleTapEnabled));
pw.append(", autoclickEnabled=").append(String.valueOf(mIsAutoclickEnabled));
pw.append(", nonInteractiveUiTimeout=").append(String.valueOf(mNonInteractiveUiTimeout));
pw.append(", interactiveUiTimeout=").append(String.valueOf(mInteractiveUiTimeout));
@@ -625,12 +625,12 @@ class AccessibilityUserState {
mIsAutoclickEnabled = enabled;
}
- public boolean isDisplayMagnificationEnabledLocked() {
- return mIsDisplayMagnificationEnabled;
+ public boolean isMagnificationSingleFingerTripleTapEnabledLocked() {
+ return mIsMagnificationSingleFingerTripleTapEnabled;
}
- public void setDisplayMagnificationEnabledLocked(boolean enabled) {
- mIsDisplayMagnificationEnabled = enabled;
+ public void setMagnificationSingleFingerTripleTapEnabledLocked(boolean enabled) {
+ mIsMagnificationSingleFingerTripleTapEnabled = enabled;
}
public boolean isFilterKeyEventsEnabledLocked() {
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index ee41a69c6f4c..65975e44ee2a 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -1437,6 +1437,10 @@ public class ContentCaptureManagerService extends
if (!mDevCfgEnableContentProtectionReceiver) {
return false;
}
+ if (mDevCfgContentProtectionRequiredGroups.isEmpty()
+ && mDevCfgContentProtectionOptionalGroups.isEmpty()) {
+ return false;
+ }
}
return mContentProtectionConsentManager.isConsentGranted(userId)
&& mContentProtectionBlocklistManager.isAllowed(packageName);
diff --git a/services/core/Android.bp b/services/core/Android.bp
index e5225f65f22a..898cdcc30e0f 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -96,11 +96,26 @@ genrule {
out: ["com/android/server/location/contexthub/ContextHubStatsLog.java"],
}
+/*
+ * This module is used to refer aconfig flag libraries that are
+ * added to the framework via static_libs.
+ * These libraries are referred here via libs prevent duplication of classes in both
+ * the framework and the system server.
+*/
+java_defaults {
+ name: "shared-framework-aconfig-libs",
+ libs: [
+ "display_flags_lib",
+ "camera_platform_flags_core_java_lib",
+ ],
+}
+
java_library_static {
name: "services.core.unboosted",
defaults: [
"platform_service_defaults",
"android.hardware.power-java_static",
+ "shared-framework-aconfig-libs",
],
srcs: [
":android.hardware.biometrics.face-V3-java-source",
@@ -181,7 +196,6 @@ java_library_static {
"android.hardware.power.stats-V2-java",
"android.hidl.manager-V1.2-java",
"cbor-java",
- "display_flags_lib",
"icu4j_calendar_astronomer",
"android.security.aaid_aidl-java",
"netd-client",
@@ -191,9 +205,8 @@ java_library_static {
"ImmutabilityAnnotation",
"securebox",
"apache-commons-math",
- "power_optimization_flags_lib",
+ "backstage_power_flags_lib",
"notification_flags_lib",
- "camera_platform_flags_core_java_lib",
"biometrics_flags_lib",
"am_flags_lib",
],
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 8df54569cccd..638abdba36ec 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -1387,11 +1387,12 @@ public abstract class PackageManagerInternal {
@UserIdInt int userId);
/**
- * Tells PackageManager when a component (except BroadcastReceivers) of the package is used
+ * Tells PackageManager when a component of the package is used
* and the package should get out of stopped state and be enabled.
*/
public abstract void notifyComponentUsed(@NonNull String packageName,
- @UserIdInt int userId, @NonNull String recentCallingPackage, @NonNull String debugInfo);
+ @UserIdInt int userId, @Nullable String recentCallingPackage,
+ @NonNull String debugInfo);
/** @deprecated For legacy shell command only. */
@Deprecated
diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
index 127c5b389d79..3c56752d08e3 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
@@ -1440,10 +1440,9 @@ public class BroadcastQueueImpl extends BroadcastQueue {
r.curComponent.getPackageName(), r.userId, Event.APP_COMPONENT_USED);
}
- // Broadcast is being executed, its package can't be stopped.
try {
- mService.mPackageManagerInt.setPackageStoppedState(
- r.curComponent.getPackageName(), false, r.userId);
+ mService.mPackageManagerInt.notifyComponentUsed(
+ r.curComponent.getPackageName(), r.userId, r.callerPackage, r.toString());
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
+ r.curComponent.getPackageName() + ": " + e);
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index d19eae5b0709..b48169788180 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -1982,8 +1982,8 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
mService.notifyPackageUse(receiverPackageName,
PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER);
- mService.mPackageManagerInt.setPackageStoppedState(
- receiverPackageName, false, r.userId);
+ mService.mPackageManagerInt.notifyComponentUsed(
+ receiverPackageName, r.userId, r.callerPackage, r.toString());
}
private void reportUsageStatsBroadcastDispatched(@NonNull ProcessRecord app,
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index a451f361a933..9bba08ad1bfd 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -2373,8 +2373,7 @@ public class OomAdjuster {
}
}
- if (ppr.getLastProviderTime() > 0
- && (ppr.getLastProviderTime() + mConstants.CONTENT_PROVIDER_RETAIN_TIME) > now) {
+ if ((ppr.getLastProviderTime() + mConstants.CONTENT_PROVIDER_RETAIN_TIME) > now) {
if (adj > PREVIOUS_APP_ADJ) {
adj = PREVIOUS_APP_ADJ;
schedGroup = SCHED_GROUP_BACKGROUND;
diff --git a/services/core/java/com/android/server/am/ProcessProviderRecord.java b/services/core/java/com/android/server/am/ProcessProviderRecord.java
index 751e8a821ab1..9b72a3afcfd9 100644
--- a/services/core/java/com/android/server/am/ProcessProviderRecord.java
+++ b/services/core/java/com/android/server/am/ProcessProviderRecord.java
@@ -34,7 +34,7 @@ final class ProcessProviderRecord {
/**
* The last time someone else was using a provider in this process.
*/
- private long mLastProviderTime;
+ private long mLastProviderTime = Long.MIN_VALUE;
/**
* class (String) -> ContentProviderRecord.
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
index a9c388c232ed..27c08763fab0 100644
--- a/services/core/java/com/android/server/am/ProcessStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -280,7 +280,7 @@ final class ProcessStateRecord {
* The last time the process was in the TOP state or greater.
*/
@GuardedBy("mService")
- private long mLastTopTime;
+ private long mLastTopTime = Long.MIN_VALUE;
/**
* Is this an empty background process?
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 16e3fdf2a6ab..2d231b3cf42e 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -152,6 +152,7 @@ public class SettingsToPropertiesMapper {
"preload_safety",
"responsible_apis",
"rust",
+ "safety_center",
"system_performance",
"test_suites",
"text",
@@ -159,7 +160,14 @@ public class SettingsToPropertiesMapper {
"tv_system_ui",
"vibrator",
"virtual_devices",
+ "wear_calling_messaging",
+ "wear_connectivity",
+ "wear_esim_carriers",
"wear_frameworks",
+ "wear_health_services",
+ "wear_media",
+ "wear_offload",
+ "wear_security",
"wear_system_health",
"wear_systems",
"window_surfaces",
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index 26d99d843c7e..bb9ea285385b 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -2,7 +2,7 @@ package: "com.android.server.am"
flag {
name: "oomadjuster_correctness_rewrite"
- namespace: "android_platform_power_optimization"
+ namespace: "backstage_power"
description: "Utilize new OomAdjuster implementation"
bug: "298055811"
is_fixed_read_only: true
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 9cfac9af3991..eea3d3885b34 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -68,6 +68,7 @@ import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
+import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -496,8 +497,9 @@ public class AudioDeviceBroker {
AudioDeviceInfo.TYPE_AUX_LINE
};
- /*package */ static boolean isValidCommunicationDevice(AudioDeviceInfo device) {
- return isValidCommunicationDeviceType(device.getType());
+ /*package */ static boolean isValidCommunicationDevice(@NonNull AudioDeviceInfo device) {
+ Objects.requireNonNull(device, "device must not be null");
+ return device.isSink() && isValidCommunicationDeviceType(device.getType());
}
private static boolean isValidCommunicationDeviceType(int deviceType) {
diff --git a/services/core/java/com/android/server/audio/MusicFxHelper.java b/services/core/java/com/android/server/audio/MusicFxHelper.java
index 6c0fef5f628d..5f4e4c3bc4e0 100644
--- a/services/core/java/com/android/server/audio/MusicFxHelper.java
+++ b/services/core/java/com/android/server/audio/MusicFxHelper.java
@@ -157,7 +157,8 @@ public class MusicFxHelper {
Log.w(TAG, " inside handle MSG_EFFECT_CLIENT_GONE");
// Once the uid is no longer running, close all remain audio session(s) for this UID
if (mClientUidSessionMap.get(Integer.valueOf(uid)) != null) {
- final List<Integer> sessions = mClientUidSessionMap.get(Integer.valueOf(uid));
+ final List<Integer> sessions =
+ new ArrayList(mClientUidSessionMap.get(Integer.valueOf(uid)));
Log.i(TAG, "UID " + uid + " gone, closing " + sessions.size() + " sessions");
for (Integer session : sessions) {
Intent intent = new Intent(
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 906c66d4fe8a..76dde5463534 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -1474,11 +1474,11 @@ public class ClipboardService extends SystemService {
.getDrawable(R.drawable.ic_safety_protection);
toastToShow = Toast.makeCustomToastWithIcon(toastContext,
UiThread.get().getLooper(), message,
- Toast.LENGTH_SHORT, safetyProtectionIcon);
+ Toast.LENGTH_LONG, safetyProtectionIcon);
} else {
toastToShow = Toast.makeText(
toastContext, UiThread.get().getLooper(), message,
- Toast.LENGTH_SHORT);
+ Toast.LENGTH_LONG);
}
toastToShow.show();
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 35a4f475c878..cb2302a60248 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -758,7 +758,8 @@ public final class DisplayManagerService extends SystemService {
mContext.registerReceiver(mIdleModeReceiver, filter);
- mSmallAreaDetectionController = SmallAreaDetectionController.create(mContext);
+ mSmallAreaDetectionController = (mFlags.isSmallAreaDetectionEnabled())
+ ? SmallAreaDetectionController.create(mContext) : null;
}
@VisibleForTesting
@@ -2973,7 +2974,7 @@ public final class DisplayManagerService extends SystemService {
// Check if the target app is in cached mode
private boolean isUidCached(int uid) {
- if (mActivityManagerInternal == null) {
+ if (mActivityManagerInternal == null || uid < FIRST_APPLICATION_UID) {
return false;
}
int procState = mActivityManagerInternal.getUidProcessState(uid);
diff --git a/services/core/java/com/android/server/display/SmallAreaDetectionController.java b/services/core/java/com/android/server/display/SmallAreaDetectionController.java
index adaa5390cb9b..bf384b02d95e 100644
--- a/services/core/java/com/android/server/display/SmallAreaDetectionController.java
+++ b/services/core/java/com/android/server/display/SmallAreaDetectionController.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.PackageManagerInternal;
+import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.provider.DeviceConfigInterface;
import android.util.ArrayMap;
@@ -30,15 +31,14 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
-import com.android.server.pm.UserManagerInternal;
+import com.android.server.pm.pkg.PackageStateInternal;
import java.io.PrintWriter;
-import java.util.Arrays;
import java.util.Map;
final class SmallAreaDetectionController {
- private static native void nativeUpdateSmallAreaDetection(int[] uids, float[] thresholds);
- private static native void nativeSetSmallAreaDetectionThreshold(int uid, float threshold);
+ private static native void nativeUpdateSmallAreaDetection(int[] appIds, float[] thresholds);
+ private static native void nativeSetSmallAreaDetectionThreshold(int appId, float threshold);
// TODO(b/281720315): Move this to DeviceConfig once server side ready.
private static final String KEY_SMALL_AREA_DETECTION_ALLOWLIST =
@@ -47,12 +47,8 @@ final class SmallAreaDetectionController {
private final Object mLock = new Object();
private final Context mContext;
private final PackageManagerInternal mPackageManager;
- private final UserManagerInternal mUserManager;
@GuardedBy("mLock")
private final Map<String, Float> mAllowPkgMap = new ArrayMap<>();
- // TODO(b/298722189): Update allowlist when user changes
- @GuardedBy("mLock")
- private int[] mUserIds;
static SmallAreaDetectionController create(@NonNull Context context) {
final SmallAreaDetectionController controller =
@@ -67,7 +63,6 @@ final class SmallAreaDetectionController {
SmallAreaDetectionController(Context context, DeviceConfigInterface deviceConfig) {
mContext = context;
mPackageManager = LocalServices.getService(PackageManagerInternal.class);
- mUserManager = LocalServices.getService(UserManagerInternal.class);
deviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
BackgroundThread.getExecutor(),
new SmallAreaDetectionController.OnPropertiesChangedListener());
@@ -76,6 +71,7 @@ final class SmallAreaDetectionController {
@VisibleForTesting
void updateAllowlist(@Nullable String property) {
+ final Map<String, Float> allowPkgMap = new ArrayMap<>();
synchronized (mLock) {
mAllowPkgMap.clear();
if (property != null) {
@@ -86,8 +82,11 @@ final class SmallAreaDetectionController {
.getStringArray(R.array.config_smallAreaDetectionAllowlist);
for (String defaultMapString : defaultMapStrings) putToAllowlist(defaultMapString);
}
- updateSmallAreaDetection();
+
+ if (mAllowPkgMap.isEmpty()) return;
+ allowPkgMap.putAll(mAllowPkgMap);
}
+ updateSmallAreaDetection(allowPkgMap);
}
@GuardedBy("mLock")
@@ -105,43 +104,32 @@ final class SmallAreaDetectionController {
}
}
- @GuardedBy("mLock")
- private void updateUidListForAllUsers(SparseArray<Float> list, String pkg, float threshold) {
- for (int i = 0; i < mUserIds.length; i++) {
- final int userId = mUserIds[i];
- final int uid = mPackageManager.getPackageUid(pkg, 0, userId);
- if (uid > 0) list.put(uid, threshold);
- }
- }
-
- @GuardedBy("mLock")
- private void updateSmallAreaDetection() {
- if (mAllowPkgMap.isEmpty()) return;
-
- mUserIds = mUserManager.getUserIds();
-
- final SparseArray<Float> uidThresholdList = new SparseArray<>();
- for (String pkg : mAllowPkgMap.keySet()) {
- final float threshold = mAllowPkgMap.get(pkg);
- updateUidListForAllUsers(uidThresholdList, pkg, threshold);
+ private void updateSmallAreaDetection(Map<String, Float> allowPkgMap) {
+ final SparseArray<Float> appIdThresholdList = new SparseArray(allowPkgMap.size());
+ for (String pkg : allowPkgMap.keySet()) {
+ final float threshold = allowPkgMap.get(pkg);
+ final PackageStateInternal stage = mPackageManager.getPackageStateInternal(pkg);
+ if (stage != null) {
+ appIdThresholdList.put(stage.getAppId(), threshold);
+ }
}
- final int[] uids = new int[uidThresholdList.size()];
- final float[] thresholds = new float[uidThresholdList.size()];
- for (int i = 0; i < uidThresholdList.size(); i++) {
- uids[i] = uidThresholdList.keyAt(i);
- thresholds[i] = uidThresholdList.valueAt(i);
+ final int[] appIds = new int[appIdThresholdList.size()];
+ final float[] thresholds = new float[appIdThresholdList.size()];
+ for (int i = 0; i < appIdThresholdList.size(); i++) {
+ appIds[i] = appIdThresholdList.keyAt(i);
+ thresholds[i] = appIdThresholdList.valueAt(i);
}
- updateSmallAreaDetection(uids, thresholds);
+ updateSmallAreaDetection(appIds, thresholds);
}
@VisibleForTesting
- void updateSmallAreaDetection(int[] uids, float[] thresholds) {
- nativeUpdateSmallAreaDetection(uids, thresholds);
+ void updateSmallAreaDetection(int[] appIds, float[] thresholds) {
+ nativeUpdateSmallAreaDetection(appIds, thresholds);
}
- void setSmallAreaDetectionThreshold(int uid, float threshold) {
- nativeSetSmallAreaDetectionThreshold(uid, threshold);
+ void setSmallAreaDetectionThreshold(int appId, float threshold) {
+ nativeSetSmallAreaDetectionThreshold(appId, threshold);
}
void dump(PrintWriter pw) {
@@ -151,7 +139,6 @@ final class SmallAreaDetectionController {
for (String pkg : mAllowPkgMap.keySet()) {
pw.println(" " + pkg + " threshold = " + mAllowPkgMap.get(pkg));
}
- pw.println(" mUserIds=" + Arrays.toString(mUserIds));
}
}
@@ -167,11 +154,15 @@ final class SmallAreaDetectionController {
private final class PackageReceiver implements PackageManagerInternal.PackageListObserver {
@Override
public void onPackageAdded(@NonNull String packageName, int uid) {
+ float threshold = 0.0f;
synchronized (mLock) {
if (mAllowPkgMap.containsKey(packageName)) {
- setSmallAreaDetectionThreshold(uid, mAllowPkgMap.get(packageName));
+ threshold = mAllowPkgMap.get(packageName);
}
}
+ if (threshold > 0.0f) {
+ setSmallAreaDetectionThreshold(UserHandle.getAppId(uid), threshold);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index fae8383bb62e..d953e8e52365 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -71,6 +71,10 @@ public class DisplayManagerFlags {
Flags.FLAG_ENABLE_POWER_THROTTLING_CLAMPER,
Flags::enablePowerThrottlingClamper);
+ private final FlagState mSmallAreaDetectionFlagState = new FlagState(
+ Flags.FLAG_ENABLE_SMALL_AREA_DETECTION,
+ Flags::enableSmallAreaDetection);
+
/** Returns whether connected display management is enabled or not. */
public boolean isConnectedDisplayManagementEnabled() {
return mConnectedDisplayManagementFlagState.isEnabled();
@@ -147,6 +151,10 @@ public class DisplayManagerFlags {
return mBackUpSmoothDisplayAndForcePeakRefreshRateFlagState.isEnabled();
}
+ public boolean isSmallAreaDetectionEnabled() {
+ return mSmallAreaDetectionFlagState.isEnabled();
+ }
+
private static class FlagState {
private final String mName;
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index 9ab9c9def61b..9141814da6fc 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -104,3 +104,12 @@ flag {
bug: "211737588"
is_fixed_read_only: true
}
+
+flag {
+ name: "enable_small_area_detection"
+ namespace: "display_manager"
+ description: "Feature flag for SmallAreaDetection"
+ bug: "298722189"
+ is_fixed_read_only: true
+}
+
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index ca23844044ca..d023913c9694 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -723,7 +723,9 @@ public class DisplayModeDirector {
if (mode.getPhysicalWidth() > maxAllowedWidth
|| mode.getPhysicalHeight() > maxAllowedHeight
|| mode.getPhysicalWidth() < outSummary.minWidth
- || mode.getPhysicalHeight() < outSummary.minHeight) {
+ || mode.getPhysicalHeight() < outSummary.minHeight
+ || mode.getRefreshRate() < outSummary.minPhysicalRefreshRate
+ || mode.getRefreshRate() > outSummary.maxPhysicalRefreshRate) {
continue;
}
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index cc261a4b9797..44719f88351b 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -515,8 +515,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub
// Binder call
@Override
- public RoutingSessionInfo getSystemSessionInfoForPackage(IMediaRouter2Manager manager,
- String packageName) {
+ public RoutingSessionInfo getSystemSessionInfoForPackage(@Nullable String packageName) {
final int uid = Binder.getCallingUid();
final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
boolean setDeviceRouteSelected = false;
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionSessionIdGenerator.java b/services/core/java/com/android/server/media/projection/MediaProjectionSessionIdGenerator.java
new file mode 100644
index 000000000000..ff70cb35f9dc
--- /dev/null
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionSessionIdGenerator.java
@@ -0,0 +1,91 @@
+/*
+ * 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.media.projection;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Environment;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.File;
+
+public class MediaProjectionSessionIdGenerator {
+
+ private static final String PREFERENCES_FILE_NAME = "media_projection_session_id";
+ private static final String SESSION_ID_PREF_KEY = "media_projection_session_id_key";
+ private static final int SESSION_ID_DEFAULT_VALUE = 0;
+
+ private static final Object sInstanceLock = new Object();
+
+ @GuardedBy("sInstanceLock")
+ private static MediaProjectionSessionIdGenerator sInstance;
+
+ private final Object mSessionIdLock = new Object();
+
+ @GuardedBy("mSessionIdLock")
+ private final SharedPreferences mSharedPreferences;
+
+ /** Creates or returns an existing instance of {@link MediaProjectionSessionIdGenerator}. */
+ public static MediaProjectionSessionIdGenerator getInstance(Context context) {
+ synchronized (sInstanceLock) {
+ if (sInstance == null) {
+ File preferencesFile =
+ new File(Environment.getDataSystemDirectory(), PREFERENCES_FILE_NAME);
+ SharedPreferences preferences =
+ context.getSharedPreferences(preferencesFile, Context.MODE_PRIVATE);
+ sInstance = new MediaProjectionSessionIdGenerator(preferences);
+ }
+ return sInstance;
+ }
+ }
+
+ @VisibleForTesting
+ public MediaProjectionSessionIdGenerator(SharedPreferences sharedPreferences) {
+ this.mSharedPreferences = sharedPreferences;
+ }
+
+ /** Returns the current session ID. This value is persisted across reboots. */
+ public int getCurrentSessionId() {
+ synchronized (mSessionIdLock) {
+ return getCurrentSessionIdInternal();
+ }
+ }
+
+ /**
+ * Creates and returns a new session ID. This value will be persisted as the new current session
+ * ID, and will be persisted across reboots.
+ */
+ public int createAndGetNewSessionId() {
+ synchronized (mSessionIdLock) {
+ int newSessionId = getCurrentSessionId() + 1;
+ setSessionIdInternal(newSessionId);
+ return newSessionId;
+ }
+ }
+
+ @GuardedBy("mSessionIdLock")
+ private void setSessionIdInternal(int value) {
+ mSharedPreferences.edit().putInt(SESSION_ID_PREF_KEY, value).apply();
+ }
+
+ @GuardedBy("mSessionIdLock")
+ private int getCurrentSessionIdInternal() {
+ return mSharedPreferences.getInt(SESSION_ID_PREF_KEY, SESSION_ID_DEFAULT_VALUE);
+ }
+}
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionTimestampStore.java b/services/core/java/com/android/server/media/projection/MediaProjectionTimestampStore.java
new file mode 100644
index 000000000000..4026d0c43484
--- /dev/null
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionTimestampStore.java
@@ -0,0 +1,109 @@
+/*
+ * 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.media.projection;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Environment;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.File;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.InstantSource;
+
+/** Stores timestamps of media projection sessions. */
+public class MediaProjectionTimestampStore {
+ private static final String PREFERENCES_FILE_NAME = "media_projection_timestamp";
+ private static final String TIMESTAMP_PREF_KEY = "media_projection_timestamp_key";
+ private static final Object sInstanceLock = new Object();
+
+ @GuardedBy("sInstanceLock")
+ private static MediaProjectionTimestampStore sInstance;
+
+ private final Object mTimestampLock = new Object();
+
+ @GuardedBy("mTimestampLock")
+ private final SharedPreferences mSharedPreferences;
+
+ private final InstantSource mInstantSource;
+
+ @VisibleForTesting
+ public MediaProjectionTimestampStore(
+ SharedPreferences sharedPreferences, InstantSource instantSource) {
+ this.mSharedPreferences = sharedPreferences;
+ this.mInstantSource = instantSource;
+ }
+
+ /** Creates or returns an existing instance of {@link MediaProjectionTimestampStore}. */
+ public static MediaProjectionTimestampStore getInstance(Context context) {
+ synchronized (sInstanceLock) {
+ if (sInstance == null) {
+ File preferencesFile =
+ new File(Environment.getDataSystemDirectory(), PREFERENCES_FILE_NAME);
+ SharedPreferences preferences =
+ context.getSharedPreferences(preferencesFile, Context.MODE_PRIVATE);
+ sInstance = new MediaProjectionTimestampStore(preferences, InstantSource.system());
+ }
+ return sInstance;
+ }
+ }
+
+ /**
+ * Returns the time that has passed since the last active session, or {@code null} if there was
+ * no last active session.
+ */
+ @Nullable
+ public Duration timeSinceLastActiveSession() {
+ synchronized (mTimestampLock) {
+ Instant lastActiveSessionTimestamp = getLastActiveSessionTimestamp();
+ if (lastActiveSessionTimestamp == null) {
+ return null;
+ }
+ Instant now = mInstantSource.instant();
+ return Duration.between(lastActiveSessionTimestamp, now);
+ }
+ }
+
+ /** Registers that the current active session ended now. */
+ public void registerActiveSessionEnded() {
+ synchronized (mTimestampLock) {
+ Instant now = mInstantSource.instant();
+ setLastActiveSessionTimestamp(now);
+ }
+ }
+
+ @GuardedBy("mTimestampLock")
+ @Nullable
+ private Instant getLastActiveSessionTimestamp() {
+ long lastActiveSessionEpochMilli =
+ mSharedPreferences.getLong(TIMESTAMP_PREF_KEY, /* defValue= */ -1);
+ if (lastActiveSessionEpochMilli == -1) {
+ return null;
+ }
+ return Instant.ofEpochMilli(lastActiveSessionEpochMilli);
+ }
+
+ @GuardedBy("mTimestampLock")
+ private void setLastActiveSessionTimestamp(@NonNull Instant timestamp) {
+ mSharedPreferences.edit().putLong(TIMESTAMP_PREF_KEY, timestamp.toEpochMilli()).apply();
+ }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 837b76154363..b4d36db96c01 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3500,8 +3500,19 @@ public class NotificationManagerService extends SystemService {
null /* options */);
record = getToastRecord(callingUid, callingPid, pkg, isSystemToast, token,
text, callback, duration, windowToken, displayId, textCallback);
- mToastQueue.add(record);
- index = mToastQueue.size() - 1;
+
+ // Insert system toasts at the front of the queue
+ int systemToastInsertIdx = mToastQueue.size();
+ if (isSystemToast) {
+ systemToastInsertIdx = getInsertIndexForSystemToastLocked();
+ }
+ if (systemToastInsertIdx < mToastQueue.size()) {
+ index = systemToastInsertIdx;
+ mToastQueue.add(index, record);
+ } else {
+ mToastQueue.add(record);
+ index = mToastQueue.size() - 1;
+ }
keepProcessAliveForToastIfNeededLocked(callingPid);
}
// If it's at index 0, it's the current toast. It doesn't matter if it's
@@ -3517,6 +3528,23 @@ public class NotificationManagerService extends SystemService {
}
}
+ @GuardedBy("mToastQueue")
+ private int getInsertIndexForSystemToastLocked() {
+ // If there are other system toasts: insert after the last one
+ int idx = 0;
+ for (ToastRecord r : mToastQueue) {
+ if (idx == 0 && mIsCurrentToastShown) {
+ idx++;
+ continue;
+ }
+ if (!r.isSystemToast) {
+ return idx;
+ }
+ idx++;
+ }
+ return idx;
+ }
+
private boolean checkCanEnqueueToast(String pkg, int callingUid, int displayId,
boolean isAppRenderedToast, boolean isSystemToast) {
final boolean isPackageSuspended = isPackagePaused(pkg);
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index f59188e9fd93..0fb1f7a0780c 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -196,8 +196,12 @@ public class PackageArchiver {
for (int i = 0, size = mainActivities.length; i < size; ++i) {
var mainActivity = mainActivities[i];
Path iconPath = storeIconForParcel(packageName, mainActivity, userId, i);
- ArchiveActivityInfo activityInfo = new ArchiveActivityInfo(
- mainActivity.title, iconPath, null);
+ ArchiveActivityInfo activityInfo =
+ new ArchiveActivityInfo(
+ mainActivity.title,
+ mainActivity.originalComponentName,
+ iconPath,
+ null);
archiveActivityInfos.add(activityInfo);
}
@@ -215,8 +219,12 @@ public class PackageArchiver {
for (int i = 0, size = mainActivities.size(); i < size; i++) {
LauncherActivityInfo mainActivity = mainActivities.get(i);
Path iconPath = storeIcon(packageName, mainActivity, userId, i);
- ArchiveActivityInfo activityInfo = new ArchiveActivityInfo(
- mainActivity.getLabel().toString(), iconPath, null);
+ ArchiveActivityInfo activityInfo =
+ new ArchiveActivityInfo(
+ mainActivity.getLabel().toString(),
+ mainActivity.getComponentName(),
+ iconPath,
+ null);
archiveActivityInfos.add(activityInfo);
}
@@ -593,6 +601,7 @@ public class PackageArchiver {
}
var archivedActivity = new ArchivedActivityParcel();
archivedActivity.title = info.getTitle();
+ archivedActivity.originalComponentName = info.getOriginalComponentName();
archivedActivity.iconBitmap = bytesFromBitmapFile(info.getIconBitmap());
archivedActivity.monochromeIconBitmap = bytesFromBitmapFile(
info.getMonochromeIconBitmap());
@@ -624,6 +633,7 @@ public class PackageArchiver {
}
var archivedActivity = new ArchivedActivityParcel();
archivedActivity.title = info.getLabel().toString();
+ archivedActivity.originalComponentName = info.getComponentName();
archivedActivity.iconBitmap =
info.getActivityInfo().getIconResource() == 0 ? null : bytesFromBitmap(
drawableToBitmap(info.getIcon(/* density= */ 0)));
diff --git a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
index 651845e71924..e7499680b9a2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
+++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
@@ -747,7 +747,7 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal {
@Override
public void notifyComponentUsed(@NonNull String packageName, @UserIdInt int userId,
- @NonNull String recentCallingPackage, @NonNull String debugInfo) {
+ @Nullable String recentCallingPackage, @NonNull String debugInfo) {
mService.notifyComponentUsed(snapshot(), packageName, userId,
recentCallingPackage, debugInfo);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index abeabc96e969..839b6998bd8b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -36,6 +36,7 @@ import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;
+import static android.util.FeatureFlagUtils.SETTINGS_TREAT_PAUSE_AS_QUARANTINE;
import static com.android.internal.annotations.VisibleForTesting.Visibility;
import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_INIT_TIME;
@@ -164,6 +165,7 @@ import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.ExceptionUtils;
+import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
@@ -4576,7 +4578,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
}
void notifyComponentUsed(@NonNull Computer snapshot, @NonNull String packageName,
- @UserIdInt int userId, @NonNull String recentCallingPackage,
+ @UserIdInt int userId, @Nullable String recentCallingPackage,
@NonNull String debugInfo) {
synchronized (mLock) {
final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName);
@@ -6133,8 +6135,16 @@ public class PackageManagerService implements PackageSender, TestUtilityService
final Computer snapshot = snapshotComputer();
enforceCanSetPackagesSuspendedAsUser(snapshot, callingPackage, callingUid, userId,
"setPackagesSuspendedAsUser");
- boolean quarantined = ((flags & PackageManager.FLAG_SUSPEND_QUARANTINED) != 0)
- && Flags.quarantinedEnabled();
+ boolean quarantined = false;
+ if (Flags.quarantinedEnabled()) {
+ if ((flags & PackageManager.FLAG_SUSPEND_QUARANTINED) != 0) {
+ quarantined = true;
+ } else if (FeatureFlagUtils.isEnabled(mContext,
+ SETTINGS_TREAT_PAUSE_AS_QUARANTINE)) {
+ final String wellbeingPkg = mContext.getString(R.string.config_systemWellbeing);
+ quarantined = callingPackage.equals(wellbeingPkg);
+ }
+ }
return mSuspendPackageHelper.setPackagesSuspended(snapshot, packageNames, suspended,
appExtras, launcherExtras, dialogInfo, callingPackage, userId, callingUid,
false /* forQuietMode */, quarantined);
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 9f4e86d527ce..3ca933a66656 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -1228,6 +1228,9 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
long activityInfoToken = proto.start(
PackageProto.UserInfoProto.ArchiveState.ACTIVITY_INFOS);
proto.write(ArchiveActivityInfo.TITLE, activityInfo.getTitle());
+ proto.write(
+ ArchiveActivityInfo.ORIGINAL_COMPONENT_NAME,
+ activityInfo.getOriginalComponentName().flattenToString());
if (activityInfo.getIconBitmap() != null) {
proto.write(ArchiveActivityInfo.ICON_BITMAP_PATH,
activityInfo.getIconBitmap().toAbsolutePath().toString());
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 451b3a5256ac..e726d91c30a0 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -368,6 +368,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
private static final String ATTR_VALUE = "value";
private static final String ATTR_FIRST_INSTALL_TIME = "first-install-time";
private static final String ATTR_ARCHIVE_ACTIVITY_TITLE = "activity-title";
+ private static final String ATTR_ARCHIVE_ORIGINAL_COMPONENT_NAME = "original-component-name";
private static final String ATTR_ARCHIVE_INSTALLER_TITLE = "installer-title";
private static final String ATTR_ARCHIVE_ICON_PATH = "icon-path";
private static final String ATTR_ARCHIVE_MONOCHROME_ICON_PATH = "monochrome-icon-path";
@@ -2079,6 +2080,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
if (tagName.equals(TAG_ARCHIVE_ACTIVITY_INFO)) {
String title = parser.getAttributeValue(null,
ATTR_ARCHIVE_ACTIVITY_TITLE);
+ String originalComponentName =
+ parser.getAttributeValue(null, ATTR_ARCHIVE_ORIGINAL_COMPONENT_NAME);
String iconAttribute = parser.getAttributeValue(null,
ATTR_ARCHIVE_ICON_PATH);
Path iconPath = iconAttribute == null ? null : Path.of(iconAttribute);
@@ -2087,17 +2090,27 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
Path monochromeIconPath = monochromeAttribute == null ? null : Path.of(
monochromeAttribute);
- if (title == null || iconPath == null) {
- Slog.wtf(TAG,
- TextUtils.formatSimple("Missing attributes in tag %s. %s: %s, %s: %s",
- TAG_ARCHIVE_ACTIVITY_INFO, ATTR_ARCHIVE_ACTIVITY_TITLE, title,
+ if (title == null || originalComponentName == null || iconPath == null) {
+ Slog.wtf(
+ TAG,
+ TextUtils.formatSimple(
+ "Missing attributes in tag %s. %s: %s, %s: %s, %s: %s",
+ TAG_ARCHIVE_ACTIVITY_INFO,
+ ATTR_ARCHIVE_ACTIVITY_TITLE,
+ title,
+ ATTR_ARCHIVE_ORIGINAL_COMPONENT_NAME,
+ originalComponentName,
ATTR_ARCHIVE_ICON_PATH,
iconPath));
continue;
}
activityInfos.add(
- new ArchiveState.ArchiveActivityInfo(title, iconPath, monochromeIconPath));
+ new ArchiveState.ArchiveActivityInfo(
+ title,
+ ComponentName.unflattenFromString(originalComponentName),
+ iconPath,
+ monochromeIconPath));
}
}
return activityInfos;
@@ -2469,6 +2482,10 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
for (ArchiveState.ArchiveActivityInfo activityInfo : archiveState.getActivityInfos()) {
serializer.startTag(null, TAG_ARCHIVE_ACTIVITY_INFO);
serializer.attribute(null, ATTR_ARCHIVE_ACTIVITY_TITLE, activityInfo.getTitle());
+ serializer.attribute(
+ null,
+ ATTR_ARCHIVE_ORIGINAL_COMPONENT_NAME,
+ activityInfo.getOriginalComponentName().flattenToString());
if (activityInfo.getIconBitmap() != null) {
serializer.attribute(null, ATTR_ARCHIVE_ICON_PATH,
activityInfo.getIconBitmap().toAbsolutePath().toString());
@@ -6411,16 +6428,25 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
}
boolean clearPersistentPreferredActivity(IntentFilter filter, int userId) {
+ ArrayList<PersistentPreferredActivity> removed = null;
PersistentPreferredIntentResolver ppir = mPersistentPreferredActivities.get(userId);
Iterator<PersistentPreferredActivity> it = ppir.filterIterator();
boolean changed = false;
while (it.hasNext()) {
PersistentPreferredActivity ppa = it.next();
if (IntentFilter.filterEquals(ppa.getIntentFilter(), filter)) {
+ if (removed == null) {
+ removed = new ArrayList<>();
+ }
+ removed.add(ppa);
+ }
+ }
+ if (removed != null) {
+ for (int i = 0; i < removed.size(); i++) {
+ PersistentPreferredActivity ppa = removed.get(i);
ppir.removeFilter(ppa);
- changed = true;
- break;
}
+ changed = true;
}
if (changed) {
onChanged();
diff --git a/services/core/java/com/android/server/pm/pkg/ArchiveState.java b/services/core/java/com/android/server/pm/pkg/ArchiveState.java
index 4916a4a6d72a..1e40d44bd4ca 100644
--- a/services/core/java/com/android/server/pm/pkg/ArchiveState.java
+++ b/services/core/java/com/android/server/pm/pkg/ArchiveState.java
@@ -16,9 +16,11 @@
package com.android.server.pm.pkg;
+import android.content.ComponentName;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import com.android.internal.util.AnnotationValidations;
import com.android.internal.util.DataClass;
import java.nio.file.Path;
@@ -56,6 +58,10 @@ public class ArchiveState {
@NonNull
private final String mTitle;
+ /** The component name of the original activity (pre-archival). */
+ @NonNull
+ private final ComponentName mOriginalComponentName;
+
/**
* The path to the stored icon of the activity in the app's locale. Null if the app does
* not define any icon (default icon would be shown on the launcher).
@@ -96,11 +102,13 @@ public class ArchiveState {
@DataClass.Generated.Member
public ArchiveActivityInfo(
@NonNull String title,
+ @NonNull ComponentName originalComponentName,
@Nullable Path iconBitmap,
@Nullable Path monochromeIconBitmap) {
this.mTitle = title;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mTitle);
+ this.mOriginalComponentName = originalComponentName;
+ AnnotationValidations.validate(NonNull.class, null, mTitle);
+ AnnotationValidations.validate(NonNull.class, null, mOriginalComponentName);
this.mIconBitmap = iconBitmap;
this.mMonochromeIconBitmap = monochromeIconBitmap;
@@ -116,6 +124,14 @@ public class ArchiveState {
}
/**
+ * The component name of the original activity (pre-archival).
+ */
+ @DataClass.Generated.Member
+ public @NonNull ComponentName getOriginalComponentName() {
+ return mOriginalComponentName;
+ }
+
+ /**
* The path to the stored icon of the activity in the app's locale. Null if the app does
* not define any icon (default icon would be shown on the launcher).
*/
@@ -140,6 +156,7 @@ public class ArchiveState {
return "ArchiveActivityInfo { " +
"title = " + mTitle + ", " +
+ "originalComponentName = " + mOriginalComponentName + ", " +
"iconBitmap = " + mIconBitmap + ", " +
"monochromeIconBitmap = " + mMonochromeIconBitmap +
" }";
@@ -159,6 +176,7 @@ public class ArchiveState {
//noinspection PointlessBooleanExpression
return true
&& java.util.Objects.equals(mTitle, that.mTitle)
+ && java.util.Objects.equals(mOriginalComponentName, that.mOriginalComponentName)
&& java.util.Objects.equals(mIconBitmap, that.mIconBitmap)
&& java.util.Objects.equals(mMonochromeIconBitmap, that.mMonochromeIconBitmap);
}
@@ -171,6 +189,7 @@ public class ArchiveState {
int _hash = 1;
_hash = 31 * _hash + java.util.Objects.hashCode(mTitle);
+ _hash = 31* _hash + java.util.Objects.hashCode(mOriginalComponentName);
_hash = 31 * _hash + java.util.Objects.hashCode(mIconBitmap);
_hash = 31 * _hash + java.util.Objects.hashCode(mMonochromeIconBitmap);
return _hash;
@@ -180,7 +199,8 @@ public class ArchiveState {
time = 1693590309015L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/ArchiveState.java",
- inputSignatures = "private final @android.annotation.NonNull java.lang.String mTitle\nprivate final @android.annotation.Nullable java.nio.file.Path mIconBitmap\nprivate final @android.annotation.Nullable java.nio.file.Path mMonochromeIconBitmap\nclass ArchiveActivityInfo extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true)")
+ inputSignatures =
+ "private final @android.annotation.NonNull java.lang.String mTitle\nprivate final @android.annotation.NonNull android.content.ComponentName mOriginalComponentName\nprivate final @android.annotation.Nullable java.nio.file.Path mIconBitmap\nprivate final @android.annotation.Nullable java.nio.file.Path mMonochromeIconBitmap\nclass ArchiveActivityInfo extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true)")
@Deprecated
private void __metadata() {}
@@ -224,11 +244,9 @@ public class ArchiveState {
@NonNull List<ArchiveActivityInfo> activityInfos,
@NonNull String installerTitle) {
this.mActivityInfos = activityInfos;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mActivityInfos);
+ AnnotationValidations.validate(NonNull.class, null, mActivityInfos);
this.mInstallerTitle = installerTitle;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mInstallerTitle);
+ AnnotationValidations.validate(NonNull.class, null, mInstallerTitle);
// onConstructed(); // You can define this method to get a callback
}
diff --git a/services/core/java/com/android/server/power/Android.bp b/services/core/java/com/android/server/power/Android.bp
index 1da9dd770ba6..607d435b5410 100644
--- a/services/core/java/com/android/server/power/Android.bp
+++ b/services/core/java/com/android/server/power/Android.bp
@@ -1,5 +1,5 @@
aconfig_declarations {
- name: "power_optimization_flags",
+ name: "backstage_power_flags",
package: "com.android.server.power.optimization",
srcs: [
"stats/*.aconfig",
@@ -7,6 +7,6 @@ aconfig_declarations {
}
java_aconfig_library {
- name: "power_optimization_flags_lib",
- aconfig_declarations: "power_optimization_flags",
+ name: "backstage_power_flags_lib",
+ aconfig_declarations: "backstage_power_flags",
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index dfbcbae650cd..4a4214f7af83 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -3347,8 +3347,6 @@ public final class PowerManagerService extends SystemService
} else {
startDreaming = false;
}
- Slog.i(TAG, "handleSandman powerGroup=" + groupId + " startDreaming=" + startDreaming
- + " wakefulness=" + wakefulnessToString(wakefulness));
}
// Start dreaming if needed.
@@ -3383,23 +3381,19 @@ public final class PowerManagerService extends SystemService
if (startDreaming && isDreaming) {
mDreamsBatteryLevelDrain = 0;
if (wakefulness == WAKEFULNESS_DOZING) {
- Slog.i(TAG, "Dozing powerGroup " + groupId);
+ Slog.i(TAG, "Dozing...");
} else {
- Slog.i(TAG, "Dreaming powerGroup " + groupId);
+ Slog.i(TAG, "Dreaming...");
}
}
// If preconditions changed, wait for the next iteration to determine
// whether the dream should continue (or be restarted).
final PowerGroup powerGroup = mPowerGroups.get(groupId);
- final int newWakefulness = powerGroup.getWakefulnessLocked();
if (powerGroup.isSandmanSummonedLocked()
- || newWakefulness != wakefulness) {
+ || powerGroup.getWakefulnessLocked() != wakefulness) {
return; // wait for next cycle
}
- Slog.i(TAG, "handleSandman powerGroup=" + groupId + " isDreaming=" + isDreaming
- + " wakefulness=" + newWakefulness);
-
// Determine whether the dream should continue.
long now = mClock.uptimeMillis();
diff --git a/services/core/java/com/android/server/power/stats/flags.aconfig b/services/core/java/com/android/server/power/stats/flags.aconfig
index add806febe67..0f135715ebc3 100644
--- a/services/core/java/com/android/server/power/stats/flags.aconfig
+++ b/services/core/java/com/android/server/power/stats/flags.aconfig
@@ -2,14 +2,14 @@ package: "com.android.server.power.optimization"
flag {
name: "power_monitor_api"
- namespace: "power_optimization"
+ namespace: "backstage_power"
description: "Feature flag for ODPM API"
bug: "295027807"
}
flag {
name: "streamlined_battery_stats"
- namespace: "power_optimization"
+ namespace: "backstage_power"
description: "Feature flag for streamlined battery stats"
bug: "285646152"
}
diff --git a/services/core/java/com/android/server/stats/OWNERS b/services/core/java/com/android/server/stats/OWNERS
index 174ad3ad2e25..c33f3d9fa264 100644
--- a/services/core/java/com/android/server/stats/OWNERS
+++ b/services/core/java/com/android/server/stats/OWNERS
@@ -1,11 +1,10 @@
jeffreyhuang@google.com
joeo@google.com
-jtnguyen@google.com
+monicamwang@google.com
muhammadq@google.com
+rayhdez@google.com
rslawik@google.com
-ruchirr@google.com
sharaienko@google.com
singhtejinder@google.com
tsaichristine@google.com
yaochen@google.com
-yro@google.com
diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
index becbbf2ea76a..519acec2f7b4 100644
--- a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
+++ b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
@@ -22,11 +22,10 @@ import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.VibratorInfo;
+import android.os.vibrator.Flags;
import android.util.Slog;
import android.util.SparseArray;
import android.view.HapticFeedbackConstants;
-import android.view.flags.FeatureFlags;
-import android.view.flags.FeatureFlagsImpl;
import com.android.internal.annotations.VisibleForTesting;
@@ -56,7 +55,8 @@ public final class HapticFeedbackVibrationProvider {
// If present and valid, a vibration here will be used for an effect.
// Otherwise, the system's default vibration will be used.
@Nullable private final SparseArray<VibrationEffect> mHapticCustomizations;
- private final FeatureFlags mViewFeatureFlags;
+
+ private float mKeyboardVibrationFixedAmplitude;
/** @hide */
public HapticFeedbackVibrationProvider(Resources res, Vibrator vibrator) {
@@ -65,16 +65,14 @@ public final class HapticFeedbackVibrationProvider {
/** @hide */
public HapticFeedbackVibrationProvider(Resources res, VibratorInfo vibratorInfo) {
- this(res, vibratorInfo, loadHapticCustomizations(res, vibratorInfo),
- new FeatureFlagsImpl());
+ this(res, vibratorInfo, loadHapticCustomizations(res, vibratorInfo));
}
/** @hide */
@VisibleForTesting HapticFeedbackVibrationProvider(
Resources res,
VibratorInfo vibratorInfo,
- @Nullable SparseArray<VibrationEffect> hapticCustomizations,
- FeatureFlags viewFeatureFlags) {
+ @Nullable SparseArray<VibrationEffect> hapticCustomizations) {
mVibratorInfo = vibratorInfo;
mHapticTextHandleEnabled = res.getBoolean(
com.android.internal.R.bool.config_enableHapticTextHandle);
@@ -83,14 +81,17 @@ public final class HapticFeedbackVibrationProvider {
hapticCustomizations = null;
}
mHapticCustomizations = hapticCustomizations;
- mViewFeatureFlags = viewFeatureFlags;
-
mSafeModeEnabledVibrationEffect =
effectHasCustomization(HapticFeedbackConstants.SAFE_MODE_ENABLED)
? mHapticCustomizations.get(HapticFeedbackConstants.SAFE_MODE_ENABLED)
: VibrationSettings.createEffectFromResource(
res,
com.android.internal.R.array.config_safeModeEnabledVibePattern);
+ mKeyboardVibrationFixedAmplitude = res.getFloat(
+ com.android.internal.R.dimen.config_keyboardHapticFeedbackFixedAmplitude);
+ if (mKeyboardVibrationFixedAmplitude < 0 || mKeyboardVibrationFixedAmplitude > 1) {
+ mKeyboardVibrationFixedAmplitude = -1;
+ }
}
/**
@@ -120,6 +121,9 @@ public final class HapticFeedbackVibrationProvider {
return getVibration(effectId, VibrationEffect.EFFECT_TEXTURE_TICK);
case HapticFeedbackConstants.KEYBOARD_RELEASE:
+ case HapticFeedbackConstants.KEYBOARD_TAP: // == KEYBOARD_PRESS
+ return getKeyboardVibration(effectId);
+
case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE:
case HapticFeedbackConstants.ENTRY_BUMP:
case HapticFeedbackConstants.DRAG_CROSSING:
@@ -128,7 +132,6 @@ public final class HapticFeedbackVibrationProvider {
VibrationEffect.EFFECT_TICK,
/* fallbackForPredefinedEffect= */ false);
- case HapticFeedbackConstants.KEYBOARD_TAP: // == KEYBOARD_PRESS
case HapticFeedbackConstants.VIRTUAL_KEY:
case HapticFeedbackConstants.EDGE_RELEASE:
case HapticFeedbackConstants.CALENDAR_DATE:
@@ -204,6 +207,10 @@ public final class HapticFeedbackVibrationProvider {
case HapticFeedbackConstants.SCROLL_LIMIT:
attrs = HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES;
break;
+ case HapticFeedbackConstants.KEYBOARD_TAP:
+ case HapticFeedbackConstants.KEYBOARD_RELEASE:
+ attrs = createKeyboardVibrationAttributes();
+ break;
default:
attrs = TOUCH_VIBRATION_ATTRIBUTES;
}
@@ -212,9 +219,12 @@ public final class HapticFeedbackVibrationProvider {
if (bypassVibrationIntensitySetting) {
flags |= VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF;
}
- if (shouldBypassInterruptionPolicy(effectId, mViewFeatureFlags)) {
+ if (shouldBypassInterruptionPolicy(effectId)) {
flags |= VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
}
+ if (shouldBypassIntensityScale(effectId)) {
+ flags |= VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE;
+ }
return flags == 0 ? attrs : new VibrationAttributes.Builder(attrs).setFlags(flags).build();
}
@@ -295,6 +305,64 @@ public final class HapticFeedbackVibrationProvider {
return mHapticCustomizations != null && mHapticCustomizations.contains(effectId);
}
+ private VibrationEffect getKeyboardVibration(int effectId) {
+ if (effectHasCustomization(effectId)) {
+ return mHapticCustomizations.get(effectId);
+ }
+
+ int primitiveId;
+ int predefinedEffectId;
+ boolean predefinedEffectFallback;
+
+ switch (effectId) {
+ case HapticFeedbackConstants.KEYBOARD_RELEASE:
+ primitiveId = VibrationEffect.Composition.PRIMITIVE_TICK;
+ predefinedEffectId = VibrationEffect.EFFECT_TICK;
+ predefinedEffectFallback = false;
+ break;
+ case HapticFeedbackConstants.KEYBOARD_TAP:
+ default:
+ primitiveId = VibrationEffect.Composition.PRIMITIVE_CLICK;
+ predefinedEffectId = VibrationEffect.EFFECT_CLICK;
+ predefinedEffectFallback = true;
+ }
+ if (Flags.keyboardCategoryEnabled() && mKeyboardVibrationFixedAmplitude > 0) {
+ if (mVibratorInfo.isPrimitiveSupported(primitiveId)) {
+ return VibrationEffect.startComposition()
+ .addPrimitive(primitiveId, mKeyboardVibrationFixedAmplitude)
+ .compose();
+ }
+ }
+ return getVibration(effectId, predefinedEffectId,
+ /* fallbackForPredefinedEffect= */ predefinedEffectFallback);
+ }
+
+ private boolean shouldBypassIntensityScale(int effectId) {
+ if (!Flags.keyboardCategoryEnabled() || mKeyboardVibrationFixedAmplitude < 0) {
+ // shouldn't bypass if not support keyboard category or no fixed amplitude
+ return false;
+ }
+ switch (effectId) {
+ case HapticFeedbackConstants.KEYBOARD_TAP:
+ return mVibratorInfo.isPrimitiveSupported(
+ VibrationEffect.Composition.PRIMITIVE_CLICK);
+ case HapticFeedbackConstants.KEYBOARD_RELEASE:
+ return mVibratorInfo.isPrimitiveSupported(
+ VibrationEffect.Composition.PRIMITIVE_TICK);
+ }
+ return false;
+ }
+
+ private static VibrationAttributes createKeyboardVibrationAttributes() {
+ if (!Flags.keyboardCategoryEnabled()) {
+ return TOUCH_VIBRATION_ATTRIBUTES;
+ }
+
+ return new VibrationAttributes.Builder(TOUCH_VIBRATION_ATTRIBUTES)
+ .setCategory(VibrationAttributes.CATEGORY_KEYBOARD)
+ .build();
+ }
+
@Nullable
private static SparseArray<VibrationEffect> loadHapticCustomizations(
Resources res, VibratorInfo vibratorInfo) {
@@ -306,8 +374,7 @@ public final class HapticFeedbackVibrationProvider {
}
}
- private static boolean shouldBypassInterruptionPolicy(
- int effectId, FeatureFlags viewFeatureFlags) {
+ private static boolean shouldBypassInterruptionPolicy(int effectId) {
switch (effectId) {
case HapticFeedbackConstants.SCROLL_TICK:
case HapticFeedbackConstants.SCROLL_ITEM_FOCUS:
@@ -315,7 +382,7 @@ public final class HapticFeedbackVibrationProvider {
// The SCROLL_* constants should bypass interruption filter, so that scroll haptics
// can play regardless of focus modes like DND. Guard this behavior by the feature
// flag controlling the general scroll feedback APIs.
- return viewFeatureFlags.scrollFeedbackApi();
+ return android.view.flags.Flags.scrollFeedbackApi();
default:
return false;
}
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index db8a9ae553cd..1d5cac54d12b 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -16,6 +16,7 @@
package com.android.server.vibrator;
+import static android.os.VibrationAttributes.CATEGORY_KEYBOARD;
import static android.os.VibrationAttributes.USAGE_ACCESSIBILITY;
import static android.os.VibrationAttributes.USAGE_ALARM;
import static android.os.VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
@@ -52,6 +53,7 @@ import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.Vibrator.VibrationIntensity;
+import android.os.vibrator.Flags;
import android.os.vibrator.VibrationConfig;
import android.provider.Settings;
import android.util.IndentingPrintWriter;
@@ -188,6 +190,8 @@ final class VibrationSettings {
@GuardedBy("mLock")
private boolean mVibrateOn;
@GuardedBy("mLock")
+ private boolean mKeyboardVibrationOn;
+ @GuardedBy("mLock")
private int mRingerMode;
@GuardedBy("mLock")
private boolean mOnWirelessCharger;
@@ -295,6 +299,8 @@ final class VibrationSettings {
Settings.System.getUriFor(Settings.System.NOTIFICATION_VIBRATION_INTENSITY));
registerSettingsObserver(
Settings.System.getUriFor(Settings.System.RING_VIBRATION_INTENSITY));
+ registerSettingsObserver(
+ Settings.System.getUriFor(Settings.System.KEYBOARD_VIBRATION_ENABLED));
if (mVibrationConfig.ignoreVibrationsOnWirelessCharger()) {
Intent batteryStatus = mContext.registerReceiver(
@@ -418,14 +424,9 @@ final class VibrationSettings {
}
if (!callerInfo.attrs.isFlagSet(
- VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)) {
- if (!mVibrateOn && (VIBRATE_ON_DISABLED_USAGE_ALLOWED != usage)) {
- return Vibration.Status.IGNORED_FOR_SETTINGS;
- }
-
- if (getCurrentIntensity(usage) == Vibrator.VIBRATION_INTENSITY_OFF) {
- return Vibration.Status.IGNORED_FOR_SETTINGS;
- }
+ VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
+ && !shouldVibrateForUserSetting(callerInfo)) {
+ return Vibration.Status.IGNORED_FOR_SETTINGS;
}
if (!callerInfo.attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY)) {
@@ -497,6 +498,30 @@ final class VibrationSettings {
return mRingerMode != AudioManager.RINGER_MODE_SILENT;
}
+ /**
+ * Return {@code true} if the device should vibrate for user setting, and
+ * {@code false} to ignore the vibration.
+ */
+ @GuardedBy("mLock")
+ private boolean shouldVibrateForUserSetting(Vibration.CallerInfo callerInfo) {
+ final int usage = callerInfo.attrs.getUsage();
+ if (!mVibrateOn && (VIBRATE_ON_DISABLED_USAGE_ALLOWED != usage)) {
+ // Main setting disabled.
+ return false;
+ }
+
+ if (Flags.keyboardCategoryEnabled()) {
+ int category = callerInfo.attrs.getCategory();
+ if (usage == USAGE_TOUCH && category == CATEGORY_KEYBOARD) {
+ // Keyboard touch has a different user setting.
+ return mKeyboardVibrationOn;
+ }
+ }
+
+ // Apply individual user setting based on usage.
+ return getCurrentIntensity(usage) != Vibrator.VIBRATION_INTENSITY_OFF;
+ }
+
/** Update all cached settings and triggers registered listeners. */
void update() {
updateSettings();
@@ -508,6 +533,8 @@ final class VibrationSettings {
synchronized (mLock) {
mVibrateInputDevices = loadSystemSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0) > 0;
mVibrateOn = loadSystemSetting(Settings.System.VIBRATE_ON, 1) > 0;
+ mKeyboardVibrationOn = loadSystemSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED,
+ mVibrationConfig.isDefaultKeyboardVibrationEnabled() ? 1 : 0) > 0;
int alarmIntensity = toIntensity(
loadSystemSetting(Settings.System.ALARM_VIBRATION_INTENSITY, -1),
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 45bd1521bc35..ace7777c9b58 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -97,7 +97,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
new VibrationAttributes.Builder().build();
private static final int ATTRIBUTES_ALL_BYPASS_FLAGS =
VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY
- | VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF;
+ | VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF
+ | VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE;
/** Fixed large duration used to note repeating vibrations to {@link IBatteryStats}. */
private static final long BATTERY_STATS_REPEATING_VIBRATION_DURATION = 5_000;
@@ -771,8 +772,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
private Vibration.EndInfo startVibrationLocked(HalVibration vib) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationLocked");
try {
- // Scale effect before dispatching it to the input devices or the vibration thread.
- vib.scaleEffects(mVibrationScaler::scale);
+ if (!vib.callerInfo.attrs.isFlagSet(
+ VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE)) {
+ // Scale effect before dispatching it to the input devices or the vibration thread.
+ vib.scaleEffects(mVibrationScaler::scale);
+ }
boolean inputDevicesAvailable = mInputDeviceDelegate.vibrateIfAvailable(
vib.callerInfo, vib.getEffectToPlay());
if (inputDevicesAvailable) {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 0718f2f284a5..c7a3c4349f4c 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -51,6 +51,7 @@ import android.app.ILocalWallpaperColorConsumer;
import android.app.IWallpaperManager;
import android.app.IWallpaperManagerCallback;
import android.app.PendingIntent;
+import android.app.UidObserver;
import android.app.UserSwitchObserver;
import android.app.WallpaperColors;
import android.app.WallpaperInfo;
@@ -103,6 +104,7 @@ import android.service.wallpaper.WallpaperService;
import android.system.ErrnoException;
import android.system.Os;
import android.util.EventLog;
+import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -120,6 +122,7 @@ import com.android.server.ServiceThread;
import com.android.server.SystemService;
import com.android.server.pm.UserManagerInternal;
import com.android.server.utils.TimingsTraceAndSlog;
+import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
import org.xmlpull.v1.XmlPullParserException;
@@ -1645,6 +1648,40 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
mWallpaperDisplayHelper = new WallpaperDisplayHelper(dm, mWindowManagerInternal);
mWallpaperCropper = new WallpaperCropper(mWallpaperDisplayHelper);
mActivityManager = mContext.getSystemService(ActivityManager.class);
+
+ if (mContext.getResources().getBoolean(
+ R.bool.config_pauseWallpaperRenderWhenStateChangeEnabled)) {
+ // Pause wallpaper rendering engine as soon as a performance impacted app is launched.
+ final String[] pauseRenderList = mContext.getResources().getStringArray(
+ R.array.pause_wallpaper_render_when_state_change);
+ final IntArray pauseRenderUids = new IntArray();
+ for (String pauseRenderApp : pauseRenderList) {
+ try {
+ int uid = mContext.getPackageManager().getApplicationInfo(
+ pauseRenderApp, 0).uid;
+ pauseRenderUids.add(uid);
+ } catch (Exception e) {
+ Slog.e(TAG, e.toString());
+ }
+ }
+ if (pauseRenderUids.size() > 0) {
+ try {
+ ActivityManager.getService().registerUidObserverForUids(new UidObserver() {
+ @Override
+ public void onUidStateChanged(int uid, int procState, long procStateSeq,
+ int capability) {
+ pauseOrResumeRenderingImmediately(
+ procState == ActivityManager.PROCESS_STATE_TOP);
+ }
+ }, ActivityManager.UID_OBSERVER_PROCSTATE,
+ ActivityManager.PROCESS_STATE_TOP, "android",
+ pauseRenderUids.toArray());
+ } catch (RemoteException e) {
+ Slog.e(TAG, e.toString());
+ }
+ }
+ }
+
mMonitor = new MyPackageMonitor();
mColorsChangedListeners = new SparseArray<>();
mWallpaperDataParser = new WallpaperDataParser(mContext, mWallpaperDisplayHelper,
@@ -2625,6 +2662,35 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
+ private void pauseOrResumeRenderingImmediately(boolean pause) {
+ synchronized (mLock) {
+ final WallpaperData[] wallpapers = mIsLockscreenLiveWallpaperEnabled
+ ? getActiveWallpapers() : new WallpaperData[] {
+ mWallpaperMap.get(mCurrentUserId) };
+ for (WallpaperData data : wallpapers) {
+ if (data.connection == null || data.connection.mInfo == null) {
+ continue;
+ }
+ if (pause || LocalServices.getService(ActivityTaskManagerInternal.class)
+ .isUidForeground(data.connection.mInfo.getServiceInfo()
+ .applicationInfo.uid)) {
+ if (data.connection.containsDisplay(
+ mWindowManagerInternal.getTopFocusedDisplayId())) {
+ data.connection.forEachDisplayConnector(displayConnector -> {
+ if (displayConnector.mEngine != null) {
+ try {
+ displayConnector.mEngine.setVisibility(!pause);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to set visibility", e);
+ }
+ }
+ });
+ }
+ }
+ }
+ }
+ }
+
/**
* Propagate a wake event to the wallpaper engine(s).
*/
@@ -3982,7 +4048,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
if (wallpaper == null) {
// common case, this is the first lookup post-boot of the system or
// unified lock, so we bring up the saved state lazily now and recheck.
- int whichLoad = (which == FLAG_LOCK) ? FLAG_LOCK : FLAG_SYSTEM;
+ // if we're loading the system wallpaper for the first time, also load the lock
+ // wallpaper to determine if the system wallpaper is system+lock or system only.
+ int whichLoad = (which == FLAG_LOCK) ? FLAG_LOCK : FLAG_SYSTEM | FLAG_LOCK;
loadSettingsLocked(userId, false, whichLoad);
wallpaper = whichSet.get(userId);
if (wallpaper == null) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index de335d3d013e..c02178506052 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3002,7 +3002,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
|| mLastResumedActivity == null) {
return;
}
- var userInfo = mUserManager.getUserInfo(mLastResumedActivity.mUserId);
+ var userInfo = getUserManager().getUserInfo(mLastResumedActivity.mUserId);
if (userInfo == null || !userInfo.isManagedProfile()) {
return;
}
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 42376685498a..6d59b297de48 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -276,6 +276,10 @@ class BackNavigationController {
// activity, we won't close the activity.
backType = BackNavigationInfo.TYPE_DIALOG_CLOSE;
removedWindowContainer = window;
+ } else if (!currentActivity.occludesParent() || currentActivity.showWallpaper()) {
+ // skip if current activity is translucent
+ backType = BackNavigationInfo.TYPE_CALLBACK;
+ removedWindowContainer = window;
} else if (prevActivity != null) {
if (!isOccluded || prevActivity.canShowWhenLocked()) {
// We have another Activity in the same currentTask to go to
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index 64a230effb38..823fbc9b0f3e 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -28,6 +28,9 @@ import com.android.window.flags.Flags;
* black layers of varying opacity at various Z-levels which create the effect of a Dim.
*/
public abstract class Dimmer {
+
+ static final boolean DIMMER_REFACTOR = Flags.dimmerRefactor();
+
/**
* The {@link WindowContainer} that our Dims are bounded to. We may be dimming on behalf of the
* host, some controller of it, or one of the hosts children.
@@ -40,7 +43,7 @@ public abstract class Dimmer {
// Constructs the correct type of dimmer
static Dimmer create(WindowContainer host) {
- return Flags.dimmerRefactor() ? new SmoothDimmer(host) : new LegacyDimmer(host);
+ return DIMMER_REFACTOR ? new SmoothDimmer(host) : new LegacyDimmer(host);
}
@NonNull
@@ -48,32 +51,34 @@ public abstract class Dimmer {
return mHost;
}
- protected abstract void dim(
- WindowContainer container, int relativeLayer, float alpha, int blurRadius);
-
/**
- * Place a dim above the given container, which should be a child of the host container.
- * for each call to {@link WindowContainer#prepareSurfaces} the Dim state will be reset
- * and the child should call dimAbove again to request the Dim to continue.
+ * Position the dim relatively to the dimming container.
+ * Normally called together with #setAppearance, it can be called alone to keep the dim parented
+ * to a visible container until the next dimming container is ready.
+ * If multiple containers call this method, only the changes relative to the topmost will be
+ * applied.
*
- * @param container The container which to dim above. Should be a child of our host.
- * @param alpha The alpha at which to Dim.
+ * For each call to {@link WindowContainer#prepareSurfaces()} the DimState will be reset, and
+ * the child of the host should call adjustRelativeLayer and {@link Dimmer#adjustAppearance} to
+ * continue dimming. Indeed, this method won't be able to keep dimming or get a new DimState
+ * without also adjusting the appearance.
+ * @param container The container which to dim above. Should be a child of the host.
+ * @param relativeLayer The position of the dim wrt the container
*/
- void dimAbove(@NonNull WindowContainer container, float alpha) {
- dim(container, 1, alpha, 0);
- }
+ protected abstract void adjustRelativeLayer(WindowContainer container, int relativeLayer);
/**
- * Like {@link #dimAbove} but places the dim below the given container.
- *
- * @param container The container which to dim below. Should be a child of our host.
- * @param alpha The alpha at which to Dim.
- * @param blurRadius The amount of blur added to the Dim.
+ * Set the aspect of the dim layer, and request to keep dimming.
+ * For each call to {@link WindowContainer#prepareSurfaces} the Dim state will be reset, and the
+ * child should call setAppearance again to request the Dim to continue.
+ * If multiple containers call this method, only the changes relative to the topmost will be
+ * applied.
+ * @param container Container requesting the dim
+ * @param alpha Dim amount
+ * @param blurRadius Blur amount
*/
-
- void dimBelow(@NonNull WindowContainer container, float alpha, int blurRadius) {
- dim(container, -1, alpha, blurRadius);
- }
+ protected abstract void adjustAppearance(
+ WindowContainer container, float alpha, int blurRadius);
/**
* Mark all dims as pending completion on the next call to {@link #updateDims}
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 02f5c217e5d8..cd114fcf9e21 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -68,6 +68,7 @@ class InsetsSourceProvider {
private final Rect mTmpRect = new Rect();
private final InsetsStateController mStateController;
private final InsetsSourceControl mFakeControl;
+ private final Consumer<Transaction> mSetLeashPositionConsumer;
private @Nullable InsetsSourceControl mControl;
private @Nullable InsetsControlTarget mControlTarget;
private @Nullable InsetsControlTarget mPendingControlTarget;
@@ -85,16 +86,7 @@ class InsetsSourceProvider {
private boolean mInsetsHintStale = true;
private @Flags int mFlagsFromFrameProvider;
private @Flags int mFlagsFromServer;
-
- private final Consumer<Transaction> mSetLeashPositionConsumer = t -> {
- if (mControl != null) {
- final SurfaceControl leash = mControl.getLeash();
- if (leash != null) {
- final Point position = mControl.getSurfacePosition();
- t.setPosition(leash, position.x, position.y);
- }
- }
- };
+ private boolean mHasPendingPosition;
/** The visibility override from the current controlling window. */
private boolean mClientVisible;
@@ -129,6 +121,21 @@ class InsetsSourceProvider {
source.getId(), source.getType(), null /* leash */, false /* initialVisible */,
new Point(), Insets.NONE);
mControllable = (InsetsPolicy.CONTROLLABLE_TYPES & source.getType()) != 0;
+ mSetLeashPositionConsumer = t -> {
+ if (mControl != null) {
+ final SurfaceControl leash = mControl.getLeash();
+ if (leash != null) {
+ final Point position = mControl.getSurfacePosition();
+ t.setPosition(leash, position.x, position.y);
+ }
+ }
+ if (mHasPendingPosition) {
+ mHasPendingPosition = false;
+ if (mPendingControlTarget != mControlTarget) {
+ mStateController.notifyControlTargetChanged(mPendingControlTarget, this);
+ }
+ }
+ };
}
InsetsSource getSource() {
@@ -185,9 +192,8 @@ class InsetsSourceProvider {
mWindowContainer.getInsetsSourceProviders().put(mSource.getId(), this);
if (mControllable) {
mWindowContainer.setControllableInsetProvider(this);
- if (mPendingControlTarget != null) {
+ if (mPendingControlTarget != mControlTarget) {
updateControlForTarget(mPendingControlTarget, true /* force */);
- mPendingControlTarget = null;
}
}
}
@@ -344,6 +350,7 @@ class InsetsSourceProvider {
changed = true;
if (windowState != null && windowState.getWindowFrames().didFrameSizeChange()
&& windowState.mWinAnimator.getShown() && mWindowContainer.okToDisplay()) {
+ mHasPendingPosition = true;
windowState.applyWithNextDraw(mSetLeashPositionConsumer);
} else {
Transaction t = mWindowContainer.getSyncTransaction();
@@ -465,18 +472,23 @@ class InsetsSourceProvider {
// to control the window for now.
return;
}
+ mPendingControlTarget = target;
if (mWindowContainer != null && mWindowContainer.getSurfaceControl() == null) {
// if window doesn't have a surface, set it null and return.
setWindowContainer(null, null, null);
}
if (mWindowContainer == null) {
- mPendingControlTarget = target;
return;
}
if (target == mControlTarget && !force) {
return;
}
+ if (mHasPendingPosition) {
+ // Don't create a new leash while having a pending position. Otherwise, the position
+ // will be changed earlier than expected, which can cause flicker.
+ return;
+ }
if (target == null) {
// Cancelling the animation will invoke onAnimationCancelled, resetting all the fields.
mWindowContainer.cancelAnimation();
@@ -618,6 +630,7 @@ class InsetsSourceProvider {
}
pw.print(prefix);
pw.print("mIsLeashReadyForDispatching="); pw.print(mIsLeashReadyForDispatching);
+ pw.print("mHasPendingPosition="); pw.print(mHasPendingPosition);
pw.println();
if (mWindowContainer != null) {
pw.print(prefix + "mWindowContainer=");
@@ -631,7 +644,7 @@ class InsetsSourceProvider {
pw.print(prefix + "mControlTarget=");
pw.println(mControlTarget);
}
- if (mPendingControlTarget != null) {
+ if (mPendingControlTarget != mControlTarget) {
pw.print(prefix + "mPendingControlTarget=");
pw.println(mPendingControlTarget);
}
@@ -652,7 +665,8 @@ class InsetsSourceProvider {
if (mControlTarget != null && mControlTarget.getWindow() != null) {
mControlTarget.getWindow().dumpDebug(proto, CONTROL_TARGET, logLevel);
}
- if (mPendingControlTarget != null && mPendingControlTarget.getWindow() != null) {
+ if (mPendingControlTarget != null && mPendingControlTarget != mControlTarget
+ && mPendingControlTarget.getWindow() != null) {
mPendingControlTarget.getWindow().dumpDebug(proto, PENDING_CONTROL_TARGET, logLevel);
}
if (mFakeControlTarget != null && mFakeControlTarget.getWindow() != null) {
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 081ebe0e7cbd..c4d01291f558 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -278,6 +278,12 @@ class InsetsStateController {
notifyPendingInsetsControlChanged();
}
+ void notifyControlTargetChanged(@Nullable InsetsControlTarget target,
+ InsetsSourceProvider provider) {
+ onControlTargetChanged(provider, target, false /* fake */);
+ notifyPendingInsetsControlChanged();
+ }
+
void notifyControlRevoked(@NonNull InsetsControlTarget previousControlTarget,
InsetsSourceProvider provider) {
removeFromControlMaps(previousControlTarget, provider, false /* fake */);
diff --git a/services/core/java/com/android/server/wm/LegacyDimmer.java b/services/core/java/com/android/server/wm/LegacyDimmer.java
index ccf956ecef1e..3265e605ab15 100644
--- a/services/core/java/com/android/server/wm/LegacyDimmer.java
+++ b/services/core/java/com/android/server/wm/LegacyDimmer.java
@@ -134,8 +134,9 @@ public class LegacyDimmer extends Dimmer {
boolean mAnimateExit = true;
/**
- * Used for Dims not associated with a WindowContainer. See {@link Dimmer#dimAbove} for
- * details on Dim lifecycle.
+ * Used for Dims not associated with a WindowContainer.
+ * See {@link Dimmer#adjustRelativeLayer(WindowContainer, int)} for details on Dim
+ * lifecycle.
*/
boolean mDontReset;
SurfaceAnimator mSurfaceAnimator;
@@ -218,9 +219,8 @@ public class LegacyDimmer extends Dimmer {
}
@Override
- protected void dim(WindowContainer container, int relativeLayer, float alpha, int blurRadius) {
+ protected void adjustAppearance(WindowContainer container, float alpha, int blurRadius) {
final DimState d = obtainDimState(container);
-
if (d == null) {
return;
}
@@ -229,14 +229,21 @@ public class LegacyDimmer extends Dimmer {
// in the correct Z from lowest Z to highest. This ensures that the dim layer is always
// relative to the highest Z layer with a dim.
SurfaceControl.Transaction t = mHost.getPendingTransaction();
- t.setRelativeLayer(d.mDimLayer, container.getSurfaceControl(), relativeLayer);
t.setAlpha(d.mDimLayer, alpha);
t.setBackgroundBlurRadius(d.mDimLayer, blurRadius);
-
d.mDimming = true;
}
@Override
+ protected void adjustRelativeLayer(WindowContainer container, int relativeLayer) {
+ final DimState d = mDimState;
+ if (d != null) {
+ SurfaceControl.Transaction t = mHost.getPendingTransaction();
+ t.setRelativeLayer(d.mDimLayer, container.getSurfaceControl(), relativeLayer);
+ }
+ }
+
+ @Override
boolean updateDims(SurfaceControl.Transaction t) {
if (mDimState == null) {
return false;
diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS
index f6c364008c62..f8c39d0906a0 100644
--- a/services/core/java/com/android/server/wm/OWNERS
+++ b/services/core/java/com/android/server/wm/OWNERS
@@ -16,6 +16,7 @@ lihongyu@google.com
mariiasand@google.com
rgl@google.com
yunfanc@google.com
+wilsonshih@google.com
per-file BackgroundActivityStartController.java = set noparent
per-file BackgroundActivityStartController.java = brufino@google.com, topjohnwu@google.com, achim@google.com, ogunwale@google.com, louischang@google.com, lus@google.com
diff --git a/services/core/java/com/android/server/wm/SmoothDimmer.java b/services/core/java/com/android/server/wm/SmoothDimmer.java
index 6ddbd2c8eb67..2549bbf70e9f 100644
--- a/services/core/java/com/android/server/wm/SmoothDimmer.java
+++ b/services/core/java/com/android/server/wm/SmoothDimmer.java
@@ -63,8 +63,9 @@ class SmoothDimmer extends Dimmer {
boolean mAnimateExit = true;
/**
- * Used for Dims not associated with a WindowContainer. See {@link Dimmer#dimAbove} for
- * details on Dim lifecycle.
+ * Used for Dims not associated with a WindowContainer.
+ * See {@link Dimmer#adjustRelativeLayer(WindowContainer, int)} for details on Dim
+ * lifecycle.
*/
boolean mDontReset;
@@ -105,22 +106,34 @@ class SmoothDimmer extends Dimmer {
}
void setExitParameters(WindowContainer container) {
- setRequestedParameters(container, -1, 0, 0);
+ setRequestedRelativeParent(container, -1 /* relativeLayer */);
+ setRequestedAppearance(0f /* alpha */, 0 /* blur */);
}
+
// Sets a requested change without applying it immediately
- void setRequestedParameters(WindowContainer container, int relativeLayer, float alpha,
- int blurRadius) {
- mRequestedProperties.mDimmingContainer = container;
+ void setRequestedRelativeParent(WindowContainer relativeParent, int relativeLayer) {
+ mRequestedProperties.mDimmingContainer = relativeParent;
mRequestedProperties.mRelativeLayer = relativeLayer;
+ }
+
+ // Sets a requested change without applying it immediately
+ void setRequestedAppearance(float alpha, int blurRadius) {
mRequestedProperties.mAlpha = alpha;
mRequestedProperties.mBlurRadius = blurRadius;
}
/**
* Commit the last changes we received. Called after
- * {@link Change#setRequestedParameters(WindowContainer, int, float, int)}
+ * {@link Change#setExitParameters(WindowContainer)},
+ * {@link Change#setRequestedRelativeParent(WindowContainer, int)}, or
+ * {@link Change#setRequestedAppearance(float, int)}
*/
void applyChanges(SurfaceControl.Transaction t) {
+ if (mRequestedProperties.mDimmingContainer == null) {
+ Log.e(TAG, this + " does not have a dimming container. Have you forgotten to "
+ + "call adjustRelativeLayer?");
+ return;
+ }
if (mRequestedProperties.mDimmingContainer.mSurfaceControl == null) {
Log.w(TAG, "container " + mRequestedProperties.mDimmingContainer
+ "does not have a surface");
@@ -276,14 +289,19 @@ class SmoothDimmer extends Dimmer {
}
@Override
- protected void dim(WindowContainer container, int relativeLayer, float alpha, int blurRadius) {
+ protected void adjustAppearance(WindowContainer container, float alpha, int blurRadius) {
final DimState d = obtainDimState(container);
-
- mDimState.mRequestedProperties.mDimmingContainer = container;
- mDimState.setRequestedParameters(container, relativeLayer, alpha, blurRadius);
+ mDimState.setRequestedAppearance(alpha, blurRadius);
d.mDimming = true;
}
+ @Override
+ protected void adjustRelativeLayer(WindowContainer container, int relativeLayer) {
+ if (mDimState != null) {
+ mDimState.setRequestedRelativeParent(container, relativeLayer);
+ }
+ }
+
boolean updateDims(SurfaceControl.Transaction t) {
if (mDimState == null) {
return false;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 016b0ff55826..4922e9028ed9 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -489,10 +489,6 @@ class Task extends TaskFragment {
private boolean mForceShowForAllUsers;
- /** When set, will force the task to report as invisible. */
- static final int FLAG_FORCE_HIDDEN_FOR_PINNED_TASK = 1;
- static final int FLAG_FORCE_HIDDEN_FOR_TASK_ORG = 1 << 1;
- private int mForceHiddenFlags = 0;
private boolean mForceTranslucent = false;
// The display category name for this task.
@@ -4495,20 +4491,13 @@ class Task extends TaskFragment {
* Sets/unsets the forced-hidden state flag for this task depending on {@param set}.
* @return Whether the force hidden state changed
*/
- boolean setForceHidden(int flags, boolean set) {
- int newFlags = mForceHiddenFlags;
- if (set) {
- newFlags |= flags;
- } else {
- newFlags &= ~flags;
- }
- if (mForceHiddenFlags == newFlags) {
- return false;
- }
-
+ @Override
+ boolean setForceHidden(@FlagForceHidden int flags, boolean set) {
final boolean wasHidden = isForceHidden();
final boolean wasVisible = isVisible();
- mForceHiddenFlags = newFlags;
+ if (!super.setForceHidden(flags, set)) {
+ return false;
+ }
final boolean nowHidden = isForceHidden();
if (wasHidden != nowHidden) {
final String reason = "setForceHidden";
@@ -4539,11 +4528,6 @@ class Task extends TaskFragment {
return super.isAlwaysOnTop();
}
- @Override
- protected boolean isForceHidden() {
- return mForceHiddenFlags != 0;
- }
-
boolean isForceHiddenForPinnedTask() {
return (mForceHiddenFlags & FLAG_FORCE_HIDDEN_FOR_PINNED_TASK) != 0;
}
@@ -5651,6 +5635,8 @@ class Task extends TaskFragment {
if (noAnimation) {
mDisplayContent.prepareAppTransition(TRANSIT_NONE);
mTaskSupervisor.mNoAnimActivities.add(top);
+ mTransitionController.collect(top);
+ mTransitionController.setNoAnimation(top);
ActivityOptions.abort(options);
} else {
updateTransitLocked(TRANSIT_TO_FRONT, options);
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 7c5bc6e3bb28..906b3b55e015 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -362,6 +362,19 @@ class TaskFragment extends WindowContainer<WindowContainer> {
*/
private boolean mIsolatedNav;
+ /** When set, will force the task to report as invisible. */
+ static final int FLAG_FORCE_HIDDEN_FOR_PINNED_TASK = 1;
+ static final int FLAG_FORCE_HIDDEN_FOR_TASK_ORG = 1 << 1;
+ static final int FLAG_FORCE_HIDDEN_FOR_TASK_FRAGMENT_ORG = 1 << 2;
+
+ @IntDef(prefix = {"FLAG_FORCE_HIDDEN_"}, value = {
+ FLAG_FORCE_HIDDEN_FOR_PINNED_TASK,
+ FLAG_FORCE_HIDDEN_FOR_TASK_ORG,
+ FLAG_FORCE_HIDDEN_FOR_TASK_FRAGMENT_ORG,
+ }, flag = true)
+ @interface FlagForceHidden {}
+ protected int mForceHiddenFlags = 0;
+
final Point mLastSurfaceSize = new Point();
private final Rect mTmpBounds = new Rect();
@@ -845,7 +858,25 @@ class TaskFragment extends WindowContainer<WindowContainer> {
* Returns whether this TaskFragment is currently forced to be hidden for any reason.
*/
protected boolean isForceHidden() {
- return false;
+ return mForceHiddenFlags != 0;
+ }
+
+ /**
+ * Sets/unsets the forced-hidden state flag for this task depending on {@param set}.
+ * @return Whether the force hidden state changed
+ */
+ boolean setForceHidden(@FlagForceHidden int flags, boolean set) {
+ int newFlags = mForceHiddenFlags;
+ if (set) {
+ newFlags |= flags;
+ } else {
+ newFlags &= ~flags;
+ }
+ if (mForceHiddenFlags == newFlags) {
+ return false;
+ }
+ mForceHiddenFlags = newFlags;
+ return true;
}
protected boolean isForceTranslucent() {
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 04164c20a372..ff766beee337 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -51,7 +51,6 @@ import android.window.ITaskFragmentOrganizer;
import android.window.ITaskFragmentOrganizerController;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentOperation;
-import android.window.TaskFragmentOrganizerToken;
import android.window.TaskFragmentParentInfo;
import android.window.TaskFragmentTransaction;
import android.window.WindowContainerTransaction;
@@ -745,9 +744,9 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
}
}
- boolean isSystemOrganizer(@NonNull TaskFragmentOrganizerToken token) {
+ boolean isSystemOrganizer(@NonNull IBinder organizerToken) {
final TaskFragmentOrganizerState state =
- mTaskFragmentOrganizerState.get(token.asBinder());
+ mTaskFragmentOrganizerState.get(organizerToken);
return state != null && state.mIsSystemOrganizer;
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 93db1caf5fdf..7d65c61193b5 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -3321,8 +3321,8 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
mFrozen.add(wc);
final ChangeInfo changeInfo = Objects.requireNonNull(mChanges.get(wc));
changeInfo.mSnapshot = snapshotSurface;
- if (isDisplayRotation) {
- // This isn't cheap, so only do it for display rotations.
+ if (changeInfo.mRotation != wc.mDisplayContent.getRotation()) {
+ // This isn't cheap, so only do it for rotation change.
changeInfo.mSnapshotLuma = TransitionAnimation.getBorderLuma(
buffer, screenshotBuffer.getColorSpace());
}
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index de7871e3c231..8ac21e41f7f4 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -757,7 +757,7 @@ class TransitionController {
final TransitionRequestInfo request = new TransitionRequestInfo(transition.mType,
startTaskInfo, pipTaskInfo, remoteTransition, displayChange,
- transition.getFlags());
+ transition.getFlags(), transition.getSyncId());
transition.mLogger.mRequestTimeNs = SystemClock.elapsedRealtimeNanos();
transition.mLogger.mRequest = request;
@@ -1592,8 +1592,8 @@ class TransitionController {
TransitionInfo mInfo;
private String buildOnSendLog() {
- StringBuilder sb = new StringBuilder("Sent Transition #").append(mSyncId)
- .append(" createdAt=").append(TimeUtils.logTimeOfDay(mCreateWallTimeMs));
+ StringBuilder sb = new StringBuilder("Sent Transition (#").append(mSyncId)
+ .append(") createdAt=").append(TimeUtils.logTimeOfDay(mCreateWallTimeMs));
if (mRequest != null) {
sb.append(" via request=").append(mRequest);
}
@@ -1617,7 +1617,8 @@ class TransitionController {
void logOnSend() {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "%s", buildOnSendLog());
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN, " startWCT=%s", mStartWCT);
- ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN, " info=%s", mInfo);
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN, " info=%s",
+ mInfo.toString(" " /* prefix */));
}
private static String toMsString(long nanos) {
@@ -1625,8 +1626,8 @@ class TransitionController {
}
private String buildOnFinishLog() {
- StringBuilder sb = new StringBuilder("Finish Transition #").append(mSyncId)
- .append(": created at ").append(TimeUtils.logTimeOfDay(mCreateWallTimeMs));
+ StringBuilder sb = new StringBuilder("Finish Transition (#").append(mSyncId)
+ .append("): created at ").append(TimeUtils.logTimeOfDay(mCreateWallTimeMs));
sb.append(" collect-started=").append(toMsString(mCollectTimeNs - mCreateTimeNs));
if (mRequestTimeNs != 0) {
sb.append(" request-sent=").append(toMsString(mRequestTimeNs - mCreateTimeNs));
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 674ff487800f..94e66ffd8373 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -536,7 +536,7 @@ class WallpaperController {
window.mWallpaperY = y;
window.mWallpaperXStep = xStep;
window.mWallpaperYStep = yStep;
- updateWallpaperOffsetLocked(window, true);
+ updateWallpaperOffsetLocked(window, !mService.mFlags.mWallpaperOffsetAsync);
}
}
@@ -561,7 +561,7 @@ class WallpaperController {
if (window.mWallpaperDisplayOffsetX != x || window.mWallpaperDisplayOffsetY != y) {
window.mWallpaperDisplayOffsetX = x;
window.mWallpaperDisplayOffsetY = y;
- updateWallpaperOffsetLocked(window, true);
+ updateWallpaperOffsetLocked(window, !mService.mFlags.mWallpaperOffsetAsync);
}
}
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 50ef52a4d9dd..1ed14310a500 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -117,7 +117,8 @@ class WallpaperWindowToken extends WindowToken {
final WallpaperController wallpaperController = mDisplayContent.mWallpaperController;
for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
final WindowState wallpaper = mChildren.get(wallpaperNdx);
- if (wallpaperController.updateWallpaperOffset(wallpaper, sync)) {
+ if (wallpaperController.updateWallpaperOffset(wallpaper,
+ sync && !mWmService.mFlags.mWallpaperOffsetAsync)) {
// We only want to be synchronous with one wallpaper.
sync = false;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerFlags.java b/services/core/java/com/android/server/wm/WindowManagerFlags.java
index 5b9acb2f67c4..46677107c670 100644
--- a/services/core/java/com/android/server/wm/WindowManagerFlags.java
+++ b/services/core/java/com/android/server/wm/WindowManagerFlags.java
@@ -47,5 +47,7 @@ class WindowManagerFlags {
final boolean mWindowStateResizeItemFlag = Flags.windowStateResizeItemFlag();
+ final boolean mWallpaperOffsetAsync = Flags.wallpaperOffsetAsync();
+
/* End Available Flags */
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9663f3aba7f1..88f72f9dbc90 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8461,16 +8461,18 @@ public class WindowManagerService extends IWindowManager.Stub
return true;
}
// For a task session, find the activity identified by the launch cookie.
- final WindowContainerToken wct = getTaskWindowContainerTokenForLaunchCookie(
+ final WindowContainerInfo wci = getTaskWindowContainerInfoForLaunchCookie(
incomingSession.getTokenToRecord());
- if (wct == null) {
+ if (wci == null) {
Slog.w(TAG, "Handling a new recording session; unable to find the "
+ "WindowContainerToken");
return false;
}
// Replace the launch cookie in the session details with the task's
// WindowContainerToken.
- incomingSession.setTokenToRecord(wct.asBinder());
+ incomingSession.setTokenToRecord(wci.getToken().asBinder());
+ // Also replace the UNKNOWN target UID with the actual UID.
+ incomingSession.setTargetUid(wci.getUid());
mContentRecordingController.setContentRecordingSessionLocked(incomingSession,
WindowManagerService.this);
return true;
@@ -8798,21 +8800,41 @@ public class WindowManagerService extends IWindowManager.Stub
mAtmService.setFocusedTask(task.mTaskId, touchedActivity);
}
+ @VisibleForTesting
+ static class WindowContainerInfo {
+ private final int mUid;
+ @NonNull private final WindowContainerToken mToken;
+
+ private WindowContainerInfo(int uid, @NonNull WindowContainerToken token) {
+ this.mUid = uid;
+ this.mToken = token;
+ }
+
+ public int getUid() {
+ return mUid;
+ }
+
+ @NonNull
+ public WindowContainerToken getToken() {
+ return mToken;
+ }
+ }
+
/**
- * Retrieve the {@link WindowContainerToken} of the task that contains the activity started
- * with the given launch cookie.
+ * Retrieve the {@link WindowContainerInfo} of the task that contains the activity started with
+ * the given launch cookie.
*
* @param launchCookie the launch cookie set on the {@link ActivityOptions} when starting an
- * activity
+ * activity
* @return a token representing the task containing the activity started with the given launch
- * cookie, or {@code null} if the token couldn't be found.
+ * cookie, or {@code null} if the token couldn't be found.
*/
@VisibleForTesting
@Nullable
- WindowContainerToken getTaskWindowContainerTokenForLaunchCookie(@NonNull IBinder launchCookie) {
+ WindowContainerInfo getTaskWindowContainerInfoForLaunchCookie(@NonNull IBinder launchCookie) {
// Find the activity identified by the launch cookie.
- final ActivityRecord targetActivity = mRoot.getActivity(
- activity -> activity.mLaunchCookie == launchCookie);
+ final ActivityRecord targetActivity =
+ mRoot.getActivity(activity -> activity.mLaunchCookie == launchCookie);
if (targetActivity == null) {
Slog.w(TAG, "Unable to find the activity for this launch cookie");
return null;
@@ -8827,7 +8849,7 @@ public class WindowManagerService extends IWindowManager.Stub
Slog.w(TAG, "Unable to find the WindowContainerToken for " + targetActivity.getName());
return null;
}
- return taskWindowContainerToken;
+ return new WindowContainerInfo(targetActivity.getUid(), taskWindowContainerToken);
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index dd9a88f72bde..5ed6caffe1fb 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -24,7 +24,9 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.TaskFragmentOperation.OP_TYPE_CLEAR_ADJACENT_TASK_FRAGMENTS;
import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT;
+import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_BOTTOM_OF_TASK;
import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT;
+import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_TOP_OF_TASK;
import static android.window.TaskFragmentOperation.OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS;
@@ -34,6 +36,8 @@ import static android.window.TaskFragmentOperation.OP_TYPE_SET_ISOLATED_NAVIGATI
import static android.window.TaskFragmentOperation.OP_TYPE_SET_RELATIVE_BOUNDS;
import static android.window.TaskFragmentOperation.OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_UNKNOWN;
+import static android.window.WindowContainerTransaction.Change.CHANGE_FOCUSABLE;
+import static android.window.WindowContainerTransaction.Change.CHANGE_HIDDEN;
import static android.window.WindowContainerTransaction.Change.CHANGE_RELATIVE_BOUNDS;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_ADD_TASK_FRAGMENT_OPERATION;
@@ -61,6 +65,7 @@ import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
+import static com.android.server.wm.TaskFragment.FLAG_FORCE_HIDDEN_FOR_TASK_FRAGMENT_ORG;
import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
@@ -821,6 +826,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
return TRANSACT_EFFECTS_NONE;
}
+ int effects = TRANSACT_EFFECTS_NONE;
// When the TaskFragment is resized, we may want to create a change transition for it, for
// which we want to defer the surface update until we determine whether or not to start
// change transition.
@@ -843,7 +849,14 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
c.getConfiguration().windowConfiguration.setBounds(absBounds);
taskFragment.setRelativeEmbeddedBounds(relBounds);
}
- final int effects = applyChanges(taskFragment, c);
+ if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) {
+ if (taskFragment.setForceHidden(
+ FLAG_FORCE_HIDDEN_FOR_TASK_FRAGMENT_ORG, c.getHidden())) {
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
+ }
+ }
+ effects |= applyChanges(taskFragment, c);
+
if (taskFragment.shouldStartChangeTransition(mTmpBounds0, mTmpBounds1)) {
taskFragment.initializeChangeTransition(mTmpBounds0);
}
@@ -1393,6 +1406,24 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
taskFragment.setIsolatedNav(isolatedNav);
break;
}
+ case OP_TYPE_REORDER_TO_BOTTOM_OF_TASK: {
+ final Task task = taskFragment.getTask();
+ if (task != null) {
+ task.mChildren.remove(taskFragment);
+ task.mChildren.add(0, taskFragment);
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
+ }
+ break;
+ }
+ case OP_TYPE_REORDER_TO_TOP_OF_TASK: {
+ final Task task = taskFragment.getTask();
+ if (task != null) {
+ task.mChildren.remove(taskFragment);
+ task.mChildren.add(taskFragment);
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
+ }
+ break;
+ }
}
return effects;
}
@@ -1420,6 +1451,18 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
return false;
}
+ if ((opType == OP_TYPE_REORDER_TO_BOTTOM_OF_TASK
+ || opType == OP_TYPE_REORDER_TO_TOP_OF_TASK)
+ && !mTaskFragmentOrganizerController.isSystemOrganizer(organizer.asBinder())) {
+ final Throwable exception = new SecurityException(
+ "Only a system organizer can perform OP_TYPE_REORDER_TO_BOTTOM_OF_TASK or "
+ + "OP_TYPE_REORDER_TO_TOP_OF_TASK."
+ );
+ sendTaskFragmentOperationFailure(organizer, errorCallbackToken, taskFragment,
+ opType, exception);
+ return false;
+ }
+
final IBinder secondaryFragmentToken = operation.getSecondaryFragmentToken();
return secondaryFragmentToken == null
|| validateTaskFragment(mLaunchTaskFragments.get(secondaryFragmentToken), opType,
@@ -1920,6 +1963,11 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
* For config change on {@link TaskFragment}, we only support the following operations:
* {@link WindowContainerTransaction#setRelativeBounds(WindowContainerToken, Rect)},
* {@link WindowContainerTransaction#setWindowingMode(WindowContainerToken, int)}.
+ *
+ * For a system organizer, we additionally support
+ * {@link WindowContainerTransaction#setHidden(WindowContainerToken, boolean)}, and
+ * {@link WindowContainerTransaction#setFocusable(WindowContainerToken, boolean)}. See
+ * {@link TaskFragmentOrganizerController#registerOrganizer(ITaskFragmentOrganizer, boolean)}
*/
private void enforceTaskFragmentConfigChangeAllowed(@NonNull String func,
@Nullable WindowContainer wc, @NonNull WindowContainerTransaction.Change change,
@@ -1938,31 +1986,49 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
throw new SecurityException(msg);
}
- final int changeMask = change.getChangeMask();
- final int configSetMask = change.getConfigSetMask();
- final int windowSetMask = change.getWindowSetMask();
- if (changeMask == 0 && configSetMask == 0 && windowSetMask == 0
- && change.getWindowingMode() >= 0) {
- // The change contains only setWindowingMode, which is allowed.
- return;
+ final int originalChangeMask = change.getChangeMask();
+ final int originalConfigSetMask = change.getConfigSetMask();
+ final int originalWindowSetMask = change.getWindowSetMask();
+
+ int changeMaskToBeChecked = originalChangeMask;
+ int configSetMaskToBeChecked = originalConfigSetMask;
+ int windowSetMaskToBeChecked = originalWindowSetMask;
+
+ if (mTaskFragmentOrganizerController.isSystemOrganizer(organizer.asBinder())) {
+ // System organizer is allowed to update the hidden and focusable state.
+ // We unset the CHANGE_HIDDEN and CHANGE_FOCUSABLE bits because they are checked here.
+ changeMaskToBeChecked &= ~CHANGE_HIDDEN;
+ changeMaskToBeChecked &= ~CHANGE_FOCUSABLE;
}
- if (changeMask != CHANGE_RELATIVE_BOUNDS
- || configSetMask != ActivityInfo.CONFIG_WINDOW_CONFIGURATION
- || windowSetMask != WindowConfiguration.WINDOW_CONFIG_BOUNDS) {
- // None of the change should be requested from a TaskFragment organizer except
- // setRelativeBounds and setWindowingMode.
- // For setRelativeBounds, we don't need to check whether it is outside of the Task
+
+ // setRelativeBounds is allowed.
+ if ((changeMaskToBeChecked & CHANGE_RELATIVE_BOUNDS) != 0
+ && (configSetMaskToBeChecked & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0
+ && (windowSetMaskToBeChecked & WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0) {
+ // For setRelativeBounds, we don't need to check whether it is outside the Task
// bounds, because it is possible that the Task is also resizing, for which we don't
// want to throw an exception. The bounds will be adjusted in
// TaskFragment#translateRelativeBoundsToAbsoluteBounds.
- String msg = "Permission Denial: " + func + " from pid="
- + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
- + " trying to apply changes of changeMask=" + changeMask
- + " configSetMask=" + configSetMask + " windowSetMask=" + windowSetMask
- + " to TaskFragment=" + tf + " TaskFragmentOrganizer=" + organizer;
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
+ changeMaskToBeChecked &= ~CHANGE_RELATIVE_BOUNDS;
+ configSetMaskToBeChecked &= ~ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
+ windowSetMaskToBeChecked &= ~WindowConfiguration.WINDOW_CONFIG_BOUNDS;
}
+
+ if (changeMaskToBeChecked == 0 && configSetMaskToBeChecked == 0
+ && windowSetMaskToBeChecked == 0) {
+ // All the changes have been checked.
+ // Note that setWindowingMode is always allowed, so we don't need to check the mask.
+ return;
+ }
+
+ final String msg = "Permission Denial: " + func + " from pid="
+ + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+ + " trying to apply changes of changeMask=" + originalChangeMask
+ + " configSetMask=" + originalConfigSetMask
+ + " windowSetMask=" + originalWindowSetMask
+ + " to TaskFragment=" + tf + " TaskFragmentOrganizer=" + organizer;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
}
private void createTaskFragment(@NonNull TaskFragmentCreationParams creationParams,
@@ -2019,7 +2085,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
TaskFragmentOrganizerToken organizerToken = creationParams.getOrganizer();
taskFragment.setTaskFragmentOrganizer(organizerToken,
ownerActivity.getUid(), ownerActivity.info.processName,
- mTaskFragmentOrganizerController.isSystemOrganizer(organizerToken));
+ mTaskFragmentOrganizerController.isSystemOrganizer(organizerToken.asBinder()));
final int position;
if (creationParams.getPairedPrimaryFragmentToken() != null) {
// When there is a paired primary TaskFragment, we want to place the new TaskFragment
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 7f36aec69480..3a793e921c68 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -5136,8 +5136,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
private void applyDims() {
if (((mAttrs.flags & FLAG_DIM_BEHIND) != 0 || shouldDrawBlurBehind())
- && mToken.isVisibleRequested() && isVisibleNow() && !mHidden
- && mTransitionController.canApplyDim(getTask())) {
+ && (Dimmer.DIMMER_REFACTOR ? mWinAnimator.getShown() : isVisibleNow())
+ && !mHidden && mTransitionController.canApplyDim(getTask())) {
// Only show the Dimmer when the following is satisfied:
// 1. The window has the flag FLAG_DIM_BEHIND or blur behind is requested
// 2. The WindowToken is not hidden so dims aren't shown when the window is exiting.
@@ -5147,7 +5147,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mIsDimming = true;
final float dimAmount = (mAttrs.flags & FLAG_DIM_BEHIND) != 0 ? mAttrs.dimAmount : 0;
final int blurRadius = shouldDrawBlurBehind() ? mAttrs.getBlurBehindRadius() : 0;
- getDimmer().dimBelow(this, dimAmount, blurRadius);
+ // If the window is visible from surface flinger perspective (mWinAnimator.getShown())
+ // but not window manager visible (!isVisibleNow()), it can still be the parent of the
+ // dim, but can not create a new surface or continue a dim alone.
+ if (isVisibleNow()) {
+ getDimmer().adjustAppearance(this, dimAmount, blurRadius);
+ }
+ getDimmer().adjustRelativeLayer(this, -1 /* relativeLayer */);
}
}
@@ -5207,12 +5213,17 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
void prepareSurfaces() {
mIsDimming = false;
if (mHasSurface) {
- applyDims();
+ if (!Dimmer.DIMMER_REFACTOR) {
+ applyDims();
+ }
updateSurfacePositionNonOrganized();
// Send information to SurfaceFlinger about the priority of the current window.
updateFrameRateSelectionPriorityIfNeeded();
updateScaleIfNeeded();
mWinAnimator.prepareSurfaceLocked(getSyncTransaction());
+ if (Dimmer.DIMMER_REFACTOR) {
+ applyDims();
+ }
}
super.prepareSurfaces();
}
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index e434f296bbb5..7d21dbf85a66 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -569,6 +569,7 @@ class WindowToken extends WindowContainer<WindowState> {
&& asActivityRecord() != null && isVisible()) {
// Trigger an activity level rotation transition.
mTransitionController.requestTransitionIfNeeded(WindowManager.TRANSIT_CHANGE, this);
+ mTransitionController.collectVisibleChange(this);
mTransitionController.setReady(this);
}
final int originalRotation = getWindowConfiguration().getRotation();
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 709d5e39463c..24ee16389fd2 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -189,7 +189,7 @@ cc_defaults {
"android.hardware.thermal@1.0",
"android.hardware.thermal-V1-ndk",
"android.hardware.tv.input@1.0",
- "android.hardware.tv.input-V1-ndk",
+ "android.hardware.tv.input-V2-ndk",
"android.hardware.vibrator-V2-cpp",
"android.hardware.vibrator@1.0",
"android.hardware.vibrator@1.1",
@@ -244,5 +244,5 @@ filegroup {
filegroup {
name: "lib_oomConnection_native",
- srcs: ["com_android_server_am_OomConnection.cpp",],
+ srcs: ["com_android_server_am_OomConnection.cpp"],
}
diff --git a/services/core/jni/com_android_server_display_SmallAreaDetectionController.cpp b/services/core/jni/com_android_server_display_SmallAreaDetectionController.cpp
index b256f168f2af..1844d3063cd8 100644
--- a/services/core/jni/com_android_server_display_SmallAreaDetectionController.cpp
+++ b/services/core/jni/com_android_server_display_SmallAreaDetectionController.cpp
@@ -24,33 +24,33 @@
#include "utils/Log.h"
namespace android {
-static void nativeUpdateSmallAreaDetection(JNIEnv* env, jclass clazz, jintArray juids,
+static void nativeUpdateSmallAreaDetection(JNIEnv* env, jclass clazz, jintArray jappIds,
jfloatArray jthresholds) {
- if (juids == nullptr || jthresholds == nullptr) return;
+ if (jappIds == nullptr || jthresholds == nullptr) return;
- ScopedIntArrayRO uids(env, juids);
+ ScopedIntArrayRO appIds(env, jappIds);
ScopedFloatArrayRO thresholds(env, jthresholds);
- if (uids.size() != thresholds.size()) {
- ALOGE("uids size exceeds thresholds size!");
+ if (appIds.size() != thresholds.size()) {
+ ALOGE("appIds size exceeds thresholds size!");
return;
}
- std::vector<int32_t> uidVector;
+ std::vector<int32_t> appIdVector;
std::vector<float> thresholdVector;
- size_t size = uids.size();
- uidVector.reserve(size);
+ size_t size = appIds.size();
+ appIdVector.reserve(size);
thresholdVector.reserve(size);
for (int i = 0; i < size; i++) {
- uidVector.push_back(static_cast<int32_t>(uids[i]));
+ appIdVector.push_back(static_cast<int32_t>(appIds[i]));
thresholdVector.push_back(static_cast<float>(thresholds[i]));
}
- SurfaceComposerClient::updateSmallAreaDetection(uidVector, thresholdVector);
+ SurfaceComposerClient::updateSmallAreaDetection(appIdVector, thresholdVector);
}
-static void nativeSetSmallAreaDetectionThreshold(JNIEnv* env, jclass clazz, jint uid,
+static void nativeSetSmallAreaDetectionThreshold(JNIEnv* env, jclass clazz, jint appId,
jfloat threshold) {
- SurfaceComposerClient::setSmallAreaDetectionThreshold(uid, threshold);
+ SurfaceComposerClient::setSmallAreaDetectionThreshold(appId, threshold);
}
static const JNINativeMethod gMethods[] = {
diff --git a/services/core/jni/tvinput/JTvInputHal.cpp b/services/core/jni/tvinput/JTvInputHal.cpp
index c7366173ecda..dc05462c1a1c 100644
--- a/services/core/jni/tvinput/JTvInputHal.cpp
+++ b/services/core/jni/tvinput/JTvInputHal.cpp
@@ -368,12 +368,20 @@ JTvInputHal::TvInputEventWrapper JTvInputHal::TvInputEventWrapper::createEventWr
}
JTvInputHal::TvMessageEventWrapper JTvInputHal::TvMessageEventWrapper::createEventWrapper(
- const AidlTvMessageEvent& aidlTvMessageEvent) {
+ const AidlTvMessageEvent& aidlTvMessageEvent, bool isLegacyMessage) {
+ auto messageList = aidlTvMessageEvent.messages;
TvMessageEventWrapper event;
- event.messages.insert(event.messages.begin(), std::begin(aidlTvMessageEvent.messages) + 1,
- std::end(aidlTvMessageEvent.messages));
+ // Handle backwards compatibility for V1
+ if (isLegacyMessage) {
+ event.deviceId = messageList[0].groupId;
+ event.messages.insert(event.messages.begin(), std::begin(messageList) + 1,
+ std::end(messageList));
+ } else {
+ event.deviceId = aidlTvMessageEvent.deviceId;
+ event.messages.insert(event.messages.begin(), std::begin(messageList),
+ std::end(messageList));
+ }
event.streamId = aidlTvMessageEvent.streamId;
- event.deviceId = aidlTvMessageEvent.messages[0].groupId;
event.type = aidlTvMessageEvent.type;
return event;
}
@@ -449,15 +457,30 @@ JTvInputHal::TvInputCallback::TvInputCallback(JTvInputHal* hal) {
::ndk::ScopedAStatus JTvInputHal::TvInputCallback::notifyTvMessageEvent(
const AidlTvMessageEvent& event) {
const std::string DEVICE_ID_SUBTYPE = "device_id";
- if (event.messages.size() > 1 && event.messages[0].subType == DEVICE_ID_SUBTYPE) {
- mHal->mLooper
- ->sendMessage(new NotifyTvMessageHandler(mHal,
- TvMessageEventWrapper::createEventWrapper(
- event)),
- static_cast<int>(event.type));
+ ::ndk::ScopedAStatus status = ::ndk::ScopedAStatus::ok();
+ int32_t aidlVersion = 0;
+ if (mHal->mTvInput->getAidlInterfaceVersion(&aidlVersion).isOk() && event.messages.size() > 0) {
+ bool validLegacyMessage = aidlVersion == 1 &&
+ event.messages[0].subType == DEVICE_ID_SUBTYPE && event.messages.size() > 1;
+ bool validTvMessage = aidlVersion > 1 && event.messages.size() > 0;
+ if (validLegacyMessage || validTvMessage) {
+ mHal->mLooper->sendMessage(
+ new NotifyTvMessageHandler(mHal,
+ TvMessageEventWrapper::
+ createEventWrapper(event,
+ validLegacyMessage)),
+ static_cast<int>(event.type));
+ } else {
+ status = ::ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ ALOGE("The TVMessage event was malformed for HAL version: %d", aidlVersion);
+ }
+ } else {
+ status = ::ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ ALOGE("The TVMessage event was empty or the HAL version (version: %d) could not "
+ "be inferred.",
+ aidlVersion);
}
-
- return ::ndk::ScopedAStatus::ok();
+ return status;
}
JTvInputHal::ITvInputWrapper::ITvInputWrapper(std::shared_ptr<AidlITvInput>& aidlTvInput)
@@ -521,4 +544,12 @@ JTvInputHal::ITvInputWrapper::ITvInputWrapper(std::shared_ptr<AidlITvInput>& aid
}
}
+::ndk::ScopedAStatus JTvInputHal::ITvInputWrapper::getAidlInterfaceVersion(int32_t* _aidl_return) {
+ if (mIsHidl) {
+ return ::ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ } else {
+ return mAidlTvInput->getInterfaceVersion(_aidl_return);
+ }
+}
+
} // namespace android
diff --git a/services/core/jni/tvinput/JTvInputHal.h b/services/core/jni/tvinput/JTvInputHal.h
index 1d8d1629f67d..6026a107c67f 100644
--- a/services/core/jni/tvinput/JTvInputHal.h
+++ b/services/core/jni/tvinput/JTvInputHal.h
@@ -138,7 +138,7 @@ private:
TvMessageEventWrapper() {}
static TvMessageEventWrapper createEventWrapper(
- const AidlTvMessageEvent& aidlTvMessageEvent);
+ const AidlTvMessageEvent& aidlTvMessageEvent, bool isLegacyMessage);
int streamId;
int deviceId;
@@ -195,6 +195,7 @@ private:
::ndk::ScopedAStatus getTvMessageQueueDesc(
MQDescriptor<int8_t, SynchronizedReadWrite>* out_queue, int32_t in_deviceId,
int32_t in_streamId);
+ ::ndk::ScopedAStatus getAidlInterfaceVersion(int32_t* _aidl_return);
private:
::ndk::ScopedAStatus hidlSetCallback(const std::shared_ptr<TvInputCallback>& in_callback);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index c26aee8af83e..924e2f8ec654 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -338,6 +338,8 @@ public final class SystemServer implements Dumpable {
"com.android.clockwork.modes.ModeManagerService";
private static final String WEAR_DISPLAY_SERVICE_CLASS =
"com.android.clockwork.display.WearDisplayService";
+ private static final String WEAR_DEBUG_SERVICE_CLASS =
+ "com.android.clockwork.debug.WearDebugService";
private static final String WEAR_TIME_SERVICE_CLASS =
"com.android.clockwork.time.WearTimeService";
private static final String WEAR_SETTINGS_SERVICE_CLASS =
@@ -2636,6 +2638,12 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startService(WEAR_DISPLAY_SERVICE_CLASS);
t.traceEnd();
+ if (Build.IS_DEBUGGABLE) {
+ t.traceBegin("StartWearDebugService");
+ mSystemServiceManager.startService(WEAR_DEBUG_SERVICE_CLASS);
+ t.traceEnd();
+ }
+
t.traceBegin("StartWearTimeService");
mSystemServiceManager.startService(WEAR_TIME_SERVICE_CLASS);
t.traceEnd();
diff --git a/services/midi/Android.bp b/services/midi/Android.bp
index 5adcfbaf432e..4b5f8a7bf0ac 100644
--- a/services/midi/Android.bp
+++ b/services/midi/Android.bp
@@ -19,4 +19,7 @@ java_library_static {
defaults: ["platform_service_defaults"],
srcs: [":services.midi-sources"],
libs: ["services.core"],
+ static_libs: [
+ "aconfig_midi_flags_java_lib",
+ ],
}
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index a8902fcf77af..2f47cc7160b7 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -16,6 +16,8 @@
package com.android.server.midi;
+import static com.android.media.midi.flags.Flags.virtualUmp;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
@@ -1549,6 +1551,12 @@ public class MidiService extends IMidiManager.Stub {
return;
}
+ if (!virtualUmp()) {
+ Log.w(TAG, "Skipping MIDI device service " + serviceInfo.packageName
+ + ": virtual UMP flag not enabled");
+ return;
+ }
+
Bundle properties = null;
int numPorts = 0;
boolean isPrivate = 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 952cfc48b583..cbedcaf97358 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
@@ -43,6 +43,7 @@ import static org.mockito.Mockito.when;
import android.annotation.NonNull;
import android.app.PropertyInvalidatedCache;
+import android.content.ComponentName;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.SuspendDialogInfo;
@@ -873,12 +874,20 @@ public class PackageManagerSettingsTests {
.setUid(packageSetting.getAppId())
.hideAsFinal());
- ArchiveState archiveState = new ArchiveState(
- List.of(new ArchiveState.ArchiveActivityInfo("title1", Path.of("/path1"),
- Path.of("/monochromePath1")),
- new ArchiveState.ArchiveActivityInfo("title2", Path.of("/path2"),
- Path.of("/monochromePath2"))),
- "installerTitle");
+ ArchiveState archiveState =
+ new ArchiveState(
+ List.of(
+ new ArchiveState.ArchiveActivityInfo(
+ "title1",
+ new ComponentName("pkg1", "class1"),
+ Path.of("/path1"),
+ Path.of("/monochromePath1")),
+ new ArchiveState.ArchiveActivityInfo(
+ "title2",
+ new ComponentName("pkg2", "class2"),
+ Path.of("/path2"),
+ Path.of("/monochromePath2"))),
+ "installerTitle");
packageSetting.modifyUserState(UserHandle.SYSTEM.getIdentifier()).setArchiveState(
archiveState);
settings.mPackages.put(PACKAGE_NAME_1, packageSetting);
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 58ae7406580e..87a297b0e86f 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
@@ -24,6 +24,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
+import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.overlay.OverlayPaths;
@@ -192,8 +193,8 @@ public class PackageUserStateTest {
return new SuspendParams(dialogInfo, appExtras, launcherExtras);
}
- private static PersistableBundle createPersistableBundle(String lKey, long lValue, String sKey,
- String sValue, String dKey, double dValue) {
+ private static PersistableBundle createPersistableBundle(
+ String lKey, long lValue, String sKey, String sValue, String dKey, double dValue) {
final PersistableBundle result = new PersistableBundle(3);
if (lKey != null) {
result.putLong("com.unit_test." + lKey, lValue);
@@ -320,6 +321,7 @@ public class PackageUserStateTest {
assertEquals(0L, state.getLastPackageUsageTimeInMills()[i]);
}
}
+
private static void assertLastPackageUsageSet(
PackageStateUnserialized state, int reason, long value) throws Exception {
for (int i = state.getLastPackageUsageTimeInMills().length - 1; i >= 0; --i) {
@@ -330,6 +332,7 @@ public class PackageUserStateTest {
}
}
}
+
@Test
public void testPackageUseReasons() throws Exception {
PackageSetting packageSetting = Mockito.mock(PackageSetting.class);
@@ -377,6 +380,7 @@ public class PackageUserStateTest {
assertTrue(testState.setOverlayPaths(new OverlayPaths.Builder().build()));
assertFalse(testState.setOverlayPaths(null));
}
+
@Test
public void testSharedLibOverlayPaths() {
final PackageUserStateImpl testState = new PackageUserStateImpl();
@@ -401,8 +405,12 @@ public class PackageUserStateTest {
@Test
public void archiveState() {
PackageUserStateImpl packageUserState = new PackageUserStateImpl();
- ArchiveState.ArchiveActivityInfo archiveActivityInfo = new ArchiveState.ArchiveActivityInfo(
- "appTitle", Path.of("/path1"), Path.of("/path2"));
+ ArchiveState.ArchiveActivityInfo archiveActivityInfo =
+ new ArchiveState.ArchiveActivityInfo(
+ "appTitle",
+ new ComponentName("pkg", "class"),
+ Path.of("/path1"),
+ Path.of("/path2"));
ArchiveState archiveState = new ArchiveState(List.of(archiveActivityInfo),
"installerTitle");
packageUserState.setArchiveState(archiveState);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index d021f1d5aaea..16d72e40fbb5 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -117,7 +117,6 @@ import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.notifications.DisplayNotificationManager;
import com.android.server.input.InputManagerInternal;
import com.android.server.lights.LightsManager;
-import com.android.server.pm.UserManagerInternal;
import com.android.server.sensors.SensorManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -312,7 +311,6 @@ public class DisplayManagerServiceTest {
@Mock SensorManager mSensorManager;
@Mock DisplayDeviceConfig mMockDisplayDeviceConfig;
@Mock PackageManagerInternal mMockPackageManagerInternal;
- @Mock UserManagerInternal mMockUserManagerInternal;
@Captor ArgumentCaptor<ContentRecordingSession> mContentRecordingSessionCaptor;
@@ -336,8 +334,6 @@ public class DisplayManagerServiceTest {
VirtualDeviceManagerInternal.class, mMockVirtualDeviceManagerInternal);
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, mMockPackageManagerInternal);
- LocalServices.removeServiceForTest(UserManagerInternal.class);
- LocalServices.addService(UserManagerInternal.class, mMockUserManagerInternal);
// TODO: b/287945043
mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
mResources = Mockito.spy(mContext.getResources());
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index c4f72b307eb7..6a95d5c57024 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -102,6 +102,9 @@ import com.android.server.sensors.SensorManagerInternal.ProximityActiveListener;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.testutils.FakeDeviceConfigInterface;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -121,26 +124,28 @@ import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
-import junitparams.JUnitParamsRunner;
-import junitparams.Parameters;
-
@SmallTest
@RunWith(JUnitParamsRunner.class)
public class DisplayModeDirectorTest {
public static Collection<Object[]> getAppRequestedSizeTestCases() {
var appRequestedSizeTestCases = Arrays.asList(new Object[][] {
- {DEFAULT_MODE_75.getModeId(), Float.POSITIVE_INFINITY,
- DEFAULT_MODE_75.getRefreshRate(), Map.of()},
- {APP_MODE_HIGH_90.getModeId(), Float.POSITIVE_INFINITY,
- APP_MODE_HIGH_90.getRefreshRate(),
- Map.of(
+ {/*expectedBaseModeId*/ DEFAULT_MODE_75.getModeId(),
+ /*expectedPhysicalRefreshRate*/ Float.POSITIVE_INFINITY,
+ /*expectedAppRequestedRefreshRate*/ DEFAULT_MODE_75.getRefreshRate(),
+ /*votesWithPriorities*/ Map.of()},
+ {/*expectedBaseModeId*/ APP_MODE_HIGH_90.getModeId(),
+ /*expectedPhysicalRefreshRate*/ Float.POSITIVE_INFINITY,
+ /*expectedAppRequestedRefreshRate*/ APP_MODE_HIGH_90.getRefreshRate(),
+ /*votesWithPriorities*/ Map.of(
Vote.PRIORITY_APP_REQUEST_SIZE,
Vote.forSize(APP_MODE_HIGH_90.getPhysicalWidth(),
APP_MODE_HIGH_90.getPhysicalHeight()),
Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
Vote.forBaseModeRefreshRate(APP_MODE_HIGH_90.getRefreshRate()))},
- {LIMIT_MODE_70.getModeId(), Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY,
- Map.of(
+ {/*expectedBaseModeId*/ LIMIT_MODE_70.getModeId(),
+ /*expectedPhysicalRefreshRate*/ Float.POSITIVE_INFINITY,
+ /*expectedAppRequestedRefreshRate*/ Float.POSITIVE_INFINITY,
+ /*votesWithPriorities*/ Map.of(
Vote.PRIORITY_APP_REQUEST_SIZE,
Vote.forSize(APP_MODE_HIGH_90.getPhysicalWidth(),
APP_MODE_HIGH_90.getPhysicalHeight()),
@@ -149,9 +154,10 @@ public class DisplayModeDirectorTest {
Vote.PRIORITY_LOW_POWER_MODE,
Vote.forSize(LIMIT_MODE_70.getPhysicalWidth(),
LIMIT_MODE_70.getPhysicalHeight()))},
- {LIMIT_MODE_70.getModeId(), LIMIT_MODE_70.getRefreshRate(),
- LIMIT_MODE_70.getRefreshRate(),
- Map.of(
+ {/*expectedBaseModeId*/ LIMIT_MODE_70.getModeId(),
+ /*expectedPhysicalRefreshRate*/ LIMIT_MODE_70.getRefreshRate(),
+ /*expectedAppRequestedRefreshRate*/ LIMIT_MODE_70.getRefreshRate(),
+ /*votesWithPriorities*/ Map.of(
Vote.PRIORITY_APP_REQUEST_SIZE,
Vote.forSize(APP_MODE_65.getPhysicalWidth(),
APP_MODE_65.getPhysicalHeight()),
@@ -160,9 +166,10 @@ public class DisplayModeDirectorTest {
Vote.PRIORITY_LOW_POWER_MODE,
Vote.forSize(LIMIT_MODE_70.getPhysicalWidth(),
LIMIT_MODE_70.getPhysicalHeight()))},
- {LIMIT_MODE_70.getModeId(), LIMIT_MODE_70.getRefreshRate(),
- LIMIT_MODE_70.getRefreshRate(),
- Map.of(
+ {/*expectedBaseModeId*/ LIMIT_MODE_70.getModeId(),
+ /*expectedPhysicalRefreshRate*/ LIMIT_MODE_70.getRefreshRate(),
+ /*expectedAppRequestedRefreshRate*/ LIMIT_MODE_70.getRefreshRate(),
+ /*votesWithPriorities*/ Map.of(
Vote.PRIORITY_APP_REQUEST_SIZE,
Vote.forSize(APP_MODE_65.getPhysicalWidth(),
APP_MODE_65.getPhysicalHeight()),
@@ -173,10 +180,12 @@ public class DisplayModeDirectorTest {
0, 0,
LIMIT_MODE_70.getPhysicalWidth(),
LIMIT_MODE_70.getPhysicalHeight(),
- 0, Float.POSITIVE_INFINITY)), false},
- {APP_MODE_65.getModeId(), APP_MODE_65.getRefreshRate(),
- APP_MODE_65.getRefreshRate(),
- Map.of(
+ 0, Float.POSITIVE_INFINITY)),
+ /*displayResolutionRangeVotingEnabled*/ false},
+ {/*expectedBaseModeId*/ APP_MODE_65.getModeId(),
+ /*expectedPhysicalRefreshRate*/ APP_MODE_65.getRefreshRate(),
+ /*expectedAppRequestedRefreshRate*/ APP_MODE_65.getRefreshRate(),
+ /*votesWithPriorities*/ Map.of(
Vote.PRIORITY_APP_REQUEST_SIZE,
Vote.forSize(APP_MODE_65.getPhysicalWidth(),
APP_MODE_65.getPhysicalHeight()),
@@ -187,7 +196,40 @@ public class DisplayModeDirectorTest {
0, 0,
LIMIT_MODE_70.getPhysicalWidth(),
LIMIT_MODE_70.getPhysicalHeight(),
- 0, Float.POSITIVE_INFINITY)), true}});
+ 0, Float.POSITIVE_INFINITY)),
+ /*displayResolutionRangeVotingEnabled*/ true},
+ {/*expectedBaseModeId*/ DEFAULT_MODE_75.getModeId(),
+ /*expectedPhysicalRefreshRate*/ APP_MODE_65.getRefreshRate(),
+ /*expectedAppRequestedRefreshRate*/ APP_MODE_HIGH_90.getRefreshRate(),
+ /*votesWithPriorities*/ Map.of(
+ Vote.PRIORITY_APP_REQUEST_SIZE,
+ Vote.forSize(APP_MODE_HIGH_90.getPhysicalWidth(),
+ APP_MODE_HIGH_90.getPhysicalHeight()),
+ Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ Vote.forBaseModeRefreshRate(APP_MODE_HIGH_90.getRefreshRate()),
+ Vote.PRIORITY_LOW_POWER_MODE,
+ Vote.forSizeAndPhysicalRefreshRatesRange(
+ 0, 0,
+ LIMIT_MODE_70.getPhysicalWidth(),
+ LIMIT_MODE_70.getPhysicalHeight(),
+ 0, APP_MODE_65.getRefreshRate())),
+ /*displayResolutionRangeVotingEnabled*/ false},
+ {/*expectedBaseModeId*/ DEFAULT_MODE_60.getModeId(), // Resolution == APP_MODE_65
+ /*expectedPhysicalRefreshRate*/ APP_MODE_65.getRefreshRate(),
+ /*expectedAppRequestedRefreshRate*/ APP_MODE_65.getRefreshRate(),
+ /*votesWithPriorities*/ Map.of(
+ Vote.PRIORITY_APP_REQUEST_SIZE,
+ Vote.forSize(APP_MODE_HIGH_90.getPhysicalWidth(),
+ APP_MODE_HIGH_90.getPhysicalHeight()),
+ Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ Vote.forBaseModeRefreshRate(APP_MODE_HIGH_90.getRefreshRate()),
+ Vote.PRIORITY_LOW_POWER_MODE,
+ Vote.forSizeAndPhysicalRefreshRatesRange(
+ 0, 0,
+ LIMIT_MODE_70.getPhysicalWidth(),
+ LIMIT_MODE_70.getPhysicalHeight(),
+ 0, APP_MODE_65.getRefreshRate())),
+ /*displayResolutionRangeVotingEnabled*/ true}});
final var res = new ArrayList<Object[]>(appRequestedSizeTestCases.size() * 2);
@@ -218,6 +260,8 @@ public class DisplayModeDirectorTest {
private static final boolean DEBUG = false;
private static final float FLOAT_TOLERANCE = 0.01f;
+ private static final Display.Mode DEFAULT_MODE_60 = new Display.Mode(
+ /*modeId=*/60, /*width=*/1900, /*height=*/1900, 60);
private static final Display.Mode APP_MODE_65 = new Display.Mode(
/*modeId=*/65, /*width=*/1900, /*height=*/1900, 65);
private static final Display.Mode LIMIT_MODE_70 = new Display.Mode(
@@ -227,8 +271,7 @@ public class DisplayModeDirectorTest {
private static final Display.Mode APP_MODE_HIGH_90 = new Display.Mode(
/*modeId=*/90, /*width=*/3000, /*height=*/3000, 90);
private static final Display.Mode[] TEST_MODES = new Display.Mode[] {
- new Display.Mode(
- /*modeId=*/60, /*width=*/1900, /*height=*/1900, 60),
+ DEFAULT_MODE_60,
APP_MODE_65,
LIMIT_MODE_70,
DEFAULT_MODE_75,
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java
index 596a3f3d0400..b3605ccfc25d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java
@@ -280,7 +280,9 @@ public class AsyncProcessStartTest {
0, 0);
// Sleep until timeout should have triggered
- SystemClock.sleep(ActivityManagerService.PROC_START_TIMEOUT + 1000);
+ if (wedge) {
+ SystemClock.sleep(ActivityManagerService.PROC_START_TIMEOUT + 1000);
+ }
return app;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 410ae35aa790..367e14b37180 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -228,7 +228,7 @@ public class BroadcastQueueTest {
LocalServices.removeServiceForTest(AlarmManagerInternal.class);
LocalServices.addService(AlarmManagerInternal.class, mAlarmManagerInt);
doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent();
- doNothing().when(mPackageManagerInt).setPackageStoppedState(any(), anyBoolean(), anyInt());
+ doNothing().when(mPackageManagerInt).notifyComponentUsed(any(), anyInt(), any(), any());
doAnswer((invocation) -> {
return getUidForPackage(invocation.getArgument(0));
}).when(mPackageManagerInt).getPackageUid(any(), anyLong(), eq(UserHandle.USER_SYSTEM));
@@ -1014,8 +1014,9 @@ public class BroadcastQueueTest {
eq(PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER));
// Confirm that we unstopped manifest receivers
- verify(mAms.mPackageManagerInt, atLeastOnce()).setPackageStoppedState(
- eq(receiverApp.info.packageName), eq(false), eq(UserHandle.USER_SYSTEM));
+ verify(mAms.mPackageManagerInt, atLeastOnce()).notifyComponentUsed(
+ eq(receiverApp.info.packageName), eq(UserHandle.USER_SYSTEM),
+ eq(callerApp.info.packageName), any());
}
// Confirm that we've reported expected usage events
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 76b41b70cdf4..c493f8479045 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -2629,7 +2629,7 @@ public class MockingOomAdjusterTests {
PROCESS_STATE_NONEXISTENT, PROCESS_STATE_NONEXISTENT,
0, 0, false, false, false, ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE,
false, false, false, hasShownUi, false, false, false, false, false, false, null,
- 0, 0, 0, true, 0, null, false);
+ 0, Long.MIN_VALUE, Long.MIN_VALUE, true, 0, null, false);
}
private ProcessRecord makeProcessRecord(ActivityManagerService service, int pid, int uid,
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/SmallAreaDetectionControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/SmallAreaDetectionControllerTest.java
index 1ce79a5b596b..05ac5b5720e6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/SmallAreaDetectionControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/SmallAreaDetectionControllerTest.java
@@ -16,8 +16,6 @@
package com.android.server.display;
-import static android.os.Process.INVALID_UID;
-
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
@@ -35,7 +33,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.LocalServices;
-import com.android.server.pm.UserManagerInternal;
+import com.android.server.pm.pkg.PackageStateInternal;
import org.junit.Before;
import org.junit.Rule;
@@ -55,7 +53,10 @@ public class SmallAreaDetectionControllerTest {
@Mock
private PackageManagerInternal mMockPackageManagerInternal;
@Mock
- private UserManagerInternal mMockUserManagerInternal;
+ private PackageStateInternal mMockPkgStateA;
+ @Mock
+ private PackageStateInternal mMockPkgStateB;
+
private SmallAreaDetectionController mSmallAreaDetectionController;
@@ -64,29 +65,18 @@ public class SmallAreaDetectionControllerTest {
private static final String PKG_NOT_INSTALLED = "com.not.installed";
private static final float THRESHOLD_A = 0.05f;
private static final float THRESHOLD_B = 0.07f;
- private static final int USER_1 = 110;
- private static final int USER_2 = 111;
- private static final int UID_A_1 = 11011111;
- private static final int UID_A_2 = 11111111;
- private static final int UID_B_1 = 11022222;
- private static final int UID_B_2 = 11122222;
+ private static final int APP_ID_A = 11111;
+ private static final int APP_ID_B = 22222;
@Before
public void setup() {
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, mMockPackageManagerInternal);
- LocalServices.removeServiceForTest(UserManagerInternal.class);
- LocalServices.addService(UserManagerInternal.class, mMockUserManagerInternal);
-
- when(mMockUserManagerInternal.getUserIds()).thenReturn(new int[]{USER_1, USER_2});
- when(mMockPackageManagerInternal.getPackageUid(PKG_A, 0, USER_1)).thenReturn(UID_A_1);
- when(mMockPackageManagerInternal.getPackageUid(PKG_A, 0, USER_2)).thenReturn(UID_A_2);
- when(mMockPackageManagerInternal.getPackageUid(PKG_B, 0, USER_1)).thenReturn(UID_B_1);
- when(mMockPackageManagerInternal.getPackageUid(PKG_B, 0, USER_2)).thenReturn(UID_B_2);
- when(mMockPackageManagerInternal.getPackageUid(PKG_NOT_INSTALLED, 0, USER_1)).thenReturn(
- INVALID_UID);
- when(mMockPackageManagerInternal.getPackageUid(PKG_NOT_INSTALLED, 0, USER_2)).thenReturn(
- INVALID_UID);
+
+ when(mMockPackageManagerInternal.getPackageStateInternal(PKG_A)).thenReturn(mMockPkgStateA);
+ when(mMockPackageManagerInternal.getPackageStateInternal(PKG_B)).thenReturn(mMockPkgStateB);
+ when(mMockPkgStateA.getAppId()).thenReturn(APP_ID_A);
+ when(mMockPkgStateB.getAppId()).thenReturn(APP_ID_B);
mSmallAreaDetectionController = spy(new SmallAreaDetectionController(
new ContextWrapper(ApplicationProvider.getApplicationContext()),
@@ -99,9 +89,9 @@ public class SmallAreaDetectionControllerTest {
final String property = PKG_A + ":" + THRESHOLD_A + "," + PKG_B + ":" + THRESHOLD_B;
mSmallAreaDetectionController.updateAllowlist(property);
- final int[] resultUidArray = {UID_A_1, UID_B_1, UID_A_2, UID_B_2};
- final float[] resultThresholdArray = {THRESHOLD_A, THRESHOLD_B, THRESHOLD_A, THRESHOLD_B};
- verify(mSmallAreaDetectionController).updateSmallAreaDetection(eq(resultUidArray),
+ final int[] resultAppIdArray = {APP_ID_A, APP_ID_B};
+ final float[] resultThresholdArray = {THRESHOLD_A, THRESHOLD_B};
+ verify(mSmallAreaDetectionController).updateSmallAreaDetection(eq(resultAppIdArray),
eq(resultThresholdArray));
}
@@ -110,9 +100,9 @@ public class SmallAreaDetectionControllerTest {
final String property = PKG_A + "," + PKG_B + ":" + THRESHOLD_B;
mSmallAreaDetectionController.updateAllowlist(property);
- final int[] resultUidArray = {UID_B_1, UID_B_2};
- final float[] resultThresholdArray = {THRESHOLD_B, THRESHOLD_B};
- verify(mSmallAreaDetectionController).updateSmallAreaDetection(eq(resultUidArray),
+ final int[] resultAppIdArray = {APP_ID_B};
+ final float[] resultThresholdArray = {THRESHOLD_B};
+ verify(mSmallAreaDetectionController).updateSmallAreaDetection(eq(resultAppIdArray),
eq(resultThresholdArray));
}
@@ -122,9 +112,9 @@ public class SmallAreaDetectionControllerTest {
PKG_A + ":" + THRESHOLD_A + "," + PKG_NOT_INSTALLED + ":" + THRESHOLD_B;
mSmallAreaDetectionController.updateAllowlist(property);
- final int[] resultUidArray = {UID_A_1, UID_A_2};
- final float[] resultThresholdArray = {THRESHOLD_A, THRESHOLD_A};
- verify(mSmallAreaDetectionController).updateSmallAreaDetection(eq(resultUidArray),
+ final int[] resultAppIdArray = {APP_ID_A};
+ final float[] resultThresholdArray = {THRESHOLD_A};
+ verify(mSmallAreaDetectionController).updateSmallAreaDetection(eq(resultAppIdArray),
eq(resultThresholdArray));
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
index eb50556821eb..610ea903767e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
@@ -35,6 +35,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
@@ -427,6 +428,7 @@ public class PackageArchiverTest {
for (LauncherActivityInfo mainActivity : createLauncherActivities()) {
ArchiveState.ArchiveActivityInfo activityInfo = new ArchiveState.ArchiveActivityInfo(
mainActivity.getLabel().toString(),
+ mainActivity.getComponentName(),
ICON_PATH, null);
activityInfos.add(activityInfo);
}
@@ -437,9 +439,11 @@ public class PackageArchiverTest {
ActivityInfo activityInfo = mock(ActivityInfo.class);
LauncherActivityInfo activity1 = mock(LauncherActivityInfo.class);
when(activity1.getLabel()).thenReturn("activity1");
+ when(activity1.getComponentName()).thenReturn(new ComponentName("pkg1", "class1"));
when(activity1.getActivityInfo()).thenReturn(activityInfo);
LauncherActivityInfo activity2 = mock(LauncherActivityInfo.class);
when(activity2.getLabel()).thenReturn("activity2");
+ when(activity2.getComponentName()).thenReturn(new ComponentName("pkg2", "class2"));
when(activity2.getActivityInfo()).thenReturn(activityInfo);
return List.of(activity1, activity2);
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index cf315a4e0b9a..b9e45bab7ab3 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -16,6 +16,7 @@
package com.android.server.accessibility;
+import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
@@ -68,6 +69,7 @@ import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
import android.testing.TestableContext;
+import android.util.ArraySet;
import android.view.Display;
import android.view.DisplayAdjustments;
import android.view.DisplayInfo;
@@ -600,6 +602,74 @@ public class AccessibilityManagerServiceTest {
}
@Test
+ public void testPackagesForceStopped_disablesRelevantService() {
+ final AccessibilityServiceInfo info_a = new AccessibilityServiceInfo();
+ info_a.setComponentName(COMPONENT_NAME);
+ final AccessibilityServiceInfo info_b = new AccessibilityServiceInfo();
+ info_b.setComponentName(new ComponentName("package", "class"));
+
+ AccessibilityUserState userState = mA11yms.getCurrentUserState();
+ userState.mInstalledServices.clear();
+ userState.mInstalledServices.add(info_a);
+ userState.mInstalledServices.add(info_b);
+ userState.mEnabledServices.clear();
+ userState.mEnabledServices.add(info_a.getComponentName());
+ userState.mEnabledServices.add(info_b.getComponentName());
+
+ synchronized (mA11yms.getLock()) {
+ mA11yms.onPackagesForceStoppedLocked(
+ new String[]{info_a.getComponentName().getPackageName()}, userState);
+ }
+
+ //Assert user state change
+ userState = mA11yms.getCurrentUserState();
+ assertThat(userState.mEnabledServices).containsExactly(info_b.getComponentName());
+ //Assert setting change
+ final Set<ComponentName> componentsFromSetting = new ArraySet<>();
+ mA11yms.readComponentNamesFromSettingLocked(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+ userState.mUserId, componentsFromSetting);
+ assertThat(componentsFromSetting).containsExactly(info_b.getComponentName());
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DISABLE_CONTINUOUS_SHORTCUT_ON_FORCE_STOP)
+ public void testPackagesForceStopped_fromContinuousService_removesButtonTarget() {
+ final AccessibilityServiceInfo info_a = new AccessibilityServiceInfo();
+ info_a.setComponentName(COMPONENT_NAME);
+ info_a.flags = FLAG_REQUEST_ACCESSIBILITY_BUTTON;
+ final AccessibilityServiceInfo info_b = new AccessibilityServiceInfo();
+ info_b.setComponentName(new ComponentName("package", "class"));
+
+ AccessibilityUserState userState = mA11yms.getCurrentUserState();
+ userState.mInstalledServices.clear();
+ userState.mInstalledServices.add(info_a);
+ userState.mInstalledServices.add(info_b);
+ userState.mAccessibilityButtonTargets.clear();
+ userState.mAccessibilityButtonTargets.add(info_a.getComponentName().flattenToString());
+ userState.mAccessibilityButtonTargets.add(info_b.getComponentName().flattenToString());
+
+ // despite force stopping both packages, only the first service has the relevant flag,
+ // so only the first should be removed.
+ synchronized (mA11yms.getLock()) {
+ mA11yms.onPackagesForceStoppedLocked(
+ new String[]{
+ info_a.getComponentName().getPackageName(),
+ info_b.getComponentName().getPackageName()},
+ userState);
+ }
+
+ //Assert user state change
+ userState = mA11yms.getCurrentUserState();
+ assertThat(userState.mAccessibilityButtonTargets).containsExactly(
+ info_b.getComponentName().flattenToString());
+ //Assert setting change
+ final Set<String> targetsFromSetting = new ArraySet<>();
+ mA11yms.readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
+ userState.mUserId, str -> str, targetsFromSetting);
+ assertThat(targetsFromSetting).containsExactly(info_b.getComponentName().flattenToString());
+ }
+
+ @Test
@RequiresFlagsDisabled(Flags.FLAG_SCAN_PACKAGES_WITHOUT_LOCK)
// Test old behavior to validate lock detection for the old (locked access) case.
public void testPackageMonitorScanPackages_scansWhileHoldingLock() {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
index 63281b77ade7..71007f53f0e1 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
@@ -155,7 +155,7 @@ public class AccessibilityUserStateTest {
mUserState.mAccessibilityButtonTargets.add(COMPONENT_NAME.flattenToString());
mUserState.setTargetAssignedToAccessibilityButton(COMPONENT_NAME.flattenToString());
mUserState.setTouchExplorationEnabledLocked(true);
- mUserState.setDisplayMagnificationEnabledLocked(true);
+ mUserState.setMagnificationSingleFingerTripleTapEnabledLocked(true);
mUserState.setAutoclickEnabledLocked(true);
mUserState.setUserNonInteractiveUiTimeoutLocked(30);
mUserState.setUserInteractiveUiTimeoutLocked(30);
@@ -177,7 +177,7 @@ public class AccessibilityUserStateTest {
assertTrue(mUserState.mAccessibilityButtonTargets.isEmpty());
assertNull(mUserState.getTargetAssignedToAccessibilityButton());
assertFalse(mUserState.isTouchExplorationEnabledLocked());
- assertFalse(mUserState.isDisplayMagnificationEnabledLocked());
+ assertFalse(mUserState.isMagnificationSingleFingerTripleTapEnabledLocked());
assertFalse(mUserState.isAutoclickEnabledLocked());
assertEquals(0, mUserState.getUserNonInteractiveUiTimeoutLocked());
assertEquals(0, mUserState.getUserInteractiveUiTimeoutLocked());
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index e8cbcf9a6874..a3d415e4918f 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -337,11 +337,7 @@ public class VirtualDeviceManagerServiceTest {
LocalServices.removeServiceForTest(DisplayManagerInternal.class);
LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
- mSetFlagsRule.disableFlags(Flags.FLAG_VDM_PUBLIC_APIS);
- mSetFlagsRule.disableFlags(Flags.FLAG_DYNAMIC_POLICY);
- mSetFlagsRule.disableFlags(Flags.FLAG_STREAM_PERMISSIONS);
- mSetFlagsRule.disableFlags(Flags.FLAG_VDM_CUSTOM_HOME);
- mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_NATIVE_VDM);
+ mSetFlagsRule.initAllFlagsToReleaseConfigDefault();
doReturn(true).when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt());
doNothing().when(mInputManagerInternalMock).setPointerAcceleration(anyFloat(), anyInt());
diff --git a/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
index 12d6161eb718..5cc84b197e03 100644
--- a/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
@@ -59,6 +59,7 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.util.Collections;
import java.util.List;
/**
@@ -103,6 +104,10 @@ public class ContentCaptureManagerServiceTest {
private boolean mDevCfgEnableContentProtectionReceiver;
+ private List<List<String>> mDevCfgContentProtectionRequiredGroups = List.of(List.of("a"));
+
+ private List<List<String>> mDevCfgContentProtectionOptionalGroups = Collections.emptyList();
+
private int mContentProtectionBlocklistManagersCreated;
private int mContentProtectionServiceInfosCreated;
@@ -374,7 +379,21 @@ public class ContentCaptureManagerServiceTest {
}
@Test
- public void isContentProtectionReceiverEnabled_withoutManagers() {
+ public void isContentProtectionReceiverEnabled_true() {
+ when(mMockContentProtectionConsentManager.isConsentGranted(USER_ID)).thenReturn(true);
+ when(mMockContentProtectionBlocklistManager.isAllowed(PACKAGE_NAME)).thenReturn(true);
+ mDevCfgEnableContentProtectionReceiver = true;
+ mContentCaptureManagerService = new TestContentCaptureManagerService();
+
+ boolean actual =
+ mContentCaptureManagerService.mGlobalContentCaptureOptions.isWhitelisted(
+ USER_ID, PACKAGE_NAME);
+
+ assertThat(actual).isTrue();
+ }
+
+ @Test
+ public void isContentProtectionReceiverEnabled_false_withoutManagers() {
boolean actual =
mContentCaptureManagerService.mGlobalContentCaptureOptions.isWhitelisted(
USER_ID, PACKAGE_NAME);
@@ -385,7 +404,7 @@ public class ContentCaptureManagerServiceTest {
}
@Test
- public void isContentProtectionReceiverEnabled_disabledWithFlag() {
+ public void isContentProtectionReceiverEnabled_false_disabledWithFlag() {
mDevCfgEnableContentProtectionReceiver = true;
mContentCaptureManagerService = new TestContentCaptureManagerService();
mContentCaptureManagerService.mDevCfgEnableContentProtectionReceiver = false;
@@ -400,6 +419,22 @@ public class ContentCaptureManagerServiceTest {
}
@Test
+ public void isContentProtectionReceiverEnabled_false_emptyGroups() {
+ mDevCfgEnableContentProtectionReceiver = true;
+ mDevCfgContentProtectionRequiredGroups = Collections.emptyList();
+ mDevCfgContentProtectionOptionalGroups = Collections.emptyList();
+ mContentCaptureManagerService = new TestContentCaptureManagerService();
+
+ boolean actual =
+ mContentCaptureManagerService.mGlobalContentCaptureOptions.isWhitelisted(
+ USER_ID, PACKAGE_NAME);
+
+ assertThat(actual).isFalse();
+ verify(mMockContentProtectionConsentManager, never()).isConsentGranted(anyInt());
+ verify(mMockContentProtectionBlocklistManager, never()).isAllowed(anyString());
+ }
+
+ @Test
public void onLoginDetected_disabledAfterConstructor() {
mDevCfgEnableContentProtectionReceiver = true;
mContentCaptureManagerService = new TestContentCaptureManagerService();
@@ -525,6 +560,10 @@ public class ContentCaptureManagerServiceTest {
super(sContext);
this.mDevCfgEnableContentProtectionReceiver =
ContentCaptureManagerServiceTest.this.mDevCfgEnableContentProtectionReceiver;
+ this.mDevCfgContentProtectionRequiredGroups =
+ ContentCaptureManagerServiceTest.this.mDevCfgContentProtectionRequiredGroups;
+ this.mDevCfgContentProtectionOptionalGroups =
+ ContentCaptureManagerServiceTest.this.mDevCfgContentProtectionOptionalGroups;
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionSessionIdGeneratorTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionSessionIdGeneratorTest.java
new file mode 100644
index 000000000000..07cdf4df47ae
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionSessionIdGeneratorTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.media.projection;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+/**
+ * Tests for the {@link MediaProjectionSessionIdGenerator} class.
+ *
+ * <p>Build/Install/Run: atest FrameworksServicesTests:MediaProjectionSessionIdGeneratorTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class MediaProjectionSessionIdGeneratorTest {
+
+ private static final String TEST_PREFS_FILE = "media-projection-session-id-test";
+
+ private final Context mContext =
+ InstrumentationRegistry.getInstrumentation().getTargetContext();
+ private final File mSharedPreferencesFile = new File(mContext.getCacheDir(), TEST_PREFS_FILE);
+ private final SharedPreferences mSharedPreferences = createSharePreferences();
+ private final MediaProjectionSessionIdGenerator mGenerator =
+ createGenerator(mSharedPreferences);
+
+ @Before
+ public void setUp() {
+ mSharedPreferences.edit().clear().commit();
+ }
+
+ @After
+ public void tearDown() {
+ mSharedPreferences.edit().clear().commit();
+ mSharedPreferencesFile.delete();
+ }
+
+ @Test
+ public void getCurrentSessionId_byDefault_returns0() {
+ assertThat(mGenerator.getCurrentSessionId()).isEqualTo(0);
+ }
+
+ @Test
+ public void getCurrentSessionId_multipleTimes_returnsSameValue() {
+ assertThat(mGenerator.getCurrentSessionId()).isEqualTo(0);
+ assertThat(mGenerator.getCurrentSessionId()).isEqualTo(0);
+ assertThat(mGenerator.getCurrentSessionId()).isEqualTo(0);
+ }
+
+ @Test
+ public void createAndGetNewSessionId_returnsIncrementedId() {
+ int previousValue = mGenerator.getCurrentSessionId();
+
+ int newValue = mGenerator.createAndGetNewSessionId();
+
+ assertThat(newValue).isEqualTo(previousValue + 1);
+ }
+
+ @Test
+ public void createAndGetNewSessionId_persistsNewValue() {
+ int newValue = mGenerator.createAndGetNewSessionId();
+
+ MediaProjectionSessionIdGenerator newInstance = createGenerator(createSharePreferences());
+
+ assertThat(newInstance.getCurrentSessionId()).isEqualTo(newValue);
+ }
+
+ private SharedPreferences createSharePreferences() {
+ return mContext.getSharedPreferences(mSharedPreferencesFile, Context.MODE_PRIVATE);
+ }
+
+ private MediaProjectionSessionIdGenerator createGenerator(SharedPreferences sharedPreferences) {
+ return new MediaProjectionSessionIdGenerator(sharedPreferences);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionTimestampStoreTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionTimestampStoreTest.java
new file mode 100644
index 000000000000..7723541dc160
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionTimestampStoreTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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.media.projection;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.InstantSource;
+
+/**
+ * Tests for the {@link MediaProjectionTimestampStore} class.
+ *
+ * <p>Build/Install/Run: atest FrameworksServicesTests:MediaProjectionTimestampStoreTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class MediaProjectionTimestampStoreTest {
+
+ private static final String TEST_PREFS_FILE = "media-projection-timestamp-test";
+
+ private final Context mContext =
+ InstrumentationRegistry.getInstrumentation().getTargetContext();
+ private final File mSharedPreferencesFile = new File(mContext.getCacheDir(), TEST_PREFS_FILE);
+ private final SharedPreferences mSharedPreferences = createSharePreferences();
+
+ private Instant mCurrentInstant = Instant.ofEpochMilli(0);
+
+ private final InstantSource mInstantSource = () -> mCurrentInstant;
+ private final MediaProjectionTimestampStore mStore =
+ new MediaProjectionTimestampStore(mSharedPreferences, mInstantSource);
+
+ @Before
+ public void setUp() {
+ mSharedPreferences.edit().clear().commit();
+ }
+
+ @After
+ public void tearDown() {
+ mSharedPreferences.edit().clear().commit();
+ mSharedPreferencesFile.delete();
+ }
+
+ @Test
+ public void timeSinceLastActiveSession_byDefault_returnsNull() {
+ assertThat(mStore.timeSinceLastActiveSession()).isNull();
+ }
+
+ @Test
+ public void timeSinceLastActiveSession_returnsBasedOnLastActiveSessionEnded() {
+ mCurrentInstant = Instant.ofEpochMilli(0);
+ mStore.registerActiveSessionEnded();
+
+ mCurrentInstant = mCurrentInstant.plusSeconds(60);
+
+ assertThat(mStore.timeSinceLastActiveSession()).isEqualTo(Duration.ofSeconds(60));
+ }
+
+ @Test
+ public void timeSinceLastActiveSession_valueIsPersisted() {
+ mCurrentInstant = Instant.ofEpochMilli(0);
+ mStore.registerActiveSessionEnded();
+
+ MediaProjectionTimestampStore newStoreInstance =
+ new MediaProjectionTimestampStore(createSharePreferences(), mInstantSource);
+ mCurrentInstant = mCurrentInstant.plusSeconds(123);
+
+ assertThat(newStoreInstance.timeSinceLastActiveSession())
+ .isEqualTo(Duration.ofSeconds(123));
+ }
+
+ private SharedPreferences createSharePreferences() {
+ return mContext.getSharedPreferences(mSharedPreferencesFile, Context.MODE_PRIVATE);
+ }
+}
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 3d4b4a62e5ac..75d012a8e1f2 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -7769,6 +7769,74 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
assertEquals(NotificationManagerService.MAX_PACKAGE_TOASTS, mService.mToastQueue.size());
}
+ @Test
+ public void testPrioritizeSystemToasts() throws Exception {
+ // Insert non-system toasts
+ final String testPackage = "testPackageName";
+ assertEquals(0, mService.mToastQueue.size());
+ mService.isSystemUid = false;
+ mService.isSystemAppId = false;
+ setToastRateIsWithinQuota(true);
+ setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
+
+ // package is not suspended
+ when(mPackageManager.isPackageSuspendedForUser(testPackage, mUserId))
+ .thenReturn(false);
+
+ INotificationManager nmService = (INotificationManager) mService.mService;
+
+ // Enqueue maximum number of toasts for test package
+ for (int i = 0; i < NotificationManagerService.MAX_PACKAGE_TOASTS; i++) {
+ enqueueTextToast(testPackage, "Text");
+ }
+
+ // Enqueue system toast
+ final String testPackageSystem = "testPackageNameSystem";
+ mService.isSystemUid = true;
+ setIfPackageHasPermissionToAvoidToastRateLimiting(testPackageSystem, false);
+ when(mPackageManager.isPackageSuspendedForUser(testPackageSystem, mUserId))
+ .thenReturn(false);
+
+ enqueueToast(testPackageSystem, new TestableToastCallback());
+
+ // System toast is inserted at the front of the queue, behind current showing toast
+ assertEquals(testPackageSystem, mService.mToastQueue.get(1).pkg);
+ }
+
+ @Test
+ public void testPrioritizeSystemToasts_enqueueAfterExistingSystemToast() throws Exception {
+ // Insert system toasts
+ final String testPackageSystem1 = "testPackageNameSystem1";
+ assertEquals(0, mService.mToastQueue.size());
+ mService.isSystemUid = true;
+ setToastRateIsWithinQuota(true);
+ setIfPackageHasPermissionToAvoidToastRateLimiting(testPackageSystem1, false);
+
+ // package is not suspended
+ when(mPackageManager.isPackageSuspendedForUser(testPackageSystem1, mUserId))
+ .thenReturn(false);
+
+ INotificationManager nmService = (INotificationManager) mService.mService;
+
+ // Enqueue maximum number of toasts for test package
+ for (int i = 0; i < NotificationManagerService.MAX_PACKAGE_TOASTS; i++) {
+ enqueueTextToast(testPackageSystem1, "Text");
+ }
+
+ // Enqueue another system toast
+ final String testPackageSystem2 = "testPackageNameSystem2";
+ mService.isSystemUid = true;
+ setIfPackageHasPermissionToAvoidToastRateLimiting(testPackageSystem2, false);
+ when(mPackageManager.isPackageSuspendedForUser(testPackageSystem2, mUserId))
+ .thenReturn(false);
+
+ enqueueToast(testPackageSystem2, new TestableToastCallback());
+
+ // System toast is inserted at the back of the queue, after the other system toasts
+ assertEquals(testPackageSystem2,
+ mService.mToastQueue.get(mService.mToastQueue.size() - 1).pkg);
+ }
+
private void setAppInForegroundForToasts(int uid, boolean inForeground) {
int importance = (inForeground) ? IMPORTANCE_FOREGROUND : IMPORTANCE_NONE;
when(mActivityManager.getUidImportance(mUid)).thenReturn(importance);
diff --git a/services/tests/vibrator/Android.bp b/services/tests/vibrator/Android.bp
index 95441060f0e5..6f37967bf7f0 100644
--- a/services/tests/vibrator/Android.bp
+++ b/services/tests/vibrator/Android.bp
@@ -35,6 +35,7 @@ android_test {
"platform-test-annotations",
"service-permission.stubs.system_server",
"services.core",
+ "flag-junit",
],
platform_apis: true,
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
index 000355598281..3d0dca0de87e 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
@@ -16,20 +16,24 @@
package com.android.server.vibrator;
+import static android.os.VibrationAttributes.CATEGORY_KEYBOARD;
import static android.os.VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
import static android.os.VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF;
+import static android.os.VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE;
import static android.os.VibrationEffect.Composition.PRIMITIVE_CLICK;
import static android.os.VibrationEffect.Composition.PRIMITIVE_TICK;
+import static android.os.VibrationEffect.EFFECT_CLICK;
import static android.os.VibrationEffect.EFFECT_TEXTURE_TICK;
import static android.os.VibrationEffect.EFFECT_TICK;
import static android.view.HapticFeedbackConstants.CLOCK_TICK;
import static android.view.HapticFeedbackConstants.CONTEXT_CLICK;
+import static android.view.HapticFeedbackConstants.KEYBOARD_RELEASE;
+import static android.view.HapticFeedbackConstants.KEYBOARD_TAP;
import static android.view.HapticFeedbackConstants.SAFE_MODE_ENABLED;
-import static android.view.HapticFeedbackConstants.TEXT_HANDLE_MOVE;
import static android.view.HapticFeedbackConstants.SCROLL_ITEM_FOCUS;
import static android.view.HapticFeedbackConstants.SCROLL_LIMIT;
import static android.view.HapticFeedbackConstants.SCROLL_TICK;
-
+import static android.view.HapticFeedbackConstants.TEXT_HANDLE_MOVE;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -42,9 +46,10 @@ import android.hardware.vibrator.IVibrator;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.VibratorInfo;
+import android.os.vibrator.Flags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.util.AtomicFile;
import android.util.SparseArray;
-import android.view.flags.FeatureFlags;
import androidx.test.InstrumentationRegistry;
@@ -62,6 +67,8 @@ import java.io.FileOutputStream;
public class HapticFeedbackVibrationProviderTest {
@Rule public MockitoRule rule = MockitoJUnit.rule();
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private static final VibrationEffect PRIMITIVE_TICK_EFFECT =
VibrationEffect.startComposition().addPrimitive(PRIMITIVE_TICK, 0.2497f).compose();
private static final VibrationEffect PRIMITIVE_CLICK_EFFECT =
@@ -69,11 +76,15 @@ public class HapticFeedbackVibrationProviderTest {
private static final int[] SCROLL_FEEDBACK_CONSTANTS =
new int[] {SCROLL_ITEM_FOCUS, SCROLL_LIMIT, SCROLL_TICK};
+ private static final int[] KEYBOARD_FEEDBACK_CONSTANTS =
+ new int[] {KEYBOARD_TAP, KEYBOARD_RELEASE};
+
+ private static final float KEYBOARD_VIBRATION_FIXED_AMPLITUDE = 0.62f;
+
private Context mContext = InstrumentationRegistry.getContext();
private VibratorInfo mVibratorInfo = VibratorInfo.EMPTY_VIBRATOR_INFO;
@Mock private Resources mResourcesMock;
- @Mock private FeatureFlags mViewFeatureFlags;
@Test
public void testNonExistentCustomization_useDefault() throws Exception {
@@ -214,6 +225,62 @@ public class HapticFeedbackVibrationProviderTest {
}
@Test
+ public void testKeyboardHaptic_noFixedAmplitude_defaultVibrationReturned() {
+ mockVibratorPrimitiveSupport(PRIMITIVE_CLICK, PRIMITIVE_TICK);
+ SparseArray<VibrationEffect> customizations = new SparseArray<>();
+ customizations.put(KEYBOARD_TAP, PRIMITIVE_CLICK_EFFECT);
+ customizations.put(KEYBOARD_RELEASE, PRIMITIVE_TICK_EFFECT);
+
+ // Test with a customization available for `KEYBOARD_TAP` & `KEYBOARD_RELEASE`.
+ HapticFeedbackVibrationProvider hapticProvider = createProvider(customizations);
+
+ assertThat(hapticProvider.getVibrationForHapticFeedback(KEYBOARD_TAP))
+ .isEqualTo(PRIMITIVE_CLICK_EFFECT);
+ assertThat(hapticProvider.getVibrationForHapticFeedback(KEYBOARD_RELEASE))
+ .isEqualTo(PRIMITIVE_TICK_EFFECT);
+
+ // Test with no customization available for `KEYBOARD_TAP` & `KEYBOARD_RELEASE`.
+ hapticProvider = createProviderWithDefaultCustomizations();
+
+ assertThat(hapticProvider.getVibrationForHapticFeedback(KEYBOARD_TAP))
+ .isEqualTo(VibrationEffect.get(EFFECT_CLICK, true /* fallback */));
+ assertThat(hapticProvider.getVibrationForHapticFeedback(KEYBOARD_RELEASE))
+ .isEqualTo(VibrationEffect.get(EFFECT_TICK, false /* fallback */));
+ }
+
+ @Test
+ public void testKeyboardHaptic_fixAmplitude_keyboardCategoryOff_defaultVibrationReturned() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
+ mockVibratorPrimitiveSupport(PRIMITIVE_CLICK, PRIMITIVE_TICK);
+ mockKeyboardVibrationFixedAmplitude(KEYBOARD_VIBRATION_FIXED_AMPLITUDE);
+
+ HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+
+ assertThat(hapticProvider.getVibrationForHapticFeedback(KEYBOARD_TAP))
+ .isEqualTo(VibrationEffect.get(EFFECT_CLICK, true /* fallback */));
+ assertThat(hapticProvider.getVibrationForHapticFeedback(KEYBOARD_RELEASE))
+ .isEqualTo(VibrationEffect.get(EFFECT_TICK, false /* fallback */));
+ }
+
+ @Test
+ public void testKeyboardHaptic_fixAmplitude_keyboardCategoryOn_keyboardVibrationReturned() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
+ mockVibratorPrimitiveSupport(PRIMITIVE_CLICK, PRIMITIVE_TICK);
+ mockKeyboardVibrationFixedAmplitude(KEYBOARD_VIBRATION_FIXED_AMPLITUDE);
+
+ HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+
+ assertThat(hapticProvider.getVibrationForHapticFeedback(KEYBOARD_TAP))
+ .isEqualTo(VibrationEffect.startComposition()
+ .addPrimitive(PRIMITIVE_CLICK, KEYBOARD_VIBRATION_FIXED_AMPLITUDE)
+ .compose());
+ assertThat(hapticProvider.getVibrationForHapticFeedback(KEYBOARD_RELEASE))
+ .isEqualTo(VibrationEffect.startComposition()
+ .addPrimitive(PRIMITIVE_TICK, KEYBOARD_VIBRATION_FIXED_AMPLITUDE)
+ .compose());
+ }
+
+ @Test
public void testVibrationAttribute_forNotBypassingIntensitySettings() {
HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
@@ -235,7 +302,7 @@ public class HapticFeedbackVibrationProviderTest {
@Test
public void testVibrationAttribute_scrollFeedback_scrollApiFlagOn_bypassInterruptPolicy() {
- when(mViewFeatureFlags.scrollFeedbackApi()).thenReturn(true);
+ mSetFlagsRule.enableFlags(android.view.flags.Flags.FLAG_SCROLL_FEEDBACK_API);
HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
for (int effectId : SCROLL_FEEDBACK_CONSTANTS) {
@@ -248,7 +315,7 @@ public class HapticFeedbackVibrationProviderTest {
@Test
public void testVibrationAttribute_scrollFeedback_scrollApiFlagOff_noBypassInterruptPolicy() {
- when(mViewFeatureFlags.scrollFeedbackApi()).thenReturn(false);
+ mSetFlagsRule.disableFlags(android.view.flags.Flags.FLAG_SCROLL_FEEDBACK_API);
HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
for (int effectId : SCROLL_FEEDBACK_CONSTANTS) {
@@ -259,14 +326,71 @@ public class HapticFeedbackVibrationProviderTest {
}
}
+ @Test
+ public void testVibrationAttribute_keyboardCategoryOff_notUseKeyboardCategory() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
+ HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+
+ for (int effectId : KEYBOARD_FEEDBACK_CONSTANTS) {
+ VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
+ effectId, /* bypassVibrationIntensitySetting= */ false);
+ assertWithMessage("Expected no CATEGORY_KEYBOARD for effect " + effectId)
+ .that(attrs.getCategory()).isEqualTo(0);
+ }
+ }
+
+ @Test
+ public void testVibrationAttribute_keyboardCategoryOn_useKeyboardCategory() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
+ HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+
+ for (int effectId : KEYBOARD_FEEDBACK_CONSTANTS) {
+ VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
+ effectId, /* bypassVibrationIntensitySetting= */ false);
+ assertWithMessage("Expected CATEGORY_KEYBOARD for effect " + effectId)
+ .that(attrs.getCategory()).isEqualTo(CATEGORY_KEYBOARD);
+ }
+ }
+
+ @Test
+ public void testVibrationAttribute_noFixAmplitude_keyboardCategoryOn_noBypassIntensityScale() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
+ mockVibratorPrimitiveSupport(PRIMITIVE_CLICK, PRIMITIVE_TICK);
+ mockKeyboardVibrationFixedAmplitude(-1);
+ HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+
+ for (int effectId : KEYBOARD_FEEDBACK_CONSTANTS) {
+ VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
+ effectId, /* bypassVibrationIntensitySetting= */ false);
+ assertWithMessage("Expected no FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE for effect "
+ + effectId)
+ .that(attrs.isFlagSet(FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE)).isFalse();
+ }
+ }
+
+ @Test
+ public void testVibrationAttribute_fixAmplitude_keyboardCategoryOn_bypassIntensityScale() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
+ mockVibratorPrimitiveSupport(PRIMITIVE_CLICK, PRIMITIVE_TICK);
+ mockKeyboardVibrationFixedAmplitude(KEYBOARD_VIBRATION_FIXED_AMPLITUDE);
+ HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+
+ for (int effectId : KEYBOARD_FEEDBACK_CONSTANTS) {
+ VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
+ effectId, /* bypassVibrationIntensitySetting= */ false);
+ assertWithMessage("Expected FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE for effect "
+ + effectId)
+ .that(attrs.isFlagSet(FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE)).isTrue();
+ }
+ }
+
private HapticFeedbackVibrationProvider createProviderWithDefaultCustomizations() {
return createProvider(/* customizations= */ null);
}
private HapticFeedbackVibrationProvider createProvider(
SparseArray<VibrationEffect> customizations) {
- return new HapticFeedbackVibrationProvider(
- mResourcesMock, mVibratorInfo, customizations, mViewFeatureFlags);
+ return new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo, customizations);
}
private void mockVibratorPrimitiveSupport(int... supportedPrimitives) {
@@ -287,6 +411,11 @@ public class HapticFeedbackVibrationProviderTest {
.thenReturn(vibrationPattern);
}
+ private void mockKeyboardVibrationFixedAmplitude(float amplitude) {
+ when(mResourcesMock.getFloat(R.dimen.config_keyboardHapticFeedbackFixedAmplitude))
+ .thenReturn(amplitude);
+ }
+
private void setupCustomizationFile(String xml) throws Exception {
File file = new File(mContext.getCacheDir(), "test.xml");
file.createNewFile();
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
index 1ae096617dce..7a2bb5a90846 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -69,7 +69,11 @@ import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.test.TestLooper;
+import android.os.vibrator.Flags;
import android.os.vibrator.VibrationConfig;
+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.util.ArraySet;
import android.view.Display;
@@ -95,6 +99,9 @@ import java.util.Set;
public class VibrationSettingsTest {
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
private static final int UID = 1;
private static final int VIRTUAL_DISPLAY_ID = 1;
private static final String SYSUI_PACKAGE_NAME = "sysui";
@@ -606,6 +613,47 @@ public class VibrationSettingsTest {
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED)
+ public void shouldIgnoreVibration_withKeyboardSettingsOff_shouldIgnoreKeyboardVibration() {
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_MEDIUM);
+ setUserSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, 0);
+
+ // Keyboard touch ignored.
+ assertVibrationIgnoredForAttributes(
+ new VibrationAttributes.Builder()
+ .setUsage(USAGE_TOUCH)
+ .setCategory(VibrationAttributes.CATEGORY_KEYBOARD)
+ .build(),
+ Vibration.Status.IGNORED_FOR_SETTINGS);
+
+ // General touch and keyboard touch with bypass flag not ignored.
+ assertVibrationNotIgnoredForUsage(USAGE_TOUCH);
+ assertVibrationNotIgnoredForAttributes(
+ new VibrationAttributes.Builder()
+ .setUsage(USAGE_TOUCH)
+ .setCategory(VibrationAttributes.CATEGORY_KEYBOARD)
+ .setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
+ .build());
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED)
+ public void shouldIgnoreVibration_withKeyboardSettingsOn_shouldNotIgnoreKeyboardVibration() {
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
+ setUserSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, 1);
+
+ // General touch ignored.
+ assertVibrationIgnoredForUsage(USAGE_TOUCH, Vibration.Status.IGNORED_FOR_SETTINGS);
+
+ // Keyboard touch not ignored.
+ assertVibrationNotIgnoredForAttributes(
+ new VibrationAttributes.Builder()
+ .setUsage(USAGE_TOUCH)
+ .setCategory(VibrationAttributes.CATEGORY_KEYBOARD)
+ .build());
+ }
+
+ @Test
public void shouldIgnoreVibrationFromVirtualDisplays_displayNonVirtual_neverIgnored() {
// Vibrations from the primary display is never ignored regardless of the creation and
// removal of virtual displays and of the changes of apps running on virtual displays.
@@ -895,6 +943,14 @@ public class VibrationSettingsTest {
mVibrationSettings.shouldIgnoreVibration(callerInfo));
}
+ private void assertVibrationIgnoredForAttributes(VibrationAttributes attrs,
+ Vibration.Status expectedStatus) {
+ Vibration.CallerInfo callerInfo = new Vibration.CallerInfo(attrs, UID,
+ Display.DEFAULT_DISPLAY, null, null);
+ assertEquals(errorMessageForAttributes(attrs), expectedStatus,
+ mVibrationSettings.shouldIgnoreVibration(callerInfo));
+ }
+
private void assertVibrationNotIgnoredForUsage(@VibrationAttributes.Usage int usage) {
assertVibrationNotIgnoredForUsageAndFlags(usage, /* flags= */ 0);
}
@@ -919,10 +975,21 @@ public class VibrationSettingsTest {
mVibrationSettings.shouldIgnoreVibration(callerInfo));
}
+ private void assertVibrationNotIgnoredForAttributes(VibrationAttributes attrs) {
+ Vibration.CallerInfo callerInfo = new Vibration.CallerInfo(attrs, UID,
+ Display.DEFAULT_DISPLAY, null, null);
+ assertNull(errorMessageForAttributes(attrs),
+ mVibrationSettings.shouldIgnoreVibration(callerInfo));
+ }
+
private String errorMessageForUsage(int usage) {
return "Error for usage " + VibrationAttributes.usageToString(usage);
}
+ private String errorMessageForAttributes(VibrationAttributes attrs) {
+ return "Error for attributes " + attrs;
+ }
+
private void setDefaultIntensity(@Vibrator.VibrationIntensity int intensity) {
when(mVibrationConfigMock.getDefaultVibrationIntensity(anyInt())).thenReturn(intensity);
}
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 40e0e84dca59..3dfaed69dea6 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -82,6 +82,7 @@ import android.os.vibrator.PrimitiveSegment;
import android.os.vibrator.StepSegment;
import android.os.vibrator.VibrationConfig;
import android.os.vibrator.VibrationEffectSegment;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -89,7 +90,7 @@ import android.util.SparseBooleanArray;
import android.view.Display;
import android.view.HapticFeedbackConstants;
import android.view.InputDevice;
-import android.view.flags.FeatureFlags;
+import android.view.flags.Flags;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.FlakyTest;
@@ -155,6 +156,8 @@ public class VibratorManagerServiceTest {
@Rule
public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Mock
private VibratorManagerService.NativeWrapper mNativeWrapperMock;
@Mock
@@ -175,8 +178,6 @@ public class VibratorManagerServiceTest {
private VirtualDeviceManagerInternal mVirtualDeviceManagerInternalMock;
@Mock
private AudioManager mAudioManagerMock;
- @Mock
- private FeatureFlags mViewFeatureFlags;
private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>();
@@ -326,8 +327,7 @@ public class VibratorManagerServiceTest {
HapticFeedbackVibrationProvider createHapticFeedbackVibrationProvider(
Resources resources, VibratorInfo vibratorInfo) {
return new HapticFeedbackVibrationProvider(
- resources, vibratorInfo, mHapticFeedbackVibrationMap,
- mViewFeatureFlags);
+ resources, vibratorInfo, mHapticFeedbackVibrationMap);
}
});
return mService;
@@ -1354,7 +1354,7 @@ public class VibratorManagerServiceTest {
denyPermission(android.Manifest.permission.MODIFY_PHONE_STATE);
denyPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING);
// Flag override to enable the scroll feedack constants to bypass interruption policies.
- when(mViewFeatureFlags.scrollFeedbackApi()).thenReturn(true);
+ mSetFlagsRule.enableFlags(Flags.FLAG_SCROLL_FEEDBACK_API);
mHapticFeedbackVibrationMap.put(
HapticFeedbackConstants.SCROLL_TICK,
VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index 8e7ba7030e82..dd7dec0bdb2b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -130,12 +130,22 @@ public class BackNavigationControllerTests extends WindowTestsBase {
// verify if back animation would start.
assertTrue("Animation scheduled", backNavigationInfo.isPrepareRemoteAnimation());
- // reset drawing status
+ // reset drawing status to test translucent activity
backNavigationInfo.onBackNavigationFinished(false);
mBackNavigationController.clearBackAnimations();
- topTask.forAllWindows(w -> {
- makeWindowVisibleAndDrawn(w);
- }, true);
+ final ActivityRecord topActivity = topTask.getTopMostActivity();
+ makeWindowVisibleAndDrawn(topActivity.findMainWindow());
+ // simulate translucent
+ topActivity.setOccludesParent(false);
+ backNavigationInfo = startBackNavigation();
+ assertThat(typeToString(backNavigationInfo.getType()))
+ .isEqualTo(typeToString(BackNavigationInfo.TYPE_CALLBACK));
+
+ // reset drawing status to test keyguard occludes
+ topActivity.setOccludesParent(true);
+ backNavigationInfo.onBackNavigationFinished(false);
+ mBackNavigationController.clearBackAnimations();
+ makeWindowVisibleAndDrawn(topActivity.findMainWindow());
setupKeyguardOccluded();
backNavigationInfo = startBackNavigation();
assertThat(typeToString(backNavigationInfo.getType()))
@@ -201,9 +211,7 @@ public class BackNavigationControllerTests extends WindowTestsBase {
// reset drawing status
backNavigationInfo.onBackNavigationFinished(false);
mBackNavigationController.clearBackAnimations();
- testCase.recordFront.forAllWindows(w -> {
- makeWindowVisibleAndDrawn(w);
- }, true);
+ makeWindowVisibleAndDrawn(testCase.recordFront.findMainWindow());
setupKeyguardOccluded();
backNavigationInfo = startBackNavigation();
assertThat(typeToString(backNavigationInfo.getType()))
diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index 84d42d42f834..6a738befba9a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -163,8 +163,8 @@ public class DimmerTests extends WindowTestsBase {
public void testUpdateDimsAppliesCrop() {
mHost.addChild(mChild, 0);
- final float alpha = 0.8f;
- mDimmer.dimAbove(mChild, alpha);
+ mDimmer.adjustAppearance(mChild, 1, 1);
+ mDimmer.adjustRelativeLayer(mChild, -1);
int width = 100;
int height = 300;
@@ -176,42 +176,13 @@ public class DimmerTests extends WindowTestsBase {
}
@Test
- public void testDimAboveWithChildCreatesSurfaceAboveChild_Smooth() {
- assumeTrue(Flags.dimmerRefactor());
- final float alpha = 0.8f;
- mHost.addChild(mChild, 0);
- mDimmer.dimAbove(mChild, alpha);
- SurfaceControl dimLayer = mDimmer.getDimLayer();
-
- assertNotNull("Dimmer should have created a surface", dimLayer);
-
- mDimmer.updateDims(mTransaction);
- verify(sTestAnimation).startAnimation(eq(dimLayer), eq(mTransaction),
- anyInt(), any(SurfaceAnimator.OnAnimationFinishedCallback.class));
- verify(mTransaction).setRelativeLayer(dimLayer, mChild.mControl, 1);
- verify(mTransaction, lastCall()).setAlpha(dimLayer, alpha);
- }
-
- @Test
- public void testDimAboveWithChildCreatesSurfaceAboveChild_Legacy() {
- assumeFalse(Flags.dimmerRefactor());
- final float alpha = 0.8f;
- mHost.addChild(mChild, 0);
- mDimmer.dimAbove(mChild, alpha);
- SurfaceControl dimLayer = mDimmer.getDimLayer();
-
- assertNotNull("Dimmer should have created a surface", dimLayer);
-
- verify(mHost.getPendingTransaction()).setAlpha(dimLayer, alpha);
- verify(mHost.getPendingTransaction()).setRelativeLayer(dimLayer, mChild.mControl, 1);
- }
-
- @Test
public void testDimBelowWithChildSurfaceCreatesSurfaceBelowChild_Smooth() {
assumeTrue(Flags.dimmerRefactor());
final float alpha = 0.7f;
+ final int blur = 50;
mHost.addChild(mChild, 0);
- mDimmer.dimBelow(mChild, alpha, 50);
+ mDimmer.adjustAppearance(mChild, alpha, blur);
+ mDimmer.adjustRelativeLayer(mChild, -1);
SurfaceControl dimLayer = mDimmer.getDimLayer();
assertNotNull("Dimmer should have created a surface", dimLayer);
@@ -221,7 +192,7 @@ public class DimmerTests extends WindowTestsBase {
anyInt(), any(SurfaceAnimator.OnAnimationFinishedCallback.class));
verify(mTransaction).setRelativeLayer(dimLayer, mChild.mControl, -1);
verify(mTransaction, lastCall()).setAlpha(dimLayer, alpha);
- verify(mTransaction).setBackgroundBlurRadius(dimLayer, 50);
+ verify(mTransaction).setBackgroundBlurRadius(dimLayer, blur);
}
@Test
@@ -229,7 +200,8 @@ public class DimmerTests extends WindowTestsBase {
assumeFalse(Flags.dimmerRefactor());
final float alpha = 0.7f;
mHost.addChild(mChild, 0);
- mDimmer.dimBelow(mChild, alpha, 50);
+ mDimmer.adjustAppearance(mChild, alpha, 20);
+ mDimmer.adjustRelativeLayer(mChild, -1);
SurfaceControl dimLayer = mDimmer.getDimLayer();
assertNotNull("Dimmer should have created a surface", dimLayer);
@@ -244,12 +216,15 @@ public class DimmerTests extends WindowTestsBase {
mHost.addChild(mChild, 0);
final float alpha = 0.8f;
+ final int blur = 50;
// Dim once
- mDimmer.dimBelow(mChild, alpha, 0);
+ mDimmer.adjustAppearance(mChild, alpha, blur);
+ mDimmer.adjustRelativeLayer(mChild, -1);
SurfaceControl dimLayer = mDimmer.getDimLayer();
mDimmer.updateDims(mTransaction);
// Reset, and don't dim
mDimmer.resetDimStates();
+ mDimmer.adjustRelativeLayer(mChild, -1);
mDimmer.updateDims(mTransaction);
verify(mTransaction).show(dimLayer);
verify(mTransaction).remove(dimLayer);
@@ -261,7 +236,8 @@ public class DimmerTests extends WindowTestsBase {
mHost.addChild(mChild, 0);
final float alpha = 0.8f;
- mDimmer.dimAbove(mChild, alpha);
+ mDimmer.adjustAppearance(mChild, alpha, 20);
+ mDimmer.adjustRelativeLayer(mChild, -1);
SurfaceControl dimLayer = mDimmer.getDimLayer();
mDimmer.resetDimStates();
@@ -278,13 +254,16 @@ public class DimmerTests extends WindowTestsBase {
mHost.addChild(mChild, 0);
final float alpha = 0.8f;
+ final int blur = 20;
// Dim once
- mDimmer.dimBelow(mChild, alpha, 0);
+ mDimmer.adjustAppearance(mChild, alpha, blur);
+ mDimmer.adjustRelativeLayer(mChild, -1);
SurfaceControl dimLayer = mDimmer.getDimLayer();
mDimmer.updateDims(mTransaction);
// Reset and dim again
mDimmer.resetDimStates();
- mDimmer.dimAbove(mChild, alpha);
+ mDimmer.adjustAppearance(mChild, alpha, blur);
+ mDimmer.adjustRelativeLayer(mChild, -1);
mDimmer.updateDims(mTransaction);
verify(mTransaction).show(dimLayer);
verify(mTransaction, never()).remove(dimLayer);
@@ -294,7 +273,8 @@ public class DimmerTests extends WindowTestsBase {
public void testDimUpdateWhileDimming() {
mHost.addChild(mChild, 0);
final float alpha = 0.8f;
- mDimmer.dimAbove(mChild, alpha);
+ mDimmer.adjustAppearance(mChild, alpha, 20);
+ mDimmer.adjustRelativeLayer(mChild, -1);
final Rect bounds = mDimmer.getDimBounds();
SurfaceControl dimLayer = mDimmer.getDimLayer();
@@ -314,7 +294,8 @@ public class DimmerTests extends WindowTestsBase {
public void testRemoveDimImmediately_Smooth() {
assumeTrue(Flags.dimmerRefactor());
mHost.addChild(mChild, 0);
- mDimmer.dimAbove(mChild, 1);
+ mDimmer.adjustAppearance(mChild, 1, 2);
+ mDimmer.adjustRelativeLayer(mChild, -1);
SurfaceControl dimLayer = mDimmer.getDimLayer();
mDimmer.updateDims(mTransaction);
verify(mTransaction, times(1)).show(dimLayer);
@@ -333,7 +314,8 @@ public class DimmerTests extends WindowTestsBase {
public void testRemoveDimImmediately_Legacy() {
assumeFalse(Flags.dimmerRefactor());
mHost.addChild(mChild, 0);
- mDimmer.dimAbove(mChild, 1);
+ mDimmer.adjustAppearance(mChild, 1, 0);
+ mDimmer.adjustRelativeLayer(mChild, -1);
SurfaceControl dimLayer = mDimmer.getDimLayer();
mDimmer.updateDims(mTransaction);
verify(mTransaction, times(1)).show(dimLayer);
@@ -351,16 +333,16 @@ public class DimmerTests extends WindowTestsBase {
@Test
public void testDimmerWithBlurUpdatesTransaction_Legacy() {
assumeFalse(Flags.dimmerRefactor());
- TestWindowContainer child = new TestWindowContainer(mWm);
- mHost.addChild(child, 0);
+ mHost.addChild(mChild, 0);
final int blurRadius = 50;
- mDimmer.dimBelow(child, 0, blurRadius);
+ mDimmer.adjustAppearance(mChild, 1, blurRadius);
+ mDimmer.adjustRelativeLayer(mChild, -1);
SurfaceControl dimLayer = mDimmer.getDimLayer();
assertNotNull("Dimmer should have created a surface", dimLayer);
verify(mHost.getPendingTransaction()).setBackgroundBlurRadius(dimLayer, blurRadius);
- verify(mHost.getPendingTransaction()).setRelativeLayer(dimLayer, child.mControl, -1);
+ verify(mHost.getPendingTransaction()).setRelativeLayer(dimLayer, mChild.mControl, -1);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 2bf13857e537..6235b3b67145 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -26,7 +26,9 @@ import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT;
+import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_BOTTOM_OF_TASK;
import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT;
+import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_TOP_OF_TASK;
import static android.window.TaskFragmentOperation.OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS;
import static android.window.TaskFragmentOperation.OP_TYPE_SET_ANIMATION_PARAMS;
@@ -1655,6 +1657,127 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
assertEquals(frontMostTaskFragment, tf0);
}
+ @Test
+ public void testApplyTransaction_reorderToBottomOfTask() {
+ mController.unregisterOrganizer(mIOrganizer);
+ mController.registerOrganizerInternal(mIOrganizer, true /* isSystemOrganizer */);
+ final Task task = createTask(mDisplayContent);
+ // Create a non-embedded Activity at the bottom.
+ final ActivityRecord bottomActivity = new ActivityBuilder(mAtm)
+ .setTask(task)
+ .build();
+ final TaskFragment tf0 = createTaskFragment(task);
+ final TaskFragment tf1 = createTaskFragment(task);
+ // Create a non-embedded Activity at the top.
+ final ActivityRecord topActivity = new ActivityBuilder(mAtm)
+ .setTask(task)
+ .build();
+
+ // Ensure correct order of the children before the operation
+ assertEquals(topActivity, task.getChildAt(3).asActivityRecord());
+ assertEquals(tf1, task.getChildAt(2).asTaskFragment());
+ assertEquals(tf0, task.getChildAt(1).asTaskFragment());
+ assertEquals(bottomActivity, task.getChildAt(0).asActivityRecord());
+
+ // Reorder TaskFragment to bottom
+ final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
+ OP_TYPE_REORDER_TO_BOTTOM_OF_TASK).build();
+ mTransaction.addTaskFragmentOperation(tf1.getFragmentToken(), operation);
+ assertApplyTransactionAllowed(mTransaction);
+
+ // Ensure correct order of the children after the operation
+ assertEquals(topActivity, task.getChildAt(3).asActivityRecord());
+ assertEquals(tf0, task.getChildAt(2).asTaskFragment());
+ assertEquals(bottomActivity, task.getChildAt(1).asActivityRecord());
+ assertEquals(tf1, task.getChildAt(0).asTaskFragment());
+ }
+
+ @Test
+ public void testApplyTransaction_reorderToTopOfTask() {
+ mController.unregisterOrganizer(mIOrganizer);
+ mController.registerOrganizerInternal(mIOrganizer, true /* isSystemOrganizer */);
+ final Task task = createTask(mDisplayContent);
+ // Create a non-embedded Activity at the bottom.
+ final ActivityRecord bottomActivity = new ActivityBuilder(mAtm)
+ .setTask(task)
+ .build();
+ final TaskFragment tf0 = createTaskFragment(task);
+ final TaskFragment tf1 = createTaskFragment(task);
+ // Create a non-embedded Activity at the top.
+ final ActivityRecord topActivity = new ActivityBuilder(mAtm)
+ .setTask(task)
+ .build();
+
+ // Ensure correct order of the children before the operation
+ assertEquals(topActivity, task.getChildAt(3).asActivityRecord());
+ assertEquals(tf1, task.getChildAt(2).asTaskFragment());
+ assertEquals(tf0, task.getChildAt(1).asTaskFragment());
+ assertEquals(bottomActivity, task.getChildAt(0).asActivityRecord());
+
+ // Reorder TaskFragment to top
+ final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
+ OP_TYPE_REORDER_TO_TOP_OF_TASK).build();
+ mTransaction.addTaskFragmentOperation(tf0.getFragmentToken(), operation);
+ assertApplyTransactionAllowed(mTransaction);
+
+ // Ensure correct order of the children after the operation
+ assertEquals(tf0, task.getChildAt(3).asTaskFragment());
+ assertEquals(topActivity, task.getChildAt(2).asActivityRecord());
+ assertEquals(tf1, task.getChildAt(1).asTaskFragment());
+ assertEquals(bottomActivity, task.getChildAt(0).asActivityRecord());
+ }
+
+ @Test
+ public void testApplyTransaction_reorderToBottomOfTask_failsIfNotSystemOrganizer() {
+ testApplyTransaction_reorder_failsIfNotSystemOrganizer_common(
+ OP_TYPE_REORDER_TO_BOTTOM_OF_TASK);
+ }
+
+ @Test
+ public void testApplyTransaction_reorderToTopOfTask_failsIfNotSystemOrganizer() {
+ testApplyTransaction_reorder_failsIfNotSystemOrganizer_common(
+ OP_TYPE_REORDER_TO_TOP_OF_TASK);
+ }
+
+ private void testApplyTransaction_reorder_failsIfNotSystemOrganizer_common(
+ @TaskFragmentOperation.OperationType int opType) {
+ final Task task = createTask(mDisplayContent);
+ // Create a non-embedded Activity at the bottom.
+ final ActivityRecord bottomActivity = new ActivityBuilder(mAtm)
+ .setTask(task)
+ .build();
+ final TaskFragment tf0 = createTaskFragment(task);
+ final TaskFragment tf1 = createTaskFragment(task);
+ // Create a non-embedded Activity at the top.
+ final ActivityRecord topActivity = new ActivityBuilder(mAtm)
+ .setTask(task)
+ .build();
+
+ // Ensure correct order of the children before the operation
+ assertEquals(topActivity, task.getChildAt(3).asActivityRecord());
+ assertEquals(tf1, task.getChildAt(2).asTaskFragment());
+ assertEquals(tf0, task.getChildAt(1).asTaskFragment());
+ assertEquals(bottomActivity, task.getChildAt(0).asActivityRecord());
+
+ // Apply reorder transaction, which is expected to fail for non-system organizer.
+ final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
+ opType).build();
+ mTransaction
+ .addTaskFragmentOperation(tf0.getFragmentToken(), operation)
+ .setErrorCallbackToken(mErrorToken);
+ assertApplyTransactionAllowed(mTransaction);
+ // The pending event will be dispatched on the handler (from requestTraversal).
+ waitHandlerIdle(mWm.mAnimationHandler);
+
+ assertTaskFragmentErrorTransaction(opType, SecurityException.class);
+
+ // Ensure no change to the order of the children after the operation
+ assertEquals(topActivity, task.getChildAt(3).asActivityRecord());
+ assertEquals(tf1, task.getChildAt(2).asTaskFragment());
+ assertEquals(tf0, task.getChildAt(1).asTaskFragment());
+ assertEquals(bottomActivity, task.getChildAt(0).asActivityRecord());
+ }
+
/**
* Creates a {@link TaskFragment} with the {@link WindowContainerTransaction}. Calls
* {@link WindowOrganizerController#applyTransaction(WindowContainerTransaction)} to apply the
@@ -1782,6 +1905,19 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
assertEquals(activityToken, change.getActivityToken());
}
+ /** Setups an embedded TaskFragment. */
+ private TaskFragment createTaskFragment(Task task) {
+ final IBinder token = new Binder();
+ TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+ .setParentTask(task)
+ .setFragmentToken(token)
+ .setOrganizer(mOrganizer)
+ .createActivityCount(1)
+ .build();
+ mWindowOrganizerController.mLaunchTaskFragments.put(token, taskFragment);
+ return taskFragment;
+ }
+
/** Setups an embedded TaskFragment in a PIP Task. */
private void setupTaskFragmentInPip() {
mTaskFragment = new TaskFragmentBuilder(mAtm)
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 e86fc366a631..eaeb8049b81a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -100,6 +100,9 @@ import androidx.test.platform.app.InstrumentationRegistry;
import com.android.compatibility.common.util.AdoptShellPermissionsRule;
import com.android.internal.os.IResultReceiver;
import com.android.server.LocalServices;
+import com.android.server.wm.WindowManagerService.WindowContainerInfo;
+
+import com.google.common.truth.Expect;
import org.junit.Rule;
import org.junit.Test;
@@ -125,6 +128,9 @@ public class WindowManagerServiceTests extends WindowTestsBase {
InstrumentationRegistry.getInstrumentation().getUiAutomation(),
ADD_TRUSTED_DISPLAY);
+ @Rule
+ public Expect mExpect = Expect.create();
+
@Test
public void testIsRequestedOrientationMapped() {
mWm.setOrientationRequestPolicy(/* isIgnoreOrientationRequestDisabled*/ true,
@@ -674,64 +680,68 @@ public class WindowManagerServiceTests extends WindowTestsBase {
@Test
public void testGetTaskWindowContainerTokenForLaunchCookie_nullCookie() {
- WindowContainerToken wct = mWm.getTaskWindowContainerTokenForLaunchCookie(null);
- assertThat(wct).isNull();
+ WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForLaunchCookie(null);
+ assertThat(wci).isNull();
}
@Test
public void testGetTaskWindowContainerTokenForLaunchCookie_invalidCookie() {
Binder cookie = new Binder("test cookie");
- WindowContainerToken wct = mWm.getTaskWindowContainerTokenForLaunchCookie(cookie);
- assertThat(wct).isNull();
+ WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForLaunchCookie(cookie);
+ assertThat(wci).isNull();
final ActivityRecord testActivity = new ActivityBuilder(mAtm)
.setCreateTask(true)
.build();
- wct = mWm.getTaskWindowContainerTokenForLaunchCookie(cookie);
- assertThat(wct).isNull();
+ wci = mWm.getTaskWindowContainerInfoForLaunchCookie(cookie);
+ assertThat(wci).isNull();
}
@Test
public void testGetTaskWindowContainerTokenForLaunchCookie_validCookie() {
final Binder cookie = new Binder("ginger cookie");
final WindowContainerToken launchRootTask = mock(WindowContainerToken.class);
- setupActivityWithLaunchCookie(cookie, launchRootTask);
+ final int uid = 123;
+ setupActivityWithLaunchCookie(cookie, launchRootTask, uid);
- WindowContainerToken wct = mWm.getTaskWindowContainerTokenForLaunchCookie(cookie);
- assertThat(wct).isEqualTo(launchRootTask);
+ WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForLaunchCookie(cookie);
+ mExpect.that(wci.getToken()).isEqualTo(launchRootTask);
+ mExpect.that(wci.getUid()).isEqualTo(uid);
}
@Test
public void testGetTaskWindowContainerTokenForLaunchCookie_multipleCookies() {
final Binder cookie1 = new Binder("ginger cookie");
final WindowContainerToken launchRootTask1 = mock(WindowContainerToken.class);
- setupActivityWithLaunchCookie(cookie1, launchRootTask1);
+ final int uid1 = 123;
+ setupActivityWithLaunchCookie(cookie1, launchRootTask1, uid1);
setupActivityWithLaunchCookie(new Binder("choc chip cookie"),
- mock(WindowContainerToken.class));
+ mock(WindowContainerToken.class), /* uid= */ 456);
setupActivityWithLaunchCookie(new Binder("peanut butter cookie"),
- mock(WindowContainerToken.class));
+ mock(WindowContainerToken.class), /* uid= */ 789);
- WindowContainerToken wct = mWm.getTaskWindowContainerTokenForLaunchCookie(cookie1);
- assertThat(wct).isEqualTo(launchRootTask1);
+ WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForLaunchCookie(cookie1);
+ mExpect.that(wci.getToken()).isEqualTo(launchRootTask1);
+ mExpect.that(wci.getUid()).isEqualTo(uid1);
}
@Test
public void testGetTaskWindowContainerTokenForLaunchCookie_multipleCookies_noneValid() {
setupActivityWithLaunchCookie(new Binder("ginger cookie"),
- mock(WindowContainerToken.class));
+ mock(WindowContainerToken.class), /* uid= */ 123);
setupActivityWithLaunchCookie(new Binder("choc chip cookie"),
- mock(WindowContainerToken.class));
+ mock(WindowContainerToken.class), /* uid= */ 456);
setupActivityWithLaunchCookie(new Binder("peanut butter cookie"),
- mock(WindowContainerToken.class));
+ mock(WindowContainerToken.class), /* uid= */ 789);
- WindowContainerToken wct = mWm.getTaskWindowContainerTokenForLaunchCookie(
+ WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForLaunchCookie(
new Binder("some other cookie"));
- assertThat(wct).isNull();
+ assertThat(wci).isNull();
}
@Test
@@ -778,17 +788,18 @@ public class WindowManagerServiceTests extends WindowTestsBase {
}
@Test
- public void setContentRecordingSession_matchingTask_mutatesSessionWithWindowContainerToken() {
+ public void setContentRecordingSession_matchingTask_mutatesSessionWithWindowContainerInfo() {
WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
Task task = createTask(mDefaultDisplay);
ActivityRecord activityRecord = createActivityRecord(task);
- ContentRecordingSession session = ContentRecordingSession.createTaskSession(
- activityRecord.mLaunchCookie);
+ ContentRecordingSession session =
+ ContentRecordingSession.createTaskSession(activityRecord.mLaunchCookie);
wmInternal.setContentRecordingSession(session);
- assertThat(session.getTokenToRecord()).isEqualTo(
- task.mRemoteToken.toWindowContainerToken().asBinder());
+ mExpect.that(session.getTokenToRecord())
+ .isEqualTo(task.mRemoteToken.toWindowContainerToken().asBinder());
+ mExpect.that(session.getTargetUid()).isEqualTo(activityRecord.getUid());
}
@Test
@@ -1010,12 +1021,12 @@ public class WindowManagerServiceTests extends WindowTestsBase {
}
}
- private void setupActivityWithLaunchCookie(IBinder launchCookie, WindowContainerToken wct) {
+ private void setupActivityWithLaunchCookie(
+ IBinder launchCookie, WindowContainerToken wct, int uid) {
final WindowContainer.RemoteToken remoteToken = mock(WindowContainer.RemoteToken.class);
when(remoteToken.toWindowContainerToken()).thenReturn(wct);
- final ActivityRecord testActivity = new ActivityBuilder(mAtm)
- .setCreateTask(true)
- .build();
+ final ActivityRecord testActivity =
+ new ActivityBuilder(mAtm).setCreateTask(true).setUid(uid).build();
testActivity.mLaunchCookie = launchCookie;
testActivity.getTask().mRemoteToken = remoteToken;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 7168670f9652..0b77fd828745 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -40,6 +40,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.wm.testing.Assert.assertThrows;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.WindowContainer.SYNC_STATE_READY;
@@ -58,6 +59,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityOptions;
@@ -77,11 +79,13 @@ import android.util.Rational;
import android.view.Display;
import android.view.SurfaceControl;
import android.view.WindowInsets;
+import android.window.ITaskFragmentOrganizer;
import android.window.ITaskOrganizer;
import android.window.IWindowContainerTransactionCallback;
import android.window.StartingWindowInfo;
import android.window.StartingWindowRemovalInfo;
import android.window.TaskAppearedInfo;
+import android.window.TaskFragmentOrganizer;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -579,6 +583,87 @@ public class WindowOrganizerTests extends WindowTestsBase {
}
@Test
+ public void testTaskFragmentHiddenAndFocusableChanges() {
+ removeGlobalMinSizeRestriction();
+ final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN).build();
+
+ final WindowContainerTransaction t = new WindowContainerTransaction();
+ final TaskFragmentOrganizer organizer =
+ createTaskFragmentOrganizer(t, true /* isSystemOrganizer */);
+
+ final IBinder token = new Binder();
+ final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+ .setParentTask(rootTask)
+ .setFragmentToken(token)
+ .setOrganizer(organizer)
+ .createActivityCount(1)
+ .build();
+
+ // Should be visible and focusable initially.
+ assertTrue(rootTask.shouldBeVisible(null));
+ assertTrue(taskFragment.shouldBeVisible(null));
+ assertTrue(taskFragment.isFocusable());
+ assertTrue(taskFragment.isTopActivityFocusable());
+
+ // Apply transaction to the TaskFragment hidden and not focusable.
+ t.setHidden(taskFragment.mRemoteToken.toWindowContainerToken(), true);
+ t.setFocusable(taskFragment.mRemoteToken.toWindowContainerToken(), false);
+ mWm.mAtmService.mWindowOrganizerController.applyTaskFragmentTransactionLocked(
+ t, TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE,
+ false /* shouldApplyIndependently */);
+
+ // Should be not visible and not focusable after the transaction.
+ assertFalse(taskFragment.shouldBeVisible(null));
+ assertFalse(taskFragment.isFocusable());
+ assertFalse(taskFragment.isTopActivityFocusable());
+
+ // Apply transaction to the TaskFragment not hidden and focusable.
+ t.setHidden(taskFragment.mRemoteToken.toWindowContainerToken(), false);
+ t.setFocusable(taskFragment.mRemoteToken.toWindowContainerToken(), true);
+ mWm.mAtmService.mWindowOrganizerController.applyTaskFragmentTransactionLocked(
+ t, TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE,
+ false /* shouldApplyIndependently */);
+
+ // Should be visible and focusable after the transaction.
+ assertTrue(taskFragment.shouldBeVisible(null));
+ assertTrue(taskFragment.isFocusable());
+ assertTrue(taskFragment.isTopActivityFocusable());
+ }
+
+ @Test
+ public void testTaskFragmentHiddenAndFocusableChanges_throwsWhenNotSystemOrganizer() {
+ removeGlobalMinSizeRestriction();
+ final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN).build();
+
+ final WindowContainerTransaction t = new WindowContainerTransaction();
+ final TaskFragmentOrganizer organizer =
+ createTaskFragmentOrganizer(t, false /* isSystemOrganizer */);
+
+ final IBinder token = new Binder();
+ final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+ .setParentTask(rootTask)
+ .setFragmentToken(token)
+ .setOrganizer(organizer)
+ .createActivityCount(1)
+ .build();
+
+ assertTrue(rootTask.shouldBeVisible(null));
+ assertTrue(taskFragment.shouldBeVisible(null));
+
+ t.setHidden(taskFragment.mRemoteToken.toWindowContainerToken(), true);
+ t.setFocusable(taskFragment.mRemoteToken.toWindowContainerToken(), false);
+
+ // Non-system organizers are not allow to update the hidden and focusable states.
+ assertThrows(SecurityException.class, () ->
+ mWm.mAtmService.mWindowOrganizerController.applyTaskFragmentTransactionLocked(
+ t, TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE,
+ false /* shouldApplyIndependently */)
+ );
+ }
+
+ @Test
public void testContainerTranslucentChanges() {
removeGlobalMinSizeRestriction();
final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
@@ -1600,4 +1685,20 @@ public class WindowOrganizerTests extends WindowTestsBase {
assertTrue(taskIds.contains(expectedTasks[i].mTaskId));
}
}
+
+ @NonNull
+ private TaskFragmentOrganizer createTaskFragmentOrganizer(
+ @NonNull WindowContainerTransaction t, boolean isSystemOrganizer) {
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ final ITaskFragmentOrganizer organizerInterface =
+ ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder());
+ mWm.mAtmService.mWindowOrganizerController.mTaskFragmentOrganizerController
+ .registerOrganizerInternal(
+ ITaskFragmentOrganizer.Stub.asInterface(
+ organizer.getOrganizerToken().asBinder()),
+ isSystemOrganizer);
+ t.setTaskFragmentOrganizer(organizerInterface);
+
+ return organizer;
+ }
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 2e6278d9a75d..55b5d11d938a 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -2127,15 +2127,13 @@ public class UsageStatsService extends SystemService implements
}
private boolean canReportUsageStats() {
- final boolean isSystem = isCallingUidSystem();
- if (!Flags.reportUsageStatsPermission()) {
- // If the flag is disabled, do no check for the new permission and instead return
- // true only if the calling uid is system since System UID can always report stats.
- return isSystem;
- }
- return isSystem
- || getContext().checkCallingPermission(Manifest.permission.REPORT_USAGE_STATS)
- == PackageManager.PERMISSION_GRANTED;
+ if (isCallingUidSystem()) {
+ // System UID can always report UsageStats
+ return true;
+ }
+
+ return getContext().checkCallingPermission(Manifest.permission.REPORT_USAGE_STATS)
+ == PackageManager.PERMISSION_GRANTED;
}
private boolean hasObserverPermission() {
@@ -2627,9 +2625,12 @@ public class UsageStatsService extends SystemService implements
return;
}
- if (!canReportUsageStats()) {
- throw new SecurityException("Only the system or holders of the REPORT_USAGE_STATS"
- + " permission are allowed to call reportChooserSelection");
+ if (Flags.reportUsageStatsPermission()) {
+ if (!canReportUsageStats()) {
+ throw new SecurityException(
+ "Only the system or holders of the REPORT_USAGE_STATS"
+ + " permission are allowed to call reportChooserSelection");
+ }
}
// Verify if this package exists before reporting an event for it.
@@ -2649,9 +2650,17 @@ public class UsageStatsService extends SystemService implements
@Override
public void reportUserInteraction(String packageName, int userId) {
Objects.requireNonNull(packageName);
- if (!canReportUsageStats()) {
- throw new SecurityException("Only the system or holders of the REPORT_USAGE_STATS"
- + " permission are allowed to call reportUserInteraction");
+ if (Flags.reportUsageStatsPermission()) {
+ if (!canReportUsageStats()) {
+ throw new SecurityException(
+ "Only the system or holders of the REPORT_USAGE_STATS"
+ + " permission are allowed to call reportUserInteraction");
+ }
+ } else {
+ if (!isCallingUidSystem()) {
+ throw new SecurityException("Only system is allowed to call"
+ + " reportUserInteraction");
+ }
}
final Event event = new Event(USER_INTERACTION, SystemClock.elapsedRealtime());
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java
index 42b08e3c5442..93b5a40a5fc0 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java
@@ -20,6 +20,8 @@ import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD;
import static android.Manifest.permission.LOG_COMPAT_CHANGE;
import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG;
import static android.Manifest.permission.RECORD_AUDIO;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_DEFAULT;
import static android.service.attention.AttentionService.PROXIMITY_UNKNOWN;
import static android.service.voice.HotwordDetectionService.AUDIO_SOURCE_EXTERNAL;
import static android.service.voice.HotwordDetectionService.ENABLE_PROXIMITY_RESULT;
@@ -753,11 +755,21 @@ abstract class DetectorSession {
"Failed to obtain permission RECORD_AUDIO for identity "
+ mVoiceInteractorIdentity);
}
- mAppOpsManager.noteOpNoThrow(
- AppOpsPolicy.getVoiceActivationOp(),
- mVoiceInteractorIdentity.uid, mVoiceInteractorIdentity.packageName,
- mVoiceInteractorIdentity.attributionTag,
- HOTWORD_DETECTION_OP_MESSAGE);
+ int opMode = mAppOpsManager.unsafeCheckOpNoThrow(
+ AppOpsManager.opToPublicName(AppOpsPolicy.getVoiceActivationOp()),
+ mVoiceInteractorIdentity.uid,
+ mVoiceInteractorIdentity.packageName);
+ if (opMode == MODE_DEFAULT || opMode == MODE_ALLOWED) {
+ mAppOpsManager.noteOpNoThrow(
+ AppOpsPolicy.getVoiceActivationOp(),
+ mVoiceInteractorIdentity.uid, mVoiceInteractorIdentity.packageName,
+ mVoiceInteractorIdentity.attributionTag,
+ HOTWORD_DETECTION_OP_MESSAGE);
+ } else {
+ throw new SecurityException(
+ "The app op OP_RECEIVE_SANDBOX_TRIGGER_AUDIO is denied for "
+ + "identity" + mVoiceInteractorIdentity);
+ }
} else {
enforcePermissionForDataDelivery(mContext, mVoiceInteractorIdentity,
RECORD_AUDIO, HOTWORD_DETECTION_OP_MESSAGE);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 138e575a6872..1c689d0d5ce3 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -58,6 +58,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
+import android.os.PermissionEnforcer;
import android.os.PersistableBundle;
import android.os.RemoteCallback;
import android.os.RemoteCallbackList;
@@ -67,6 +68,7 @@ import android.os.SharedMemory;
import android.os.ShellCallback;
import android.os.Trace;
import android.os.UserHandle;
+import android.permission.flags.Flags;
import android.provider.Settings;
import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
import android.service.voice.IVisualQueryDetectionVoiceInteractionCallback;
@@ -1286,6 +1288,17 @@ public class VoiceInteractionManagerService extends SystemService {
}
}
+ // Enforce permissions that are flag controlled. The flag value decides if the permission
+ // should be enforced.
+ private void initAndVerifyDetector_enforcePermissionWithFlags() {
+ PermissionEnforcer enforcer = mContext.getSystemService(PermissionEnforcer.class);
+ if (Flags.voiceActivationPermissionApis()) {
+ enforcer.enforcePermission(
+ android.Manifest.permission.RECEIVE_SANDBOX_TRIGGER_AUDIO,
+ getCallingPid(), getCallingUid());
+ }
+ }
+
@android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION)
@Override
public void initAndVerifyDetector(
@@ -1295,7 +1308,13 @@ public class VoiceInteractionManagerService extends SystemService {
@NonNull IBinder token,
IHotwordRecognitionStatusCallback callback,
int detectorType) {
+ // TODO(b/305787465): Remove the MANAGE_HOTWORD_DETECTION permission enforcement on the
+ // {@link #initAndVerifyDetector(Identity, PersistableBundle, ShareMemory, IBinder,
+ // IHotwordRecognitionStatusCallback, int)}
+ // and replace with the permission RECEIVE_SANDBOX_TRIGGER_AUDIO when it is fully
+ // launched.
super.initAndVerifyDetector_enforcePermission();
+ initAndVerifyDetector_enforcePermissionWithFlags();
synchronized (this) {
enforceIsCurrentVoiceInteractionService();
diff --git a/telephony/java/android/telephony/BarringInfo.java b/telephony/java/android/telephony/BarringInfo.java
index 971fc781a719..e20e4d200251 100644
--- a/telephony/java/android/telephony/BarringInfo.java
+++ b/telephony/java/android/telephony/BarringInfo.java
@@ -202,6 +202,24 @@ public final class BarringInfo implements Parcelable {
&& mConditionalBarringTimeSeconds == other.mConditionalBarringTimeSeconds;
}
+ private static String barringTypeToString(@BarringType int barringType) {
+ return switch (barringType) {
+ case BARRING_TYPE_NONE -> "NONE";
+ case BARRING_TYPE_CONDITIONAL -> "CONDITIONAL";
+ case BARRING_TYPE_UNCONDITIONAL -> "UNCONDITIONAL";
+ case BARRING_TYPE_UNKNOWN -> "UNKNOWN";
+ default -> "UNKNOWN(" + barringType + ")";
+ };
+ }
+
+ @Override
+ public String toString() {
+ return "BarringServiceInfo {mBarringType=" + barringTypeToString(mBarringType)
+ + ", mIsConditionallyBarred=" + mIsConditionallyBarred
+ + ", mConditionalBarringFactor=" + mConditionalBarringFactor
+ + ", mConditionalBarringTimeSeconds=" + mConditionalBarringTimeSeconds + "}";
+ }
+
/** @hide */
public BarringServiceInfo(Parcel p) {
mBarringType = p.readInt();
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 90fa69ff8a15..73220c353b36 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -15033,15 +15033,6 @@ public class TelephonyManager {
@TestApi
public static final int HAL_SERVICE_IMS = 7;
- /**
- * HAL service type that supports the HAL APIs implementation of IRadioSatellite
- * {@link RadioSatelliteProxy}
- * @hide
- */
- @TestApi
- @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
- public static final int HAL_SERVICE_SATELLITE = 8;
-
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"HAL_SERVICE_"},
@@ -15054,7 +15045,6 @@ public class TelephonyManager {
HAL_SERVICE_SIM,
HAL_SERVICE_VOICE,
HAL_SERVICE_IMS,
- HAL_SERVICE_SATELLITE
})
public @interface HalService {}
diff --git a/telephony/java/android/telephony/satellite/INtnSignalStrengthCallback.aidl b/telephony/java/android/telephony/satellite/INtnSignalStrengthCallback.aidl
new file mode 100644
index 000000000000..54cab48dd1e4
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/INtnSignalStrengthCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite;
+
+import android.telephony.satellite.NtnSignalStrength;
+
+/**
+ * Interface for non-terrestrial signal strength notify callback.
+ * @hide
+ */
+oneway interface INtnSignalStrengthCallback {
+ /**
+ * Called when NTN signal strength changes.
+ * @param ntnSignalStrength The new NTN signal strength.
+ */
+ void onNtnSignalStrengthChanged(in NtnSignalStrength ntnSignalStrength);
+}
diff --git a/telephony/java/android/telephony/satellite/NtnSignalStrength.aidl b/telephony/java/android/telephony/satellite/NtnSignalStrength.aidl
new file mode 100644
index 000000000000..a79cb695ef24
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/NtnSignalStrength.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite;
+
+parcelable NtnSignalStrength;
diff --git a/telephony/java/android/telephony/satellite/NtnSignalStrength.java b/telephony/java/android/telephony/satellite/NtnSignalStrength.java
new file mode 100644
index 000000000000..16d765455d21
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/NtnSignalStrength.java
@@ -0,0 +1,149 @@
+/*
+ * 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.telephony.satellite;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.telephony.flags.Flags;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * NTN signal strength related information.
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+public final class NtnSignalStrength implements Parcelable {
+ /** Non-terrestrial network signal strength is not available. */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public static final int NTN_SIGNAL_STRENGTH_NONE = 0;
+ /** Non-terrestrial network signal strength is poor. */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public static final int NTN_SIGNAL_STRENGTH_POOR = 1;
+ /** Non-terrestrial network signal strength is moderate. */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public static final int NTN_SIGNAL_STRENGTH_MODERATE = 2;
+ /** Non-terrestrial network signal strength is good. */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public static final int NTN_SIGNAL_STRENGTH_GOOD = 3;
+ /** Non-terrestrial network signal strength is great. */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public static final int NTN_SIGNAL_STRENGTH_GREAT = 4;
+ @NtnSignalStrengthLevel private int mLevel;
+
+ /** @hide */
+ @IntDef(prefix = "NTN_SIGNAL_STRENGTH_", value = {
+ NTN_SIGNAL_STRENGTH_NONE,
+ NTN_SIGNAL_STRENGTH_POOR,
+ NTN_SIGNAL_STRENGTH_MODERATE,
+ NTN_SIGNAL_STRENGTH_GOOD,
+ NTN_SIGNAL_STRENGTH_GREAT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface NtnSignalStrengthLevel {}
+
+ /**
+ * Create a parcelable object to inform the current non-terrestrial signal strength
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public NtnSignalStrength(@NtnSignalStrengthLevel int level) {
+ this.mLevel = level;
+ }
+
+ /**
+ * This constructor is used to create a copy of an existing NtnSignalStrength object.
+ */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public NtnSignalStrength(@Nullable NtnSignalStrength source) {
+ this.mLevel = (source == null) ? NTN_SIGNAL_STRENGTH_NONE : source.getLevel();
+ }
+
+ private NtnSignalStrength(Parcel in) {
+ readFromParcel(in);
+ }
+
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ @NtnSignalStrengthLevel public int getLevel() {
+ return mLevel;
+ }
+
+ /**
+ * @return 0
+ */
+ @Override
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * @param out The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written.
+ * May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
+ */
+ @Override
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeInt(mLevel);
+ }
+
+ private void readFromParcel(Parcel in) {
+ mLevel = in.readInt();
+ }
+
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ @NonNull public static final Creator<NtnSignalStrength> CREATOR =
+ new Creator<NtnSignalStrength>() {
+ @Override public NtnSignalStrength createFromParcel(Parcel in) {
+ return new NtnSignalStrength(in);
+ }
+
+ @Override public NtnSignalStrength[] newArray(int size) {
+ return new NtnSignalStrength[size];
+ }
+ };
+
+ @Override
+ public int hashCode() {
+ return mLevel;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null || getClass() != obj.getClass()) return false;
+
+ NtnSignalStrength that = (NtnSignalStrength) obj;
+ return mLevel == that.mLevel;
+ }
+
+ @Override public String toString() {
+ return "NtnSignalStrength{"
+ + "mLevel=" + mLevel
+ + '}';
+ }
+}
diff --git a/telephony/java/android/telephony/satellite/NtnSignalStrengthCallback.java b/telephony/java/android/telephony/satellite/NtnSignalStrengthCallback.java
new file mode 100644
index 000000000000..4b79590b9bc6
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/NtnSignalStrengthCallback.java
@@ -0,0 +1,39 @@
+/*
+ * 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.telephony.satellite;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import com.android.internal.telephony.flags.Flags;
+
+/**
+ * A callback class for notifying satellite signal strength change.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+public interface NtnSignalStrengthCallback {
+ /**
+ * Called when non-terrestrial network signal strength changes.
+ * @param ntnSignalStrength The new non-terrestrial network signal strength.
+ */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ void onNtnSignalStrengthChanged(@NonNull NtnSignalStrength ntnSignalStrength);
+}
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index 7322aeb8bfca..21d93bd3077f 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -35,7 +35,9 @@ import android.os.OutcomeReceiver;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
import android.telephony.TelephonyFrameworkInitializer;
+import android.telephony.TelephonyManager;
import com.android.internal.telephony.IIntegerConsumer;
import com.android.internal.telephony.ITelephony;
@@ -77,6 +79,8 @@ public final class SatelliteManager {
private static final ConcurrentHashMap<SatelliteTransmissionUpdateCallback,
ISatelliteTransmissionUpdateCallback> sSatelliteTransmissionUpdateCallbackMap =
new ConcurrentHashMap<>();
+ private static final ConcurrentHashMap<NtnSignalStrengthCallback, INtnSignalStrengthCallback>
+ sNtnSignalStrengthCallbackMap = new ConcurrentHashMap<>();
private final int mSubId;
@@ -192,6 +196,14 @@ public final class SatelliteManager {
public static final String KEY_SATELLITE_NEXT_VISIBILITY = "satellite_next_visibility";
/**
+ * Bundle key to get the response from
+ * {@link #requestNtnSignalStrength(Executor, OutcomeReceiver)}.
+ * @hide
+ */
+
+ public static final String KEY_NTN_SIGNAL_STRENGTH = "ntn_signal_strength";
+
+ /**
* The request was successfully processed.
*/
@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
@@ -1866,6 +1878,165 @@ public final class SatelliteManager {
return new HashSet<>();
}
+ /**
+ * Request to get the signal strength of the satellite connection.
+ *
+ * <p>
+ * Note: This API is specifically designed for OEM enabled satellite connectivity only.
+ * For satellite connectivity enabled using carrier roaming, please refer to
+ * {@link android.telephony.TelephonyCallback.SignalStrengthsListener}, and
+ * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+ * </p>
+ *
+ * @param executor The executor on which the callback will be called.
+ * @param callback The callback object to which the result will be delivered. If the request is
+ * successful, {@link OutcomeReceiver#onResult(Object)} will return an instance of
+ * {@link NtnSignalStrength} with a value of {@link NtnSignalStrength.NtnSignalStrengthLevel}
+ * The {@link NtnSignalStrength#NTN_SIGNAL_STRENGTH_NONE} will be returned if there is no
+ * signal strength data available.
+ * If the request is not successful, {@link OutcomeReceiver#onError(Throwable)} will return a
+ * {@link SatelliteException} with the {@link SatelliteResult}.
+ *
+ * @throws SecurityException if the caller doesn't have required permission.
+ * @throws IllegalStateException if the Telephony process is not currently available or
+ * satellite is not supported.
+ */
+ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ @NonNull
+ public void requestNtnSignalStrength(@NonNull @CallbackExecutor Executor executor,
+ @NonNull OutcomeReceiver<NtnSignalStrength, SatelliteException> callback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ ResultReceiver receiver = new ResultReceiver(null) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ if (resultCode == SATELLITE_RESULT_SUCCESS) {
+ if (resultData.containsKey(KEY_NTN_SIGNAL_STRENGTH)) {
+ NtnSignalStrength ntnSignalStrength =
+ resultData.getParcelable(KEY_NTN_SIGNAL_STRENGTH,
+ NtnSignalStrength.class);
+ executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+ callback.onResult(ntnSignalStrength)));
+ } else {
+ loge("KEY_NTN_SIGNAL_STRENGTH does not exist.");
+ executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+ callback.onError(new SatelliteException(
+ SATELLITE_RESULT_REQUEST_FAILED))));
+ }
+ } else {
+ executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+ callback.onError(new SatelliteException(resultCode))));
+ }
+ }
+ };
+ telephony.requestNtnSignalStrength(mSubId, receiver);
+ } else {
+ throw new IllegalStateException("Telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ loge("requestNtnSignalStrength() RemoteException: " + ex);
+ ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Registers for NTN signal strength changed from satellite modem.
+ *
+ * <p>
+ * Note: This API is specifically designed for OEM enabled satellite connectivity only.
+ * For satellite connectivity enabled using carrier roaming, please refer to
+ * {@link android.telephony.TelephonyCallback.SignalStrengthsListener}, and
+ * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+ * </p>
+ *
+ * @param executor The executor on which the callback will be called.
+ * @param callback The callback to handle the NTN signal strength changed event.
+ *
+ * @return The {@link SatelliteResult} result of the operation.
+ *
+ * @throws SecurityException if the caller doesn't have required permission.
+ * @throws IllegalStateException if the Telephony process is not currently available.
+ */
+ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ @SatelliteResult public int registerForNtnSignalStrengthChanged(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull NtnSignalStrengthCallback callback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ INtnSignalStrengthCallback internalCallback =
+ new INtnSignalStrengthCallback.Stub() {
+ @Override
+ public void onNtnSignalStrengthChanged(
+ NtnSignalStrength ntnSignalStrength) {
+ executor.execute(() -> Binder.withCleanCallingIdentity(
+ () -> callback.onNtnSignalStrengthChanged(
+ ntnSignalStrength)));
+ }
+ };
+ sNtnSignalStrengthCallbackMap.put(callback, internalCallback);
+ return telephony.registerForNtnSignalStrengthChanged(mSubId, internalCallback);
+ } else {
+ throw new IllegalStateException("Telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ loge("registerForNtnSignalStrengthChanged() RemoteException: " + ex);
+ ex.rethrowFromSystemServer();
+ }
+ return SATELLITE_RESULT_REQUEST_FAILED;
+ }
+
+ /**
+ * Unregisters for NTN signal strength changed from satellite modem.
+ * If callback was not registered before, the request will be ignored.
+ *
+ * <p>
+ * Note: This API is specifically designed for OEM enabled satellite connectivity only.
+ * For satellite connectivity enabled using carrier roaming, please refer to
+ * {@link TelephonyManager#unregisterTelephonyCallback(TelephonyCallback)}..
+ * </p>
+ *
+ * @param callback The callback that was passed to
+ * {@link #registerForNtnSignalStrengthChanged(Executor, NtnSignalStrengthCallback)}.
+ *
+ * @throws SecurityException if the caller doesn't have required permission.
+ * @throws IllegalStateException if the Telephony process is not currently available.
+ */
+ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ public void unregisterForNtnSignalStrengthChanged(@NonNull NtnSignalStrengthCallback callback) {
+ Objects.requireNonNull(callback);
+ INtnSignalStrengthCallback internalCallback =
+ sNtnSignalStrengthCallbackMap.remove(callback);
+
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ if (internalCallback != null) {
+ telephony.unregisterForNtnSignalStrengthChanged(mSubId, internalCallback);
+ } else {
+ loge("unregisterForNtnSignalStrengthChanged: No internal callback.");
+ }
+ } else {
+ throw new IllegalStateException("Telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ loge("unregisterForNtnSignalStrengthChanged() RemoteException: " + ex);
+ ex.rethrowFromSystemServer();
+ }
+
+ }
+
+
private static ITelephony getITelephony() {
ITelephony binder = ITelephony.Stub.asInterface(TelephonyFrameworkInitializer
.getTelephonyServiceManager()
diff --git a/telephony/java/android/telephony/satellite/stub/INtnSignalStrengthConsumer.aidl b/telephony/java/android/telephony/satellite/stub/INtnSignalStrengthConsumer.aidl
new file mode 100644
index 000000000000..b7712bd83cf6
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/stub/INtnSignalStrengthConsumer.aidl
@@ -0,0 +1,28 @@
+/*
+ * 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.telephony.satellite.stub;
+
+import android.telephony.satellite.NtnSignalStrength;
+
+/**
+ * Consumer pattern for a request that receives the signal strength of non-terrestrial network from
+ * the SatelliteService.
+ * @hide
+ */
+oneway interface INtnSignalStrengthConsumer {
+ void accept(in NtnSignalStrength result);
+}
diff --git a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
index 7fda550c599c..6b47db1e2251 100644
--- a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
+++ b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
@@ -16,6 +16,7 @@
package android.telephony.satellite.stub;
+import android.telephony.satellite.stub.INtnSignalStrengthConsumer;
import android.telephony.satellite.stub.ISatelliteCapabilitiesConsumer;
import android.telephony.satellite.stub.ISatelliteListener;
import android.telephony.satellite.stub.SatelliteDatagram;
@@ -454,4 +455,44 @@ oneway interface ISatellite {
*/
void requestIsSatelliteEnabledForCarrier(int simSlot, in IIntegerConsumer resultCallback,
in IBooleanConsumer callback);
+
+ /**
+ * Request to get the signal strength of the satellite connection.
+ *
+ * @param resultCallback The {@link SatelliteError} result of the operation.
+ * @param callback The callback to handle the NTN signal strength changed event.
+ */
+ void requestSignalStrength(in IIntegerConsumer resultCallback,
+ in INtnSignalStrengthConsumer callback);
+
+ /**
+ * The satellite service should report the NTN signal strength via
+ * ISatelliteListener#onNtnSignalStrengthChanged when the NTN signal strength changes.
+ *
+ * @param resultCallback The callback to receive the error code result of the operation.
+ *
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ */
+ void startSendingNtnSignalStrength(in IIntegerConsumer resultCallback);
+
+ /**
+ * The satellite service should stop reporting NTN signal strength to the framework. This will
+ * be called when device is screen off to save power by not letting signal strength updates to
+ * wake up application processor.
+ *
+ * @param resultCallback The callback to receive the error code result of the operation.
+ *
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ */
+ void stopSendingNtnSignalStrength(in IIntegerConsumer resultCallback);
}
diff --git a/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl b/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl
index d68716291b8e..d44ddfa1ee7f 100644
--- a/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl
+++ b/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl
@@ -16,6 +16,7 @@
package android.telephony.satellite.stub;
+import android.telephony.satellite.stub.NtnSignalStrength;
import android.telephony.satellite.stub.NTRadioTechnology;
import android.telephony.satellite.stub.PointingInfo;
import android.telephony.satellite.stub.SatelliteDatagram;
@@ -58,4 +59,10 @@ oneway interface ISatelliteListener {
* @param state The current satellite modem state.
*/
void onSatelliteModemStateChanged(in SatelliteModemState state);
+
+ /**
+ * Called when NTN signal strength changes.
+ * @param ntnSignalStrength The new NTN signal strength.
+ */
+ void onNtnSignalStrengthChanged(in NtnSignalStrength ntnSignalStrength);
}
diff --git a/telephony/java/android/telephony/satellite/stub/NtnSignalStrength.aidl b/telephony/java/android/telephony/satellite/stub/NtnSignalStrength.aidl
new file mode 100644
index 000000000000..f48900583167
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/stub/NtnSignalStrength.aidl
@@ -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 android.telephony.satellite.stub;
+
+import android.telephony.satellite.stub.NtnSignalStrengthLevel;
+
+/**
+ * @hide
+ */
+parcelable NtnSignalStrength {
+ /**
+ * Non-terrestrial signal strength. The value represents the level of signal strength which can
+ * be translated to the number of signal bars.
+ */
+ NtnSignalStrengthLevel signalStrengthLevel;
+}
diff --git a/telephony/java/android/telephony/satellite/stub/NtnSignalStrengthLevel.aidl b/telephony/java/android/telephony/satellite/stub/NtnSignalStrengthLevel.aidl
new file mode 100644
index 000000000000..53b13733941c
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/stub/NtnSignalStrengthLevel.aidl
@@ -0,0 +1,29 @@
+/*
+ * 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.telephony.satellite.stub;
+
+/**
+ * {@hide}
+ */
+@Backing(type="int")
+enum NtnSignalStrengthLevel {
+ NTN_SIGNAL_STRENGTH_NONE = 0,
+ NTN_SIGNAL_STRENGTH_POOR = 1,
+ NTN_SIGNAL_STRENGTH_MODERATE = 2,
+ NTN_SIGNAL_STRENGTH_GOOD = 3,
+ NTN_SIGNAL_STRENGTH_GREAT = 4
+}
diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
index 4cee01e8bd39..a636a6128e61 100644
--- a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
+++ b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
@@ -241,6 +241,30 @@ public class SatelliteImplBase extends SatelliteService {
"requestIsSatelliteEnabledForCarrier");
}
+ @Override
+ public void requestSignalStrength(IIntegerConsumer resultCallback,
+ INtnSignalStrengthConsumer callback) throws RemoteException {
+ executeMethodAsync(
+ () -> SatelliteImplBase.this.requestSignalStrength(resultCallback, callback),
+ "requestSignalStrength");
+ }
+
+ @Override
+ public void startSendingNtnSignalStrength(IIntegerConsumer resultCallback)
+ throws RemoteException {
+ executeMethodAsync(
+ () -> SatelliteImplBase.this.startSendingNtnSignalStrength(resultCallback),
+ "startSendingNtnSignalStrength");
+ }
+
+ @Override
+ public void stopSendingNtnSignalStrength(IIntegerConsumer resultCallback)
+ throws RemoteException {
+ executeMethodAsync(
+ () -> SatelliteImplBase.this.stopSendingNtnSignalStrength(resultCallback),
+ "stopSendingNtnSignalStrength");
+ }
+
// Call the methods with a clean calling identity on the executor and wait indefinitely for
// the future to return.
private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException {
@@ -728,4 +752,35 @@ public class SatelliteImplBase extends SatelliteService {
@NonNull IIntegerConsumer resultCallback, @NonNull IBooleanConsumer callback) {
// stub implementation
}
+
+ /**
+ * Request to get the signal strength of the satellite connection.
+ *
+ * @param resultCallback The {@link SatelliteError} result of the operation.
+ * @param callback The callback to handle the NTN signal strength changed event.
+ */
+ public void requestSignalStrength(@NonNull IIntegerConsumer resultCallback,
+ INtnSignalStrengthConsumer callback) {
+ // stub implementation
+ }
+
+ /**
+ * Requests to deliver signal strength changed events through the
+ * {@link ISatelliteListener#onNtnSignalStrengthChanged(NtnSignalStrength ntnSignalStrength)}
+ * callback.
+ *
+ * @param resultCallback The {@link SatelliteError} result of the operation.
+ */
+ public void startSendingNtnSignalStrength(@NonNull IIntegerConsumer resultCallback) {
+ // stub implementation
+ }
+
+ /**
+ * Requests to stop signal strength changed events
+ *
+ * @param resultCallback The {@link SatelliteError} result of the operation.
+ */
+ public void stopSendingNtnSignalStrength(@NonNull IIntegerConsumer resultCallback){
+ // stub implementation
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 3aa5a5a14bc8..58e702688bd3 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -67,10 +67,12 @@ import android.telephony.ims.aidl.IImsRcsFeature;
import android.telephony.ims.aidl.IImsRegistration;
import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.telephony.ims.aidl.IRcsConfigCallback;
+import android.telephony.satellite.INtnSignalStrengthCallback;
import android.telephony.satellite.ISatelliteDatagramCallback;
import android.telephony.satellite.ISatelliteTransmissionUpdateCallback;
import android.telephony.satellite.ISatelliteProvisionStateCallback;
import android.telephony.satellite.ISatelliteStateCallback;
+import android.telephony.satellite.NtnSignalStrength;
import android.telephony.satellite.SatelliteCapabilities;
import android.telephony.satellite.SatelliteDatagram;
import com.android.ims.internal.IImsServiceFeatureCallback;
@@ -2837,7 +2839,6 @@ interface ITelephony {
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
void deprovisionSatelliteService(int subId, in String token, in IIntegerConsumer callback);
-
/**
* Registers for provision state changed from satellite modem.
*
@@ -3071,4 +3072,40 @@ interface ITelephony {
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
int[] getSatelliteAttachRestrictionReasonsForCarrier(int subId);
+
+ /**
+ * Request to get the signal strength of the satellite connection.
+ *
+ * @param subId The subId of the subscription to request for.
+ * @param receiver Result receiver to get the error code of the request and the current signal
+ * strength of the satellite connection.
+ */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+ void requestNtnSignalStrength(int subId, in ResultReceiver receiver);
+
+ /**
+ * Registers for NTN signal strength changed from satellite modem.
+ *
+ * @param subId The subId of the subscription to request for.
+ * @param callback The callback to handle the NTN signal strength changed event.
+ *
+ * @return The {@link SatelliteResult} result of the operation.
+ */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+ int registerForNtnSignalStrengthChanged(int subId, in INtnSignalStrengthCallback callback);
+
+ /**
+ * Unregisters for NTN signal strength changed from satellite modem.
+ * If callback was not registered before, the request will be ignored.
+ *
+ * @param subId The subId of the subscription to unregister for provision state changed.
+ * @param callback The callback that was passed to
+ * {@link #registerForNtnSignalStrengthChanged(Executor, NtnSignalStrengthCallback)}.
+ */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+ void unregisterForNtnSignalStrengthChanged(int subId,
+ in INtnSignalStrengthCallback callback);
}
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 72e4389e0788..a20f26c1807b 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -120,31 +120,6 @@ public interface RILConstants {
int BLOCKED_DUE_TO_CALL = 69; /* SMS is blocked due to call control */
int RF_HARDWARE_ISSUE = 70; /* RF HW issue is detected */
int NO_RF_CALIBRATION_INFO = 71; /* No RF calibration in device */
- int ENCODING_NOT_SUPPORTED = 72; /* The encoding scheme is not supported by
- either the network or the MS. */
- int FEATURE_NOT_SUPPORTED = 73; /* The requesting feature is not supported by
- the service provider. */
- int INVALID_CONTACT = 74; /* The contact to be added is either not
- existing or not valid. */
- int MODEM_INCOMPATIBLE = 75; /* The modem of the MS is not compatible with
- the service provider. */
- int NETWORK_TIMEOUT = 76; /* Modem timeout to receive ACK or response from
- network after sending a request to it. */
- int NO_SATELLITE_SIGNAL = 77; /* Modem fails to communicate with the satellite
- network since there is no satellite signal.*/
- int NOT_SUFFICIENT_ACCOUNT_BALANCE = 78; /* The request cannot be performed since the
- subscriber's account balance is not
- sufficient. */
- int RADIO_TECHNOLOGY_NOT_SUPPORTED = 79; /* The radio technology is not supported by the
- service provider. */
- int SUBSCRIBER_NOT_AUTHORIZED = 80; /* The subscription is not authorized to
- register with the service provider. */
- int SWITCHED_FROM_SATELLITE_TO_TERRESTRIAL = 81; /* While processing a request from the
- Framework the satellite modem detects
- terrestrial signal, aborts the request, and
- switches to the terrestrial network. */
- int UNIDENTIFIED_SUBSCRIBER = 82; /* The subscriber is not registered with the
- service provider */
// Below is list of OEM specific error codes which can by used by OEMs in case they don't want to
// reveal particular replacement for Generic failure
@@ -568,22 +543,7 @@ public interface RILConstants {
int RIL_REQUEST_IS_N1_MODE_ENABLED = 242;
int RIL_REQUEST_SET_LOCATION_PRIVACY_SETTING = 243;
int RIL_REQUEST_GET_LOCATION_PRIVACY_SETTING = 244;
- int RIL_REQUEST_GET_SATELLITE_CAPABILITIES = 245;
- int RIL_REQUEST_SET_SATELLITE_POWER = 246;
- int RIL_REQUEST_GET_SATELLITE_POWER = 247;
- int RIL_REQUEST_PROVISION_SATELLITE_SERVICE = 248;
- int RIL_REQUEST_ADD_ALLOWED_SATELLITE_CONTACTS = 249;
- int RIL_REQUEST_REMOVE_ALLOWED_SATELLITE_CONTACTS = 250;
- int RIL_REQUEST_SEND_SATELLITE_MESSAGES = 251;
- int RIL_REQUEST_GET_PENDING_SATELLITE_MESSAGES = 252;
- int RIL_REQUEST_GET_SATELLITE_MODE = 253;
- int RIL_REQUEST_SET_SATELLITE_INDICATION_FILTER = 254;
- int RIL_REQUEST_START_SENDING_SATELLITE_POINTING_INFO = 255;
- int RIL_REQUEST_STOP_SENDING_SATELLITE_POINTING_INFO = 256;
- int RIL_REQUEST_GET_MAX_CHARACTERS_PER_SATELLITE_TEXT_MESSAGE = 257;
- int RIL_REQUEST_GET_TIME_FOR_NEXT_SATELLITE_VISIBILITY = 258;
- int RIL_REQUEST_IS_NULL_CIPHER_AND_INTEGRITY_ENABLED = 259;
- int RIL_REQUEST_SET_SATELLITE_PLMN = 260;
+ int RIL_REQUEST_IS_NULL_CIPHER_AND_INTEGRITY_ENABLED = 245;
/* Responses begin */
int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
@@ -645,13 +605,6 @@ public interface RILConstants {
int RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_CHANGED = 1053;
int RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_RECORDS_RECEIVED = 1054;
int RIL_UNSOL_SLICING_CONFIG_CHANGED = 1055;
- int RIL_UNSOL_PENDING_SATELLITE_MESSAGE_COUNT = 1056;
- int RIL_UNSOL_NEW_SATELLITE_MESSAGES = 1057;
- int RIL_UNSOL_SATELLITE_MESSAGES_TRANSFER_COMPLETE = 1058;
- int RIL_UNSOL_SATELLITE_POINTING_INFO_CHANGED = 1059;
- int RIL_UNSOL_SATELLITE_MODE_CHANGED = 1060;
- int RIL_UNSOL_SATELLITE_RADIO_TECHNOLOGY_CHANGED = 1061;
- int RIL_UNSOL_SATELLITE_PROVISION_STATE_CHANGED = 1062;
/* The following unsols are not defined in RIL.h */
int RIL_UNSOL_HAL_NON_RIL_BASE = 1100;
diff --git a/tools/aapt2/integration-tests/AutoVersionTest/Android.bp b/tools/aapt2/integration-tests/AutoVersionTest/Android.bp
index bfd35083366e..c901efa707f4 100644
--- a/tools/aapt2/integration-tests/AutoVersionTest/Android.bp
+++ b/tools/aapt2/integration-tests/AutoVersionTest/Android.bp
@@ -26,4 +26,5 @@ package {
android_test {
name: "AaptAutoVersionTest",
sdk_version: "current",
+ use_resource_processor: false,
}
diff --git a/tools/aapt2/integration-tests/BasicTest/Android.bp b/tools/aapt2/integration-tests/BasicTest/Android.bp
index 7db9d2698cc7..d0649ea4ef9c 100644
--- a/tools/aapt2/integration-tests/BasicTest/Android.bp
+++ b/tools/aapt2/integration-tests/BasicTest/Android.bp
@@ -26,4 +26,5 @@ package {
android_test {
name: "AaptBasicTest",
sdk_version: "current",
+ use_resource_processor: false,
}
diff --git a/tools/aapt2/integration-tests/StaticLibTest/App/Android.bp b/tools/aapt2/integration-tests/StaticLibTest/App/Android.bp
index 80404eeb8d8e..ebb4e9f479d6 100644
--- a/tools/aapt2/integration-tests/StaticLibTest/App/Android.bp
+++ b/tools/aapt2/integration-tests/StaticLibTest/App/Android.bp
@@ -24,9 +24,9 @@ package {
}
android_test {
-
name: "AaptTestStaticLib_App",
sdk_version: "current",
+ use_resource_processor: false,
srcs: ["src/**/*.java"],
asset_dirs: [
"assets",
diff --git a/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.bp b/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.bp
index a84da43c70c8..ee12a92906a8 100644
--- a/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.bp
+++ b/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.bp
@@ -26,6 +26,7 @@ package {
android_library {
name: "AaptTestStaticLib_LibOne",
sdk_version: "current",
+ use_resource_processor: false,
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
}
diff --git a/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.bp b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.bp
index d386c3a35d20..83b2362496fc 100644
--- a/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.bp
+++ b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.bp
@@ -26,6 +26,7 @@ package {
android_library {
name: "AaptTestStaticLib_LibTwo",
sdk_version: "current",
+ use_resource_processor: false,
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
libs: ["AaptTestStaticLib_LibOne"],
diff --git a/tools/aapt2/integration-tests/SymlinkTest/Android.bp b/tools/aapt2/integration-tests/SymlinkTest/Android.bp
index 1e8cf86ed811..15a6a207d6d1 100644
--- a/tools/aapt2/integration-tests/SymlinkTest/Android.bp
+++ b/tools/aapt2/integration-tests/SymlinkTest/Android.bp
@@ -26,4 +26,5 @@ package {
android_test {
name: "AaptSymlinkTest",
sdk_version: "current",
+ use_resource_processor: false,
}
diff --git a/tools/hoststubgen/hoststubgen/Android.bp b/tools/hoststubgen/hoststubgen/Android.bp
index 0599c1d01906..226e2fadf735 100644
--- a/tools/hoststubgen/hoststubgen/Android.bp
+++ b/tools/hoststubgen/hoststubgen/Android.bp
@@ -7,9 +7,38 @@ package {
default_applicable_licenses: ["frameworks_base_license"],
}
+// Visibility only for ravenwood prototype uses.
+genrule_defaults {
+ name: "hoststubgen-for-prototype-only-genrule",
+ visibility: [
+ ":__subpackages__",
+ "//frameworks/base/ravenwood:__subpackages__",
+ ],
+}
+
+// Visibility only for ravenwood prototype uses.
+java_defaults {
+ name: "hoststubgen-for-prototype-only-java",
+ visibility: [
+ ":__subpackages__",
+ "//frameworks/base/ravenwood:__subpackages__",
+ ],
+}
+
+// Visibility only for ravenwood prototype uses.
+filegroup_defaults {
+ name: "hoststubgen-for-prototype-only-filegroup",
+ visibility: [
+ ":__subpackages__",
+ "//frameworks/base/ravenwood:__subpackages__",
+ ],
+}
+
// This library contains the standard hoststubgen annotations.
+// This is only for the prototype. The productionized version is "ravenwood-annotations".
java_library {
name: "hoststubgen-annotations",
+ defaults: ["hoststubgen-for-prototype-only-java"],
srcs: [
"annotations-src/**/*.java",
],
@@ -18,7 +47,6 @@ java_library {
// Seems like we need it to avoid circular deps.
// Copied it from "app-compat-annotations".
sdk_version: "core_current",
- visibility: ["//visibility:public"],
}
// This library contains helper classes used in the host side test environment at runtime.
@@ -55,12 +83,13 @@ java_binary_host {
}
// File that contains the standard command line argumetns to hoststubgen.
+// This is only for the prototype. The productionized version is "ravenwood-standard-options".
filegroup {
name: "hoststubgen-standard-options",
+ defaults: ["hoststubgen-for-prototype-only-filegroup"],
srcs: [
"hoststubgen-standard-options.txt",
],
- visibility: ["//visibility:public"],
}
hoststubgen_common_options = "$(location hoststubgen) " +
@@ -93,7 +122,6 @@ genrule_defaults {
"hoststubgen_keep_all.txt",
"hoststubgen_dump.txt",
],
- // visibility: ["//visibility:public"],
}
// Generate the stub/impl from framework-all, with hidden APIs.
@@ -111,8 +139,10 @@ java_genrule_host {
}
// Extract the stub jar from "framework-all-host" for subsequent build rules.
+// This is only for the prototype. Do not use it in "productionized" build rules.
java_genrule_host {
name: "framework-all-hidden-api-host-stub",
+ defaults: ["hoststubgen-for-prototype-only-genrule"],
cmd: "cp $(in) $(out)",
srcs: [
":framework-all-hidden-api-host{host_stub.jar}",
@@ -120,12 +150,13 @@ java_genrule_host {
out: [
"host_stub.jar",
],
- visibility: ["//visibility:public"],
}
// Extract the impl jar from "framework-all-host" for subsequent build rules.
+// This is only for the prototype. Do not use it in "productionized" build rules.
java_genrule_host {
name: "framework-all-hidden-api-host-impl",
+ defaults: ["hoststubgen-for-prototype-only-genrule"],
cmd: "cp $(in) $(out)",
srcs: [
":framework-all-hidden-api-host{host_impl.jar}",
@@ -133,11 +164,11 @@ java_genrule_host {
out: [
"host_impl.jar",
],
- visibility: ["//visibility:public"],
}
// Generate the stub/impl from framework-all, with only public/system/test APIs, without
// hidden APIs.
+// This is only for the prototype. Do not use it in "productionized" build rules.
java_genrule_host {
name: "framework-all-test-api-host",
defaults: ["hoststubgen-command-defaults"],
@@ -154,8 +185,10 @@ java_genrule_host {
}
// Extract the stub jar from "framework-all-test-api-host" for subsequent build rules.
+// This is only for the prototype. Do not use it in "productionized" build rules.
java_genrule_host {
name: "framework-all-test-api-host-stub",
+ defaults: ["hoststubgen-for-prototype-only-genrule"],
cmd: "cp $(in) $(out)",
srcs: [
":framework-all-test-api-host{host_stub.jar}",
@@ -163,12 +196,13 @@ java_genrule_host {
out: [
"host_stub.jar",
],
- visibility: ["//visibility:public"],
}
// Extract the impl jar from "framework-all-test-api-host" for subsequent build rules.
+// This is only for the prototype. Do not use it in "productionized" build rules.
java_genrule_host {
name: "framework-all-test-api-host-impl",
+ defaults: ["hoststubgen-for-prototype-only-genrule"],
cmd: "cp $(in) $(out)",
srcs: [
":framework-all-test-api-host{host_impl.jar}",
@@ -176,7 +210,6 @@ java_genrule_host {
out: [
"host_impl.jar",
],
- visibility: ["//visibility:public"],
}
// This library contains helper classes to build hostside tests/targets.
@@ -186,6 +219,7 @@ java_genrule_host {
// Ideally this library should be empty.
java_library_host {
name: "hoststubgen-helper-framework-buildtime",
+ defaults: ["hoststubgen-for-prototype-only-java"],
srcs: [
"helper-framework-buildtime-src/**/*.java",
],
@@ -195,13 +229,13 @@ java_library_host {
"framework-all-hidden-api-host-impl",
"junit",
],
- visibility: ["//visibility:public"],
}
// This module contains "fake" libcore/dalvik classes, framework native substitution, etc,
// that are needed at runtime.
java_library_host {
name: "hoststubgen-helper-framework-runtime",
+ defaults: ["hoststubgen-for-prototype-only-java"],
srcs: [
"helper-framework-runtime-src/**/*.java",
],
@@ -209,7 +243,6 @@ java_library_host {
"hoststubgen-helper-runtime",
"framework-all-hidden-api-host-impl",
],
- visibility: ["//visibility:public"],
}
// Defaults for host side test modules.
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh b/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh
index 6bf074b1bf9f..523106f51fc8 100755
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh
@@ -36,7 +36,7 @@ mkdir -p $out
HOSTSTUBGEN=hoststubgen
# Rebuild the tool and the dependencies. These are the only things we build with the build system.
-run m $HOSTSTUBGEN hoststubgen-annotations hoststubgen-helper-runtime truth-prebuilt junit
+run m $HOSTSTUBGEN hoststubgen-annotations hoststubgen-helper-runtime truth junit
# Build tiny-framework
@@ -55,7 +55,7 @@ framework_compile_classpaths=(
test_compile_classpaths=(
$SOONG_INT/external/junit/junit/android_common/combined/junit.jar
- $ANDROID_HOST_OUT/framework/truth-prebuilt.jar
+ $SOONG_INT/external/truth/truth/android_common/combined/truth.jar
)
test_runtime_classpaths=(
diff --git a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
index 2a199d27a60e..58638e8e1af4 100644
--- a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
+++ b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
@@ -113,6 +113,7 @@ public class WifiNl80211Manager {
private HashMap<String, IPnoScanEvent> mPnoScanEventHandlers = new HashMap<>();
private HashMap<String, IApInterfaceEventCallback> mApInterfaceListeners = new HashMap<>();
private Runnable mDeathEventHandler;
+ private Object mLock = new Object();
/**
* Ensures that no more than one sendMgmtFrame operation runs concurrently.
*/
@@ -625,13 +626,15 @@ public class WifiNl80211Manager {
@VisibleForTesting
public void binderDied() {
mEventHandler.post(() -> {
- Log.e(TAG, "Wificond died!");
- clearState();
- // Invalidate the global wificond handle on death. Will be refreshed
- // on the next setup call.
- mWificond = null;
- if (mDeathEventHandler != null) {
- mDeathEventHandler.run();
+ synchronized (mLock) {
+ Log.e(TAG, "Wificond died!");
+ clearState();
+ // Invalidate the global wificond handle on death. Will be refreshed
+ // on the next setup call.
+ mWificond = null;
+ if (mDeathEventHandler != null) {
+ mDeathEventHandler.run();
+ }
}
});
}
@@ -867,26 +870,28 @@ public class WifiNl80211Manager {
* @return Returns true on success.
*/
public boolean tearDownInterfaces() {
- Log.d(TAG, "tearing down interfaces in wificond");
- // Explicitly refresh the wificodn handler because |tearDownInterfaces()|
- // could be used to cleanup before we setup any interfaces.
- if (!retrieveWificondAndRegisterForDeath()) {
- return false;
- }
+ synchronized (mLock) {
+ Log.d(TAG, "tearing down interfaces in wificond");
+ // Explicitly refresh the wificond handler because |tearDownInterfaces()|
+ // could be used to cleanup before we setup any interfaces.
+ if (!retrieveWificondAndRegisterForDeath()) {
+ return false;
+ }
- try {
- for (Map.Entry<String, IWifiScannerImpl> entry : mWificondScanners.entrySet()) {
- entry.getValue().unsubscribeScanEvents();
- entry.getValue().unsubscribePnoScanEvents();
+ try {
+ for (Map.Entry<String, IWifiScannerImpl> entry : mWificondScanners.entrySet()) {
+ entry.getValue().unsubscribeScanEvents();
+ entry.getValue().unsubscribePnoScanEvents();
+ }
+ mWificond.tearDownInterfaces();
+ clearState();
+ return true;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to tear down interfaces due to remote exception");
}
- mWificond.tearDownInterfaces();
- clearState();
- return true;
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to tear down interfaces due to remote exception");
- }
- return false;
+ return false;
+ }
}
/** Helper function to look up the interface handle using name */