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.bp9
-rw-r--r--api/StubLibraries.bp8
-rw-r--r--api/javadoc-lint-baseline146
-rw-r--r--core/api/current.txt36
-rw-r--r--core/api/system-current.txt26
-rw-r--r--core/api/test-current.txt12
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java15
-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/content/AttributionSource.java1
-rw-r--r--core/java/android/content/ContextParams.java5
-rw-r--r--core/java/android/content/Intent.java3
-rw-r--r--core/java/android/content/pm/ArchivedActivityParcel.aidl3
-rw-r--r--core/java/android/content/pm/PackageManager.java1
-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.aconfig14
-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/ContentRecordingSession.java111
-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.java176
-rw-r--r--core/java/android/view/WindowManager.java3
-rw-r--r--core/java/android/view/accessibility/AccessibilityInteractionClient.java3
-rw-r--r--core/java/android/view/accessibility/AccessibilityWindowInfo.java1
-rw-r--r--core/java/android/view/accessibility/flags/accessibility_flags.aconfig7
-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/flags/refresh_rate_flags.aconfig7
-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.xml2
-rw-r--r--core/res/res/values/config.xml7
-rw-r--r--core/res/res/values/symbols.xml2
-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/AndroidKeyStoreMaintenance.java19
-rw-r--r--keystore/java/android/security/KeyStore.java33
-rw-r--r--keystore/java/android/security/keystore/KeyGenParameterSpec.java36
-rw-r--r--libs/WindowManager/Shell/res/values-television/config.xml6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java48
-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/WMShellModule.java4
-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/DefaultMixedHandler.java85
-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/WindowDecoration.java5
-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/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.kt24
-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/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt7
-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/java/com/android/server/accessibility/AccessibilityInputFilter.java10
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java19
-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/ProcessRecord.java4
-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/MovePackageHelper.java21
-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.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java8
-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/com_android_server_pdb_PersistentDataBlockService.cpp133
-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/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--tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt16
-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
472 files changed, 10055 insertions, 4220 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 30b442336148..e1621008cc33 100644
--- a/api/ApiDocs.bp
+++ b/api/ApiDocs.bp
@@ -124,7 +124,6 @@ droidstubs {
"packages/modules/Media/apex/aidl/stable",
],
},
- extensions_info_file: ":sdk-extensions-info",
}
droidstubs {
@@ -132,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 dfe023a3c181..66aeb0f7cbaf 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -3286,10 +3286,10 @@ package android.accessibilityservice {
public abstract class AccessibilityService extends android.app.Service {
ctor public AccessibilityService();
- method @Deprecated public void attachAccessibilityOverlayToDisplay(int, @NonNull android.view.SurfaceControl);
- method public void attachAccessibilityOverlayToDisplay(int, @NonNull android.view.SurfaceControl, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
- method @Deprecated public void attachAccessibilityOverlayToWindow(int, @NonNull android.view.SurfaceControl);
- method public void attachAccessibilityOverlayToWindow(int, @NonNull android.view.SurfaceControl, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
+ method public void attachAccessibilityOverlayToDisplay(int, @NonNull android.view.SurfaceControl);
+ method @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks") public void attachAccessibilityOverlayToDisplay(int, @NonNull android.view.SurfaceControl, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
+ method public void attachAccessibilityOverlayToWindow(int, @NonNull android.view.SurfaceControl);
+ method @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks") public void attachAccessibilityOverlayToWindow(int, @NonNull android.view.SurfaceControl, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
method public boolean clearCache();
method public boolean clearCachedSubtree(@NonNull android.view.accessibility.AccessibilityNodeInfo);
method public final void disableSelf();
@@ -3401,9 +3401,9 @@ package android.accessibilityservice {
field public static final int GLOBAL_ACTION_RECENTS = 3; // 0x3
field public static final int GLOBAL_ACTION_TAKE_SCREENSHOT = 9; // 0x9
field public static final int GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7; // 0x7
- field public static final int OVERLAY_RESULT_INTERNAL_ERROR = 1; // 0x1
- field public static final int OVERLAY_RESULT_INVALID = 2; // 0x2
- field public static final int OVERLAY_RESULT_SUCCESS = 0; // 0x0
+ field @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks") public static final int OVERLAY_RESULT_INTERNAL_ERROR = 1; // 0x1
+ field @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks") public static final int OVERLAY_RESULT_INVALID = 2; // 0x2
+ field @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks") public static final int OVERLAY_RESULT_SUCCESS = 0; // 0x0
field public static final String SERVICE_INTERFACE = "android.accessibilityservice.AccessibilityService";
field public static final String SERVICE_META_DATA = "android.accessibilityservice";
field public static final int SHOW_MODE_AUTO = 0; // 0x0
@@ -9788,7 +9788,7 @@ package android.content {
method @NonNull public android.content.AttributionSource.Builder setAttributionTag(@Nullable String);
method @FlaggedApi("android.permission.flags.device_aware_permission_apis") @NonNull public android.content.AttributionSource.Builder setDeviceId(int);
method @Deprecated @NonNull public android.content.AttributionSource.Builder setNext(@Nullable android.content.AttributionSource);
- method @NonNull public android.content.AttributionSource.Builder setNextAttributionSource(@NonNull android.content.AttributionSource);
+ method @FlaggedApi("android.permission.flags.set_next_attribution_source") @NonNull public android.content.AttributionSource.Builder setNextAttributionSource(@NonNull android.content.AttributionSource);
method @NonNull public android.content.AttributionSource.Builder setPackageName(@Nullable String);
method @NonNull public android.content.AttributionSource.Builder setPid(int);
}
@@ -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 9ecce144f9de..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";
@@ -3555,7 +3555,7 @@ package android.content {
field public static final String ACTION_SHOW_SUSPENDED_APP_DETAILS = "android.intent.action.SHOW_SUSPENDED_APP_DETAILS";
field @Deprecated public static final String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
field public static final String ACTION_SPLIT_CONFIGURATION_CHANGED = "android.intent.action.SPLIT_CONFIGURATION_CHANGED";
- field public static final String ACTION_UNARCHIVE_PACKAGE = "android.intent.action.UNARCHIVE_PACKAGE";
+ field @FlaggedApi("android.content.pm.archiving") public static final String ACTION_UNARCHIVE_PACKAGE = "android.intent.action.UNARCHIVE_PACKAGE";
field public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
field public static final String ACTION_USER_ADDED = "android.intent.action.USER_ADDED";
field public static final String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
@@ -4028,7 +4028,7 @@ package android.content.pm {
field @Deprecated public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1; // 0x1
field @Deprecated public static final int MASK_PERMISSION_FLAGS = 255; // 0xff
field public static final int MATCH_ANY_USER = 4194304; // 0x400000
- field public static final long MATCH_ARCHIVED_PACKAGES = 4294967296L; // 0x100000000L
+ field @FlaggedApi("android.content.pm.archiving") public static final long MATCH_ARCHIVED_PACKAGES = 4294967296L; // 0x100000000L
field public static final int MATCH_CLONE_PROFILE = 536870912; // 0x20000000
field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
field public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 536870912; // 0x20000000
@@ -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 0e857a9c5f93..384b9573528e 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;
@@ -3585,8 +3584,8 @@ package android.view {
field public static final int ACCESSIBILITY_TITLE_CHANGED = 33554432; // 0x2000000
field public static final int FLAG_SLIPPERY = 536870912; // 0x20000000
field public CharSequence accessibilityTitle;
- field public float preferredMaxDisplayRefreshRate;
- field public float preferredMinDisplayRefreshRate;
+ field @FlaggedApi("android.view.flags.wm_display_refresh_rate_test") public float preferredMaxDisplayRefreshRate;
+ field @FlaggedApi("android.view.flags.wm_display_refresh_rate_test") public float preferredMinDisplayRefreshRate;
field public int privateFlags;
}
@@ -3616,7 +3615,6 @@ package android.view.accessibility {
public final class AccessibilityWindowInfo implements android.os.Parcelable {
method public static void setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger);
- field public static final int UNDEFINED_WINDOW_ID = -1; // 0xffffffff
}
}
@@ -3799,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/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 3370c121acfe..1000612ee0e2 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -23,6 +23,7 @@ import android.accessibilityservice.GestureDescription.MotionEventGenerator;
import android.annotation.CallbackExecutor;
import android.annotation.CheckResult;
import android.annotation.ColorInt;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -793,6 +794,7 @@ public abstract class AccessibilityService extends Service {
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
+ @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")
@IntDef(
prefix = {"OVERLAY_RESULT_"},
value = {
@@ -803,6 +805,7 @@ public abstract class AccessibilityService extends Service {
public @interface AttachOverlayResult {}
/** Result code indicating the overlay was successfully attached. */
+ @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")
public static final int OVERLAY_RESULT_SUCCESS = 0;
/**
@@ -810,6 +813,7 @@ public abstract class AccessibilityService extends Service {
* error and not
* because of problems with the input.
*/
+ @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")
public static final int OVERLAY_RESULT_INTERNAL_ERROR = 1;
/**
@@ -817,6 +821,7 @@ public abstract class AccessibilityService extends Service {
* specified display or
* window id was invalid.
*/
+ @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")
public static final int OVERLAY_RESULT_INVALID = 2;
private int mConnectionId = AccessibilityInteractionClient.NO_ID;
@@ -3506,11 +3511,7 @@ public abstract class AccessibilityService extends Service {
* @param displayId the display to which the SurfaceControl should be attached.
* @param sc the SurfaceControl containing the overlay content
*
- * @deprecated Use
- * {@link #attachAccessibilityOverlayToDisplay(int, SurfaceControl, Executor, IntConsumer)}
- * instead.
*/
- @Deprecated
public void attachAccessibilityOverlayToDisplay(int displayId, @NonNull SurfaceControl sc) {
Preconditions.checkNotNull(sc, "SurfaceControl cannot be null");
AccessibilityInteractionClient.getInstance(this)
@@ -3547,6 +3548,7 @@ public abstract class AccessibilityService extends Service {
* @see #OVERLAY_RESULT_INVALID
* @see #OVERLAY_RESULT_INTERNAL_ERROR
*/
+ @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")
public void attachAccessibilityOverlayToDisplay(
int displayId,
@NonNull SurfaceControl sc,
@@ -3581,11 +3583,7 @@ public abstract class AccessibilityService extends Service {
* @param accessibilityWindowId The window id, from {@link AccessibilityWindowInfo#getId()}.
* @param sc the SurfaceControl containing the overlay content
*
- * @deprecated Use
- * {@link #attachAccessibilityOverlayToWindow(int, SurfaceControl, Executor,IntConsumer)}
- * instead.
*/
- @Deprecated
public void attachAccessibilityOverlayToWindow(
int accessibilityWindowId, @NonNull SurfaceControl sc) {
Preconditions.checkNotNull(sc, "SurfaceControl cannot be null");
@@ -3623,6 +3621,7 @@ public abstract class AccessibilityService extends Service {
* @see #OVERLAY_RESULT_INVALID
* @see #OVERLAY_RESULT_INTERNAL_ERROR
*/
+ @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")
public void attachAccessibilityOverlayToWindow(
int accessibilityWindowId,
@NonNull SurfaceControl sc,
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/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index 20eae9ade429..62fbcaff79e3 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -744,6 +744,7 @@ public final class AttributionSource implements Parcelable {
/**
* The next app to receive the permission protected data.
*/
+ @FlaggedApi(Flags.FLAG_SET_NEXT_ATTRIBUTION_SOURCE)
public @NonNull Builder setNextAttributionSource(@NonNull AttributionSource value) {
checkNotUsed();
mBuilderFieldsSet |= 0x20;
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 1eb2cd174a7f..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";
@@ -5323,6 +5325,7 @@ public class Intent implements Parcelable, Cloneable {
* @hide
*/
@SystemApi
+ @FlaggedApi(android.content.pm.Flags.FLAG_ARCHIVING)
public static final String ACTION_UNARCHIVE_PACKAGE = "android.intent.action.UNARCHIVE_PACKAGE";
// ---------------------------------------------------------------------
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/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index f98ed4b8b37a..1b60f8ed904f 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1252,6 +1252,7 @@ public abstract class PackageManager {
*/
// TODO(b/278553670) Unhide and update @links before launch.
@SystemApi
+ @FlaggedApi(android.content.pm.Flags.FLAG_ARCHIVING)
public static final long MATCH_ARCHIVED_PACKAGES = 1L << 32;
/**
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 37559b32e841..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: "android_platform_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 d8534dd5fbc1..3f06a91f6e5b 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -21,3 +21,17 @@ flag {
description: "enable role controller in system server"
bug: "302562590"
}
+
+flag {
+ name: "set_next_attribution_source"
+ namespace: "permissions"
+ 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/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/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..9d15c78a6e9e 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;
}
@@ -11810,4 +11896,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/WindowManager.java b/core/java/android/view/WindowManager.java
index a3b93b433dda..4f03ce98ccac 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -17,6 +17,7 @@
package android.view;
import static android.content.pm.ActivityInfo.COLOR_MODE_DEFAULT;
+import static android.view.flags.Flags.FLAG_WM_DISPLAY_REFRESH_RATE_TEST;
import static android.view.View.STATUS_BAR_DISABLE_BACK;
import static android.view.View.STATUS_BAR_DISABLE_CLOCK;
import static android.view.View.STATUS_BAR_DISABLE_EXPAND;
@@ -3905,6 +3906,7 @@ public interface WindowManager extends ViewManager {
* This value is ignored if {@link #preferredDisplayModeId} is set.
* @hide
*/
+ @FlaggedApi(FLAG_WM_DISPLAY_REFRESH_RATE_TEST)
@TestApi
public float preferredMinDisplayRefreshRate;
@@ -3914,6 +3916,7 @@ public interface WindowManager extends ViewManager {
* This value is ignored if {@link #preferredDisplayModeId} is set.
* @hide
*/
+ @FlaggedApi(FLAG_WM_DISPLAY_REFRESH_RATE_TEST)
@TestApi
public float preferredMaxDisplayRefreshRate;
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 60f46e60ec9e..12ce0f47c460 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -1731,6 +1731,9 @@ public final class AccessibilityInteractionClient
@Override
public void sendAttachOverlayResult(
@AccessibilityService.AttachOverlayResult int result, int interactionId) {
+ if (!Flags.a11yOverlayCallbacks()) {
+ return;
+ }
synchronized (mInstanceLock) {
if (mAttachAccessibilityOverlayCallbacks.contains(interactionId)) {
final Pair<Executor, IntConsumer> pair =
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index 09b2f9c1ee15..fa0052cf664a 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -99,7 +99,6 @@ public final class AccessibilityWindowInfo implements Parcelable {
/** @hide */
public static final int UNDEFINED_CONNECTION_ID = -1;
/** @hide */
- @TestApi
public static final int UNDEFINED_WINDOW_ID = -1;
/** @hide */
public static final int ANY_WINDOW_ID = -2;
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index 85dadd4a061d..cc612ed93b2f 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -13,3 +13,10 @@ flag {
description: "Allows the a11y shortcut disambig dialog to appear on the lockscreen"
bug: "303871725"
}
+
+flag {
+ name: "a11y_overlay_callbacks"
+ namespace: "accessibility"
+ description: "Whether to allow the passing of result callbacks when attaching a11y overlays."
+ bug: "304478691"
+}
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/flags/refresh_rate_flags.aconfig b/core/java/android/view/flags/refresh_rate_flags.aconfig
index 56b5fac1c64a..fd9689013af3 100644
--- a/core/java/android/view/flags/refresh_rate_flags.aconfig
+++ b/core/java/android/view/flags/refresh_rate_flags.aconfig
@@ -26,4 +26,11 @@ flag {
namespace: "core_graphics"
description: "Enable the `setFrameRate` callback"
bug: "299946220"
+}
+
+flag {
+ name: "wm_display_refresh_rate_test"
+ namespace: "core_graphics"
+ description: "Adds WindowManager display refresh rate fields to test API"
+ bug: "304475199"
} \ No newline at end of file
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 88b578ba772b..cffbaa75ab76 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -6123,7 +6123,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" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b211ac2fd316..929133f792c0 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>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 76744ea67589..2b9194f8c54e 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" />
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/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
index 31c2eb2efaed..b7ea04fdfe07 100644
--- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java
+++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
@@ -128,25 +128,6 @@ public class AndroidKeyStoreMaintenance {
}
/**
- * Queries user state from Keystore 2.0.
- *
- * @param userId - Android user id of the user.
- * @return UserState enum variant as integer if successful or an error
- */
- public static int getState(int userId) {
- StrictMode.noteDiskRead();
- try {
- return getService().getState(userId);
- } catch (ServiceSpecificException e) {
- Log.e(TAG, "getState failed", e);
- return e.errorCode;
- } catch (Exception e) {
- Log.e(TAG, "Can not connect to keystore", e);
- return SYSTEM_ERROR;
- }
- }
-
- /**
* Informs Keystore 2.0 that an off body event was detected.
*/
public static void onDeviceOffBody() {
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 8045f55f6b4c..11b827117aa3 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -19,8 +19,6 @@ package android.security;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.StrictMode;
-import android.os.UserHandle;
-import android.security.maintenance.UserState;
/**
* @hide This should not be made public in its present form because it
@@ -37,15 +35,6 @@ public class KeyStore {
// Used for UID field to indicate the calling UID.
public static final int UID_SELF = -1;
- // States
- public enum State {
- @UnsupportedAppUsage
- UNLOCKED,
- @UnsupportedAppUsage
- LOCKED,
- UNINITIALIZED
- };
-
private static final KeyStore KEY_STORE = new KeyStore();
@UnsupportedAppUsage
@@ -55,28 +44,6 @@ public class KeyStore {
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public State state(int userId) {
- int userState = AndroidKeyStoreMaintenance.getState(userId);
- switch (userState) {
- case UserState.UNINITIALIZED:
- return KeyStore.State.UNINITIALIZED;
- case UserState.LSKF_UNLOCKED:
- return KeyStore.State.UNLOCKED;
- case UserState.LSKF_LOCKED:
- return KeyStore.State.LOCKED;
- default:
- throw new AssertionError(userState);
- }
- }
-
- /** @hide */
- @UnsupportedAppUsage
- public State state() {
- return state(UserHandle.myUserId());
- }
-
- /** @hide */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public byte[] get(String key) {
return null;
}
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/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/activityembedding/ActivityEmbeddingController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
index 06ce37148eaf..8cf869b175ef 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
@@ -87,33 +87,28 @@ public class ActivityEmbeddingController implements Transitions.TransitionHandle
mTransitions.addHandler(this);
}
- @Override
- public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction startTransaction,
- @NonNull SurfaceControl.Transaction finishTransaction,
- @NonNull Transitions.TransitionFinishCallback finishCallback) {
- boolean containsEmbeddingSplit = false;
- boolean containsNonEmbeddedChange = false;
- final List<TransitionInfo.Change> changes = info.getChanges();
- for (int i = changes.size() - 1; i >= 0; i--) {
- final TransitionInfo.Change change = changes.get(i);
- if (!change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) {
- containsNonEmbeddedChange = true;
- } else if (!change.hasFlags(FLAG_FILLS_TASK)) {
+ /** Whether ActivityEmbeddingController should animate this transition. */
+ public boolean shouldAnimate(@NonNull TransitionInfo info) {
+ boolean containsEmbeddingChange = false;
+ for (TransitionInfo.Change change : info.getChanges()) {
+ if (!change.hasFlags(FLAG_FILLS_TASK) && change.hasFlags(
+ FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) {
// Whether the Task contains any ActivityEmbedding split before or after the
// transition.
- containsEmbeddingSplit = true;
+ containsEmbeddingChange = true;
}
}
- if (!containsEmbeddingSplit) {
+ if (!containsEmbeddingChange) {
// Let the system to play the default animation if there is no ActivityEmbedding split
// window. This allows to play the app customized animation when there is no embedding,
// such as the device is in a folded state.
return false;
}
- if (containsNonEmbeddedChange && !handleNonEmbeddedChanges(changes)) {
+
+ if (containsNonEmbeddedChange(info) && !handleNonEmbeddedChanges(info.getChanges())) {
return false;
}
+
final TransitionInfo.AnimationOptions options = info.getAnimationOptions();
if (options != null
// Scene-transition will be handled by app side.
@@ -123,6 +118,17 @@ public class ActivityEmbeddingController implements Transitions.TransitionHandle
return false;
}
+ return true;
+ }
+
+ @Override
+ public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+
+ if (!shouldAnimate(info)) return false;
+
// Start ActivityEmbedding animation.
mTransitionCallbacks.put(transition, finishCallback);
mAnimationRunner.startAnimation(transition, info, startTransaction, finishTransaction);
@@ -136,6 +142,16 @@ public class ActivityEmbeddingController implements Transitions.TransitionHandle
mAnimationRunner.cancelAnimationFromMerge();
}
+ /** Whether TransitionInfo contains non-ActivityEmbedding embedded window. */
+ private boolean containsNonEmbeddedChange(@NonNull TransitionInfo info) {
+ for (TransitionInfo.Change change : info.getChanges()) {
+ if (!change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private boolean handleNonEmbeddedChanges(List<TransitionInfo.Change> changes) {
final Rect nonClosingEmbeddedArea = new Rect();
for (int i = changes.size() - 1; i >= 0; i--) {
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/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 14a040a40874..a533ca5fa8fb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -32,6 +32,7 @@ import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
+import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
import com.android.wm.shell.bubbles.BubbleController;
import com.android.wm.shell.bubbles.BubbleData;
import com.android.wm.shell.bubbles.BubbleDataRepository;
@@ -366,11 +367,12 @@ public abstract class WMShellModule {
KeyguardTransitionHandler keyguardTransitionHandler,
Optional<DesktopTasksController> desktopTasksController,
Optional<UnfoldTransitionHandler> unfoldHandler,
+ Optional<ActivityEmbeddingController> activityEmbeddingController,
Transitions transitions) {
return new DefaultMixedHandler(shellInit, transitions, splitScreenOptional,
pipTransitionController, recentsTransitionHandler,
keyguardTransitionHandler, desktopTasksController,
- unfoldHandler);
+ unfoldHandler, activityEmbeddingController);
}
@WMSingleton
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/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 451e61855943..918a5a4bd53e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -21,7 +21,9 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_CHANGE;
+import static android.view.WindowManager.TRANSIT_PIP;
import static android.view.WindowManager.TRANSIT_TO_BACK;
+import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
@@ -43,6 +45,7 @@ import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
import com.android.wm.shell.common.split.SplitScreenUtils;
import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopTasksController;
@@ -74,6 +77,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
private final KeyguardTransitionHandler mKeyguardHandler;
private DesktopTasksController mDesktopTasksController;
private UnfoldTransitionHandler mUnfoldHandler;
+ private ActivityEmbeddingController mActivityEmbeddingController;
private static class MixedTransition {
static final int TYPE_ENTER_PIP_FROM_SPLIT = 1;
@@ -93,9 +97,12 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
/** Recents Transition while in desktop mode. */
static final int TYPE_RECENTS_DURING_DESKTOP = 6;
- /** Fuld/Unfold transition. */
+ /** Fold/Unfold transition. */
static final int TYPE_UNFOLD = 7;
+ /** Enter pip from one of the Activity Embedding windows. */
+ static final int TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING = 8;
+
/** The default animation for this mixed transition. */
static final int ANIM_TYPE_DEFAULT = 0;
@@ -150,7 +157,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
Optional<RecentsTransitionHandler> recentsHandlerOptional,
KeyguardTransitionHandler keyguardHandler,
Optional<DesktopTasksController> desktopTasksControllerOptional,
- Optional<UnfoldTransitionHandler> unfoldHandler) {
+ Optional<UnfoldTransitionHandler> unfoldHandler,
+ Optional<ActivityEmbeddingController> activityEmbeddingController) {
mPlayer = player;
mKeyguardHandler = keyguardHandler;
if (Transitions.ENABLE_SHELL_TRANSITIONS
@@ -170,6 +178,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
}
mDesktopTasksController = desktopTasksControllerOptional.orElse(null);
mUnfoldHandler = unfoldHandler.orElse(null);
+ mActivityEmbeddingController = activityEmbeddingController.orElse(null);
}, this);
}
}
@@ -192,6 +201,16 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
mPipHandler.augmentRequest(transition, request, out);
mSplitHandler.addEnterOrExitIfNeeded(request, out);
return out;
+ } else if (request.getType() == TRANSIT_PIP
+ && (request.getFlags() & FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY) != 0 && (
+ mActivityEmbeddingController != null)) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ " Got a PiP-enter request from an Activity Embedding split");
+ mActiveTransitions.add(new MixedTransition(
+ MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING, transition));
+ // Postpone transition splitting to later.
+ WindowContainerTransaction out = new WindowContainerTransaction();
+ return out;
} else if (request.getRemoteTransition() != null
&& TransitionUtil.isOpeningType(request.getType())
&& (request.getTriggerTask() == null
@@ -355,6 +374,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
return animateEnterPipFromSplit(mixed, info, startTransaction, finishTransaction,
finishCallback);
+ } else if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) {
+ return animateEnterPipFromActivityEmbedding(mixed, info, startTransaction,
+ finishTransaction, finishCallback);
} else if (mixed.mType == MixedTransition.TYPE_DISPLAY_AND_SPLIT_CHANGE) {
return false;
} else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) {
@@ -400,6 +422,58 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
}
}
+ private boolean animateEnterPipFromActivityEmbedding(@NonNull MixedTransition mixed,
+ @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animating a mixed transition for "
+ + "entering PIP from an Activity Embedding window");
+ // Split into two transitions (wct)
+ TransitionInfo.Change pipChange = null;
+ final TransitionInfo everythingElse = subCopy(info, TRANSIT_TO_BACK, true /* changes */);
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ TransitionInfo.Change change = info.getChanges().get(i);
+ if (mPipHandler.isEnteringPip(change, info.getType())) {
+ if (pipChange != null) {
+ throw new IllegalStateException("More than 1 pip-entering changes in one"
+ + " transition? " + info);
+ }
+ pipChange = change;
+ // going backwards, so remove-by-index is fine.
+ everythingElse.getChanges().remove(i);
+ }
+ }
+
+ final Transitions.TransitionFinishCallback finishCB = (wct) -> {
+ --mixed.mInFlightSubAnimations;
+ mixed.joinFinishArgs(wct);
+ if (mixed.mInFlightSubAnimations > 0) return;
+ mActiveTransitions.remove(mixed);
+ finishCallback.onTransitionFinished(mixed.mFinishWCT);
+ };
+
+ if (!mActivityEmbeddingController.shouldAnimate(everythingElse)) {
+ // Fallback to dispatching to other handlers.
+ return false;
+ }
+
+ // PIP window should always be on the highest Z order.
+ if (pipChange != null) {
+ mixed.mInFlightSubAnimations = 2;
+ mPipHandler.startEnterAnimation(
+ pipChange, startTransaction.setLayer(pipChange.getLeash(), Integer.MAX_VALUE),
+ finishTransaction,
+ finishCB);
+ } else {
+ mixed.mInFlightSubAnimations = 1;
+ }
+
+ mActivityEmbeddingController.startAnimation(mixed.mTransition, everythingElse,
+ startTransaction, finishTransaction, finishCB);
+ return true;
+ }
+
private boolean animateOpenIntentWithRemoteAndPip(@NonNull MixedTransition mixed,
@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@@ -811,6 +885,10 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
} else {
mPipHandler.end();
}
+ } else if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) {
+ mPipHandler.end();
+ mActivityEmbeddingController.mergeAnimation(transition, info, t, mergeTarget,
+ finishCallback);
} else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) {
mPipHandler.end();
if (mixed.mLeftoversHandler != null) {
@@ -851,6 +929,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
if (mixed == null) return;
if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
mPipHandler.onTransitionConsumed(transition, aborted, finishT);
+ } else if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING) {
+ mPipHandler.onTransitionConsumed(transition, aborted, finishT);
+ mActivityEmbeddingController.onTransitionConsumed(transition, aborted, finishT);
} else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) {
mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT);
} else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) {
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/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/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/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 4764f223d121..ae0c8da73c40 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")
@@ -259,7 +263,7 @@ object Flags {
// TODO(b/290652751): Tracking bug.
@JvmField
val MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA =
- unreleasedFlag("migrate_split_keyguard_bottom_area", teamfood = true)
+ releasedFlag("migrate_split_keyguard_bottom_area")
// TODO(b/297037052): Tracking bug.
@JvmField
@@ -274,7 +278,7 @@ object Flags {
/** Migrate the lock icon view to the new keyguard root view. */
// TODO(b/286552209): Tracking bug.
- @JvmField val MIGRATE_LOCK_ICON = unreleasedFlag("migrate_lock_icon", teamfood = true)
+ @JvmField val MIGRATE_LOCK_ICON = releasedFlag("migrate_lock_icon")
// TODO(b/288276738): Tracking bug.
@JvmField val WIDGET_ON_KEYGUARD = unreleasedFlag("widget_on_keyguard")
@@ -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")
@@ -503,10 +507,9 @@ object Flags {
@Keep
@JvmField
val WM_ENABLE_PARTIAL_SCREEN_SHARING =
- unreleasedFlag(
- name = "record_task_content",
+ releasedFlag(
+ name = "enable_record_task_content",
namespace = DeviceConfig.NAMESPACE_WINDOW_MANAGER,
- teamfood = true
)
// TODO(b/254512674): Tracking Bug
@@ -770,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)
@@ -797,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
@@ -818,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/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt
index 9371d4e2d465..342a440d972b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt
@@ -50,11 +50,8 @@ constructor(
override fun addViews(constraintLayout: ConstraintLayout) {
if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
- val view =
- LayoutInflater.from(constraintLayout.context)
- .inflate(R.layout.ambient_indication, constraintLayout, false)
-
- constraintLayout.addView(view)
+ LayoutInflater.from(constraintLayout.context)
+ .inflate(R.layout.ambient_indication, constraintLayout, true)
}
}
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/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 203d9a1e72cd..e65a185adfd2 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2794,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;
@@ -3148,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;
@@ -3393,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);
@@ -3422,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);
@@ -5107,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/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 3771c05a294e..f02b8c737f90 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -511,8 +511,8 @@ class ProcessRecord implements WindowProcessListener {
pw.print(prefix); pw.print("pid="); pw.println(mPid);
pw.print(prefix); pw.print("lastActivityTime=");
TimeUtils.formatDuration(mLastActivityTime, nowUptime, pw);
- pw.print(prefix); pw.print("startUptimeTime=");
- TimeUtils.formatDuration(mStartElapsedTime, nowUptime, pw);
+ pw.print(prefix); pw.print("startUpTime=");
+ TimeUtils.formatDuration(mStartUptime, nowUptime, pw);
pw.print(prefix); pw.print("startElapsedTime=");
TimeUtils.formatDuration(mStartElapsedTime, nowElapsedTime, pw);
pw.println();
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/MovePackageHelper.java b/services/core/java/com/android/server/pm/MovePackageHelper.java
index 148e0df7cf64..9ad8318c2b5f 100644
--- a/services/core/java/com/android/server/pm/MovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/MovePackageHelper.java
@@ -89,6 +89,21 @@ public final class MovePackageHelper {
if (packageState == null || packageState.getPkg() == null) {
throw new PackageManagerException(MOVE_FAILED_DOESNT_EXIST, "Missing package");
}
+ final int[] installedUserIds = PackageStateUtils.queryInstalledUsers(packageState,
+ mPm.mUserManager.getUserIds(), true);
+ final UserHandle userForMove;
+ if (installedUserIds.length > 0) {
+ userForMove = UserHandle.of(installedUserIds[0]);
+ } else {
+ throw new PackageManagerException(MOVE_FAILED_DOESNT_EXIST,
+ "Package is not installed for any user");
+ }
+ for (int userId : installedUserIds) {
+ if (snapshot.shouldFilterApplicationIncludingUninstalled(packageState, callingUid,
+ userId)) {
+ throw new PackageManagerException(MOVE_FAILED_DOESNT_EXIST, "Missing package");
+ }
+ }
final AndroidPackage pkg = packageState.getPkg();
if (packageState.isSystem()) {
throw new PackageManagerException(MOVE_FAILED_SYSTEM_PACKAGE,
@@ -134,8 +149,6 @@ public final class MovePackageHelper {
final String label = String.valueOf(pm.getApplicationLabel(
AndroidPackageUtils.generateAppInfoWithoutState(pkg)));
final int targetSdkVersion = pkg.getTargetSdkVersion();
- final int[] installedUserIds = PackageStateUtils.queryInstalledUsers(packageState,
- mPm.mUserManager.getUserIds(), true);
final String fromCodePath;
if (codeFile.getParentFile().getName().startsWith(
PackageManagerService.RANDOM_DIR_PREFIX)) {
@@ -303,8 +316,8 @@ public final class MovePackageHelper {
final PackageLite lite = ret.isSuccess() ? ret.getResult() : null;
final InstallingSession installingSession = new InstallingSession(origin, move,
installObserver, installFlags, /* developmentInstallFlags= */ 0, installSource,
- volumeUuid, user, packageAbiOverride, PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED,
- lite, mPm);
+ volumeUuid, userForMove, packageAbiOverride,
+ PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED, lite, mPm);
installingSession.movePackage();
}
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..4200fbf39ade 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -3982,7 +3982,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 0b673211a1c9..c02178506052 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -68,6 +68,7 @@ import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_PIP;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT;
+import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_DREAM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
@@ -3001,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;
}
@@ -3686,6 +3687,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
getTransitionController(), mWindowManager.mSyncEngine)
: null;
+ if (r.getTaskFragment() != null && r.getTaskFragment().isEmbeddedWithBoundsOverride()
+ && transition != null) {
+ transition.addFlag(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY);
+ }
+
final Runnable enterPipRunnable = () -> {
synchronized (mGlobalLock) {
if (r.getParent() == null) {
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/com_android_server_pdb_PersistentDataBlockService.cpp b/services/core/jni/com_android_server_pdb_PersistentDataBlockService.cpp
index fc5a11360c8c..1e3cfd049e65 100644
--- a/services/core/jni/com_android_server_pdb_PersistentDataBlockService.cpp
+++ b/services/core/jni/com_android_server_pdb_PersistentDataBlockService.cpp
@@ -15,106 +15,99 @@
*/
#include <android_runtime/AndroidRuntime.h>
-#include <nativehelper/JNIHelp.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
#include <jni.h>
+#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedUtfChars.h>
-
-#include <utils/misc.h>
+#include <string.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <utils/Log.h>
+#include <utils/misc.h>
+namespace android {
-#include <inttypes.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <string.h>
+uint64_t get_block_device_size(int fd) {
+ uint64_t size = 0;
+ int ret;
-namespace android {
+ ret = ioctl(fd, BLKGETSIZE64, &size);
- uint64_t get_block_device_size(int fd)
- {
- uint64_t size = 0;
- int ret;
+ if (ret) return 0;
- ret = ioctl(fd, BLKGETSIZE64, &size);
+ return size;
+}
- if (ret)
- return 0;
+int wipe_block_device(int fd) {
+ uint64_t range[2];
+ int ret;
+ uint64_t len = get_block_device_size(fd);
- return size;
- }
+ range[0] = 0;
+ range[1] = len;
- int wipe_block_device(int fd)
- {
- uint64_t range[2];
- int ret;
- uint64_t len = get_block_device_size(fd);
+ if (range[1] == 0) return 0;
+ ret = ioctl(fd, BLKSECDISCARD, &range);
+ if (ret < 0) {
+ ALOGE("Something went wrong secure discarding block: %s\n", strerror(errno));
range[0] = 0;
range[1] = len;
-
- if (range[1] == 0)
- return 0;
-
- ret = ioctl(fd, BLKSECDISCARD, &range);
+ ret = ioctl(fd, BLKDISCARD, &range);
if (ret < 0) {
- ALOGE("Something went wrong secure discarding block: %s\n", strerror(errno));
- range[0] = 0;
- range[1] = len;
- ret = ioctl(fd, BLKDISCARD, &range);
- if (ret < 0) {
- ALOGE("Discard failed: %s\n", strerror(errno));
- return -1;
- } else {
- ALOGE("Wipe via secure discard failed, used non-secure discard instead\n");
- return 0;
- }
-
+ ALOGE("Discard failed: %s\n", strerror(errno));
+ return -1;
+ } else {
+ ALOGE("Wipe via secure discard failed, used non-secure discard instead\n");
+ return 0;
}
-
- return ret;
}
- static jlong com_android_server_pdb_PersistentDataBlockService_getBlockDeviceSize(JNIEnv *env, jclass, jstring jpath)
- {
- ScopedUtfChars path(env, jpath);
- int fd = open(path.c_str(), O_RDONLY);
+ return ret;
+}
- if (fd < 0)
- return 0;
+static jlong com_android_server_pdb_PersistentDataBlockService_getBlockDeviceSize(JNIEnv *env,
+ jclass,
+ jstring jpath) {
+ ScopedUtfChars path(env, jpath);
+ int fd = open(path.c_str(), O_RDONLY);
- const uint64_t size = get_block_device_size(fd);
+ if (fd < 0) return 0;
- close(fd);
+ const uint64_t size = get_block_device_size(fd);
- return size;
- }
+ close(fd);
- static int com_android_server_pdb_PersistentDataBlockService_wipe(JNIEnv *env, jclass, jstring jpath) {
- ScopedUtfChars path(env, jpath);
- int fd = open(path.c_str(), O_WRONLY);
+ return size;
+}
- if (fd < 0)
- return 0;
+static int com_android_server_pdb_PersistentDataBlockService_wipe(JNIEnv *env, jclass,
+ jstring jpath) {
+ ScopedUtfChars path(env, jpath);
+ int fd = open(path.c_str(), O_WRONLY);
- const int ret = wipe_block_device(fd);
+ if (fd < 0) return 0;
- close(fd);
+ const int ret = wipe_block_device(fd);
- return ret;
- }
+ close(fd);
- static const JNINativeMethod sMethods[] = {
- /* name, signature, funcPtr */
- {"nativeGetBlockDeviceSize", "(Ljava/lang/String;)J", (void*)com_android_server_pdb_PersistentDataBlockService_getBlockDeviceSize},
- {"nativeWipe", "(Ljava/lang/String;)I", (void*)com_android_server_pdb_PersistentDataBlockService_wipe},
- };
+ return ret;
+}
- int register_android_server_pdb_PersistentDataBlockService(JNIEnv* env)
- {
- return jniRegisterNativeMethods(env, "com/android/server/pdb/PersistentDataBlockService",
- sMethods, NELEM(sMethods));
- }
+static const JNINativeMethod sMethods[] = {
+ /* name, signature, funcPtr */
+ {"nativeGetBlockDeviceSize", "(Ljava/lang/String;)J",
+ (void *)com_android_server_pdb_PersistentDataBlockService_getBlockDeviceSize},
+ {"nativeWipe", "(Ljava/lang/String;)I",
+ (void *)com_android_server_pdb_PersistentDataBlockService_wipe},
+};
+
+int register_android_server_pdb_PersistentDataBlockService(JNIEnv *env) {
+ return jniRegisterNativeMethods(env, "com/android/server/pdb/PersistentDataBlockService",
+ sMethods, NELEM(sMethods));
+}
} /* namespace android */ \ No newline at end of file
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/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/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
index 359845dc0de6..47d6d235848f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
@@ -36,7 +36,8 @@ import org.junit.runners.Parameterized
/**
* Test launching a secondary Activity into Picture-In-Picture mode.
*
- * Setup: Start from a split A|B. Transition: B enters PIP, observe the window shrink to the bottom
+ * Setup: Start from a split A|B.
+ * Transition: B enters PIP, observe the window first goes fullscreen then shrink to the bottom
* right corner on screen.
*
* To run this test: `atest FlickerTestsOther:SecondaryActivityEnterPipTest`
@@ -63,7 +64,16 @@ class SecondaryActivityEnterPipTest(flicker: LegacyFlickerTest) :
}
}
- /** Main and secondary activity start from a split each taking half of the screen. */
+ /**
+ * We expect the background layer to be visible during this transition.
+ */
+ @Presubmit
+ @Test
+ override fun backgroundLayerNeverVisible(): Unit {}
+
+ /**
+ * Main and secondary activity start from a split each taking half of the screen.
+ */
@Presubmit
@Test
fun layersStartFromEqualSplit() {
@@ -109,7 +119,7 @@ class SecondaryActivityEnterPipTest(flicker: LegacyFlickerTest) :
.isVisible(TRANSITION_SNAPSHOT)
.isInvisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
.then()
- .isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ .isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT, isOptional = true)
}
flicker.assertLayersEnd {
visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
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.