summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/Activity.java20
-rw-r--r--core/java/android/content/IntentFilter.java10
-rw-r--r--core/java/android/content/pm/CrossProfileApps.java5
-rw-r--r--core/java/android/os/UserManager.java14
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java7
-rw-r--r--core/java/android/text/flags/flags.aconfig9
-rw-r--r--core/java/android/util/FeatureFlagUtils.java2
-rw-r--r--core/java/android/view/ViewRootImpl.java5
-rw-r--r--core/java/android/view/WindowManager.java6
-rw-r--r--core/java/android/view/autofill/AutofillManager.java6
-rw-r--r--core/java/android/window/BackNavigationInfo.java29
-rw-r--r--core/java/android/window/flags/wallpaper_manager.aconfig9
-rw-r--r--core/java/com/android/internal/policy/PhoneWindow.java27
-rw-r--r--core/java/com/android/internal/widget/ActionBarOverlayLayout.java7
-rw-r--r--core/java/com/android/internal/widget/DecorContentParent.java2
-rw-r--r--core/jni/android_text_Hyphenator.cpp24
-rw-r--r--core/res/res/drawable/ic_zen_mode_type_bedtime.xml25
-rw-r--r--core/res/res/drawable/ic_zen_mode_type_driving.xml25
-rw-r--r--core/res/res/drawable/ic_zen_mode_type_immersive.xml25
-rw-r--r--core/res/res/drawable/ic_zen_mode_type_managed.xml25
-rw-r--r--core/res/res/drawable/ic_zen_mode_type_other.xml25
-rw-r--r--core/res/res/drawable/ic_zen_mode_type_schedule_calendar.xml25
-rw-r--r--core/res/res/drawable/ic_zen_mode_type_schedule_time.xml26
-rw-r--r--core/res/res/drawable/ic_zen_mode_type_theater.xml25
-rw-r--r--core/res/res/drawable/ic_zen_mode_type_unknown.xml25
-rw-r--r--core/res/res/values/config.xml10
-rw-r--r--core/res/res/values/dimens.xml3
-rw-r--r--core/res/res/values/symbols.xml14
-rw-r--r--core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml3
-rw-r--r--core/tests/batterystatstests/BatteryStatsViewer/res/drawable/ic_launcher_background.xml170
-rw-r--r--core/tests/batterystatstests/BatteryStatsViewer/res/drawable/ic_launcher_foreground.xml42
-rw-r--r--core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_picker_layout.xml1
-rw-r--r--core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml2
-rw-r--r--core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-anydpi-v26/ic_launcher.xml5
-rw-r--r--core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-anydpi-v26/ic_launcher_round.xml5
-rw-r--r--core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-anydpi/ic_launcher.xml6
-rw-r--r--core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-anydpi/ic_launcher_round.xml5
-rw-r--r--core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-hdpi/ic_launcher.webpbin0 -> 2886 bytes
-rw-r--r--core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-hdpi/ic_launcher_foreground.webpbin0 -> 8446 bytes
-rw-r--r--core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-hdpi/ic_launcher_round.webpbin0 -> 5268 bytes
-rw-r--r--core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-mdpi/ic_launcher.webpbin0 -> 1744 bytes
-rw-r--r--core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-mdpi/ic_launcher_foreground.webpbin0 -> 4370 bytes
-rw-r--r--core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-mdpi/ic_launcher_round.webpbin0 -> 3104 bytes
-rw-r--r--core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xhdpi/ic_launcher.webpbin0 -> 4502 bytes
-rw-r--r--core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xhdpi/ic_launcher_foreground.webpbin0 -> 13518 bytes
-rw-r--r--core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xhdpi/ic_launcher_round.webpbin0 -> 7884 bytes
-rw-r--r--core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxhdpi/ic_launcher.webpbin0 -> 8348 bytes
-rw-r--r--core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxhdpi/ic_launcher_foreground.webpbin0 -> 25966 bytes
-rw-r--r--core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxhdpi/ic_launcher_round.webpbin0 -> 14420 bytes
-rw-r--r--core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxxhdpi/ic_launcher.webpbin0 -> 13130 bytes
-rw-r--r--core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxxhdpi/ic_launcher_foreground.webpbin0 -> 41658 bytes
-rw-r--r--core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxxhdpi/ic_launcher_round.webpbin0 -> 21102 bytes
-rw-r--r--core/tests/batterystatstests/BatteryStatsViewer/res/values/ic_launcher_background.xml4
-rw-r--r--core/tests/batterystatstests/BatteryStatsViewer/res/values/styles.xml1
-rw-r--r--data/etc/privapp-permissions-platform.xml27
-rw-r--r--libs/WindowManager/Shell/aconfig/multitasking.aconfig10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt70
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt165
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt117
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt7
-rw-r--r--packages/EasterEgg/src/com/android/egg/landroid/MainActivity.kt24
-rw-r--r--packages/SettingsLib/Spa/build.gradle.kts2
-rw-r--r--packages/SettingsLib/Spa/gradle/libs.versions.toml2
-rw-r--r--packages/SettingsLib/Spa/gradle/wrapper/gradle-8.8-bin.zip (renamed from packages/SettingsLib/Spa/gradle/wrapper/gradle-8.7-bin.zip)bin134184980 -> 138039528 bytes
-rw-r--r--packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jarbin43462 -> 43453 bytes
-rw-r--r--packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties2
-rwxr-xr-xpackages/SettingsLib/Spa/gradlew2
-rw-r--r--packages/SettingsLib/Spa/settings.gradle.kts3
-rw-r--r--packages/SettingsLib/Spa/spa/build.gradle.kts8
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt5
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt5
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaLogger.kt1
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/PageLogger.kt4
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBarTest.kt2
-rw-r--r--packages/SystemUI/Android.bp2
-rw-r--r--packages/SystemUI/OWNERS6
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelTest.kt57
-rw-r--r--packages/SystemUI/res/values/strings.xml4
-rw-r--r--packages/SystemUI/src/com/android/systemui/ScreenDecorations.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/FpsUnlockTracker.kt208
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/DeviceEntryUnlockTrackerViewBinder.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepository.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegate.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelper.kt84
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegate.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt185
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/model/OngoingCallModel.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/data/repository/VolumeDialogRepository.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractor.kt43
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt56
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt55
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegateTest.kt80
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt144
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegateTest.kt79
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt160
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/volume/data/repository/VolumeDialogRepositoryTest.kt69
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractorTest.kt67
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/keyguard/KeyguardUnlockAnimationControllerKosmos.kt24
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperKosmos.kt31
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/VolumeDialogRepositoryKosmos.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt)5
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractorKosmos.kt23
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java61
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java86
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java5
-rw-r--r--services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java92
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java50
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java8
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/FillUi.java6
-rw-r--r--services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java23
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java9
-rw-r--r--services/core/java/com/android/server/am/UserController.java6
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java11
-rw-r--r--services/core/java/com/android/server/biometrics/AuthSession.java57
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java14
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java2
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodBindingController.java12
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java106
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodSettings.java2
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodSettingsRepository.java2
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java13
-rw-r--r--services/core/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java8
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java17
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java4
-rw-r--r--services/core/java/com/android/server/power/ThermalManagerService.java12
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java20
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java15
-rw-r--r--services/core/java/com/android/server/wm/BackNavigationController.java10
-rw-r--r--services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java196
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java32
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java7
-rw-r--r--services/core/java/com/android/server/wm/LetterboxUiController.java18
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java2
-rw-r--r--services/core/java/com/android/server/wm/Task.java5
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp5
-rw-r--r--services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java10
-rw-r--r--services/tests/ondeviceintelligencetests/Android.bp62
-rw-r--r--services/tests/ondeviceintelligencetests/AndroidManifest.xml30
-rw-r--r--services/tests/ondeviceintelligencetests/AndroidTest.xml30
-rw-r--r--services/tests/ondeviceintelligencetests/src/com/android/server/ondeviceintelligence/InferenceInfoStoreTest.java105
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java84
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java118
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java55
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java69
-rw-r--r--services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java378
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java357
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java29
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java29
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskTests.java12
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategory.java6
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategoryFactory.java27
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabels.java37
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabelsFactory.java118
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataTypeFactory.java24
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabel.java16
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabelFactory.java10
-rw-r--r--tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/XmlUtils.java34
-rw-r--r--tools/app_metadata_bundles/src/test/java/com/android/asllib/AllTests.java3
-rw-r--r--tools/app_metadata_bundles/src/test/java/com/android/asllib/AslgenTests.java3
-rw-r--r--tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataCategoryTest.java212
-rw-r--r--tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataLabelsTest.java32
-rw-r--r--tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SystemAppSafetyLabelTest.java12
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/with-system-app-safety-label.xml2
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/with-system-app-safety-label.xml2
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-actions-in-app.xml15
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-app-performance.xml9
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-audio.xml9
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-calendar.xml3
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-contacts.xml3
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-email-text-message.xml9
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-financial.xml12
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-health-fitness.xml6
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-identifiers.xml3
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-location.xml6
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-empty-purpose.xml3
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-missing-purpose.xml3
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-partial.xml6
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-unrecognized-type.xml3
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal.xml28
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-photo-video.xml6
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-search-and-browsing.xml3
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-storage.xml3
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-unrecognized-type.xml3
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-unrecognized.xml3
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-collected-shared.xml9
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-invalid-bool.xml4
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-valid-bool.xml4
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-ephemeral-collision.xml14
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-ephemeral.xml14
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-invalid-bool.xml4
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-valid-bool.xml4
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-shared-invalid-bool.xml4
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-shared-valid-bool.xml4
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal-partial.xml24
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal.xml1
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-accessed-valid-bool.xml1
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-collected-ephemeral.xml38
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-shared-valid-bool.xml1
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-data-labels.xml4
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-data-labels.xml1
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/hr/missing-bool.xml (renamed from tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/hr/missing-url.xml)0
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/hr/valid.xml2
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/missing-bool.xml (renamed from tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/missing-url.xml)0
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/valid.xml2
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/hr.xml12
-rw-r--r--tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/od.xml4
240 files changed, 4388 insertions, 1707 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 807fa48117b3..c8e1e4d205dd 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1150,6 +1150,11 @@ public class Activity extends ContextThemeWrapper
*
* <p>To keep the Intent instance for future use, call {@link #setIntent(Intent)}, and use
* this method to retrieve it.
+ *
+ * <p>Note that in {@link #onNewIntent}, this method will return the original Intent. You can
+ * use {@link #setIntent(Intent)} to update it to the new Intent.
+ *
+ * @return {@link Intent} instance that started this activity, or that was kept for future use
*/
public Intent getIntent() {
return mIntent;
@@ -1170,9 +1175,14 @@ public class Activity extends ContextThemeWrapper
}
/**
- * Returns the ComponentCaller instance of the app that launched this activity with the intent
- * from {@link #getIntent()}. To keep the value of the ComponentCaller instance for new intents,
- * call {@link #setIntent(Intent, ComponentCaller)} instead of {@link #setIntent(Intent)}.
+ * Returns the ComponentCaller instance of the app that started this activity.
+ *
+ * <p>To keep the ComponentCaller instance for future use, call
+ * {@link #setIntent(Intent, ComponentCaller)}, and use this method to retrieve it.
+ *
+ * <p>Note that in {@link #onNewIntent}, this method will return the original ComponentCaller.
+ * You can use {@link #setIntent(Intent, ComponentCaller)} to update it to the new
+ * ComponentCaller.
*
* @return {@link ComponentCaller} instance corresponding to the intent from
* {@link #getIntent()}, or {@code null} if the activity was not launched with that
@@ -7156,8 +7166,8 @@ public class Activity extends ContextThemeWrapper
/**
* Returns the ComponentCaller instance of the app that initially launched this activity.
*
- * <p>Note that calls to {@link #onNewIntent} have no effect on the returned value of this
- * method.
+ * <p>Note that calls to {@link #onNewIntent} and {@link #setIntent} have no effect on the
+ * returned value of this method.
*
* @return {@link ComponentCaller} instance
* @see ComponentCaller
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 86d061cecb88..e895d7be1102 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -823,6 +823,16 @@ public class IntentFilter implements Parcelable {
}
/**
+ * Returns the number of actions in the filter, or {@code 0} if there are no actions.
+ * <p> This method provides a safe alternative to {@link #countActions()}, which
+ * may throw an exception if there are no actions.
+ * @hide
+ */
+ public final int safeCountActions() {
+ return mActions == null ? 0 : mActions.size();
+ }
+
+ /**
* Return an action in the filter.
*/
public final String getAction(int index) {
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index 57ee622de910..bf51f00e8446 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -304,6 +304,7 @@ public class CrossProfileApps {
* <li>It is not equal to the calling user</li>
* <li>It is in the same profile group of calling user profile</li>
* <li>It is enabled</li>
+ * <li>It is not hidden (ex. profile type {@link UserManager#USER_TYPE_PROFILE_PRIVATE})</li>
* </ul>
*
* @see UserManager#getUserProfiles()
@@ -460,8 +461,8 @@ public class CrossProfileApps {
*
* <p>Specifically, returns whether the following are all true:
* <ul>
- * <li>{@code UserManager#getEnabledProfileIds(int)} returns at least one other profile for the
- * calling user.</li>
+ * <li>{@code UserManager#getProfileIdsExcludingHidden(int)} returns at least one other
+ * profile for the calling user.</li>
* <li>The calling app has requested
* {@code android.Manifest.permission.INTERACT_ACROSS_PROFILES} in its manifest.</li>
* <li>The calling app is not a profile owner within the profile group of the calling user.</li>
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 20522fad6652..7926afe30bd8 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -5577,8 +5577,8 @@ public class UserManager {
}
/**
- * Enables or disables quiet mode for a managed profile. If quiet mode is enabled, apps in a
- * managed profile don't run, generate notifications, or consume data or battery.
+ * Enables or disables quiet mode for a profile. If quiet mode is enabled, apps in the profile
+ * don't run, generate notifications, or consume data or battery.
* <p>
* If a user's credential is needed to turn off quiet mode, a confirm credential screen will be
* shown to the user.
@@ -5586,8 +5586,11 @@ public class UserManager {
* The change may not happen instantly, however apps can listen for
* {@link Intent#ACTION_MANAGED_PROFILE_AVAILABLE} and
* {@link Intent#ACTION_MANAGED_PROFILE_UNAVAILABLE} broadcasts in order to be notified of
- * the change of the quiet mode. Apps can also check the current state of quiet mode by
- * calling {@link #isQuietModeEnabled(UserHandle)}.
+ * the change of the quiet mode for managed profile.
+ * Apps can listen to generic broadcasts {@link Intent#ACTION_PROFILE_AVAILABLE} and
+ * {@link Intent#ACTION_PROFILE_UNAVAILABLE} to be notified of the change in quiet mode for
+ * any profiles. Apps can also check the current state of quiet mode by calling
+ * {@link #isQuietModeEnabled(UserHandle)}.
* <p>
* The caller must either be the foreground default launcher or have one of these permissions:
* {@code MANAGE_USERS} or {@code MODIFY_QUIET_MODE}.
@@ -5597,7 +5600,7 @@ public class UserManager {
* @return {@code false} if user's credential is needed in order to turn off quiet mode,
* {@code true} otherwise
* @throws SecurityException if the caller is invalid
- * @throws IllegalArgumentException if {@code userHandle} is not a managed profile
+ * @throws IllegalArgumentException if {@code userHandle} is not a profile
*
* @see #isQuietModeEnabled(UserHandle)
*/
@@ -5662,7 +5665,6 @@ public class UserManager {
/**
* Returns whether the given profile is in quiet mode or not.
- * Notes: Quiet mode is only supported for managed profiles.
*
* @param userHandle The user handle of the profile to be queried.
* @return true if the profile is in quiet mode, false otherwise.
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 95897855586d..8271caf57353 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -29,6 +29,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static com.android.window.flags.Flags.FLAG_OFFLOAD_COLOR_EXTRACTION;
import static com.android.window.flags.Flags.noConsecutiveVisibilityEvents;
+import static com.android.window.flags.Flags.noVisibilityEventOnDisplayStateChange;
import static com.android.window.flags.Flags.offloadColorExtraction;
import static com.android.window.flags.Flags.windowSessionRelayoutInfo;
@@ -2387,8 +2388,10 @@ public abstract class WallpaperService extends Service {
@Override
public void onDisplayChanged(int displayId) {
if (mDisplay.getDisplayId() == displayId) {
- boolean forceReport = mIsWearOs
- && mDisplay.getState() != Display.STATE_DOZE_SUSPEND;
+ boolean forceReport =
+ !noVisibilityEventOnDisplayStateChange()
+ && mIsWearOs
+ && mDisplay.getState() != Display.STATE_DOZE_SUSPEND;
reportVisibility(forceReport);
}
}
diff --git a/core/java/android/text/flags/flags.aconfig b/core/java/android/text/flags/flags.aconfig
index 7023ef7afd2f..8836c8a3a113 100644
--- a/core/java/android/text/flags/flags.aconfig
+++ b/core/java/android/text/flags/flags.aconfig
@@ -211,3 +211,12 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "rust_hyphenator"
+ namespace: "text"
+ description: "Reimplement hyphenator for safe file read"
+ # Hyphenator is initialized in Zygote
+ is_fixed_read_only: true
+ bug: "346915432"
+}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 815fc5820189..0714285c43f6 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -220,7 +220,7 @@ public class FeatureFlagUtils {
DEFAULT_FLAGS.put(SETTINGS_NEW_KEYBOARD_TRACKPAD_GESTURE, "false");
DEFAULT_FLAGS.put(SETTINGS_ENABLE_SPA, "true");
DEFAULT_FLAGS.put(SETTINGS_ENABLE_SPA_PHASE2, "false");
- DEFAULT_FLAGS.put(SETTINGS_ENABLE_SPA_METRICS, "false");
+ DEFAULT_FLAGS.put(SETTINGS_ENABLE_SPA_METRICS, "true");
DEFAULT_FLAGS.put(SETTINGS_ADB_METRICS_WRITER, "false");
DEFAULT_FLAGS.put(SETTINGS_SHOW_STYLUS_PREFERENCES, "true");
DEFAULT_FLAGS.put(SETTINGS_BIOMETRICS2_ENROLLMENT, "false");
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a11bb788bbf0..2377b869e804 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -25,6 +25,7 @@ import static android.os.Trace.TRACE_TAG_VIEW;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.DragEvent.ACTION_DRAG_LOCATION;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_APP_PROGRESS_GENERATION_ALLOWED;
import static android.view.flags.Flags.sensitiveContentPrematureProtectionRemovedFix;
import static android.view.InputDevice.SOURCE_CLASS_NONE;
import static android.view.InsetsSource.ID_IME;
@@ -271,6 +272,7 @@ import com.android.internal.inputmethod.ImeTracing;
import com.android.internal.inputmethod.InputMethodDebug;
import com.android.internal.os.IResultReceiver;
import com.android.internal.os.SomeArgs;
+import com.android.internal.policy.DecorView;
import com.android.internal.policy.PhoneFallbackEventHandler;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.view.BaseSurfaceHolder;
@@ -1576,6 +1578,9 @@ public final class ViewRootImpl implements ViewParent,
pendingInsetsController.replayAndAttach(mInsetsController);
}
}
+ if (mView instanceof DecorView) {
+ mWindowAttributes.privateFlags |= PRIVATE_FLAG_APP_PROGRESS_GENERATION_ALLOWED;
+ }
try {
mOrigWindowType = mWindowAttributes.type;
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 7b7ead4afb11..ae051f96fd03 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -3307,6 +3307,11 @@ public interface WindowManager extends ViewManager {
@UnsupportedAppUsage
public static final int PRIVATE_FLAG_NO_MOVE_ANIMATION = 1 << 6;
+ /** Window flag: the client side view can intercept back progress, so system does not
+ * need to pilfer pointers.
+ * {@hide} */
+ public static final int PRIVATE_FLAG_APP_PROGRESS_GENERATION_ALLOWED = 1 << 7;
+
/** Window flag: a special option intended for system dialogs. When
* this flag is set, the window will demand focus unconditionally when
* it is created.
@@ -3500,6 +3505,7 @@ public interface WindowManager extends ViewManager {
SYSTEM_FLAG_SHOW_FOR_ALL_USERS,
PRIVATE_FLAG_UNRESTRICTED_GESTURE_EXCLUSION,
PRIVATE_FLAG_NO_MOVE_ANIMATION,
+ PRIVATE_FLAG_APP_PROGRESS_GENERATION_ALLOWED,
PRIVATE_FLAG_SYSTEM_ERROR,
PRIVATE_FLAG_OPTIMIZE_MEASURE,
PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS,
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 58a3c80cd74b..0d4c5560837c 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -4326,14 +4326,16 @@ public final class AutofillManager {
if (mIsTrackedSaveView && mVisibleTrackedIds.isEmpty()) {
if (sVerbose) {
- Log.v(TAG, "No more visible ids. Invisible = " + mInvisibleTrackedIds);
+ Log.v(TAG, "No more visible tracked save ids. Invisible = "
+ + mInvisibleTrackedIds);
}
finishSessionLocked(/* commitReason= */ COMMIT_REASON_VIEW_CHANGED);
}
if (mVisibleDialogTrackedIds.isEmpty()) {
if (sVerbose) {
- Log.v(TAG, "No more visible ids. Invisible = " + mInvisibleDialogTrackedIds);
+ Log.v(TAG, "No more visible tracked fill dialog ids. Invisible = "
+ + mInvisibleDialogTrackedIds);
}
processNoVisibleTrackedAllViews();
}
diff --git a/core/java/android/window/BackNavigationInfo.java b/core/java/android/window/BackNavigationInfo.java
index 57bded7ff2a0..59639d04b45b 100644
--- a/core/java/android/window/BackNavigationInfo.java
+++ b/core/java/android/window/BackNavigationInfo.java
@@ -117,6 +117,8 @@ public final class BackNavigationInfo implements Parcelable {
@NonNull
private final Rect mTouchableRegion;
+ private final boolean mAppProgressGenerationAllowed;
+
/**
* Create a new {@link BackNavigationInfo} instance.
*
@@ -132,7 +134,8 @@ public final class BackNavigationInfo implements Parcelable {
boolean isAnimationCallback,
@Nullable CustomAnimationInfo customAnimationInfo,
int letterboxColor,
- @Nullable Rect touchableRegion) {
+ @Nullable Rect touchableRegion,
+ boolean appProgressGenerationAllowed) {
mType = type;
mOnBackNavigationDone = onBackNavigationDone;
mOnBackInvokedCallback = onBackInvokedCallback;
@@ -141,6 +144,7 @@ public final class BackNavigationInfo implements Parcelable {
mCustomAnimationInfo = customAnimationInfo;
mLetterboxColor = letterboxColor;
mTouchableRegion = new Rect(touchableRegion);
+ mAppProgressGenerationAllowed = appProgressGenerationAllowed;
}
private BackNavigationInfo(@NonNull Parcel in) {
@@ -152,6 +156,7 @@ public final class BackNavigationInfo implements Parcelable {
mCustomAnimationInfo = in.readTypedObject(CustomAnimationInfo.CREATOR);
mLetterboxColor = in.readInt();
mTouchableRegion = in.readTypedObject(Rect.CREATOR);
+ mAppProgressGenerationAllowed = in.readBoolean();
}
/** @hide */
@@ -165,6 +170,7 @@ public final class BackNavigationInfo implements Parcelable {
dest.writeTypedObject(mCustomAnimationInfo, flags);
dest.writeInt(mLetterboxColor);
dest.writeTypedObject(mTouchableRegion, flags);
+ dest.writeBoolean(mAppProgressGenerationAllowed);
}
/**
@@ -224,6 +230,14 @@ public final class BackNavigationInfo implements Parcelable {
}
/**
+ * @return The client side view is able to intercept back progress event.
+ * @hide
+ */
+ public boolean isAppProgressGenerationAllowed() {
+ return mAppProgressGenerationAllowed;
+ }
+
+ /**
* Callback to be called when the back preview is finished in order to notify the server that
* it can clean up the resources created for the animation.
* @hide
@@ -420,6 +434,7 @@ public final class BackNavigationInfo implements Parcelable {
private int mLetterboxColor = Color.TRANSPARENT;
private Rect mTouchableRegion;
+ private boolean mAppProgressGenerationAllowed;
/**
* @see BackNavigationInfo#getType()
@@ -502,6 +517,15 @@ public final class BackNavigationInfo implements Parcelable {
mTouchableRegion = new Rect(rect);
return this;
}
+
+ /**
+ * @param allowed Whether client side view able to intercept back progress event.
+ */
+ public Builder setAppProgressAllowed(boolean allowed) {
+ mAppProgressGenerationAllowed = allowed;
+ return this;
+ }
+
/**
* Builds and returns an instance of {@link BackNavigationInfo}
*/
@@ -512,7 +536,8 @@ public final class BackNavigationInfo implements Parcelable {
mAnimationCallback,
mCustomAnimationInfo,
mLetterboxColor,
- mTouchableRegion);
+ mTouchableRegion,
+ mAppProgressGenerationAllowed);
}
}
}
diff --git a/core/java/android/window/flags/wallpaper_manager.aconfig b/core/java/android/window/flags/wallpaper_manager.aconfig
index 150b04e87d97..01c78a0bfb1d 100644
--- a/core/java/android/window/flags/wallpaper_manager.aconfig
+++ b/core/java/android/window/flags/wallpaper_manager.aconfig
@@ -31,4 +31,11 @@ flag {
metadata {
purpose: PURPOSE_BUGFIX
}
-} \ No newline at end of file
+}
+
+flag {
+ name: "no_visibility_event_on_display_state_change"
+ namespace: "wear_frameworks"
+ description: "Prevent the system from sending visibility event on display state change."
+ bug: "331725519"
+}
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 2194c897ff0d..40d760e0064e 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -537,8 +537,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
- if (cb != null && !isDestroyed()) {
- cb.onContentChanged();
+ if (!isDestroyed()) {
+ if (cb != null) {
+ cb.onContentChanged();
+ }
+ if (mDecorContentParent != null) {
+ mDecorContentParent.notifyContentChanged();
+ }
}
mContentParentExplicitlySet = true;
}
@@ -568,8 +573,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
- if (cb != null && !isDestroyed()) {
- cb.onContentChanged();
+ if (!isDestroyed()) {
+ if (cb != null) {
+ cb.onContentChanged();
+ }
+ if (mDecorContentParent != null) {
+ mDecorContentParent.notifyContentChanged();
+ }
}
mContentParentExplicitlySet = true;
}
@@ -586,8 +596,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
mContentParent.addView(view, params);
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
- if (cb != null && !isDestroyed()) {
- cb.onContentChanged();
+ if (!isDestroyed()) {
+ if (cb != null) {
+ cb.onContentChanged();
+ }
+ if (mDecorContentParent != null) {
+ mDecorContentParent.notifyContentChanged();
+ }
}
}
diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
index 68328252abaf..ff57fd4fe2ce 100644
--- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
+++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
@@ -898,6 +898,13 @@ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentPar
mDecorToolbar.dismissPopupMenus();
}
+ @Override
+ public void notifyContentChanged() {
+ mLastBaseContentInsets.setEmpty();
+ mLastBaseInnerInsets = WindowInsets.CONSUMED;
+ mLastInnerInsets = WindowInsets.CONSUMED;
+ }
+
public static class LayoutParams extends MarginLayoutParams {
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
diff --git a/core/java/com/android/internal/widget/DecorContentParent.java b/core/java/com/android/internal/widget/DecorContentParent.java
index ac524f929d1b..8d6cfd1018c0 100644
--- a/core/java/com/android/internal/widget/DecorContentParent.java
+++ b/core/java/com/android/internal/widget/DecorContentParent.java
@@ -22,6 +22,7 @@ import android.os.Parcelable;
import android.util.SparseArray;
import android.view.Menu;
import android.view.Window;
+
import com.android.internal.view.menu.MenuPresenter;
/**
@@ -49,4 +50,5 @@ public interface DecorContentParent {
void saveToolbarHierarchyState(SparseArray<Parcelable> toolbarStates);
void restoreToolbarHierarchyState(SparseArray<Parcelable> toolbarStates);
void dismissPopups();
+ void notifyContentChanged();
}
diff --git a/core/jni/android_text_Hyphenator.cpp b/core/jni/android_text_Hyphenator.cpp
index b6bf617b40ae..89fdeeb078d0 100644
--- a/core/jni/android_text_Hyphenator.cpp
+++ b/core/jni/android_text_Hyphenator.cpp
@@ -36,41 +36,43 @@ static std::string buildFileName(const std::string& locale) {
return SYSTEM_HYPHENATOR_PREFIX + lowerLocale + SYSTEM_HYPHENATOR_SUFFIX;
}
-static const uint8_t* mmapPatternFile(const std::string& locale) {
+static std::pair<const uint8_t*, size_t> mmapPatternFile(const std::string& locale) {
const std::string hyFilePath = buildFileName(locale);
const int fd = open(hyFilePath.c_str(), O_RDONLY | O_CLOEXEC);
if (fd == -1) {
- return nullptr; // Open failed.
+ return std::make_pair(nullptr, 0); // Open failed.
}
struct stat st = {};
if (fstat(fd, &st) == -1) { // Unlikely to happen.
close(fd);
- return nullptr;
+ return std::make_pair(nullptr, 0);
}
void* ptr = mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, fd, 0 /* offset */);
close(fd);
if (ptr == MAP_FAILED) {
- return nullptr;
+ return std::make_pair(nullptr, 0);
}
- return reinterpret_cast<const uint8_t*>(ptr);
+ return std::make_pair(reinterpret_cast<const uint8_t*>(ptr), st.st_size);
}
static void addHyphenatorWithoutPatternFile(const std::string& locale, int minPrefix,
int minSuffix) {
- minikin::addHyphenator(locale, minikin::Hyphenator::loadBinary(
- nullptr, minPrefix, minSuffix, locale));
+ minikin::addHyphenator(locale,
+ minikin::Hyphenator::loadBinary(nullptr, 0, minPrefix, minSuffix,
+ locale));
}
static void addHyphenator(const std::string& locale, int minPrefix, int minSuffix) {
- const uint8_t* ptr = mmapPatternFile(locale);
- if (ptr == nullptr) {
+ std::pair<const uint8_t*, size_t> r = mmapPatternFile(locale);
+ if (r.first == nullptr) {
ALOGE("Unable to find pattern file or unable to map it for %s", locale.c_str());
return;
}
- minikin::addHyphenator(locale, minikin::Hyphenator::loadBinary(
- ptr, minPrefix, minSuffix, locale));
+ minikin::addHyphenator(locale,
+ minikin::Hyphenator::loadBinary(r.first, r.second, minPrefix, minSuffix,
+ locale));
}
static void addHyphenatorAlias(const std::string& from, const std::string& to) {
diff --git a/core/res/res/drawable/ic_zen_mode_type_bedtime.xml b/core/res/res/drawable/ic_zen_mode_type_bedtime.xml
new file mode 100644
index 000000000000..7428a71b262c
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_type_bedtime.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:tint="?android:attr/colorControlNormal"
+ android:viewportHeight="960"
+ android:viewportWidth="960">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M524,920Q440,920 366.5,888Q293,856 238.5,801.5Q184,747 152,673.5Q120,600 120,516Q120,370 213,258.5Q306,147 450,120Q432,219 461,313.5Q490,408 561,479Q632,550 726.5,579Q821,608 920,590Q894,734 782,827Q670,920 524,920ZM524,840Q612,840 687,796Q762,752 805,675Q719,667 642,631.5Q565,596 504,535Q443,474 407,397Q371,320 364,234Q287,277 243.5,352.5Q200,428 200,516Q200,651 294.5,745.5Q389,840 524,840ZM504,535Q504,535 504,535Q504,535 504,535Q504,535 504,535Q504,535 504,535Q504,535 504,535Q504,535 504,535Q504,535 504,535Q504,535 504,535Q504,535 504,535Q504,535 504,535Z" />
+</vector> \ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_type_driving.xml b/core/res/res/drawable/ic_zen_mode_type_driving.xml
new file mode 100644
index 000000000000..3cc0066c46b6
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_type_driving.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:tint="?android:attr/colorControlNormal"
+ android:viewportHeight="960"
+ android:viewportWidth="960">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M240,760L240,800Q240,817 228.5,828.5Q217,840 200,840L160,840Q143,840 131.5,828.5Q120,817 120,800L120,480L204,240Q210,222 225.5,211Q241,200 260,200L700,200Q719,200 734.5,211Q750,222 756,240L840,480L840,800Q840,817 828.5,828.5Q817,840 800,840L760,840Q743,840 731.5,828.5Q720,817 720,800L720,760L240,760ZM232,400L728,400L686,280L274,280L232,400ZM200,480L200,480L200,680L200,680L200,480ZM300,640Q325,640 342.5,622.5Q360,605 360,580Q360,555 342.5,537.5Q325,520 300,520Q275,520 257.5,537.5Q240,555 240,580Q240,605 257.5,622.5Q275,640 300,640ZM660,640Q685,640 702.5,622.5Q720,605 720,580Q720,555 702.5,537.5Q685,520 660,520Q635,520 617.5,537.5Q600,555 600,580Q600,605 617.5,622.5Q635,640 660,640ZM200,680L760,680L760,480L200,480L200,680Z" />
+</vector> \ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_type_immersive.xml b/core/res/res/drawable/ic_zen_mode_type_immersive.xml
new file mode 100644
index 000000000000..70913579b62b
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_type_immersive.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:tint="?android:attr/colorControlNormal"
+ android:viewportHeight="960"
+ android:viewportWidth="960">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M440,560L520,560L520,280L440,280L440,560ZM560,500L640,500L640,320L560,320L560,500ZM320,480L400,480L400,320L320,320L320,480ZM240,880L240,708Q183,656 151.5,586.5Q120,517 120,440Q120,290 225,185Q330,80 480,80Q605,80 701.5,153.5Q798,227 827,345L879,550Q884,569 872,584.5Q860,600 840,600L760,600L760,720Q760,753 736.5,776.5Q713,800 680,800L600,800L600,880L520,880L520,720L680,720Q680,720 680,720Q680,720 680,720L680,520L788,520L750,365Q727,274 652,217Q577,160 480,160Q364,160 282,241Q200,322 200,438Q200,498 224.5,552Q249,606 294,648L320,672L320,880L240,880ZM494,520L494,520L494,520Q494,520 494,520Q494,520 494,520Q494,520 494,520Q494,520 494,520Q494,520 494,520Q494,520 494,520L494,520L494,520L494,520Q494,520 494,520Q494,520 494,520L494,520L494,520Z" />
+</vector> \ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_type_managed.xml b/core/res/res/drawable/ic_zen_mode_type_managed.xml
new file mode 100644
index 000000000000..5e224ebbf727
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_type_managed.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:tint="?android:attr/colorControlNormal"
+ android:viewportHeight="960"
+ android:viewportWidth="960">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M234,684Q285,645 348,622.5Q411,600 480,600Q549,600 612,622.5Q675,645 726,684Q761,643 780.5,591Q800,539 800,480Q800,347 706.5,253.5Q613,160 480,160Q347,160 253.5,253.5Q160,347 160,480Q160,539 179.5,591Q199,643 234,684ZM480,520Q421,520 380.5,479.5Q340,439 340,380Q340,321 380.5,280.5Q421,240 480,240Q539,240 579.5,280.5Q620,321 620,380Q620,439 579.5,479.5Q539,520 480,520ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q533,800 580,784.5Q627,769 666,740Q627,711 580,695.5Q533,680 480,680Q427,680 380,695.5Q333,711 294,740Q333,769 380,784.5Q427,800 480,800ZM480,440Q506,440 523,423Q540,406 540,380Q540,354 523,337Q506,320 480,320Q454,320 437,337Q420,354 420,380Q420,406 437,423Q454,440 480,440ZM480,380Q480,380 480,380Q480,380 480,380Q480,380 480,380Q480,380 480,380Q480,380 480,380Q480,380 480,380Q480,380 480,380Q480,380 480,380ZM480,740Q480,740 480,740Q480,740 480,740Q480,740 480,740Q480,740 480,740Q480,740 480,740Q480,740 480,740Q480,740 480,740Q480,740 480,740Z" />
+</vector> \ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_type_other.xml b/core/res/res/drawable/ic_zen_mode_type_other.xml
new file mode 100644
index 000000000000..d236b0d7202b
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_type_other.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:tint="?android:attr/colorControlNormal"
+ android:viewportHeight="960"
+ android:viewportWidth="960">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M354,673L480,597L606,674L573,530L684,434L538,421L480,285L422,420L276,433L387,530L354,673ZM233,840L298,559L80,370L368,345L480,80L592,345L880,370L662,559L727,840L480,691L233,840ZM480,490L480,490L480,490L480,490L480,490L480,490L480,490L480,490L480,490L480,490Z" />
+</vector> \ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_type_schedule_calendar.xml b/core/res/res/drawable/ic_zen_mode_type_schedule_calendar.xml
new file mode 100644
index 000000000000..40004899ad84
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_type_schedule_calendar.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal">
+
+ <path android:fillColor="@android:color/white"
+ android:pathData="M17.0,12.0l-5.0,0.0l0.0,5.0l5.0,0.0l0.0,-5.0zM16.0,1.0l0.0,2.0L8.0,3.0L8.0,1.0L6.0,1.0l0.0,2.0L5.0,3.0c-1.11,0.0 -1.9,0.9 -1.99,2.0L3.0,19.0c0.0,1.0 0.89,2.0 2.0,2.0l14.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L21.0,5.0c0.0,-1.1 -0.9,-2.0 -2.0,-2.0l-1.0,0.0L18.0,1.0l-2.0,0.0zm3.0,18.0L5.0,19.0L5.0,8.0l14.0,0.0l0.0,11.0z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_zen_mode_type_schedule_time.xml b/core/res/res/drawable/ic_zen_mode_type_schedule_time.xml
new file mode 100644
index 000000000000..57d596a98f6b
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_type_schedule_time.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:tint="?android:attr/colorControlNormal"
+ android:viewportHeight="960"
+ android:viewportWidth="960">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M612,668L668,612L520,464L520,280L440,280L440,496L612,668ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480ZM480,800Q613,800 706.5,706.5Q800,613 800,480Q800,347 706.5,253.5Q613,160 480,160Q347,160 253.5,253.5Q160,347 160,480Q160,613 253.5,706.5Q347,800 480,800Z" />
+</vector> \ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_type_theater.xml b/core/res/res/drawable/ic_zen_mode_type_theater.xml
new file mode 100644
index 000000000000..cc66b32391a8
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_type_theater.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:tint="?android:attr/colorControlNormal"
+ android:viewportHeight="960"
+ android:viewportWidth="960">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M160,840L160,120L240,120L240,200L320,200L320,120L640,120L640,200L720,200L720,120L800,120L800,840L720,840L720,760L640,760L640,840L320,840L320,760L240,760L240,840L160,840ZM240,680L320,680L320,600L240,600L240,680ZM240,520L320,520L320,440L240,440L240,520ZM240,360L320,360L320,280L240,280L240,360ZM640,680L720,680L720,600L640,600L640,680ZM640,520L720,520L720,440L640,440L640,520ZM640,360L720,360L720,280L640,280L640,360ZM400,760L560,760L560,200L400,200L400,760ZM400,200L400,200L560,200L560,200L400,200Z" />
+</vector> \ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_type_unknown.xml b/core/res/res/drawable/ic_zen_mode_type_unknown.xml
new file mode 100644
index 000000000000..c1afd44ecfc4
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_type_unknown.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:tint="?android:attr/colorControlNormal"
+ android:viewportHeight="960"
+ android:viewportWidth="960">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M480,880Q407,871 335,840.5Q263,810 206.5,753Q150,696 115,609Q80,522 80,400L80,360L120,360Q171,360 225,373Q279,386 326,412Q338,326 380.5,235.5Q423,145 480,80Q537,145 579.5,235.5Q622,326 634,412Q681,386 735,373Q789,360 840,360L880,360L880,400Q880,522 845,609Q810,696 753.5,753Q697,810 625.5,840.5Q554,871 480,880ZM478,798Q467,632 379.5,547Q292,462 162,442Q173,613 263.5,697Q354,781 478,798ZM480,544Q495,522 516.5,498.5Q538,475 558,458Q556,401 535.5,339Q515,277 480,218Q445,277 424.5,339Q404,401 402,458Q422,475 444,498.5Q466,522 480,544ZM558,780Q595,768 635,745Q675,722 709.5,682.5Q744,643 768.5,584Q793,525 798,442Q704,456 633,504.5Q562,553 524,628Q536,660 544.5,698Q553,736 558,780ZM480,544Q480,544 480,544Q480,544 480,544Q480,544 480,544Q480,544 480,544Q480,544 480,544Q480,544 480,544Q480,544 480,544Q480,544 480,544ZM558,780Q558,780 558,780Q558,780 558,780Q558,780 558,780Q558,780 558,780Q558,780 558,780Q558,780 558,780Q558,780 558,780Q558,780 558,780ZM478,798Q478,798 478,798Q478,798 478,798Q478,798 478,798Q478,798 478,798ZM524,628L524,628Q524,628 524,628Q524,628 524,628L524,628L524,628L524,628Q524,628 524,628Q524,628 524,628L524,628Q524,628 524,628Q524,628 524,628ZM480,880L480,880Q480,880 480,880Q480,880 480,880Q480,880 480,880Q480,880 480,880Q480,880 480,880Q480,880 480,880Q480,880 480,880Q480,880 480,880Q480,880 480,880Q480,880 480,880L480,880Q480,880 480,880Q480,880 480,880L480,880Q480,880 480,880Q480,880 480,880L480,880Z" />
+</vector> \ No newline at end of file
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f43351a2e456..4e133de7c483 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -6969,9 +6969,17 @@
Note that, indefinitely repeating vibrations are not allowed as shutdown vibrations. -->
<string name="config_defaultShutdownVibrationFile" />
- <!-- Whether single finger panning is enabled when magnification is on -->
+ <!-- Whether single finger panning is enabled by default when magnification is on -->
<bool name="config_enable_a11y_magnification_single_panning">false</bool>
+ <!-- Whether the overscroll handler is enabled when fullscreen magnification is on. When true,
+ the magnification will change the scale if the user pans the magnifier horizontally past
+ the edge of the screen, or delegate the touch events to the app if the user pans vertically
+ past the edge. When false, the magnification will delegate the touch events to the app only
+ when the users uses single finger to pan the magnifier past the edge of the screen,
+ otherwise there are no extra actions. -->
+ <bool name="config_enable_a11y_fullscreen_magnification_overscroll_handler">false</bool>
+
<!-- The file path in which custom vibrations are provided for haptic feedbacks.
If the device does not specify any such file path here, if the file path specified here
does not exist, or if the contents of the file does not make up a valid customization
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index a0807ca580a2..5fea51599971 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -620,6 +620,9 @@
<!-- width of the border of the magnification thumbnail -->
<dimen name="accessibility_magnification_thumbnail_container_stroke_width">4dp</dimen>
+ <!-- The distance from the edge within which the gesture is considered to be at the edge -->
+ <dimen name="accessibility_fullscreen_magnification_gesture_edge_slop">12dp</dimen>
+
<!-- The padding ratio of the Accessibility icon foreground drawable -->
<item name="accessibility_icon_foreground_padding_ratio" type="dimen">21.88%</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c16bd241a860..7251d747be79 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -5427,6 +5427,8 @@
<java-symbol type="string" name="lockscreen_too_many_failed_attempts_countdown" />
<java-symbol type="bool" name="config_enable_a11y_magnification_single_panning" />
+ <java-symbol type="bool" name="config_enable_a11y_fullscreen_magnification_overscroll_handler" />
+ <java-symbol type="dimen" name="accessibility_fullscreen_magnification_gesture_edge_slop" />
<java-symbol type="string" name="config_hapticFeedbackCustomizationFile" />
@@ -5512,4 +5514,16 @@
<java-symbol type="string" name="face_dangling_notification_msg" />
<java-symbol type="string" name="biometric_dangling_notification_action_set_up" />
<java-symbol type="string" name="biometric_dangling_notification_action_not_now" />
+
+ <!-- Priority Modes icons -->
+ <java-symbol type="drawable" name="ic_zen_mode_type_bedtime" />
+ <java-symbol type="drawable" name="ic_zen_mode_type_driving" />
+ <java-symbol type="drawable" name="ic_zen_mode_type_immersive" />
+ <java-symbol type="drawable" name="ic_zen_mode_type_managed" />
+ <java-symbol type="drawable" name="ic_zen_mode_type_other" />
+ <java-symbol type="drawable" name="ic_zen_mode_type_schedule_calendar" />
+ <java-symbol type="drawable" name="ic_zen_mode_type_schedule_time" />
+ <java-symbol type="drawable" name="ic_zen_mode_type_theater" />
+ <java-symbol type="drawable" name="ic_zen_mode_type_unknown" />
+
</resources>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml b/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml
index 6e9d4db47c41..94bde68fdf11 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml
+++ b/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml
@@ -25,10 +25,11 @@
<application
android:theme="@style/Theme"
+ android:icon="@mipmap/ic_launcher"
+ android:roundIcon="@mipmap/ic_launcher_round"
android:label="Battery Stats Viewer">
<activity android:name=".BatteryConsumerPickerActivity"
android:label="Battery Stats"
- android:icon="@mipmap/ic_launcher"
android:launchMode="singleTop"
android:exported="true">
<intent-filter>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/ic_launcher_background.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 07d5da9cbf14..000000000000
--- a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="108dp"
- android:height="108dp"
- android:viewportWidth="108"
- android:viewportHeight="108">
- <path
- android:fillColor="#3DDC84"
- android:pathData="M0,0h108v108h-108z" />
- <path
- android:fillColor="#00000000"
- android:pathData="M9,0L9,108"
- android:strokeWidth="0.8"
- android:strokeColor="#33FFFFFF" />
- <path
- android:fillColor="#00000000"
- android:pathData="M19,0L19,108"
- android:strokeWidth="0.8"
- android:strokeColor="#33FFFFFF" />
- <path
- android:fillColor="#00000000"
- android:pathData="M29,0L29,108"
- android:strokeWidth="0.8"
- android:strokeColor="#33FFFFFF" />
- <path
- android:fillColor="#00000000"
- android:pathData="M39,0L39,108"
- android:strokeWidth="0.8"
- android:strokeColor="#33FFFFFF" />
- <path
- android:fillColor="#00000000"
- android:pathData="M49,0L49,108"
- android:strokeWidth="0.8"
- android:strokeColor="#33FFFFFF" />
- <path
- android:fillColor="#00000000"
- android:pathData="M59,0L59,108"
- android:strokeWidth="0.8"
- android:strokeColor="#33FFFFFF" />
- <path
- android:fillColor="#00000000"
- android:pathData="M69,0L69,108"
- android:strokeWidth="0.8"
- android:strokeColor="#33FFFFFF" />
- <path
- android:fillColor="#00000000"
- android:pathData="M79,0L79,108"
- android:strokeWidth="0.8"
- android:strokeColor="#33FFFFFF" />
- <path
- android:fillColor="#00000000"
- android:pathData="M89,0L89,108"
- android:strokeWidth="0.8"
- android:strokeColor="#33FFFFFF" />
- <path
- android:fillColor="#00000000"
- android:pathData="M99,0L99,108"
- android:strokeWidth="0.8"
- android:strokeColor="#33FFFFFF" />
- <path
- android:fillColor="#00000000"
- android:pathData="M0,9L108,9"
- android:strokeWidth="0.8"
- android:strokeColor="#33FFFFFF" />
- <path
- android:fillColor="#00000000"
- android:pathData="M0,19L108,19"
- android:strokeWidth="0.8"
- android:strokeColor="#33FFFFFF" />
- <path
- android:fillColor="#00000000"
- android:pathData="M0,29L108,29"
- android:strokeWidth="0.8"
- android:strokeColor="#33FFFFFF" />
- <path
- android:fillColor="#00000000"
- android:pathData="M0,39L108,39"
- android:strokeWidth="0.8"
- android:strokeColor="#33FFFFFF" />
- <path
- android:fillColor="#00000000"
- android:pathData="M0,49L108,49"
- android:strokeWidth="0.8"
- android:strokeColor="#33FFFFFF" />
- <path
- android:fillColor="#00000000"
- android:pathData="M0,59L108,59"
- android:strokeWidth="0.8"
- android:strokeColor="#33FFFFFF" />
- <path
- android:fillColor="#00000000"
- android:pathData="M0,69L108,69"
- android:strokeWidth="0.8"
- android:strokeColor="#33FFFFFF" />
- <path
- android:fillColor="#00000000"
- android:pathData="M0,79L108,79"
- android:strokeWidth="0.8"
- android:strokeColor="#33FFFFFF" />
- <path
- android:fillColor="#00000000"
- android:pathData="M0,89L108,89"
- android:strokeWidth="0.8"
- android:strokeColor="#33FFFFFF" />
- <path
- android:fillColor="#00000000"
- android:pathData="M0,99L108,99"
- android:strokeWidth="0.8"
- android:strokeColor="#33FFFFFF" />
- <path
- android:fillColor="#00000000"
- android:pathData="M19,29L89,29"
- android:strokeWidth="0.8"
- android:strokeColor="#33FFFFFF" />
- <path
- android:fillColor="#00000000"
- android:pathData="M19,39L89,39"
- android:strokeWidth="0.8"
- android:strokeColor="#33FFFFFF" />
- <path
- android:fillColor="#00000000"
- android:pathData="M19,49L89,49"
- android:strokeWidth="0.8"
- android:strokeColor="#33FFFFFF" />
- <path
- android:fillColor="#00000000"
- android:pathData="M19,59L89,59"
- android:strokeWidth="0.8"
- android:strokeColor="#33FFFFFF" />
- <path
- android:fillColor="#00000000"
- android:pathData="M19,69L89,69"
- android:strokeWidth="0.8"
- android:strokeColor="#33FFFFFF" />
- <path
- android:fillColor="#00000000"
- android:pathData="M19,79L89,79"
- android:strokeWidth="0.8"
- android:strokeColor="#33FFFFFF" />
- <path
- android:fillColor="#00000000"
- android:pathData="M29,19L29,89"
- android:strokeWidth="0.8"
- android:strokeColor="#33FFFFFF" />
- <path
- android:fillColor="#00000000"
- android:pathData="M39,19L39,89"
- android:strokeWidth="0.8"
- android:strokeColor="#33FFFFFF" />
- <path
- android:fillColor="#00000000"
- android:pathData="M49,19L49,89"
- android:strokeWidth="0.8"
- android:strokeColor="#33FFFFFF" />
- <path
- android:fillColor="#00000000"
- android:pathData="M59,19L59,89"
- android:strokeWidth="0.8"
- android:strokeColor="#33FFFFFF" />
- <path
- android:fillColor="#00000000"
- android:pathData="M69,19L69,89"
- android:strokeWidth="0.8"
- android:strokeColor="#33FFFFFF" />
- <path
- android:fillColor="#00000000"
- android:pathData="M79,19L79,89"
- android:strokeWidth="0.8"
- android:strokeColor="#33FFFFFF" />
-</vector>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/ic_launcher_foreground.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/ic_launcher_foreground.xml
deleted file mode 100644
index fc0c6ab27228..000000000000
--- a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:aapt="http://schemas.android.com/aapt"
- android:width="108dp"
- android:height="108dp"
- android:viewportWidth="108"
- android:viewportHeight="108">
- <path
- android:fillType="evenOdd"
- android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,
- 49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
- android:strokeWidth="1"
- android:strokeColor="#00000000">
- <aapt:attr name="android:fillColor">
- <gradient
- android:endX="78.5885"
- android:endY="90.9159"
- android:startX="48.7653"
- android:startY="61.0927"
- android:type="linear">
- <item
- android:color="#44000000"
- android:offset="0.0" />
- <item
- android:color="#00000000"
- android:offset="1.0" />
- </gradient>
- </aapt:attr>
- </path>
- <path
- android:fillColor="#FFFFFF"
- android:fillType="nonZero"
- android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,
- 50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,
- 37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,
- 42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,
- 40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,
- 52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,
- 56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,
- 52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
- android:strokeWidth="1"
- android:strokeColor="#00000000" />
-</vector>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_picker_layout.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_picker_layout.xml
index f35a210cae4b..987de6bcb4f4 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_picker_layout.xml
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_picker_layout.xml
@@ -17,6 +17,7 @@
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/swipe_refresh"
+ android:paddingTop="?attr/actionBarSize"
android:layout_width="match_parent"
android:layout_height="match_parent">
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml
index cf50d2ad1e91..2d276a51a1da 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml
@@ -17,11 +17,13 @@
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/swipe_refresh"
+ android:paddingTop="?attr/actionBarSize"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
+ android:paddingTop="?attr/actionBarSize"
android:layout_width="match_parent"
android:layout_height="match_parent">
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-anydpi-v26/ic_launcher.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 000000000000..036d09bc5fd5
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@color/ic_launcher_background"/>
+ <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
+</adaptive-icon> \ No newline at end of file
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-anydpi-v26/ic_launcher_round.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 000000000000..036d09bc5fd5
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@color/ic_launcher_background"/>
+ <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
+</adaptive-icon> \ No newline at end of file
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-anydpi/ic_launcher.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-anydpi/ic_launcher.xml
index 6b78462d615b..036d09bc5fd5 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-anydpi/ic_launcher.xml
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-anydpi/ic_launcher.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
- <background android:drawable="@drawable/ic_launcher_background" />
- <foreground android:drawable="@drawable/ic_launcher_foreground" />
-</adaptive-icon>
+ <background android:drawable="@color/ic_launcher_background"/>
+ <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
+</adaptive-icon> \ No newline at end of file
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-anydpi/ic_launcher_round.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-anydpi/ic_launcher_round.xml
new file mode 100644
index 000000000000..036d09bc5fd5
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-anydpi/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@color/ic_launcher_background"/>
+ <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
+</adaptive-icon> \ No newline at end of file
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-hdpi/ic_launcher.webp b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 000000000000..005798568117
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-hdpi/ic_launcher.webp
Binary files differ
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-hdpi/ic_launcher_foreground.webp b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-hdpi/ic_launcher_foreground.webp
new file mode 100644
index 000000000000..085df9d7b8c2
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-hdpi/ic_launcher_foreground.webp
Binary files differ
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-hdpi/ic_launcher_round.webp b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 000000000000..bcb3b7d85f59
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-hdpi/ic_launcher_round.webp
Binary files differ
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-mdpi/ic_launcher.webp b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 000000000000..3d1cf0ed469d
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-mdpi/ic_launcher.webp
Binary files differ
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-mdpi/ic_launcher_foreground.webp b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-mdpi/ic_launcher_foreground.webp
new file mode 100644
index 000000000000..bfd456815bae
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-mdpi/ic_launcher_foreground.webp
Binary files differ
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-mdpi/ic_launcher_round.webp b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 000000000000..4cf0d434f4e2
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-mdpi/ic_launcher_round.webp
Binary files differ
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xhdpi/ic_launcher.webp b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 000000000000..ac4f6936ae02
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xhdpi/ic_launcher.webp
Binary files differ
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xhdpi/ic_launcher_foreground.webp b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xhdpi/ic_launcher_foreground.webp
new file mode 100644
index 000000000000..cc6b7639a106
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xhdpi/ic_launcher_foreground.webp
Binary files differ
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xhdpi/ic_launcher_round.webp b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 000000000000..1f17221305aa
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xhdpi/ic_launcher_round.webp
Binary files differ
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxhdpi/ic_launcher.webp b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 000000000000..b70e14559dbc
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxhdpi/ic_launcher.webp
Binary files differ
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxhdpi/ic_launcher_foreground.webp
new file mode 100644
index 000000000000..6e46bce20904
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxhdpi/ic_launcher_foreground.webp
Binary files differ
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxhdpi/ic_launcher_round.webp b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 000000000000..3fa346c09714
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxhdpi/ic_launcher_round.webp
Binary files differ
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxxhdpi/ic_launcher.webp b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 000000000000..8b463f2ba0ff
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxxhdpi/ic_launcher.webp
Binary files differ
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
new file mode 100644
index 000000000000..849caff1db0b
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
Binary files differ
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxxhdpi/ic_launcher_round.webp b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 000000000000..bd6e312c8b4a
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxxhdpi/ic_launcher_round.webp
Binary files differ
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/values/ic_launcher_background.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/values/ic_launcher_background.xml
new file mode 100644
index 000000000000..1e90e07db569
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/values/ic_launcher_background.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="ic_launcher_background">#1A7945</color>
+</resources> \ No newline at end of file
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/values/styles.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/values/styles.xml
index 629d729e7b9a..fa30b2c8dc6f 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/res/values/styles.xml
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/values/styles.xml
@@ -21,6 +21,7 @@
<item name="colorPrimary">#34a853</item>
<item name="android:windowActionBar">true</item>
<item name="android:windowNoTitle">false</item>
+ <item name="android:windowDrawsSystemBarBackgrounds">false</item>
</style>
<style name="LoadTestCardView" parent="Widget.MaterialComponents.CardView">
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 82d2381012e5..5d4139e4be8c 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -23,23 +23,19 @@ applications that come with the platform
<!-- Needed for Build.getSerial(), which is used to send a unique number for serial, per HUIG. -->
<privapp-permissions package="android.car.usb.handler">
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
- <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
</privapp-permissions>
<privapp-permissions package="com.android.angle">
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
- <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
</privapp-permissions>
<privapp-permissions package="com.android.apps.tag">
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
- <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
</privapp-permissions>
<privapp-permissions package="com.android.backupconfirm">
<permission name="android.permission.BACKUP"/>
<permission name="android.permission.CRYPT_KEEPER"/>
- <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
</privapp-permissions>
<privapp-permissions package="com.android.credentialmanager">
@@ -50,13 +46,11 @@ applications that come with the platform
<permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<permission name="android.permission.WRITE_MEDIA_STORAGE"/>
<permission name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
- <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
</privapp-permissions>
<privapp-permissions package="com.android.imsserviceentitlement">
<permission name="android.permission.MODIFY_PHONE_STATE" />
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
- <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
</privapp-permissions>
<privapp-permissions package="com.android.launcher3">
@@ -68,7 +62,6 @@ applications that come with the platform
<permission name="android.permission.INSTALL_LOCATION_PROVIDER"/>
<permission name="android.permission.UPDATE_DEVICE_STATS"/>
<permission name="android.permission.UPDATE_APP_OPS_STATS"/>
- <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
</privapp-permissions>
<privapp-permissions package="com.android.managedprovisioning">
@@ -98,7 +91,6 @@ applications that come with the platform
<permission name="android.permission.BIND_CARRIER_MESSAGING_SERVICE"/>
<permission name="android.permission.BIND_CARRIER_SERVICES"/>
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
- <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
</privapp-permissions>
<privapp-permissions package="com.android.mtp">
@@ -108,19 +100,16 @@ applications that come with the platform
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.WRITE_MEDIA_STORAGE"/>
<permission name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
- <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
</privapp-permissions>
<privapp-permissions package="com.android.musicfx">
<permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
- <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
</privapp-permissions>
<privapp-permissions package="com.android.networkrecommendation">
<permission name="android.permission.SCORE_NETWORKS"/>
<permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
- <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
</privapp-permissions>
<privapp-permissions package="com.android.packageinstaller">
@@ -201,7 +190,6 @@ applications that come with the platform
<permission name="android.permission.USE_RESERVED_DISK"/>
<permission name="android.permission.LOG_COMPAT_CHANGE" />
<permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
- <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
</privapp-permissions>
<privapp-permissions package="com.android.providers.contacts">
@@ -215,7 +203,6 @@ applications that come with the platform
<permission name="android.permission.USE_RESERVED_DISK"/>
<permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
<permission name="android.permission.LOG_COMPAT_CHANGE" />
- <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
</privapp-permissions>
<privapp-permissions package="com.android.providers.downloads">
@@ -228,7 +215,6 @@ applications that come with the platform
<permission name="android.permission.UPDATE_APP_OPS_STATS"/>
<permission name="android.permission.UPDATE_DEVICE_STATS"/>
<permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
- <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
</privapp-permissions>
<privapp-permissions package="com.android.providers.telephony">
@@ -238,7 +224,6 @@ applications that come with the platform
<!-- Permissions required for reading and logging compat changes -->
<permission name="android.permission.LOG_COMPAT_CHANGE" />
<permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
- <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
</privapp-permissions>
<privapp-permissions package="com.android.server.telecom">
@@ -254,13 +239,11 @@ applications that come with the platform
<permission name="android.permission.MODIFY_PHONE_STATE"/>
<permission name="android.permission.STOP_APP_SWITCHES"/>
<permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
- <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
</privapp-permissions>
<privapp-permissions package="com.android.sharedstoragebackup">
<permission name="android.permission.WRITE_MEDIA_STORAGE"/>
<permission name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
- <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
</privapp-permissions>
<privapp-permissions package="com.android.shell">
@@ -602,12 +585,10 @@ applications that come with the platform
<permission name="android.permission.INTENT_FILTER_VERIFICATION_AGENT"/>
<permission name="android.permission.DOMAIN_VERIFICATION_AGENT"/>
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
- <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
</privapp-permissions>
<privapp-permissions package="com.android.soundpicker">
<permission name="android.permission.INTERACT_ACROSS_USERS" />
- <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
</privapp-permissions>
<privapp-permissions package="com.android.tv">
@@ -619,18 +600,15 @@ applications that come with the platform
<permission name="android.permission.READ_CONTENT_RATING_SYSTEMS"/>
<permission name="com.android.providers.tv.permission.ACCESS_ALL_EPG_DATA"/>
<permission name="com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS"/>
- <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
</privapp-permissions>
<privapp-permissions package="com.android.vpndialogs">
<permission name="android.permission.CONTROL_VPN"/>
- <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
</privapp-permissions>
<privapp-permissions package="com.android.wallpaper.livepicker">
<permission name="android.permission.SET_WALLPAPER_COMPONENT"/>
<permission name="android.permission.BIND_WALLPAPER"/>
- <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
</privapp-permissions>
<privapp-permissions package="com.android.wallpaper">
@@ -638,15 +616,14 @@ applications that come with the platform
<permission name="android.permission.BIND_WALLPAPER"/>
<permission name="android.permission.CUSTOMIZE_SYSTEM_UI"/>
<permission name="android.permission.SET_WALLPAPER_DIM_AMOUNT"/>
- <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
</privapp-permissions>
<privapp-permissions package="com.android.dynsystem">
<permission name="android.permission.REBOOT"/>
<permission name="android.permission.MANAGE_DYNAMIC_SYSTEM"/>
<permission name="android.permission.READ_OEM_UNLOCK_STATE"/>
- <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
</privapp-permissions>
+
<privapp-permissions package="com.android.settings">
<permission name="android.permission.INSTALL_DYNAMIC_SYSTEM"/>
<permission name="android.permission.BIND_CELL_BROADCAST_SERVICE"/>
@@ -657,12 +634,10 @@ applications that come with the platform
<privapp-permissions package="com.android.bips">
<permission name="android.permission.SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON"/>
- <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
</privapp-permissions>
<privapp-permissions package="com.android.calllogbackup">
<permission name="com.android.voicemail.permission.READ_VOICEMAIL"/>
- <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
</privapp-permissions>
<privapp-permissions package="com.android.devicediagnostics">
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 15f8c328bb56..112eb617e7a6 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -111,3 +111,13 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "animate_bubble_size_change"
+ namespace: "multitasking"
+ description: "Turns on the animation for bubble bar icons size change"
+ bug: "335575529"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 0fd21f3798ac..7041ea307b0f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -437,7 +437,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
private boolean isAppProgressGenerationAllowed() {
- return mBackNavigationInfo.getTouchableRegion().equals(mTouchableArea);
+ return mBackNavigationInfo.isAppProgressGenerationAllowed()
+ && mBackNavigationInfo.getTouchableRegion().equals(mTouchableArea);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
index 9114c7adb6d8..a3111b31a2f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
@@ -354,7 +354,7 @@ abstract class CrossActivityBackAnimation(
matrix.postScale(scale, scale, scalePivotX, 0f)
matrix.postTranslate(tempRectF.left, tempRectF.top)
transaction
- .setAlpha(leash, keepMinimumAlpha(alpha))
+ .setAlpha(leash, alpha)
.setMatrix(leash, matrix, tmpFloat9)
.setCrop(leash, cropRect)
.setCornerRadius(leash, cornerRadius)
@@ -562,9 +562,6 @@ abstract class CrossActivityBackAnimation(
}
}
-// The target will loose focus when alpha == 0, so keep a minimum value for it.
-private fun keepMinimumAlpha(transAlpha: Float) = max(transAlpha, 0.005f)
-
private fun isDarkMode(context: Context): Boolean {
return context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK ==
Configuration.UI_MODE_NIGHT_YES
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
index cee2d92244cc..641952b28bfb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
@@ -35,15 +35,16 @@ import androidx.core.util.plus
import androidx.core.util.putAll
import com.android.internal.logging.InstanceId
import com.android.internal.logging.InstanceIdSequence
+import com.android.internal.protolog.common.ProtoLog
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate
-import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON
-import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT
-import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT
+import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON
+import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT
+import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.DesktopModeStatus
import com.android.wm.shell.shared.TransitionUtil
@@ -80,6 +81,9 @@ class DesktopModeLoggerTransitionObserver(
// animation was cancelled, we restore these tasks to calculate the post-Transition state
private val tasksSavedForRecents: SparseArray<TaskInfo> = SparseArray()
+ // Caching whether the previous transition was exit to overview.
+ private var wasPreviousTransitionExitToOverview: Boolean = false
+
// The instanceId for the current logging session
private var loggerInstanceId: InstanceId? = null
@@ -101,7 +105,7 @@ class DesktopModeLoggerTransitionObserver(
finishTransaction: SurfaceControl.Transaction
) {
// this was a new recents animation
- if (info.isRecentsTransition() && tasksSavedForRecents.isEmpty()) {
+ if (info.isExitToRecentsTransition() && tasksSavedForRecents.isEmpty()) {
KtProtoLog.v(
WM_SHELL_DESKTOP_MODE,
"DesktopModeLogger: Recents animation running, saving tasks for later"
@@ -143,6 +147,7 @@ class DesktopModeLoggerTransitionObserver(
preTransitionVisibleFreeformTasks = visibleFreeformTaskInfos,
postTransitionVisibleFreeformTasks = postTransitionVisibleFreeformTasks
)
+ wasPreviousTransitionExitToOverview = info.isExitToRecentsTransition()
}
override fun onTransitionStarting(transition: IBinder) {}
@@ -309,21 +314,40 @@ class DesktopModeLoggerTransitionObserver(
}
/** Get [EnterReason] for this session enter */
- private fun getEnterReason(transitionInfo: TransitionInfo): EnterReason {
- return when (transitionInfo.type) {
- WindowManager.TRANSIT_WAKE -> EnterReason.SCREEN_ON
- Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP -> EnterReason.APP_HANDLE_DRAG
- TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON -> EnterReason.APP_HANDLE_MENU_BUTTON
- TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW -> EnterReason.APP_FROM_OVERVIEW
- TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT -> EnterReason.KEYBOARD_SHORTCUT_ENTER
- WindowManager.TRANSIT_OPEN -> EnterReason.APP_FREEFORM_INTENT
- else -> EnterReason.UNKNOWN_ENTER
+ private fun getEnterReason(transitionInfo: TransitionInfo): EnterReason =
+ when {
+ transitionInfo.type == WindowManager.TRANSIT_WAKE -> EnterReason.SCREEN_ON
+ transitionInfo.type == Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP ->
+ EnterReason.APP_HANDLE_DRAG
+ transitionInfo.type == TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON ->
+ EnterReason.APP_HANDLE_MENU_BUTTON
+ transitionInfo.type == TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW ->
+ EnterReason.APP_FROM_OVERVIEW
+ transitionInfo.type == TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT ->
+ EnterReason.KEYBOARD_SHORTCUT_ENTER
+ // NOTE: the below condition also applies for EnterReason quickswitch
+ transitionInfo.type == WindowManager.TRANSIT_TO_FRONT -> EnterReason.OVERVIEW
+ // Enter desktop mode from cancelled recents has no transition. Enter is detected on the
+ // next transition involving freeform windows.
+ // TODO(b/346564416): Modify logging for cancelled recents once it transition is
+ // changed. Also see how to account to time difference between actual enter time and
+ // time of this log. Also account for the missed session when exit happens just after
+ // a cancelled recents.
+ wasPreviousTransitionExitToOverview -> EnterReason.OVERVIEW
+ transitionInfo.type == WindowManager.TRANSIT_OPEN -> EnterReason.APP_FREEFORM_INTENT
+ else -> {
+ ProtoLog.w(
+ WM_SHELL_DESKTOP_MODE,
+ "Unknown enter reason for transition type ${transitionInfo.type}",
+ transitionInfo.type
+ )
+ EnterReason.UNKNOWN_ENTER
+ }
}
- }
/** Get [ExitReason] for this session exit */
- private fun getExitReason(transitionInfo: TransitionInfo): ExitReason {
- return when {
+ private fun getExitReason(transitionInfo: TransitionInfo): ExitReason =
+ when {
transitionInfo.type == WindowManager.TRANSIT_SLEEP -> ExitReason.SCREEN_OFF
transitionInfo.type == WindowManager.TRANSIT_CLOSE -> ExitReason.TASK_FINISHED
transitionInfo.type == TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG -> ExitReason.DRAG_TO_EXIT
@@ -331,10 +355,16 @@ class DesktopModeLoggerTransitionObserver(
ExitReason.APP_HANDLE_MENU_BUTTON_EXIT
transitionInfo.type == TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT ->
ExitReason.KEYBOARD_SHORTCUT_EXIT
- transitionInfo.isRecentsTransition() -> ExitReason.RETURN_HOME_OR_OVERVIEW
- else -> ExitReason.UNKNOWN_EXIT
+ transitionInfo.isExitToRecentsTransition() -> ExitReason.RETURN_HOME_OR_OVERVIEW
+ else -> {
+ ProtoLog.w(
+ WM_SHELL_DESKTOP_MODE,
+ "Unknown exit reason for transition type ${transitionInfo.type}",
+ transitionInfo.type
+ )
+ ExitReason.UNKNOWN_EXIT
+ }
}
- }
/** Adds tasks to the saved copy of freeform taskId, taskInfo. Only used for testing. */
@VisibleForTesting
@@ -357,7 +387,7 @@ class DesktopModeLoggerTransitionObserver(
return this.windowingMode == WINDOWING_MODE_FREEFORM
}
- private fun TransitionInfo.isRecentsTransition(): Boolean {
+ private fun TransitionInfo.isExitToRecentsTransition(): Boolean {
return this.type == WindowManager.TRANSIT_TO_FRONT &&
this.flags == WindowManager.TRANSIT_FLAG_IS_RECENTS
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index 1903586fc317..57e469d5cbd2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -550,7 +550,8 @@ public class BackAnimationControllerTest extends ShellTestCase {
.setType(type)
.setOnBackInvokedCallback(mAppCallback)
.setOnBackNavigationDone(new RemoteCallback(result))
- .setTouchableRegion(mTouchableRegion));
+ .setTouchableRegion(mTouchableRegion)
+ .setAppProgressAllowed(true));
triggerBackGesture();
mShellExecutor.flushAll();
releaseBackGesture();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
index 0d3cd10e90df..fb03f20f939c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
@@ -22,6 +22,7 @@ import android.content.Context
import android.os.IBinder
import android.testing.AndroidTestingRunner
import android.view.SurfaceControl
+import android.view.WindowManager.TRANSIT_CHANGE
import android.view.WindowManager.TRANSIT_CLOSE
import android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS
import android.view.WindowManager.TRANSIT_NONE
@@ -182,7 +183,7 @@ class DesktopModeLoggerTransitionObserverTest {
}
@Test
- fun transitEnterDesktopFromAppFromOverview_logTaskAddedAndEnterReasonUnknown() {
+ fun transitEnterDesktopFromAppFromOverview_logTaskAddedAndEnterReasonAppFromOverview() {
val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
val transitionInfo =
TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW, 0)
@@ -218,6 +219,168 @@ class DesktopModeLoggerTransitionObserverTest {
}
@Test
+ fun transitToFront_logTaskAddedAndEnterReasonOverview() {
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val transitionInfo = TransitionInfoBuilder(TRANSIT_TO_FRONT, 0).addChange(change).build()
+
+ callOnTransitionReady(transitionInfo)
+ val sessionId = transitionObserver.getLoggerSessionId()
+
+ assertThat(sessionId).isNotNull()
+ verify(desktopModeEventLogger, times(1))
+ .logSessionEnter(eq(sessionId!!), eq(EnterReason.OVERVIEW))
+ verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
+ verifyZeroInteractions(desktopModeEventLogger)
+ }
+
+ @Test
+ fun transitToFront_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() {
+ // previous exit to overview transition
+ val previousSessionId = 1
+ // add a freeform task
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ transitionObserver.setLoggerSessionId(previousSessionId)
+ val previousChange = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val previousTransitionInfo =
+ TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
+ .addChange(previousChange)
+ .build()
+
+ callOnTransitionReady(previousTransitionInfo)
+
+ verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(previousSessionId), any())
+ verify(desktopModeEventLogger, times(1))
+ .logSessionExit(eq(previousSessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW))
+
+ // Enter desktop mode from cancelled recents has no transition. Enter is detected on the
+ // next transition involving freeform windows
+
+ // TRANSIT_TO_FRONT
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val transitionInfo = TransitionInfoBuilder(TRANSIT_TO_FRONT, 0).addChange(change).build()
+
+ callOnTransitionReady(transitionInfo)
+ val sessionId = transitionObserver.getLoggerSessionId()
+
+ assertThat(sessionId).isNotNull()
+ verify(desktopModeEventLogger, times(1))
+ .logSessionEnter(eq(sessionId!!), eq(EnterReason.OVERVIEW))
+ verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
+ verifyZeroInteractions(desktopModeEventLogger)
+ }
+
+ @Test
+ fun transitChange_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() {
+ // previous exit to overview transition
+ val previousSessionId = 1
+ // add a freeform task
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ transitionObserver.setLoggerSessionId(previousSessionId)
+ val previousChange = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val previousTransitionInfo =
+ TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
+ .addChange(previousChange)
+ .build()
+
+ callOnTransitionReady(previousTransitionInfo)
+
+ verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(previousSessionId), any())
+ verify(desktopModeEventLogger, times(1))
+ .logSessionExit(eq(previousSessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW))
+
+ // Enter desktop mode from cancelled recents has no transition. Enter is detected on the
+ // next transition involving freeform windows
+
+ // TRANSIT_CHANGE
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val transitionInfo = TransitionInfoBuilder(TRANSIT_CHANGE, 0).addChange(change).build()
+
+ callOnTransitionReady(transitionInfo)
+ val sessionId = transitionObserver.getLoggerSessionId()
+
+ assertThat(sessionId).isNotNull()
+ verify(desktopModeEventLogger, times(1))
+ .logSessionEnter(eq(sessionId!!), eq(EnterReason.OVERVIEW))
+ verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
+ verifyZeroInteractions(desktopModeEventLogger)
+ }
+
+ @Test
+ fun transitOpen_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() {
+ // previous exit to overview transition
+ val previousSessionId = 1
+ // add a freeform task
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ transitionObserver.setLoggerSessionId(previousSessionId)
+ val previousChange = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val previousTransitionInfo =
+ TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
+ .addChange(previousChange)
+ .build()
+
+ callOnTransitionReady(previousTransitionInfo)
+
+ verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(previousSessionId), any())
+ verify(desktopModeEventLogger, times(1))
+ .logSessionExit(eq(previousSessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW))
+
+ // Enter desktop mode from cancelled recents has no transition. Enter is detected on the
+ // next transition involving freeform windows
+
+ // TRANSIT_OPEN
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
+
+ callOnTransitionReady(transitionInfo)
+ val sessionId = transitionObserver.getLoggerSessionId()
+
+ assertThat(sessionId).isNotNull()
+ verify(desktopModeEventLogger, times(1))
+ .logSessionEnter(eq(sessionId!!), eq(EnterReason.OVERVIEW))
+ verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
+ verifyZeroInteractions(desktopModeEventLogger)
+ }
+
+ @Test
+ @Suppress("ktlint:standard:max-line-length")
+ fun transitEnterDesktopFromAppFromOverview_previousTransitionExitToOverview_logTaskAddedAndEnterReasonAppFromOverview() {
+ // Tests for AppFromOverview precedence in compared to cancelled Overview
+
+ // previous exit to overview transition
+ val previousSessionId = 1
+ // add a freeform task
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ transitionObserver.setLoggerSessionId(previousSessionId)
+ val previousChange = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val previousTransitionInfo =
+ TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
+ .addChange(previousChange)
+ .build()
+
+ callOnTransitionReady(previousTransitionInfo)
+
+ verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(previousSessionId), any())
+ verify(desktopModeEventLogger, times(1))
+ .logSessionExit(eq(previousSessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW))
+
+ // TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val transitionInfo =
+ TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW, 0)
+ .addChange(change)
+ .build()
+
+ callOnTransitionReady(transitionInfo)
+ val sessionId = transitionObserver.getLoggerSessionId()
+
+ assertThat(sessionId).isNotNull()
+ verify(desktopModeEventLogger, times(1))
+ .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_FROM_OVERVIEW))
+ verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
+ verifyZeroInteractions(desktopModeEventLogger)
+ }
+
+ @Test
fun transitEnterDesktopFromUnknown_logTaskAddedAndEnterReasonUnknown() {
val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
val transitionInfo =
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 748ad3182393..35808d9880f0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -484,57 +484,52 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
fun moveToDesktop_landscapeDevice_resizable_undefinedOrientation_defaultLandscapeBounds() {
- doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
val task = setUpFullscreenTask()
setUpLandscapeDisplay()
controller.moveToDesktop(task, transitionSource = UNKNOWN)
- val wct = getLatestMoveToDesktopWct()
+ val wct = getLatestEnterDesktopWct()
assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
fun moveToDesktop_landscapeDevice_resizable_landscapeOrientation_defaultLandscapeBounds() {
- doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
val task = setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_LANDSCAPE)
setUpLandscapeDisplay()
controller.moveToDesktop(task, transitionSource = UNKNOWN)
- val wct = getLatestMoveToDesktopWct()
+ val wct = getLatestEnterDesktopWct()
assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
fun moveToDesktop_landscapeDevice_resizable_portraitOrientation_resizablePortraitBounds() {
- doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
val task =
setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_PORTRAIT, shouldLetterbox = true)
setUpLandscapeDisplay()
controller.moveToDesktop(task, transitionSource = UNKNOWN)
- val wct = getLatestMoveToDesktopWct()
+ val wct = getLatestEnterDesktopWct()
assertThat(findBoundsChange(wct, task)).isEqualTo(RESIZABLE_PORTRAIT_BOUNDS)
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
fun moveToDesktop_landscapeDevice_unResizable_landscapeOrientation_defaultLandscapeBounds() {
- doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
val task =
setUpFullscreenTask(isResizable = false, screenOrientation = SCREEN_ORIENTATION_LANDSCAPE)
setUpLandscapeDisplay()
controller.moveToDesktop(task, transitionSource = UNKNOWN)
- val wct = getLatestMoveToDesktopWct()
+ val wct = getLatestEnterDesktopWct()
assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
fun moveToDesktop_landscapeDevice_unResizable_portraitOrientation_unResizablePortraitBounds() {
- doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
val task =
setUpFullscreenTask(
isResizable = false,
@@ -543,26 +538,24 @@ class DesktopTasksControllerTest : ShellTestCase() {
setUpLandscapeDisplay()
controller.moveToDesktop(task, transitionSource = UNKNOWN)
- val wct = getLatestMoveToDesktopWct()
+ val wct = getLatestEnterDesktopWct()
assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_PORTRAIT_BOUNDS)
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
fun moveToDesktop_portraitDevice_resizable_undefinedOrientation_defaultPortraitBounds() {
- doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
val task = setUpFullscreenTask(deviceOrientation = ORIENTATION_PORTRAIT)
setUpPortraitDisplay()
controller.moveToDesktop(task, transitionSource = UNKNOWN)
- val wct = getLatestMoveToDesktopWct()
+ val wct = getLatestEnterDesktopWct()
assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
fun moveToDesktop_portraitDevice_resizable_portraitOrientation_defaultPortraitBounds() {
- doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
val task =
setUpFullscreenTask(
deviceOrientation = ORIENTATION_PORTRAIT,
@@ -570,14 +563,13 @@ class DesktopTasksControllerTest : ShellTestCase() {
setUpPortraitDisplay()
controller.moveToDesktop(task, transitionSource = UNKNOWN)
- val wct = getLatestMoveToDesktopWct()
+ val wct = getLatestEnterDesktopWct()
assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
fun moveToDesktop_portraitDevice_resizable_landscapeOrientation_resizableLandscapeBounds() {
- doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
val task =
setUpFullscreenTask(
deviceOrientation = ORIENTATION_PORTRAIT,
@@ -586,14 +578,13 @@ class DesktopTasksControllerTest : ShellTestCase() {
setUpPortraitDisplay()
controller.moveToDesktop(task, transitionSource = UNKNOWN)
- val wct = getLatestMoveToDesktopWct()
+ val wct = getLatestEnterDesktopWct()
assertThat(findBoundsChange(wct, task)).isEqualTo(RESIZABLE_LANDSCAPE_BOUNDS)
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
fun moveToDesktop_portraitDevice_unResizable_portraitOrientation_defaultPortraitBounds() {
- doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
val task =
setUpFullscreenTask(
isResizable = false,
@@ -602,14 +593,13 @@ class DesktopTasksControllerTest : ShellTestCase() {
setUpPortraitDisplay()
controller.moveToDesktop(task, transitionSource = UNKNOWN)
- val wct = getLatestMoveToDesktopWct()
+ val wct = getLatestEnterDesktopWct()
assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
fun moveToDesktop_portraitDevice_unResizable_landscapeOrientation_unResizableLandscapeBounds() {
- doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
val task =
setUpFullscreenTask(
isResizable = false,
@@ -619,7 +609,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
setUpPortraitDisplay()
controller.moveToDesktop(task, transitionSource = UNKNOWN)
- val wct = getLatestMoveToDesktopWct()
+ val wct = getLatestEnterDesktopWct()
assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_LANDSCAPE_BOUNDS)
}
@@ -629,7 +619,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
controller.moveToDesktop(task, transitionSource = UNKNOWN)
- val wct = getLatestMoveToDesktopWct()
+ val wct = getLatestEnterDesktopWct()
assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
}
@@ -639,7 +629,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
controller.moveToDesktop(task, transitionSource = UNKNOWN)
- val wct = getLatestMoveToDesktopWct()
+ val wct = getLatestEnterDesktopWct()
assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
.isEqualTo(WINDOWING_MODE_UNDEFINED)
}
@@ -647,7 +637,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
fun moveToDesktop_nonExistentTask_doesNothing() {
controller.moveToDesktop(999, transitionSource = UNKNOWN)
- verifyWCTNotExecuted()
+ verifyEnterDesktopWCTNotExecuted()
}
@Test
@@ -659,7 +649,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
controller.moveToDesktop(task.taskId, transitionSource = UNKNOWN)
- with(getLatestMoveToDesktopWct()) {
+ with(getLatestEnterDesktopWct()) {
assertLaunchTaskAt(0, task.taskId, WINDOWING_MODE_FREEFORM)
}
}
@@ -674,34 +664,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
controller.moveToDesktop(task, transitionSource = UNKNOWN)
- verifyWCTNotExecuted()
- }
-
- @Test
- fun moveToDesktop_deviceNotSupported_doesNothing() {
- val task = setUpFullscreenTask()
-
- // Simulate non compatible device
- doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
-
- controller.moveToDesktop(task, transitionSource = UNKNOWN)
- verifyWCTNotExecuted()
- }
-
- @Test
- fun moveToDesktop_deviceNotSupported_deviceRestrictionsOverridden_taskIsMovedToDesktop() {
- val task = setUpFullscreenTask()
-
- // Simulate non compatible device
- doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
-
- // Simulate enforce device restrictions system property overridden to false
- whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(false)
-
- controller.moveToDesktop(task, transitionSource = UNKNOWN)
-
- val wct = getLatestMoveToDesktopWct()
- assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
+ verifyEnterDesktopWCTNotExecuted()
}
@Test
@@ -710,7 +673,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
controller.moveToDesktop(task, transitionSource = UNKNOWN)
- val wct = getLatestMoveToDesktopWct()
+ val wct = getLatestEnterDesktopWct()
assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
}
@@ -724,7 +687,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
controller.moveToDesktop(fullscreenTask, transitionSource = UNKNOWN)
- with(getLatestMoveToDesktopWct()) {
+ with(getLatestEnterDesktopWct()) {
// Operations should include home task, freeform task
assertThat(hierarchyOps).hasSize(3)
assertReorderSequence(homeTask, freeformTask, fullscreenTask)
@@ -742,7 +705,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
controller.moveToDesktop(fullscreenTask, transitionSource = UNKNOWN)
- with(getLatestMoveToDesktopWct()) {
+ with(getLatestEnterDesktopWct()) {
// Operations should include wallpaper intent, freeform task, fullscreen task
assertThat(hierarchyOps).hasSize(3)
assertPendingIntentAt(index = 0, desktopWallpaperIntent)
@@ -766,7 +729,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
controller.moveToDesktop(fullscreenTaskDefault, transitionSource = UNKNOWN)
- with(getLatestMoveToDesktopWct()) {
+ with(getLatestEnterDesktopWct()) {
// Check that hierarchy operations do not include tasks from second display
assertThat(hierarchyOps.map { it.container }).doesNotContain(homeTaskSecond.token.asBinder())
assertThat(hierarchyOps.map { it.container })
@@ -778,7 +741,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
fun moveToDesktop_splitTaskExitsSplit() {
val task = setUpSplitScreenTask()
controller.moveToDesktop(task, transitionSource = UNKNOWN)
- val wct = getLatestMoveToDesktopWct()
+ val wct = getLatestEnterDesktopWct()
assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
verify(splitScreenController)
.prepareExitSplitScreen(any(), anyInt(), eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE))
@@ -788,7 +751,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
fun moveToDesktop_fullscreenTaskDoesNotExitSplit() {
val task = setUpFullscreenTask()
controller.moveToDesktop(task, transitionSource = UNKNOWN)
- val wct = getLatestMoveToDesktopWct()
+ val wct = getLatestEnterDesktopWct()
assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
verify(splitScreenController, never())
.prepareExitSplitScreen(any(), anyInt(), eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE))
@@ -803,7 +766,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
controller.moveToDesktop(newTask, transitionSource = UNKNOWN)
- val wct = getLatestMoveToDesktopWct()
+ val wct = getLatestEnterDesktopWct()
assertThat(wct.hierarchyOps.size).isEqualTo(taskLimit + 1) // visible tasks + home
wct.assertReorderAt(0, homeTask)
for (i in 1..<taskLimit) { // Skipping freeformTasks[0]
@@ -837,7 +800,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
fun moveToFullscreen_nonExistentTask_doesNothing() {
controller.moveToFullscreen(999, transitionSource = UNKNOWN)
- verifyWCTNotExecuted()
+ verifyExitDesktopWCTNotExecuted()
}
@Test
@@ -1284,7 +1247,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
controller.moveFocusedTaskToDesktop(DEFAULT_DISPLAY, transitionSource = UNKNOWN)
- val wct = getLatestMoveToDesktopWct()
+ val wct = getLatestEnterDesktopWct()
assertThat(wct.changes[task1.token.asBinder()]?.windowingMode)
.isEqualTo(WINDOWING_MODE_FREEFORM)
}
@@ -1305,7 +1268,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
controller.moveFocusedTaskToDesktop(DEFAULT_DISPLAY, transitionSource = UNKNOWN)
- val wct = getLatestMoveToDesktopWct()
+ val wct = getLatestEnterDesktopWct()
assertThat(wct.changes[task4.token.asBinder()]?.windowingMode)
.isEqualTo(WINDOWING_MODE_FREEFORM)
verify(splitScreenController)
@@ -1332,7 +1295,6 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
fun dragToDesktop_landscapeDevice_resizable_undefinedOrientation_defaultLandscapeBounds() {
- doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
val spyController = spy(controller)
whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
@@ -1349,7 +1311,6 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
fun dragToDesktop_landscapeDevice_resizable_landscapeOrientation_defaultLandscapeBounds() {
- doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
val spyController = spy(controller)
whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
@@ -1366,7 +1327,6 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
fun dragToDesktop_landscapeDevice_resizable_portraitOrientation_resizablePortraitBounds() {
- doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
val spyController = spy(controller)
whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
@@ -1384,7 +1344,6 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
fun dragToDesktop_landscapeDevice_unResizable_landscapeOrientation_defaultLandscapeBounds() {
- doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
val spyController = spy(controller)
whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
@@ -1402,7 +1361,6 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
fun dragToDesktop_landscapeDevice_unResizable_portraitOrientation_unResizablePortraitBounds() {
- doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
val spyController = spy(controller)
whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
@@ -1423,7 +1381,6 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
fun dragToDesktop_portraitDevice_resizable_undefinedOrientation_defaultPortraitBounds() {
- doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
val spyController = spy(controller)
whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
@@ -1440,7 +1397,6 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
fun dragToDesktop_portraitDevice_resizable_portraitOrientation_defaultPortraitBounds() {
- doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
val spyController = spy(controller)
whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
@@ -1460,7 +1416,6 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
fun dragToDesktop_portraitDevice_resizable_landscapeOrientation_resizableLandscapeBounds() {
- doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
val spyController = spy(controller)
whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
@@ -1481,7 +1436,6 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
fun dragToDesktop_portraitDevice_unResizable_portraitOrientation_defaultPortraitBounds() {
- doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
val spyController = spy(controller)
whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
@@ -1502,7 +1456,6 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
fun dragToDesktop_portraitDevice_unResizable_landscapeOrientation_unResizableLandscapeBounds() {
- doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
val spyController = spy(controller)
whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
@@ -1685,7 +1638,6 @@ class DesktopTasksControllerTest : ShellTestCase() {
Rect(0, 0, DISPLAY_DIMENSION_SHORT, DISPLAY_DIMENSION_LONG)
}
}
- whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(true)
whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
runningTasks.add(task)
return task
@@ -1703,7 +1655,6 @@ class DesktopTasksControllerTest : ShellTestCase() {
private fun setUpSplitScreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
val task = createSplitScreenTask(displayId)
- whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(true)
whenever(splitScreenController.isTaskInSplitScreen(task.taskId)).thenReturn(true)
whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
runningTasks.add(task)
@@ -1748,7 +1699,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
return arg.value
}
- private fun getLatestMoveToDesktopWct(): WindowContainerTransaction {
+ private fun getLatestEnterDesktopWct(): WindowContainerTransaction {
val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
if (ENABLE_SHELL_TRANSITIONS) {
verify(enterDesktopTransitionHandler).moveToDesktop(arg.capture(), any())
@@ -1790,6 +1741,22 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
}
+ private fun verifyExitDesktopWCTNotExecuted() {
+ if (ENABLE_SHELL_TRANSITIONS) {
+ verify(exitDesktopTransitionHandler, never()).startTransition(any(), any(), any(), any())
+ } else {
+ verify(shellTaskOrganizer, never()).applyTransaction(any())
+ }
+ }
+
+ private fun verifyEnterDesktopWCTNotExecuted() {
+ if (ENABLE_SHELL_TRANSITIONS) {
+ verify(enterDesktopTransitionHandler, never()).moveToDesktop(any(), any())
+ } else {
+ verify(shellTaskOrganizer, never()).applyTransaction(any())
+ }
+ }
+
private fun createTransition(
task: RunningTaskInfo?,
@WindowManager.TransitionType type: Int = TRANSIT_OPEN
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt
index c35721c11741..772e02eb9e29 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt
@@ -34,6 +34,7 @@ import com.android.credentialmanager.getflow.ProviderDisplayInfo
import com.android.credentialmanager.getflow.RequestDisplayInfo
import com.android.credentialmanager.getflow.generateDisplayTitleTextResCode
import com.android.credentialmanager.model.BiometricRequestInfo
+import com.android.credentialmanager.model.CredentialType
import com.android.credentialmanager.model.EntryInfo
import com.android.credentialmanager.model.creation.CreateOptionInfo
import com.android.credentialmanager.model.get.CredentialEntryInfo
@@ -476,7 +477,9 @@ private fun retrieveBiometricGetDisplayValues(
return null
}
val singleEntryType = selectedEntry.credentialType
- val username = selectedEntry.userName
+ val descriptionName = if (singleEntryType == CredentialType.PASSKEY &&
+ !selectedEntry.displayName.isNullOrBlank()) selectedEntry.displayName else
+ selectedEntry.userName
// TODO(b/336362538) : In W, utilize updated localization strings
displayTitleText = context.getString(
@@ -487,7 +490,7 @@ private fun retrieveBiometricGetDisplayValues(
descriptionText = context.getString(
R.string.get_dialog_description_single_tap,
getRequestDisplayInfo.appName,
- username
+ descriptionName
)
return BiometricDisplayInfo(providerIcon = icon, providerName = providerName,
diff --git a/packages/EasterEgg/src/com/android/egg/landroid/MainActivity.kt b/packages/EasterEgg/src/com/android/egg/landroid/MainActivity.kt
index 79f8b5fc6ecd..16ec1a933d92 100644
--- a/packages/EasterEgg/src/com/android/egg/landroid/MainActivity.kt
+++ b/packages/EasterEgg/src/com/android/egg/landroid/MainActivity.kt
@@ -17,6 +17,7 @@
package com.android.egg.landroid
import android.content.res.Resources
+import android.os.Build
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
@@ -119,6 +120,26 @@ fun randomSeed(): Long {
}.absoluteValue
}
+fun getDessertCode(): String =
+ when (Build.VERSION.SDK_INT) {
+ Build.VERSION_CODES.LOLLIPOP -> "LMP"
+ Build.VERSION_CODES.LOLLIPOP_MR1 -> "LM1"
+ Build.VERSION_CODES.M -> "MNC"
+ Build.VERSION_CODES.N -> "NYC"
+ Build.VERSION_CODES.N_MR1 -> "NM1"
+ Build.VERSION_CODES.O -> "OC"
+ Build.VERSION_CODES.P -> "PIE"
+ Build.VERSION_CODES.Q -> "QT"
+ Build.VERSION_CODES.R -> "RVC"
+ Build.VERSION_CODES.S -> "SC"
+ Build.VERSION_CODES.S_V2 -> "SC2"
+ Build.VERSION_CODES.TIRAMISU -> "TM"
+ Build.VERSION_CODES.UPSIDE_DOWN_CAKE -> "UDC"
+ Build.VERSION_CODES.VANILLA_ICE_CREAM -> "VIC"
+ else -> Build.VERSION.RELEASE_OR_CODENAME.replace(Regex("[a-z]*"), "")
+ }
+
+
val DEBUG_TEXT = mutableStateOf("Hello Universe")
const val SHOW_DEBUG_TEXT = false
@@ -239,7 +260,8 @@ fun Telemetry(universe: VisibleUniverse) {
text =
(with(universe.star) {
listOf(
- " STAR: $name (UDC-${universe.randomSeed % 100_000})",
+ " STAR: $name (${getDessertCode()}-" +
+ "${universe.randomSeed % 100_000})",
" CLASS: ${cls.name}",
"RADIUS: ${radius.toInt()}",
" MASS: %.3g".format(mass),
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index 232fa9204517..d6345cec7c30 100644
--- a/packages/SettingsLib/Spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/build.gradle.kts
@@ -29,7 +29,7 @@ val androidTop: String = File(rootDir, "../../../../..").canonicalPath
allprojects {
extra["androidTop"] = androidTop
- extra["jetpackComposeVersion"] = "1.7.0-SNAPSHOT"
+ extra["jetpackComposeVersion"] = "1.7.0-beta02"
}
subprojects {
diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml
index 85ad160f6d66..a842009a09bb 100644
--- a/packages/SettingsLib/Spa/gradle/libs.versions.toml
+++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml
@@ -15,7 +15,7 @@
#
[versions]
-agp = "8.3.2"
+agp = "8.5.0"
compose-compiler = "1.5.11"
dexmaker-mockito = "2.28.3"
jvm = "17"
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.7-bin.zip b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.8-bin.zip
index 7a9ac5afe013..77e6ad3f2117 100644
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.7-bin.zip
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.8-bin.zip
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
index d64cd4917707..e6441136f3d4 100644
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
index 182095e76e76..91d2a3ab217d 100644
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
@@ -16,6 +16,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=gradle-8.7-bin.zip
+distributionUrl=gradle-8.8-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/packages/SettingsLib/Spa/gradlew b/packages/SettingsLib/Spa/gradlew
index 1aa94a426907..b740cf13397a 100755
--- a/packages/SettingsLib/Spa/gradlew
+++ b/packages/SettingsLib/Spa/gradlew
@@ -55,7 +55,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
-# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
diff --git a/packages/SettingsLib/Spa/settings.gradle.kts b/packages/SettingsLib/Spa/settings.gradle.kts
index 31f462e5e918..e003c81d7a0c 100644
--- a/packages/SettingsLib/Spa/settings.gradle.kts
+++ b/packages/SettingsLib/Spa/settings.gradle.kts
@@ -36,9 +36,6 @@ dependencyResolutionManagement {
}
mavenCentral()
maven {
- url = uri("https://androidx.dev/snapshots/builds/11846308/artifacts/repository")
- }
- maven {
url = uri("https://jitpack.io")
content {
includeGroup("com.github.PhilJay")
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index f98695eb2e1d..9b8ecf773661 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -53,21 +53,21 @@ android {
dependencies {
api(project(":SettingsLibColor"))
- api("androidx.appcompat:appcompat:1.7.0-beta01")
+ api("androidx.appcompat:appcompat:1.7.0-rc01")
api("androidx.slice:slice-builders:1.1.0-alpha02")
api("androidx.slice:slice-core:1.1.0-alpha02")
api("androidx.slice:slice-view:1.1.0-alpha02")
- api("androidx.compose.material3:material3:1.3.0-SNAPSHOT")
+ api("androidx.compose.material3:material3:1.3.0-beta02")
api("androidx.compose.material:material-icons-extended:$jetpackComposeVersion")
api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion")
api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion")
api("androidx.lifecycle:lifecycle-livedata-ktx")
api("androidx.lifecycle:lifecycle-runtime-compose")
- api("androidx.navigation:navigation-compose:2.8.0-beta01")
+ api("androidx.navigation:navigation-compose:2.8.0-beta02")
api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha")
api("com.google.android.material:material:1.11.0")
debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion")
- implementation("com.airbnb.android:lottie-compose:5.2.0")
+ implementation("com.airbnb.android:lottie-compose:6.4.0")
androidTestImplementation(project(":testutils"))
androidTestImplementation(libs.dexmaker.mockito)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt
index 3fdb1d14e667..12d04f95056d 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt
@@ -33,6 +33,9 @@ data class SettingsPage(
// The name of the page provider, who creates this page. It is used to compute the unique id.
val sppName: String,
+ // The category id of the page provider which is the PageId at SettingsEnums.
+ val metricsCategory: Int = 0,
+
// The display name of the page, for better readability.
val displayName: String,
@@ -46,6 +49,7 @@ data class SettingsPage(
// TODO: cleanup it once all its usage in Settings are switched to Spp.createSettingsPage
fun create(
name: String,
+ metricsCategory: Int = 0,
displayName: String? = null,
parameter: List<NamedNavArgument> = emptyList(),
arguments: Bundle? = null
@@ -53,6 +57,7 @@ data class SettingsPage(
return SettingsPage(
id = genPageId(name, parameter, arguments),
sppName = name,
+ metricsCategory = metricsCategory,
displayName = displayName ?: name,
parameter = parameter,
arguments = arguments
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt
index 0281ab817340..95c7d2371b3d 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt
@@ -34,6 +34,10 @@ interface SettingsPageProvider {
/** The page provider name, needs to be *unique* and *stable*. */
val name: String
+ /** The category id which is the PageId at SettingsEnums.*/
+ val metricsCategory: Int
+ get() = 0
+
enum class NavType {
Page,
Dialog,
@@ -79,6 +83,7 @@ fun SettingsPageProvider.createSettingsPage(arguments: Bundle? = null): Settings
return SettingsPage(
id = genPageId(name, parameter, arguments),
sppName = name,
+ metricsCategory = metricsCategory,
displayName = displayName + parameter.normalizeArgList(arguments, eraseRuntimeValues = true)
.joinToString("") { arg -> "/$arg" },
parameter = parameter,
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaLogger.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaLogger.kt
index 215f6b964ad3..741129738af7 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaLogger.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaLogger.kt
@@ -45,6 +45,7 @@ internal const val LOG_DATA_DISPLAY_NAME = "name"
internal const val LOG_DATA_SWITCH_STATUS = "switch"
const val LOG_DATA_SESSION_NAME = "session"
+const val LOG_DATA_METRICS_CATEGORY = "metricsCategory"
/**
* The interface of logger in Spa
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/PageLogger.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/PageLogger.kt
index a9e5e393000e..f323e996f5fd 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/PageLogger.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/PageLogger.kt
@@ -19,6 +19,7 @@ package com.android.settingslib.spa.framework.util
import androidx.compose.runtime.Composable
import androidx.core.os.bundleOf
import com.android.settingslib.spa.framework.common.LOG_DATA_DISPLAY_NAME
+import com.android.settingslib.spa.framework.common.LOG_DATA_METRICS_CATEGORY
import com.android.settingslib.spa.framework.common.LOG_DATA_SESSION_NAME
import com.android.settingslib.spa.framework.common.LogCategory
import com.android.settingslib.spa.framework.common.LogEvent
@@ -45,9 +46,10 @@ private fun SettingsPage.logPageEvent(event: LogEvent, navController: NavControl
extraData = bundleOf(
LOG_DATA_DISPLAY_NAME to displayName,
LOG_DATA_SESSION_NAME to navController.sessionSourceName,
+ LOG_DATA_METRICS_CATEGORY to metricsCategory,
).apply {
val normArguments = parameter.normalize(arguments)
if (normArguments != null) putAll(normArguments)
}
)
-} \ No newline at end of file
+}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBarTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBarTest.kt
index 3dac7dbc005d..ea69eabbe971 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBarTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBarTest.kt
@@ -61,11 +61,13 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spa.testutils.rootWidth
import com.android.settingslib.spa.testutils.setContentForSizeAssertions
import com.google.common.truth.Truth.assertThat
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@OptIn(ExperimentalMaterial3Api::class)
+@Ignore("b/346785755")
@RunWith(AndroidJUnit4::class)
class CustomizedAppBarTest {
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 58c39b477a82..fde7c2caca06 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -785,6 +785,7 @@ android_app {
kotlincflags: ["-Xjvm-default=all"],
optimize: {
shrink_resources: false,
+ optimized_shrink_resources: false,
proguard_flags_files: ["proguard.flags"],
},
@@ -921,6 +922,7 @@ systemui_optimized_java_defaults {
optimize: true,
shrink: true,
shrink_resources: true,
+ optimized_shrink_resources: true,
ignore_warnings: false,
proguard_compatibility: false,
},
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index d2e5a13adfce..e03ac3de7e4e 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -11,6 +11,7 @@ aioana@google.com
alexflo@google.com
andonian@google.com
amiko@google.com
+austindelgado@google.com
aroederer@google.com
arteiro@google.com
asc@google.com
@@ -29,11 +30,13 @@ chrisgollner@google.com
cinek@google.com
cocod@google.com
darrellshi@google.com
+diyab@google.com
dupin@google.com
ethibodeau@google.com
evanlaird@google.com
florenceyang@google.com
gallmann@google.com
+graciecheng@google.com
gwasserman@google.com
hwwang@google.com
hyunyoungs@google.com
@@ -42,10 +45,12 @@ iyz@google.com
jbolinger@google.com
jdemeulenaere@google.com
jeffdq@google.com
+jeffpu@google.com
jernej@google.com
jglazier@google.com
jjaggi@google.com
jonmiranda@google.com
+joshmccloskey@google.com
joshtrask@google.com
juansmartinez@google.com
juliacr@google.com
@@ -87,6 +92,7 @@ saff@google.com
santie@google.com
shanh@google.com
snoeberger@google.com
+spdonghao@google.com
steell@google.com
stevenckng@google.com
stwu@google.com
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index 18e655085ae6..22566e70f409 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -99,7 +99,7 @@ fun SceneContainer(
if (sceneKey == currentSceneKey) {
currentDestinations
} else {
- composableScene.destinationScenes.value
+ viewModel.resolveSceneFamilies(composableScene.destinationScenes.value)
},
) {
with(composableScene) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index f3de463e880e..fa79ea01cf51 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -256,8 +256,6 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Mock
private ViewRootImpl mViewRootImpl;
@Mock
- private FpsUnlockTracker mFpsUnlockTracker;
- @Mock
private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@Mock
private Lazy<DeviceEntryUdfpsTouchOverlayViewModel> mDeviceEntryUdfpsTouchOverlayViewModel;
@@ -368,7 +366,6 @@ public class UdfpsControllerTest extends SysuiTestCase {
mock(DeviceEntryFaceAuthInteractor.class),
mUdfpsKeyguardAccessibilityDelegate,
mSelectedUserInteractor,
- mFpsUnlockTracker,
mKeyguardTransitionInteractor,
mDeviceEntryUdfpsTouchOverlayViewModel,
mDefaultUdfpsTouchOverlayViewModel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelTest.kt
index 0250c9d99e3a..baeb2dd5aa96 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelTest.kt
@@ -31,7 +31,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
-import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -42,13 +41,11 @@ class CommunalTransitionViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+ private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+ private val communalSceneRepository = kosmos.fakeCommunalSceneRepository
- private lateinit var underTest: CommunalTransitionViewModel
-
- @Before
- fun setup() {
- underTest = kosmos.communalTransitionViewModel
+ private val underTest: CommunalTransitionViewModel by lazy {
+ kosmos.communalTransitionViewModel
}
@Test
@@ -60,11 +57,7 @@ class CommunalTransitionViewModelTest : SysuiTestCase() {
enterCommunal(from = KeyguardState.LOCKSCREEN)
assertThat(isUmoOnCommunal).isTrue()
- keyguardTransitionRepository.sendTransitionSteps(
- from = KeyguardState.GLANCEABLE_HUB,
- to = KeyguardState.LOCKSCREEN,
- testScope
- )
+ exitCommunal(to = KeyguardState.LOCKSCREEN)
assertThat(isUmoOnCommunal).isFalse()
}
@@ -77,11 +70,7 @@ class CommunalTransitionViewModelTest : SysuiTestCase() {
enterCommunal(from = KeyguardState.DREAMING)
assertThat(isUmoOnCommunal).isTrue()
- keyguardTransitionRepository.sendTransitionSteps(
- from = KeyguardState.GLANCEABLE_HUB,
- to = KeyguardState.DREAMING,
- testScope
- )
+ exitCommunal(to = KeyguardState.DREAMING)
assertThat(isUmoOnCommunal).isFalse()
}
@@ -94,11 +83,7 @@ class CommunalTransitionViewModelTest : SysuiTestCase() {
enterCommunal(from = KeyguardState.OCCLUDED)
assertThat(isUmoOnCommunal).isTrue()
- keyguardTransitionRepository.sendTransitionSteps(
- from = KeyguardState.GLANCEABLE_HUB,
- to = KeyguardState.OCCLUDED,
- testScope
- )
+ exitCommunal(to = KeyguardState.OCCLUDED)
assertThat(isUmoOnCommunal).isFalse()
}
@@ -112,20 +97,42 @@ class CommunalTransitionViewModelTest : SysuiTestCase() {
assertThat(isUmoOnCommunal).isTrue()
// Communal is no longer visible.
- kosmos.fakeCommunalSceneRepository.changeScene(CommunalScenes.Blank)
- runCurrent()
+ communalSceneRepository.changeScene(CommunalScenes.Blank)
// isUmoOnCommunal returns false, even without any keyguard transition.
assertThat(isUmoOnCommunal).isFalse()
}
+ @Test
+ fun isUmoOnCommunal_idleOnCommunal_returnsTrue() =
+ testScope.runTest {
+ val isUmoOnCommunal by collectLastValue(underTest.isUmoOnCommunal)
+ assertThat(isUmoOnCommunal).isFalse()
+
+ // Communal is fully visible.
+ communalSceneRepository.changeScene(CommunalScenes.Communal)
+
+ // isUmoOnCommunal returns true, even without any keyguard transition.
+ assertThat(isUmoOnCommunal).isTrue()
+ }
+
private suspend fun TestScope.enterCommunal(from: KeyguardState) {
keyguardTransitionRepository.sendTransitionSteps(
from = from,
to = KeyguardState.GLANCEABLE_HUB,
testScope
)
- kosmos.fakeCommunalSceneRepository.changeScene(CommunalScenes.Communal)
+ communalSceneRepository.changeScene(CommunalScenes.Communal)
+ runCurrent()
+ }
+
+ private suspend fun TestScope.exitCommunal(to: KeyguardState) {
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = to,
+ testScope
+ )
+ communalSceneRepository.changeScene(CommunalScenes.Blank)
runCurrent()
}
}
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 0d23a6da0973..79be2b1f8632 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -330,6 +330,8 @@
<string name="share_to_app_stop_dialog_title">Stop sharing screen?</string>
<!-- Text telling a user that they will stop sharing their screen if they click the "Stop sharing" button [CHAR LIMIT=100] -->
<string name="share_to_app_stop_dialog_message">You will stop sharing your screen</string>
+ <!-- Text telling a user that they will stop sharing the contents of the specified [app_name] if they click the "Stop sharing" button. Note that the app name will appear in bold. [CHAR LIMIT=100] -->
+ <string name="share_to_app_stop_dialog_message_specific_app">You will stop sharing &lt;b><xliff:g id="app_name" example="Photos App">%1$s</xliff:g>&lt;/b></string>
<!-- Button to stop screen sharing [CHAR LIMIT=35] -->
<string name="share_to_app_stop_dialog_button">Stop sharing</string>
@@ -337,6 +339,8 @@
<string name="cast_to_other_device_stop_dialog_title">Stop casting screen?</string>
<!-- Text telling a user that they will stop casting their screen to a different device if they click the "Stop casting" button [CHAR LIMIT=100] -->
<string name="cast_to_other_device_stop_dialog_message">You will stop casting your screen</string>
+ <!-- Text telling a user that they will stop casting the contents of the specified [app_name] to a different device if they click the "Stop casting" button. Note that the app name will appear in bold. [CHAR LIMIT=100] -->
+ <string name="cast_to_other_device_stop_dialog_message_specific_app">You will stop casting &lt;b><xliff:g id="app_name" example="Photos App">%1$s</xliff:g>&lt;/b></string>
<!-- Button to stop screen casting to a different device [CHAR LIMIT=35] -->
<string name="cast_to_other_device_stop_dialog_button">Stop casting</string>
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 4c9af6607afa..e055e7c9683b 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -1250,6 +1250,11 @@ public class ScreenDecorations implements
if (mOverlays == null) {
return;
}
+ if (mPendingConfigChange) {
+ // Let RestartingPreDrawListener's onPreDraw call updateConfiguration
+ // -> updateOverlayProviderViews to redraw with display change synchronously.
+ return;
+ }
++mProviderRefreshToken;
for (final OverlayWindow overlay: mOverlays) {
if (overlay == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FpsUnlockTracker.kt b/packages/SystemUI/src/com/android/systemui/biometrics/FpsUnlockTracker.kt
deleted file mode 100644
index cf699a2ed349..000000000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FpsUnlockTracker.kt
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics
-
-import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START
-import android.hardware.biometrics.BiometricSourceType
-import android.hardware.biometrics.BiometricSourceType.FINGERPRINT
-import android.util.Log
-import com.android.app.tracing.TraceStateLogger
-import com.android.internal.util.LatencyTracker
-import com.android.internal.util.LatencyTracker.ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.KeyguardUnlockAnimationController
-import com.android.systemui.keyguard.KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import javax.inject.Inject
-
-private const val TAG = "FpsUnlockTracker"
-private const val TRACE_COUNTER_NAME = "FpsUnlockStage"
-private const val TRACE_TAG_AOD = "AOD"
-private const val TRACE_TAG_KEYGUARD = "KEYGUARD"
-private const val DEBUG = true
-
-/** This is a class for monitoring unlock latency of fps and logging stages in perfetto. */
-@SysUISingleton
-class FpsUnlockTracker
-@Inject
-constructor(
- private val statusBarStateController: StatusBarStateController,
- private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
- private val keyguardUnlockAnimationController: KeyguardUnlockAnimationController,
- private val latencyTracker: LatencyTracker,
-) {
- private val fpsTraceStateLogger = TraceStateLogger(TRACE_COUNTER_NAME)
- private var fpsAuthenticated: Boolean = false
-
- private val keyguardUpdateMonitorCallback =
- object : KeyguardUpdateMonitorCallback() {
- override fun onBiometricAcquired(
- biometricSourceType: BiometricSourceType?,
- acquireInfo: Int
- ) {
- if (keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed) {
- onHalAuthenticationStage(acquireInfo)
- }
- }
-
- override fun onBiometricAuthenticated(
- userId: Int,
- biometricSourceType: BiometricSourceType?,
- isStrongBiometric: Boolean
- ) {
- if (biometricSourceType == FINGERPRINT) {
- fpsAuthenticated = true
- onExitKeyguard()
- }
- }
-
- override fun onBiometricError(
- msgId: Int,
- errString: String?,
- biometricSourceType: BiometricSourceType?
- ) {
- if (biometricSourceType == FINGERPRINT) {
- latencyTracker.onActionCancel(ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME)
- }
- }
-
- override fun onBiometricRunningStateChanged(
- running: Boolean,
- biometricSourceType: BiometricSourceType?
- ) {
- if (biometricSourceType != FINGERPRINT || !running) {
- return
- }
- onWaitForAuthenticationStage()
- }
- }
-
- private val keyguardUnlockAnimationListener =
- object : KeyguardUnlockAnimationListener {
- override fun onUnlockAnimationFinished() = onUnlockedStage()
- }
-
- /** Start tracking the fps unlock. */
- fun startTracking() {
- keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
- keyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(
- keyguardUnlockAnimationListener
- )
- }
-
- /** Stop tracking the fps unlock. */
- fun stopTracking() {
- keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback)
- keyguardUnlockAnimationController.removeKeyguardUnlockAnimationListener(
- keyguardUnlockAnimationListener
- )
- }
-
- /**
- * The stage when the devices is locked and is possible to be unlocked via fps. However, in some
- * situations, it might be unlocked only via bouncer.
- */
- fun onWaitForAuthenticationStage() {
- val stage =
- if (keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed)
- FpsUnlockStage.WAIT_FOR_AUTHENTICATION.name
- else FpsUnlockStage.WAIT_FOR_AUTHENTICATION.name + "(Not allowed)"
- fpsTraceStateLogger.log(stage)
- if (DEBUG) {
- Log.d(TAG, "onWaitForAuthenticationStage: stage=$stage")
- }
- }
-
- /**
- * The stage dedicated to UDFPS, SFPS should not enter this stage. The only place where invokes
- * this function is UdfpsController#onFingerDown.
- */
- fun onUiReadyStage() {
- if (!keyguardUpdateMonitor.isUdfpsSupported || !keyguardUpdateMonitor.isUdfpsEnrolled) {
- return
- }
- fpsTraceStateLogger.log(FpsUnlockStage.UI_READY.name)
- startLatencyTracker()
- if (DEBUG) {
- Log.d(TAG, "onUiReadyStage: dozing=${statusBarStateController.isDozing}")
- }
- }
-
- /** The stage when the HAL is authenticating the fingerprint. */
- fun onHalAuthenticationStage(acquire: Int) {
- fpsTraceStateLogger.log("${FpsUnlockStage.HAL_AUTHENTICATION.name}($acquire)")
- // Start latency tracker here only for SFPS, UDFPS should start at onUiReadyStage.
- if (
- keyguardUpdateMonitor.isSfpsSupported &&
- keyguardUpdateMonitor.isSfpsEnrolled &&
- acquire == FINGERPRINT_ACQUIRED_START
- ) {
- startLatencyTracker()
- }
- if (DEBUG) {
- Log.d(
- TAG,
- "onHalAuthenticationStage: acquire=$acquire" +
- ", sfpsSupported=${keyguardUpdateMonitor.isSfpsSupported}" +
- ", sfpsEnrolled=${keyguardUpdateMonitor.isSfpsEnrolled}"
- )
- }
- }
-
- /** The stage when the authentication is succeeded and is going to exit keyguard. */
- fun onExitKeyguard() {
- fpsTraceStateLogger.log(FpsUnlockStage.EXIT_KEYGUARD.name)
- if (DEBUG) {
- Log.d(TAG, "onExitKeyguard: fpsAuthenticated=$fpsAuthenticated")
- }
- }
-
- /**
- * The stage when the unlock animation is finished which means the user can start interacting
- * with the device.
- */
- fun onUnlockedStage() {
- fpsTraceStateLogger.log(FpsUnlockStage.UNLOCKED.name)
- if (fpsAuthenticated) {
- // The device is unlocked successfully via fps, end the instrument.
- latencyTracker.onActionEnd(ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME)
- } else {
- // The device is unlocked but not via fps, maybe bouncer? Cancel the instrument.
- latencyTracker.onActionCancel(ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME)
- }
- if (DEBUG) {
- Log.d(TAG, "onUnlockedStage: fpsAuthenticated=$fpsAuthenticated")
- }
- fpsAuthenticated = false
- }
-
- private fun startLatencyTracker() {
- latencyTracker.onActionCancel(ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME)
- val tag = if (statusBarStateController.isDozing) TRACE_TAG_AOD else TRACE_TAG_KEYGUARD
- latencyTracker.onActionStart(ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME, tag)
- }
-}
-
-private enum class FpsUnlockStage {
- WAIT_FOR_AUTHENTICATION,
- UI_READY,
- HAL_AUTHENTICATION,
- EXIT_KEYGUARD,
- UNLOCKED
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 85b5faf2d556..ad142a86fa03 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -184,7 +184,6 @@ public class UdfpsController implements DozeReceiver, Dumpable {
@NonNull private final InputManager mInputManager;
@NonNull private final UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate;
@NonNull private final SelectedUserInteractor mSelectedUserInteractor;
- @NonNull private final FpsUnlockTracker mFpsUnlockTracker;
private final boolean mIgnoreRefreshRate;
private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@@ -712,7 +711,6 @@ public class UdfpsController implements DozeReceiver, Dumpable {
@NonNull DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor,
@NonNull UdfpsKeyguardAccessibilityDelegate udfpsKeyguardAccessibilityDelegate,
@NonNull SelectedUserInteractor selectedUserInteractor,
- @NonNull FpsUnlockTracker fpsUnlockTracker,
@NonNull KeyguardTransitionInteractor keyguardTransitionInteractor,
Lazy<DeviceEntryUdfpsTouchOverlayViewModel> deviceEntryUdfpsTouchOverlayViewModel,
Lazy<DefaultUdfpsTouchOverlayViewModel> defaultUdfpsTouchOverlayViewModel,
@@ -765,8 +763,6 @@ public class UdfpsController implements DozeReceiver, Dumpable {
mInputManager = inputManager;
mUdfpsKeyguardAccessibilityDelegate = udfpsKeyguardAccessibilityDelegate;
mSelectedUserInteractor = selectedUserInteractor;
- mFpsUnlockTracker = fpsUnlockTracker;
- mFpsUnlockTracker.startTracking();
mKeyguardTransitionInteractor = keyguardTransitionInteractor;
mTouchProcessor = singlePointerTouchProcessor;
@@ -1067,9 +1063,6 @@ public class UdfpsController implements DozeReceiver, Dumpable {
if (isOptical()) {
mLatencyTracker.onActionStart(ACTION_UDFPS_ILLUMINATE);
}
- if (getBiometricSessionType() == SESSION_KEYGUARD) {
- mFpsUnlockTracker.onUiReadyStage();
- }
// Refresh screen timeout and boost process priority if possible.
mPowerManager.userActivity(mSystemClock.uptimeMillis(),
PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
index 14d8caf2a5b7..e2a8a691b1fd 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
@@ -39,12 +39,14 @@ import com.android.systemui.biometrics.data.repository.PromptRepositoryImpl
import com.android.systemui.biometrics.udfps.BoundingBoxOverlapDetector
import com.android.systemui.biometrics.udfps.EllipseOverlapDetector
import com.android.systemui.biometrics.udfps.OverlapDetector
+import com.android.systemui.biometrics.ui.binder.DeviceEntryUnlockTrackerViewBinder
import com.android.systemui.biometrics.ui.binder.SideFpsOverlayViewBinder
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.ui.binder.AlternateBouncerViewBinder
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
import com.android.systemui.util.concurrency.ThreadFactory
import dagger.Binds
+import dagger.BindsOptionalOf
import dagger.Module
import dagger.Provides
import dagger.multibindings.ClassKey
@@ -101,6 +103,9 @@ interface BiometricsModule {
@SysUISingleton
fun displayStateRepository(impl: DisplayStateRepositoryImpl): DisplayStateRepository
+ @BindsOptionalOf
+ fun deviceEntryUnlockTrackerViewBinder(): DeviceEntryUnlockTrackerViewBinder
+
companion object {
/** Background [Executor] for HAL related operations. */
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index 65c5b6b3859c..408e2c2d65a1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -457,17 +457,10 @@ object BiometricViewBinder {
// Retry and confirmation when finger on sensor
launch {
- combine(
- viewModel.canTryAgainNow,
- viewModel.hasFingerOnSensor,
- viewModel.isPendingConfirmation,
- ::Triple
- )
- .collect { (canRetry, fingerAcquired, pendingConfirmation) ->
+ combine(viewModel.canTryAgainNow, viewModel.hasFingerOnSensor, ::Pair)
+ .collect { (canRetry, fingerAcquired) ->
if (canRetry && fingerAcquired) {
legacyCallback.onButtonTryAgain()
- } else if (pendingConfirmation && fingerAcquired) {
- viewModel.confirmAuthenticated()
}
}
}
@@ -497,13 +490,21 @@ class Spaghetti(
@Deprecated("TODO(b/330788871): remove after replacing AuthContainerView")
interface Callback {
fun onAuthenticated()
+
fun onUserCanceled()
+
fun onButtonNegative()
+
fun onButtonTryAgain()
+
fun onContentViewMoreOptionsButtonPressed()
+
fun onError()
+
fun onUseDeviceCredential()
+
fun onStartDelayedFingerprintSensor()
+
fun onAuthenticatedAndConfirmed()
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/DeviceEntryUnlockTrackerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/DeviceEntryUnlockTrackerViewBinder.kt
new file mode 100644
index 000000000000..78eaab262370
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/DeviceEntryUnlockTrackerViewBinder.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.ui.binder
+
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
+
+/** ViewBinder for device entry unlock tracker implemented in vendor */
+interface DeviceEntryUnlockTrackerViewBinder {
+ /**
+ * Allows vendor binds vendor's view model to the specified view.
+ * @param view the view to be bound
+ */
+ fun bind(view: KeyguardRootView) {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
index ff7ac35ba56b..9cc46506cefb 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
@@ -33,7 +33,6 @@ import com.airbnb.lottie.LottieProperty
import com.android.app.animation.Interpolators
import com.android.keyguard.KeyguardPINView
import com.android.systemui.CoreStartable
-import com.android.systemui.biometrics.FpsUnlockTracker
import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
import com.android.systemui.biometrics.domain.interactor.SideFpsSensorInteractor
@@ -65,7 +64,6 @@ constructor(
private val biometricStatusInteractor: Lazy<BiometricStatusInteractor>,
private val displayStateInteractor: Lazy<DisplayStateInteractor>,
private val deviceEntrySideFpsOverlayInteractor: Lazy<DeviceEntrySideFpsOverlayInteractor>,
- private val fpsUnlockTracker: Lazy<FpsUnlockTracker>,
private val layoutInflater: Lazy<LayoutInflater>,
private val sideFpsProgressBarViewModel: Lazy<SideFpsProgressBarViewModel>,
private val sfpsSensorInteractor: Lazy<SideFpsSensorInteractor>,
@@ -114,7 +112,6 @@ constructor(
}
}
}
- .invokeOnCompletion { fpsUnlockTracker.get().stopTracking() }
}
private var overlayView: View? = null
@@ -138,7 +135,7 @@ constructor(
displayStateInteractor.get(),
sfpsSensorInteractor.get(),
)
- bind(overlayView!!, overlayViewModel, fpsUnlockTracker.get(), windowManager.get())
+ bind(overlayView!!, overlayViewModel, windowManager.get())
overlayView!!.visibility = View.INVISIBLE
Log.d(TAG, "show(): adding overlayView $overlayView")
windowManager.get().addView(overlayView, overlayViewModel.defaultOverlayViewParams)
@@ -163,12 +160,9 @@ constructor(
fun bind(
overlayView: View,
viewModel: SideFpsOverlayViewModel,
- fpsUnlockTracker: FpsUnlockTracker,
windowManager: WindowManager
) {
overlayView.repeatWhenAttached {
- fpsUnlockTracker.startTracking()
-
val lottie = it.requireViewById<LottieAnimationView>(R.id.sidefps_animation)
lottie.addLottieOnCompositionLoadedListener { composition: LottieComposition ->
if (overlayView.visibility != View.VISIBLE) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index 7d494a5cf229..ac8807dd1b7e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -232,7 +232,6 @@ constructor(
val fingerprintStartMode: Flow<FingerprintStartMode> = _fingerprintStartMode.asStateFlow()
/** Whether a finger has been acquired by the sensor */
- // TODO(b/331948073): Add support for detecting SFPS finger without authentication running
val hasFingerBeenAcquired: Flow<Boolean> =
combine(biometricStatusInteractor.fingerprintAcquiredStatus, modalities) {
status,
@@ -617,7 +616,8 @@ constructor(
}
/** If the icon can be used as a confirmation button. */
- val isIconConfirmButton: Flow<Boolean> = size.map { it.isNotSmall }.distinctUntilChanged()
+ val isIconConfirmButton: Flow<Boolean> =
+ combine(modalities, size) { modalities, size -> modalities.hasUdfps && size.isNotSmall }
/** If the negative button should be shown. */
val isNegativeButtonVisible: Flow<Boolean> =
@@ -700,6 +700,9 @@ constructor(
failedModality: BiometricModality = BiometricModality.None,
) = coroutineScope {
if (_isAuthenticated.value.isAuthenticated) {
+ if (_isAuthenticated.value.needsUserConfirmation && hapticFeedback) {
+ vibrateOnError()
+ }
return@coroutineScope
}
@@ -823,6 +826,14 @@ constructor(
helpMessage: String = "",
) {
if (_isAuthenticated.value.isAuthenticated) {
+ // Treat second authentication with a different modality as confirmation for the first
+ if (
+ _isAuthenticated.value.needsUserConfirmation &&
+ modality != _isAuthenticated.value.authenticatedModality
+ ) {
+ confirmAuthenticated()
+ return
+ }
// TODO(jbolinger): convert to go/tex-apc?
Log.w(TAG, "Cannot show authenticated after authenticated")
return
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepository.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepository.kt
index 81fe2a5a5f0d..7f1cb5da474d 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepository.kt
@@ -97,6 +97,10 @@ constructor(
// Server could throw TimeoutException, InterruptedException or ExecutionException
Log.e(TAG, "Error calling isAutoOnSupported", e)
false
+ } catch (e: NoSuchMethodError) {
+ // TODO(b/346716614): Remove this when the flag is cleaned up.
+ Log.e(TAG, "Non-existed api isAutoOnSupported", e)
+ false
}
}
@@ -109,6 +113,9 @@ constructor(
// Server could throw IllegalStateException, TimeoutException, InterruptedException
// or ExecutionException
Log.e(TAG, "Error calling setAutoOnEnabled", e)
+ } catch (e: NoSuchMethodError) {
+ // TODO(b/346716614): Remove this when the flag is cleaned up.
+ Log.e(TAG, "Non-existed api setAutoOn", e)
}
}
}
@@ -122,6 +129,10 @@ constructor(
// or ExecutionException
Log.e(TAG, "Error calling isAutoOnEnabled", e)
false
+ } catch (e: NoSuchMethodError) {
+ // TODO(b/346716614): Remove this when the flag is cleaned up.
+ Log.e(TAG, "Non-existed api isAutoOnEnabled", e)
+ false
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt
index fce18a263902..e1408a065a37 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt
@@ -21,6 +21,7 @@ import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.communal.util.CommunalColors
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -31,13 +32,18 @@ import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToLockscreenTrans
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToGlanceableHubTransitionViewModel
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
+import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.stateIn
/** View model for transitions related to the communal hub. */
@OptIn(ExperimentalCoroutinesApi::class)
@@ -45,6 +51,7 @@ import kotlinx.coroutines.flow.merge
class CommunalTransitionViewModel
@Inject
constructor(
+ @Application applicationScope: CoroutineScope,
communalColors: CommunalColors,
glanceableHubToLockscreenTransitionViewModel: GlanceableHubToLockscreenTransitionViewModel,
lockscreenToGlanceableHubTransitionViewModel: LockscreenToGlanceableHubTransitionViewModel,
@@ -85,21 +92,32 @@ constructor(
* of UMO should be updated.
*/
val isUmoOnCommunal: Flow<Boolean> =
- allOf(
- // Only show UMO on the hub if the hub is at least partially visible. This prevents
- // the UMO from being missing on the lock screen when going from the hub to lock
- // screen in some way other than through a direct transition, such as unlocking from
- // the hub, then pressing power twice to go back to the lock screen.
- communalSceneInteractor.isCommunalVisible,
- merge(
- lockscreenToGlanceableHubTransitionViewModel.showUmo,
- glanceableHubToLockscreenTransitionViewModel.showUmo,
- dreamToGlanceableHubTransitionViewModel.showUmo,
- glanceableHubToDreamTransitionViewModel.showUmo,
- showUmoFromOccludedToGlanceableHub,
- showUmoFromGlanceableHubToOccluded,
+ anyOf(
+ communalSceneInteractor.isIdleOnCommunal,
+ allOf(
+ // Only show UMO on the hub if the hub is at least partially visible. This
+ // prevents
+ // the UMO from being missing on the lock screen when going from the hub to lock
+ // screen in some way other than through a direct transition, such as unlocking
+ // from
+ // the hub, then pressing power twice to go back to the lock screen.
+ communalSceneInteractor.isCommunalVisible,
+ merge(
+ lockscreenToGlanceableHubTransitionViewModel.showUmo,
+ glanceableHubToLockscreenTransitionViewModel.showUmo,
+ dreamToGlanceableHubTransitionViewModel.showUmo,
+ glanceableHubToDreamTransitionViewModel.showUmo,
+ showUmoFromOccludedToGlanceableHub,
+ showUmoFromGlanceableHubToOccluded,
+ )
+ .onStart { emit(false) }
+ )
+ )
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = false
)
- )
/** Whether to show communal when exiting the occluded state. */
val showCommunalFromOccluded: Flow<Boolean> = communalInteractor.showCommunalFromOccluded
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index cf8358203b6c..00566c1b04e3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -712,7 +712,6 @@ class KeyguardUnlockAnimationController @Inject constructor(
// As soon as the shade starts animating out of the way, start the canned unlock animation,
// which will finish keyguard exit when it completes. The in-window animations in the
// Launcher window will end on their own.
- if (fastUnlockTransition()) hideKeyguardViewAfterRemoteAnimation()
handler.postDelayed({
if (keyguardViewMediator.get().isShowingAndNotOccluded &&
!keyguardStateController.isKeyguardGoingAway) {
@@ -723,7 +722,7 @@ class KeyguardUnlockAnimationController @Inject constructor(
if ((wallpaperTargets?.isNotEmpty() == true)) {
fadeInWallpaper()
- if (!fastUnlockTransition()) hideKeyguardViewAfterRemoteAnimation()
+ hideKeyguardViewAfterRemoteAnimation()
} else {
keyguardViewMediator.get().exitKeyguardAndFinishSurfaceBehindRemoteAnimation(
false /* cancelled */)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 306f4ffa2a54..608e25a82eff 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -39,6 +39,7 @@ import com.android.keyguard.LegacyLockIconViewController
import com.android.keyguard.LockIconView
import com.android.keyguard.dagger.KeyguardStatusViewComponent
import com.android.systemui.CoreStartable
+import com.android.systemui.biometrics.ui.binder.DeviceEntryUnlockTrackerViewBinder
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
@@ -70,6 +71,7 @@ import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
import dagger.Lazy
+import java.util.Optional
import javax.inject.Inject
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -105,6 +107,7 @@ constructor(
private val lockscreenSceneBlueprintsLazy: Lazy<Set<LockscreenSceneBlueprint>>,
private val clockInteractor: KeyguardClockInteractor,
private val keyguardViewMediator: KeyguardViewMediator,
+ private val deviceEntryUnlockTrackerViewBinder: Optional<DeviceEntryUnlockTrackerViewBinder>,
) : CoreStartable {
private var rootViewHandle: DisposableHandle? = null
@@ -157,6 +160,9 @@ constructor(
)
}
}
+ if (deviceEntryUnlockTrackerViewBinder.isPresent) {
+ deviceEntryUnlockTrackerViewBinder.get().bind(keyguardRootView)
+ }
}
fun bindIndicationArea() {
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
index c98a49bd711d..08175c37f9bb 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
@@ -330,9 +330,16 @@ constructor(
* otherwise returns a singleton [Flow] containing [sceneKey].
*/
fun resolveSceneFamily(sceneKey: SceneKey): Flow<SceneKey> = flow {
- emitAll(sceneFamilyResolvers.get()[sceneKey]?.resolvedScene ?: flowOf(sceneKey))
+ emitAll(resolveSceneFamilyOrNull(sceneKey) ?: flowOf(sceneKey))
}
+ /**
+ * Returns the [concrete scene][Scenes] for [sceneKey] if it is a [scene family][SceneFamilies],
+ * otherwise returns `null`.
+ */
+ fun resolveSceneFamilyOrNull(sceneKey: SceneKey): StateFlow<SceneKey>? =
+ sceneFamilyResolvers.get()[sceneKey]?.resolvedScene
+
private fun isVisibleInternal(
raw: Boolean = repository.isVisible.value,
isRemoteUserInteractionOngoing: Boolean = repository.isRemoteUserInteractionOngoing.value,
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
index ab24e0bbb690..d380251d8b7e 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
@@ -147,6 +147,20 @@ constructor(
} ?: true
}
+ /**
+ * Immediately resolves any scene families present in [actionResultMap] to their current
+ * resolution target.
+ */
+ fun resolveSceneFamilies(
+ actionResultMap: Map<UserAction, UserActionResult>,
+ ): Map<UserAction, UserActionResult> {
+ return actionResultMap.mapValues { (_, actionResult) ->
+ sceneInteractor.resolveSceneFamilyOrNull(actionResult.toScene)?.value?.let {
+ actionResult.copy(toScene = it)
+ } ?: actionResult
+ }
+ }
+
private fun replaceSceneFamilies(
destinationScenes: Map<UserAction, UserActionResult>,
): Flow<Map<UserAction, UserActionResult>> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt
index 6611434b661e..f6fbe38554a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt
@@ -30,8 +30,8 @@ import com.android.systemui.statusbar.chips.domain.interactor.OngoingActivityChi
import com.android.systemui.statusbar.chips.domain.interactor.OngoingActivityChipInteractor.Companion.createDialogLaunchOnClickListener
import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndCastToOtherDeviceDialogDelegate
+import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndMediaProjectionDialogHelper
import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndShareToAppDialogDelegate
-import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.util.Utils
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
@@ -60,8 +60,8 @@ constructor(
private val mediaProjectionRepository: MediaProjectionRepository,
private val packageManager: PackageManager,
private val systemClock: SystemClock,
- private val dialogFactory: SystemUIDialog.Factory,
private val dialogTransitionAnimator: DialogTransitionAnimator,
+ private val endMediaProjectionDialogHelper: EndMediaProjectionDialogHelper,
) : OngoingActivityChipInteractor {
override val chip: StateFlow<OngoingActivityChipModel> =
mediaProjectionRepository.mediaProjectionState
@@ -70,9 +70,9 @@ constructor(
is MediaProjectionState.NotProjecting -> OngoingActivityChipModel.Hidden
is MediaProjectionState.Projecting -> {
if (isProjectionToOtherDevice(state.hostPackage)) {
- createCastToOtherDeviceChip()
+ createCastToOtherDeviceChip(state)
} else {
- createShareToAppChip()
+ createShareToAppChip(state)
}
}
}
@@ -97,7 +97,9 @@ constructor(
return Utils.isHeadlessRemoteDisplayProvider(packageManager, packageName)
}
- private fun createCastToOtherDeviceChip(): OngoingActivityChipModel.Shown {
+ private fun createCastToOtherDeviceChip(
+ state: MediaProjectionState.Projecting,
+ ): OngoingActivityChipModel.Shown {
return OngoingActivityChipModel.Shown(
icon =
Icon.Resource(
@@ -107,32 +109,39 @@ constructor(
// TODO(b/332662551): Maybe use a MediaProjection API to fetch this time.
startTimeMs = systemClock.elapsedRealtime(),
createDialogLaunchOnClickListener(
- castToOtherDeviceDialogDelegate,
+ createCastToOtherDeviceDialogDelegate(state),
dialogTransitionAnimator,
),
)
}
- private val castToOtherDeviceDialogDelegate =
+ private fun createCastToOtherDeviceDialogDelegate(state: MediaProjectionState.Projecting) =
EndCastToOtherDeviceDialogDelegate(
- dialogFactory,
+ endMediaProjectionDialogHelper,
this@MediaProjectionChipInteractor,
+ state,
)
- private fun createShareToAppChip(): OngoingActivityChipModel.Shown {
+ private fun createShareToAppChip(
+ state: MediaProjectionState.Projecting,
+ ): OngoingActivityChipModel.Shown {
return OngoingActivityChipModel.Shown(
// TODO(b/332662551): Use the right content description.
icon = Icon.Resource(SHARE_TO_APP_ICON, contentDescription = null),
// TODO(b/332662551): Maybe use a MediaProjection API to fetch this time.
startTimeMs = systemClock.elapsedRealtime(),
- createDialogLaunchOnClickListener(shareToAppDialogDelegate, dialogTransitionAnimator),
+ createDialogLaunchOnClickListener(
+ createShareToAppDialogDelegate(state),
+ dialogTransitionAnimator
+ ),
)
}
- private val shareToAppDialogDelegate =
+ private fun createShareToAppDialogDelegate(state: MediaProjectionState.Projecting) =
EndShareToAppDialogDelegate(
- dialogFactory,
+ endMediaProjectionDialogHelper,
this@MediaProjectionChipInteractor,
+ state,
)
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegate.kt
index 33cec9755b1f..596fbf89a10d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegate.kt
@@ -17,25 +17,33 @@
package com.android.systemui.statusbar.chips.mediaprojection.ui.view
import android.os.Bundle
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
import com.android.systemui.res.R
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractor
import com.android.systemui.statusbar.phone.SystemUIDialog
/** A dialog that lets the user stop an ongoing cast-screen-to-other-device event. */
class EndCastToOtherDeviceDialogDelegate(
- private val systemUIDialogFactory: SystemUIDialog.Factory,
+ private val endMediaProjectionDialogHelper: EndMediaProjectionDialogHelper,
private val interactor: MediaProjectionChipInteractor,
+ private val state: MediaProjectionState.Projecting,
) : SystemUIDialog.Delegate {
override fun createDialog(): SystemUIDialog {
- return systemUIDialogFactory.create(this)
+ return endMediaProjectionDialogHelper.createDialog(this)
}
override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
with(dialog) {
setIcon(MediaProjectionChipInteractor.CAST_TO_OTHER_DEVICE_ICON)
setTitle(R.string.cast_to_other_device_stop_dialog_title)
- // TODO(b/332662551): Use a different message if they're sharing just a single app.
- setMessage(R.string.cast_to_other_device_stop_dialog_message)
+ setMessage(
+ endMediaProjectionDialogHelper.getDialogMessage(
+ state,
+ genericMessageResId = R.string.cast_to_other_device_stop_dialog_message,
+ specificAppMessageResId =
+ R.string.cast_to_other_device_stop_dialog_message_specific_app,
+ )
+ )
// No custom on-click, because the dialog will automatically be dismissed when the
// button is clicked anyway.
setNegativeButton(R.string.close_dialog_button, /* onClick= */ null)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelper.kt
new file mode 100644
index 000000000000..347be02dbc60
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelper.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips.mediaprojection.ui.view
+
+import android.annotation.StringRes
+import android.content.Context
+import android.content.pm.PackageManager
+import android.text.Html
+import android.text.Html.FROM_HTML_MODE_LEGACY
+import android.text.TextUtils
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import javax.inject.Inject
+
+/** Helper class for showing dialogs that let users end different types of media projections. */
+@SysUISingleton
+class EndMediaProjectionDialogHelper
+@Inject
+constructor(
+ private val dialogFactory: SystemUIDialog.Factory,
+ private val packageManager: PackageManager,
+ private val context: Context
+) {
+ /** Creates a new [SystemUIDialog] using the given delegate. */
+ fun createDialog(delegate: SystemUIDialog.Delegate): SystemUIDialog {
+ return dialogFactory.create(delegate)
+ }
+
+ /**
+ * Returns the message to show in the dialog based on the specific media projection state.
+ *
+ * @param genericMessageResId a res ID for a more generic "end projection" message
+ * @param specificAppMessageResId a res ID for an "end projection" message that also lets us
+ * specify which app is currently being projected.
+ */
+ fun getDialogMessage(
+ state: MediaProjectionState.Projecting,
+ @StringRes genericMessageResId: Int,
+ @StringRes specificAppMessageResId: Int,
+ ): CharSequence {
+ when (state) {
+ is MediaProjectionState.Projecting.EntireScreen ->
+ return context.getString(genericMessageResId)
+ is MediaProjectionState.Projecting.SingleTask -> {
+ val packageName =
+ state.task.baseIntent.component?.packageName
+ ?: return context.getString(genericMessageResId)
+ try {
+ val appInfo = packageManager.getApplicationInfo(packageName, 0)
+ val appName = appInfo.loadLabel(packageManager)
+ return getSpecificAppMessageText(specificAppMessageResId, appName)
+ } catch (e: PackageManager.NameNotFoundException) {
+ // TODO(b/332662551): Log this error.
+ return context.getString(genericMessageResId)
+ }
+ }
+ }
+ }
+
+ private fun getSpecificAppMessageText(
+ @StringRes specificAppMessageResId: Int,
+ appName: CharSequence,
+ ): CharSequence {
+ // https://developer.android.com/guide/topics/resources/string-resource#StylingWithHTML
+ val escapedAppName = TextUtils.htmlEncode(appName.toString())
+ val text = context.getString(specificAppMessageResId, escapedAppName)
+ return Html.fromHtml(text, FROM_HTML_MODE_LEGACY)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegate.kt
index 3a863b10ef82..749a11f193c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegate.kt
@@ -17,25 +17,32 @@
package com.android.systemui.statusbar.chips.mediaprojection.ui.view
import android.os.Bundle
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
import com.android.systemui.res.R
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractor
import com.android.systemui.statusbar.phone.SystemUIDialog
/** A dialog that lets the user stop an ongoing share-screen-to-app event. */
class EndShareToAppDialogDelegate(
- private val systemUIDialogFactory: SystemUIDialog.Factory,
+ private val endMediaProjectionDialogHelper: EndMediaProjectionDialogHelper,
private val interactor: MediaProjectionChipInteractor,
+ private val state: MediaProjectionState.Projecting,
) : SystemUIDialog.Delegate {
override fun createDialog(): SystemUIDialog {
- return systemUIDialogFactory.create(this)
+ return endMediaProjectionDialogHelper.createDialog(this)
}
override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
with(dialog) {
setIcon(MediaProjectionChipInteractor.SHARE_TO_APP_ICON)
setTitle(R.string.share_to_app_stop_dialog_title)
- // TODO(b/332662551): Use a different message if they're sharing just a single app.
- setMessage(R.string.share_to_app_stop_dialog_message)
+ setMessage(
+ endMediaProjectionDialogHelper.getDialogMessage(
+ state,
+ genericMessageResId = R.string.share_to_app_stop_dialog_message,
+ specificAppMessageResId = R.string.share_to_app_stop_dialog_message_specific_app
+ )
+ )
// No custom on-click, because the dialog will automatically be dismissed when the
// button is clicked anyway.
setNegativeButton(R.string.close_dialog_button, /* onClick= */ null)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
index 11636bdf0f17..f95e0fb37921 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
@@ -37,6 +37,7 @@ import com.android.systemui.statusbar.phone.BoundsPair
import com.android.systemui.statusbar.phone.LetterboxAppearanceCalculator
import com.android.systemui.statusbar.phone.StatusBarBoundsProvider
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
+import com.android.systemui.statusbar.phone.ongoingcall.data.model.OngoingCallModel
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@@ -224,8 +225,8 @@ constructor(
modifiedStatusBarAttributes,
isTransientShown,
isInFullscreenMode,
- ongoingCallRepository.hasOngoingCall,
- ) { modifiedAttributes, isTransientShown, isInFullscreenMode, hasOngoingCall ->
+ ongoingCallRepository.ongoingCallState,
+ ) { modifiedAttributes, isTransientShown, isInFullscreenMode, ongoingCallState ->
if (modifiedAttributes == null) {
null
} else {
@@ -234,7 +235,7 @@ constructor(
modifiedAttributes.appearance,
isTransientShown,
isInFullscreenMode,
- hasOngoingCall,
+ hasOngoingCall = ongoingCallState is OngoingCallModel.InCall,
)
StatusBarAppearance(
statusBarMode,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 4bf122dd3b6a..3925bebe55fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -202,6 +202,7 @@ public class HeadsUpManagerPhone extends BaseHeadsUpManager implements
* Gets the touchable region needed for heads up notifications. Returns null if no touchable
* region is required (ie: no heads up notification currently exists).
*/
+ // TODO(b/347007367): With scene container enabled this method may report outdated regions
@Override
public @Nullable Region getTouchableRegion() {
NotificationEntry topEntry = getTopEntry();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
index d1189e1e5e1c..4b1ee58c2cc6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
@@ -29,6 +29,7 @@ import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
import android.view.WindowInsets;
+import com.android.compose.animation.scene.ObservableTransitionState;
import com.android.internal.policy.SystemBarUtils;
import com.android.systemui.Dumpable;
import com.android.systemui.ScreenDecorations;
@@ -38,7 +39,7 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.res.R;
import com.android.systemui.scene.domain.interactor.SceneInteractor;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
-import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.scene.shared.model.Scenes;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -67,7 +68,7 @@ public final class StatusBarTouchableRegionManager implements Dumpable {
private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
private boolean mIsStatusBarExpanded = false;
- private boolean mIsSceneContainerVisible = false;
+ private boolean mIsIdleOnGone = false;
private boolean mShouldAdjustInsets = false;
private View mNotificationShadeWindowView;
private View mNotificationPanelView;
@@ -87,7 +88,6 @@ public final class StatusBarTouchableRegionManager implements Dumpable {
NotificationShadeWindowController notificationShadeWindowController,
ConfigurationController configurationController,
HeadsUpManager headsUpManager,
- ShadeExpansionStateManager shadeExpansionStateManager,
ShadeInteractor shadeInteractor,
Provider<SceneInteractor> sceneInteractor,
JavaAdapter javaAdapter,
@@ -131,8 +131,8 @@ public final class StatusBarTouchableRegionManager implements Dumpable {
if (SceneContainerFlag.isEnabled()) {
javaAdapter.alwaysCollectFlow(
- sceneInteractor.get().isVisible(),
- this::onSceneContainerVisibilityChanged);
+ sceneInteractor.get().getTransitionState(),
+ this::onSceneChanged);
} else {
javaAdapter.alwaysCollectFlow(
shadeInteractor.isAnyExpanded(),
@@ -167,10 +167,11 @@ public final class StatusBarTouchableRegionManager implements Dumpable {
}
}
- private void onSceneContainerVisibilityChanged(Boolean isVisible) {
- if (isVisible != mIsSceneContainerVisible) {
- mIsSceneContainerVisible = isVisible;
- if (isVisible) {
+ private void onSceneChanged(ObservableTransitionState transitionState) {
+ boolean isIdleOnGone = transitionState.isIdle(Scenes.Gone);
+ if (isIdleOnGone != mIsIdleOnGone) {
+ mIsIdleOnGone = isIdleOnGone;
+ if (!isIdleOnGone) {
// make sure our state is sensible
mForceCollapsedUntilLayout = false;
}
@@ -281,7 +282,7 @@ public final class StatusBarTouchableRegionManager implements Dumpable {
// since we don't want stray touches to go through the light reveal scrim to whatever is
// underneath.
return mIsStatusBarExpanded
- || mIsSceneContainerVisible
+ || !mIsIdleOnGone
|| mPrimaryBouncerInteractor.isShowing().getValue()
|| mAlternateBouncerInteractor.isVisibleState()
|| mUnlockedScreenOffAnimationController.isAnimationPlaying();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index a7d4ce30a191..d128057d2010 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -29,35 +29,36 @@ import androidx.annotation.VisibleForTesting
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.CoreStartable
import com.android.systemui.Dumpable
-import com.android.systemui.res.R
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.statusbar.chips.ui.view.ChipChronometer
+import com.android.systemui.res.R
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
+import com.android.systemui.statusbar.chips.ui.view.ChipChronometer
import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.statusbar.phone.ongoingcall.data.model.OngoingCallModel
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.util.time.SystemClock
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.launch
import java.io.PrintWriter
import java.util.concurrent.Executor
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
-/**
- * A controller to handle the ongoing call chip in the collapsed status bar.
- */
+/** A controller to handle the ongoing call chip in the collapsed status bar. */
@SysUISingleton
-class OngoingCallController @Inject constructor(
+class OngoingCallController
+@Inject
+constructor(
@Application private val scope: CoroutineScope,
private val context: Context,
private val ongoingCallRepository: OngoingCallRepository,
@@ -79,54 +80,61 @@ class OngoingCallController @Inject constructor(
private val mListeners: MutableList<OngoingCallListener> = mutableListOf()
private val uidObserver = CallAppUidObserver()
- private val notifListener = object : NotifCollectionListener {
- // Temporary workaround for b/178406514 for testing purposes.
- //
- // b/178406514 means that posting an incoming call notif then updating it to an ongoing call
- // notif does not work (SysUI never receives the update). This workaround allows us to
- // trigger the ongoing call chip when an ongoing call notif is *added* rather than
- // *updated*, allowing us to test the chip.
- //
- // TODO(b/183229367): Remove this function override when b/178406514 is fixed.
- override fun onEntryAdded(entry: NotificationEntry) {
- onEntryUpdated(entry, true)
- }
+ private val notifListener =
+ object : NotifCollectionListener {
+ // Temporary workaround for b/178406514 for testing purposes.
+ //
+ // b/178406514 means that posting an incoming call notif then updating it to an ongoing
+ // call notif does not work (SysUI never receives the update). This workaround allows us
+ // to trigger the ongoing call chip when an ongoing call notif is *added* rather than
+ // *updated*, allowing us to test the chip.
+ //
+ // TODO(b/183229367): Remove this function override when b/178406514 is fixed.
+ override fun onEntryAdded(entry: NotificationEntry) {
+ onEntryUpdated(entry, true)
+ }
- override fun onEntryUpdated(entry: NotificationEntry) {
- // We have a new call notification or our existing call notification has been updated.
- // TODO(b/183229367): This likely won't work if you take a call from one app then
- // switch to a call from another app.
- if (callNotificationInfo == null && isCallNotification(entry) ||
- (entry.sbn.key == callNotificationInfo?.key)) {
- val newOngoingCallInfo = CallNotificationInfo(
- entry.sbn.key,
- entry.sbn.notification.getWhen(),
- entry.sbn.notification.contentIntent,
- entry.sbn.uid,
- entry.sbn.notification.extras.getInt(
- Notification.EXTRA_CALL_TYPE, -1) == CALL_TYPE_ONGOING,
- statusBarSwipedAway = callNotificationInfo?.statusBarSwipedAway ?: false
- )
- if (newOngoingCallInfo == callNotificationInfo) {
- return
+ override fun onEntryUpdated(entry: NotificationEntry) {
+ // We have a new call notification or our existing call notification has been
+ // updated.
+ // TODO(b/183229367): This likely won't work if you take a call from one app then
+ // switch to a call from another app.
+ if (
+ callNotificationInfo == null && isCallNotification(entry) ||
+ (entry.sbn.key == callNotificationInfo?.key)
+ ) {
+ val newOngoingCallInfo =
+ CallNotificationInfo(
+ entry.sbn.key,
+ entry.sbn.notification.getWhen(),
+ entry.sbn.notification.contentIntent,
+ entry.sbn.uid,
+ entry.sbn.notification.extras.getInt(
+ Notification.EXTRA_CALL_TYPE,
+ -1
+ ) == CALL_TYPE_ONGOING,
+ statusBarSwipedAway = callNotificationInfo?.statusBarSwipedAway ?: false
+ )
+ if (newOngoingCallInfo == callNotificationInfo) {
+ return
+ }
+
+ callNotificationInfo = newOngoingCallInfo
+ if (newOngoingCallInfo.isOngoing) {
+ updateChip()
+ } else {
+ removeChip()
+ }
}
+ }
- callNotificationInfo = newOngoingCallInfo
- if (newOngoingCallInfo.isOngoing) {
- updateChip()
- } else {
+ override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
+ if (entry.sbn.key == callNotificationInfo?.key) {
removeChip()
}
}
}
- override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
- if (entry.sbn.key == callNotificationInfo?.key) {
- removeChip()
- }
- }
- }
-
override fun start() {
dumpManager.registerDumpable(this)
notifCollection.addCollectionListener(notifListener)
@@ -169,8 +177,21 @@ class OngoingCallController @Inject constructor(
*/
fun hasOngoingCall(): Boolean {
return callNotificationInfo?.isOngoing == true &&
- // When the user is in the phone app, don't show the chip.
- !uidObserver.isCallAppVisible
+ // When the user is in the phone app, don't show the chip.
+ !uidObserver.isCallAppVisible
+ }
+
+ /** Creates the right [OngoingCallModel] based on the call state. */
+ private fun getOngoingCallModel(): OngoingCallModel {
+ if (hasOngoingCall()) {
+ val currentInfo =
+ callNotificationInfo
+ // This shouldn't happen, but protect against it in case
+ ?: return OngoingCallModel.NoCall
+ return OngoingCallModel.InCall(currentInfo.callStartTime)
+ } else {
+ return OngoingCallModel.NoCall
+ }
}
override fun addCallback(listener: OngoingCallListener) {
@@ -182,9 +203,7 @@ class OngoingCallController @Inject constructor(
}
override fun removeCallback(listener: OngoingCallListener) {
- synchronized(mListeners) {
- mListeners.remove(listener)
- }
+ synchronized(mListeners) { mListeners.remove(listener) }
}
private fun updateChip() {
@@ -196,8 +215,8 @@ class OngoingCallController @Inject constructor(
if (currentChipView != null && timeView != null) {
if (currentCallNotificationInfo.hasValidStartTime()) {
timeView.setShouldHideText(false)
- timeView.base = currentCallNotificationInfo.callStartTime -
- systemClock.currentTimeMillis() +
+ timeView.base =
+ currentCallNotificationInfo.callStartTime - systemClock.currentTimeMillis() +
systemClock.elapsedRealtime()
timeView.start()
} else {
@@ -218,14 +237,19 @@ class OngoingCallController @Inject constructor(
callNotificationInfo = null
if (DEBUG) {
- Log.w(TAG, "Ongoing call chip view could not be found; " +
- "Not displaying chip in status bar")
+ Log.w(
+ TAG,
+ "Ongoing call chip view could not be found; " +
+ "Not displaying chip in status bar"
+ )
}
}
}
private fun updateChipClickListener() {
- if (callNotificationInfo == null) { return }
+ if (callNotificationInfo == null) {
+ return
+ }
val currentChipView = chipView
val backgroundView =
currentChipView?.findViewById<View>(R.id.ongoing_activity_chip_background)
@@ -237,7 +261,8 @@ class OngoingCallController @Inject constructor(
intent,
ActivityTransitionAnimator.Controller.fromView(
backgroundView,
- InteractionJankMonitor.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP)
+ InteractionJankMonitor.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
+ )
)
}
}
@@ -249,9 +274,11 @@ class OngoingCallController @Inject constructor(
}
private fun updateGestureListening() {
- if (callNotificationInfo == null ||
- callNotificationInfo?.statusBarSwipedAway == true ||
- !isFullscreen) {
+ if (
+ callNotificationInfo == null ||
+ callNotificationInfo?.statusBarSwipedAway == true ||
+ !isFullscreen
+ ) {
swipeStatusBarAwayGestureHandler.removeOnGestureDetectedCallback(TAG)
} else {
swipeStatusBarAwayGestureHandler.addOnGestureDetectedCallback(TAG) { _ ->
@@ -270,30 +297,31 @@ class OngoingCallController @Inject constructor(
}
/** Tear down anything related to the chip view to prevent leaks. */
- @VisibleForTesting
- fun tearDownChipView() = chipView?.getTimeView()?.stop()
+ @VisibleForTesting fun tearDownChipView() = chipView?.getTimeView()?.stop()
private fun View.getTimeView(): ChipChronometer? {
return this.findViewById(R.id.ongoing_activity_chip_time)
}
/**
- * If there's an active ongoing call, then we will force the status bar to always show, even if
- * the user is in immersive mode. However, we also want to give users the ability to swipe away
- * the status bar if they need to access the area under the status bar.
- *
- * This method updates the status bar window appropriately when the swipe away gesture is
- * detected.
- */
+ * If there's an active ongoing call, then we will force the status bar to always show, even if
+ * the user is in immersive mode. However, we also want to give users the ability to swipe away
+ * the status bar if they need to access the area under the status bar.
+ *
+ * This method updates the status bar window appropriately when the swipe away gesture is
+ * detected.
+ */
private fun onSwipeAwayGestureDetected() {
- if (DEBUG) { Log.d(TAG, "Swipe away gesture detected") }
+ if (DEBUG) {
+ Log.d(TAG, "Swipe away gesture detected")
+ }
callNotificationInfo = callNotificationInfo?.copy(statusBarSwipedAway = true)
statusBarWindowController.setOngoingProcessRequiresStatusBarVisible(false)
swipeStatusBarAwayGestureHandler.removeOnGestureDetectedCallback(TAG)
}
private fun sendStateChangeEvent() {
- ongoingCallRepository.setHasOngoingCall(hasOngoingCall())
+ ongoingCallRepository.setOngoingCallState(getOngoingCallModel())
mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
}
@@ -308,8 +336,8 @@ class OngoingCallController @Inject constructor(
val statusBarSwipedAway: Boolean
) {
/**
- * Returns true if the notification information has a valid call start time.
- * See b/192379214.
+ * Returns true if the notification information has a valid call start time. See
+ * b/192379214.
*/
fun hasValidStartTime(): Boolean = callStartTime > 0
}
@@ -342,9 +370,10 @@ class OngoingCallController @Inject constructor(
callAppUid = uid
try {
- isCallAppVisible = isProcessVisibleToUser(
- iActivityManager.getUidProcessState(uid, context.opPackageName)
- )
+ isCallAppVisible =
+ isProcessVisibleToUser(
+ iActivityManager.getUidProcessState(uid, context.opPackageName)
+ )
if (isRegistered) {
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/model/OngoingCallModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/model/OngoingCallModel.kt
new file mode 100644
index 000000000000..aaa52a7b9e1c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/model/OngoingCallModel.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone.ongoingcall.data.model
+
+/** Represents the state of any ongoing calls. */
+sealed interface OngoingCallModel {
+ /** There is no ongoing call. */
+ data object NoCall : OngoingCallModel
+
+ /**
+ * There *is* an ongoing call.
+ *
+ * @property startTimeMs the time that the phone call started, based on the notification's
+ * `when` field. Importantly, this time is relative to
+ * [com.android.systemui.util.time.SystemClock.currentTimeMillis], **not**
+ * [com.android.systemui.util.time.SystemClock.elapsedRealtime].
+ */
+ data class InCall(val startTimeMs: Long) : OngoingCallModel
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt
index 886481e64dbe..554c47456871 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone.ongoingcall.data.repository
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.phone.ongoingcall.data.model.OngoingCallModel
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -32,15 +33,15 @@ import kotlinx.coroutines.flow.asStateFlow
*/
@SysUISingleton
class OngoingCallRepository @Inject constructor() {
- private val _hasOngoingCall = MutableStateFlow(false)
- /** True if there's currently an ongoing call notification and false otherwise. */
- val hasOngoingCall: StateFlow<Boolean> = _hasOngoingCall.asStateFlow()
+ private val _ongoingCallState = MutableStateFlow<OngoingCallModel>(OngoingCallModel.NoCall)
+ /** The current ongoing call state. */
+ val ongoingCallState: StateFlow<OngoingCallModel> = _ongoingCallState.asStateFlow()
/**
- * Sets whether there's currently an ongoing call notification. Should only be set from
+ * Sets the current ongoing call state, based on notifications. Should only be set from
* [com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController].
*/
- fun setHasOngoingCall(hasOngoingCall: Boolean) {
- _hasOngoingCall.value = hasOngoingCall
+ fun setOngoingCallState(state: OngoingCallModel) {
+ _ongoingCallState.value = state
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index f457470af906..4205dd87e747 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -135,6 +135,7 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.util.AlphaTintDrawableWrapper;
import com.android.systemui.util.RoundedCornerProgressDrawable;
import com.android.systemui.util.settings.SecureSettings;
+import com.android.systemui.volume.domain.interactor.VolumeDialogInteractor;
import com.android.systemui.volume.domain.interactor.VolumePanelNavigationInteractor;
import com.android.systemui.volume.panel.shared.flag.VolumePanelFlag;
import com.android.systemui.volume.ui.binder.VolumeDialogMenuIconBinder;
@@ -315,6 +316,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
private final com.android.systemui.util.time.SystemClock mSystemClock;
private final VolumeDialogMenuIconBinder mVolumeDialogMenuIconBinder;
private final VolumePanelFlag mVolumePanelFlag;
+ private final VolumeDialogInteractor mInteractor;
public VolumeDialogImpl(
Context context,
@@ -335,7 +337,8 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
Lazy<SecureSettings> secureSettings,
VibratorHelper vibratorHelper,
VolumeDialogMenuIconBinder volumeDialogMenuIconBinder,
- com.android.systemui.util.time.SystemClock systemClock) {
+ com.android.systemui.util.time.SystemClock systemClock,
+ VolumeDialogInteractor interactor) {
mContext =
new ContextThemeWrapper(context, R.style.volume_dialog_theme);
mHandler = new H(looper);
@@ -370,6 +373,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
mVolumeDialogMenuIconBinder = volumeDialogMenuIconBinder;
mDialogTimeoutMillis = DIALOG_TIMEOUT_MILLIS;
mVolumePanelFlag = volumePanelFlag;
+ mInteractor = interactor;
dumpManager.registerDumpable("VolumeDialogImpl", this);
@@ -1519,6 +1523,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
mShowing = true;
mIsAnimatingDismiss = false;
mDialog.show();
+ mInteractor.onDialogShown();
Events.writeEvent(Events.EVENT_SHOW_DIALOG, reason, keyguardLocked);
mController.notifyVisible(true);
mController.getCaptionsComponentState(false);
@@ -1605,6 +1610,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
}
mIsAnimatingDismiss = true;
mDialogView.animate().cancel();
+ mInteractor.onDialogDismissed();
if (mShowing) {
mShowing = false;
// Only logs when the volume dialog visibility is changed.
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
index f8ddc423e7d8..8003f3922b91 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -41,6 +41,7 @@ import com.android.systemui.volume.VolumeDialogImpl;
import com.android.systemui.volume.VolumeDialogModule;
import com.android.systemui.volume.VolumePanelDialogReceiver;
import com.android.systemui.volume.VolumeUI;
+import com.android.systemui.volume.domain.interactor.VolumeDialogInteractor;
import com.android.systemui.volume.domain.interactor.VolumePanelNavigationInteractor;
import com.android.systemui.volume.panel.dagger.VolumePanelComponent;
import com.android.systemui.volume.panel.dagger.factory.VolumePanelComponentFactory;
@@ -118,7 +119,8 @@ public interface VolumeModule {
Lazy<SecureSettings> secureSettings,
VibratorHelper vibratorHelper,
VolumeDialogMenuIconBinder volumeDialogMenuIconBinder,
- SystemClock systemClock) {
+ SystemClock systemClock,
+ VolumeDialogInteractor interactor) {
VolumeDialogImpl impl = new VolumeDialogImpl(
context,
volumeDialogController,
@@ -138,7 +140,8 @@ public interface VolumeModule {
secureSettings,
vibratorHelper,
volumeDialogMenuIconBinder,
- systemClock);
+ systemClock,
+ interactor);
impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
impl.setAutomute(true);
impl.setSilentMode(false);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/data/repository/VolumeDialogRepository.kt b/packages/SystemUI/src/com/android/systemui/volume/data/repository/VolumeDialogRepository.kt
new file mode 100644
index 000000000000..75e1c5acdd60
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/data/repository/VolumeDialogRepository.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/** A repository that encapsulates the states for Volume Dialog. */
+@SysUISingleton
+class VolumeDialogRepository @Inject constructor() {
+ private val _isDialogVisible: MutableStateFlow<Boolean> = MutableStateFlow(false)
+ /** Whether the Volume Dialog is visible. */
+ val isDialogVisible = _isDialogVisible.asStateFlow()
+
+ /** Sets whether the Volume Dialog is visible. */
+ fun setDialogVisibility(isVisible: Boolean) {
+ _isDialogVisible.value = isVisible
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractor.kt
new file mode 100644
index 000000000000..813e7074b911
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractor.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.volume.data.repository.VolumeDialogRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.StateFlow
+
+/** An interactor that propagates the UI states of the Volume Dialog. */
+@SysUISingleton
+class VolumeDialogInteractor
+@Inject
+constructor(
+ private val repository: VolumeDialogRepository,
+) {
+ /** Whether the Volume Dialog is visible. */
+ val isDialogVisible: StateFlow<Boolean> = repository.isDialogVisible
+
+ /** Notifies that the Volume Dialog is shown. */
+ fun onDialogShown() {
+ repository.setDialogVisibility(true)
+ }
+
+ /** Notifies that the Volume Dialog has been dismissed. */
+ fun onDialogDismissed() {
+ repository.setDialogVisibility(false)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index d267ad449f45..54a14a292ba1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -39,6 +39,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -1182,6 +1183,24 @@ public class ScreenDecorationsTest extends SysuiTestCase {
}
@Test
+ public void testUpdateOverlayProviderViews_PendingConfigChange() {
+ final DecorProvider cutout = new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP);
+ spyOn(cutout);
+ doNothing().when(cutout).onReloadResAndMeasure(any(), anyInt(), anyInt(), anyInt(), any());
+ mMockCutoutList.add(cutout);
+ mScreenDecorations.start();
+ doCallRealMethod().when(mScreenDecorations).updateOverlayProviderViews(any());
+
+ mScreenDecorations.mPendingConfigChange = true;
+ mScreenDecorations.updateOverlayProviderViews(null /* filterIds */);
+ verify(cutout, never()).onReloadResAndMeasure(any(), anyInt(), anyInt(), anyInt(), any());
+
+ mScreenDecorations.mPendingConfigChange = false;
+ mScreenDecorations.updateOverlayProviderViews(null /* filterIds */);
+ verify(cutout).onReloadResAndMeasure(any(), anyInt(), anyInt(), anyInt(), any());
+ }
+
+ @Test
public void testSupportHwcLayer_SwitchFrom_NotSupport() {
setupResources(0 /* radius */, 10 /* radiusTop */, 20 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index cc7dec56be40..93c6d9ee732c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -847,6 +847,28 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.NO_HAPTICS)
}
+ @Test
+ fun plays_haptic_on_error_after_auth_when_confirmation_needed() = runGenericTest {
+ val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
+ viewModel.showAuthenticated(testCase.authenticatedModality, 0)
+
+ viewModel.showTemporaryError(
+ "still sad",
+ messageAfterError = "",
+ authenticateAfterError = false,
+ hapticFeedback = true,
+ )
+
+ val haptics by collectLastValue(viewModel.hapticsToPlay)
+ if (expectConfirmation) {
+ assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.REJECT)
+ assertThat(haptics?.flag).isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
+ } else {
+ assertThat(haptics?.hapticFeedbackConstant)
+ .isEqualTo(HapticFeedbackConstants.CONFIRM)
+ }
+ }
+
private suspend fun TestScope.showTemporaryErrors(
restart: Boolean,
helpAfterError: String = "",
@@ -994,7 +1016,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
}
@Test
- fun authenticated_at_most_once() = runGenericTest {
+ fun authenticated_at_most_once_same_modality() = runGenericTest {
val authenticating by collectLastValue(viewModel.isAuthenticating)
val authenticated by collectLastValue(viewModel.isAuthenticated)
@@ -1056,6 +1078,38 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
}
@Test
+ fun second_authentication_acts_as_confirmation() = runGenericTest {
+ val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
+
+ viewModel.showAuthenticated(testCase.authenticatedModality, 0)
+
+ val authenticating by collectLastValue(viewModel.isAuthenticating)
+ val authenticated by collectLastValue(viewModel.isAuthenticated)
+ val message by collectLastValue(viewModel.message)
+ val size by collectLastValue(viewModel.size)
+ val canTryAgain by collectLastValue(viewModel.canTryAgainNow)
+
+ assertThat(authenticated?.needsUserConfirmation).isEqualTo(expectConfirmation)
+ if (expectConfirmation) {
+ assertThat(size).isEqualTo(PromptSize.MEDIUM)
+ assertButtonsVisible(
+ cancel = true,
+ confirm = true,
+ )
+
+ if (testCase.modalities.hasSfps) {
+ viewModel.showAuthenticated(BiometricModality.Fingerprint, 0)
+ assertThat(message).isEqualTo(PromptMessage.Empty)
+ assertButtonsVisible()
+ }
+ }
+
+ assertThat(authenticating).isFalse()
+ assertThat(authenticated?.isAuthenticated).isTrue()
+ assertThat(canTryAgain).isFalse()
+ }
+
+ @Test
fun auto_confirm_authentication_when_finger_down() = runGenericTest {
val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
index 128dd2353042..27b9863e20fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
@@ -44,6 +44,7 @@ import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.clearInvocations
import java.util.function.Predicate
@RunWith(AndroidJUnit4::class)
@@ -336,6 +337,10 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
false /* requestedShowSurfaceBehindKeyguard */
)
+ // Cancel the animator so we can verify only the setSurfaceBehind call below.
+ keyguardUnlockAnimationController.surfaceBehindAlphaAnimator.end()
+ clearInvocations(surfaceTransactionApplier)
+
// Set appear to 50%, we'll just verify that we're not applying the identity matrix which
// means an animation is in progress.
keyguardUnlockAnimationController.setSurfaceBehindAppearAmount(0.5f)
@@ -377,6 +382,10 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
false /* requestedShowSurfaceBehindKeyguard */
)
+ // Cancel the animator so we can verify only the setSurfaceBehind call below.
+ keyguardUnlockAnimationController.surfaceBehindAlphaAnimator.end()
+ clearInvocations(surfaceTransactionApplier)
+
keyguardUnlockAnimationController.setSurfaceBehindAppearAmount(1f)
keyguardUnlockAnimationController.setWallpaperAppearAmount(1f)
@@ -409,6 +418,10 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
false /* requestedShowSurfaceBehindKeyguard */
)
+ // Stop the animator - we just want to test whether the override is not applied.
+ keyguardUnlockAnimationController.surfaceBehindAlphaAnimator.end()
+ clearInvocations(surfaceTransactionApplier)
+
keyguardUnlockAnimationController.setSurfaceBehindAppearAmount(1f)
keyguardUnlockAnimationController.setWallpaperAppearAmount(1f)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt
index a4505a99cb77..327eec494b3c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt
@@ -28,6 +28,7 @@ import com.android.systemui.animation.mockDialogTransitionAnimator
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.mediaprojection.data.model.MediaProjectionState
import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
@@ -55,7 +56,7 @@ import org.mockito.kotlin.whenever
@SmallTest
class MediaProjectionChipInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = Kosmos().also { it.testCase = this }
private val testScope = kosmos.testScope
private val mediaProjectionRepo = kosmos.fakeMediaProjectionRepository
private val systemClock = kosmos.fakeSystemClock
@@ -178,7 +179,7 @@ class MediaProjectionChipInteractorTest : SysuiTestCase() {
}
@Test
- fun chip_castToOtherDevice_clickListenerShowsCastDialog() =
+ fun chip_castToOtherDevice_entireScreen_clickListenerShowsCastDialog() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
mediaProjectionRepo.mediaProjectionState.value =
@@ -200,7 +201,33 @@ class MediaProjectionChipInteractorTest : SysuiTestCase() {
}
@Test
- fun chip_shareToApp_clickListenerShowsShareDialog() =
+ fun chip_castToOtherDevice_singleTask_clickListenerShowsCastDialog() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ mediaProjectionRepo.mediaProjectionState.value =
+ MediaProjectionState.Projecting.SingleTask(
+ CAST_TO_OTHER_DEVICES_PACKAGE,
+ createTask(taskId = 1)
+ )
+
+ val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+
+ // Dialogs must be created on the main thread
+ context.mainExecutor.execute {
+ clickListener.onClick(chipView)
+ verify(kosmos.mockDialogTransitionAnimator)
+ .showFromView(
+ eq(mockCastDialog),
+ eq(chipBackgroundView),
+ eq(null),
+ anyBoolean(),
+ )
+ }
+ }
+
+ @Test
+ fun chip_shareToApp_entireScreen_clickListenerShowsShareDialog() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
mediaProjectionRepo.mediaProjectionState.value =
@@ -221,6 +248,28 @@ class MediaProjectionChipInteractorTest : SysuiTestCase() {
}
}
+ @Test
+ fun chip_shareToApp_singleTask_clickListenerShowsShareDialog() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+ mediaProjectionRepo.mediaProjectionState.value =
+ MediaProjectionState.Projecting.SingleTask(NORMAL_PACKAGE, createTask(taskId = 1))
+
+ val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+
+ // Dialogs must be created on the main thread
+ context.mainExecutor.execute {
+ clickListener.onClick(chipView)
+ verify(kosmos.mockDialogTransitionAnimator)
+ .showFromView(
+ eq(mockShareDialog),
+ eq(chipBackgroundView),
+ eq(null),
+ anyBoolean(),
+ )
+ }
+ }
+
companion object {
const val CAST_TO_OTHER_DEVICES_PACKAGE = "other.devices.package"
const val NORMAL_PACKAGE = "some.normal.package"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegateTest.kt
index 9a2f545fa67e..7b676e2b34e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegateTest.kt
@@ -16,22 +16,28 @@
package com.android.systemui.statusbar.chips.mediaprojection.ui.view
+import android.content.ComponentName
import android.content.DialogInterface
+import android.content.Intent
+import android.content.packageManager
+import android.content.pm.ApplicationInfo
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
+import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
import com.android.systemui.res.R
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.mediaProjectionChipInteractor
import com.android.systemui.statusbar.phone.SystemUIDialog
-import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
-import org.junit.Before
+import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
@@ -41,22 +47,14 @@ import org.mockito.kotlin.whenever
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
class EndCastToOtherDeviceDialogDelegateTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = Kosmos().also { it.testCase = this }
private val sysuiDialog = mock<SystemUIDialog>()
- private val sysuiDialogFactory = kosmos.mockSystemUIDialogFactory
- private val underTest =
- EndCastToOtherDeviceDialogDelegate(
- sysuiDialogFactory,
- kosmos.mediaProjectionChipInteractor,
- )
-
- @Before
- fun setUp() {
- whenever(sysuiDialogFactory.create(eq(underTest), eq(context))).thenReturn(sysuiDialog)
- }
+ private lateinit var underTest: EndCastToOtherDeviceDialogDelegate
@Test
fun icon() {
+ createAndSetDelegate(ENTIRE_SCREEN)
+
underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
verify(sysuiDialog).setIcon(R.drawable.ic_cast_connected)
@@ -64,20 +62,52 @@ class EndCastToOtherDeviceDialogDelegateTest : SysuiTestCase() {
@Test
fun title() {
+ createAndSetDelegate(SINGLE_TASK)
+
underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
verify(sysuiDialog).setTitle(R.string.cast_to_other_device_stop_dialog_title)
}
@Test
- fun message() {
+ fun message_entireScreen() {
+ createAndSetDelegate(ENTIRE_SCREEN)
+
underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
- verify(sysuiDialog).setMessage(R.string.cast_to_other_device_stop_dialog_message)
+ verify(sysuiDialog)
+ .setMessage(context.getString(R.string.cast_to_other_device_stop_dialog_message))
+ }
+
+ @Test
+ fun message_singleTask() {
+ val baseIntent =
+ Intent().apply { this.component = ComponentName("fake.task.package", "cls") }
+ val appInfo = mock<ApplicationInfo>()
+ whenever(appInfo.loadLabel(kosmos.packageManager)).thenReturn("Fake Package")
+ whenever(kosmos.packageManager.getApplicationInfo(eq("fake.task.package"), any<Int>()))
+ .thenReturn(appInfo)
+
+ createAndSetDelegate(
+ MediaProjectionState.Projecting.SingleTask(
+ HOST_PACKAGE,
+ createTask(taskId = 1, baseIntent = baseIntent)
+ )
+ )
+
+ underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+ // It'd be nice to use R.string.cast_to_other_device_stop_dialog_message_specific_app
+ // directly, but it includes the <b> tags which aren't in the returned string.
+ val result = argumentCaptor<CharSequence>()
+ verify(sysuiDialog).setMessage(result.capture())
+ assertThat(result.firstValue.toString()).isEqualTo("You will stop casting Fake Package")
}
@Test
fun negativeButton() {
+ createAndSetDelegate(ENTIRE_SCREEN)
+
underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
verify(sysuiDialog).setNegativeButton(R.string.close_dialog_button, null)
@@ -86,6 +116,8 @@ class EndCastToOtherDeviceDialogDelegateTest : SysuiTestCase() {
@Test
fun positiveButton() =
kosmos.testScope.runTest {
+ createAndSetDelegate(SINGLE_TASK)
+
underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
val clickListener = argumentCaptor<DialogInterface.OnClickListener>()
@@ -105,4 +137,20 @@ class EndCastToOtherDeviceDialogDelegateTest : SysuiTestCase() {
assertThat(kosmos.fakeMediaProjectionRepository.stopProjectingInvoked).isTrue()
}
+
+ private fun createAndSetDelegate(state: MediaProjectionState.Projecting) {
+ underTest =
+ EndCastToOtherDeviceDialogDelegate(
+ kosmos.endMediaProjectionDialogHelper,
+ kosmos.mediaProjectionChipInteractor,
+ state,
+ )
+ }
+
+ companion object {
+ private const val HOST_PACKAGE = "fake.host.package"
+ private val ENTIRE_SCREEN = MediaProjectionState.Projecting.EntireScreen(HOST_PACKAGE)
+ private val SINGLE_TASK =
+ MediaProjectionState.Projecting.SingleTask(HOST_PACKAGE, createTask(taskId = 1))
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt
new file mode 100644
index 000000000000..bbd1109c59e5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips.mediaprojection.ui.view
+
+import android.content.ComponentName
+import android.content.Intent
+import android.content.packageManager
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+class EndMediaProjectionDialogHelperTest : SysuiTestCase() {
+ private val kosmos = Kosmos().also { it.testCase = this }
+
+ private val underTest = kosmos.endMediaProjectionDialogHelper
+
+ @Test
+ fun createDialog_usesDelegateAndFactory() {
+ val dialog = mock<SystemUIDialog>()
+ val delegate = SystemUIDialog.Delegate { dialog }
+ whenever(kosmos.mockSystemUIDialogFactory.create(eq(delegate))).thenReturn(dialog)
+
+ underTest.createDialog(delegate)
+
+ verify(kosmos.mockSystemUIDialogFactory).create(delegate)
+ }
+
+ @Test
+ fun getDialogMessage_entireScreen_isGenericMessage() {
+ val result =
+ underTest.getDialogMessage(
+ MediaProjectionState.Projecting.EntireScreen("host.package"),
+ R.string.accessibility_home,
+ R.string.cast_to_other_device_stop_dialog_message_specific_app
+ )
+
+ assertThat(result).isEqualTo(context.getString(R.string.accessibility_home))
+ }
+
+ @Test
+ fun getDialogMessage_singleTask_cannotFindPackage_isGenericMessage() {
+ val baseIntent =
+ Intent().apply { this.component = ComponentName("fake.task.package", "cls") }
+ whenever(kosmos.packageManager.getApplicationInfo(eq("fake.task.package"), any<Int>()))
+ .thenThrow(PackageManager.NameNotFoundException())
+
+ val projectionState =
+ MediaProjectionState.Projecting.SingleTask(
+ "host.package",
+ createTask(taskId = 1, baseIntent = baseIntent)
+ )
+
+ val result =
+ underTest.getDialogMessage(
+ projectionState,
+ R.string.accessibility_home,
+ R.string.cast_to_other_device_stop_dialog_message_specific_app
+ )
+
+ assertThat(result).isEqualTo(context.getString(R.string.accessibility_home))
+ }
+
+ @Test
+ fun getDialogMessage_singleTask_findsPackage_isSpecificMessageWithAppLabel() {
+ val baseIntent =
+ Intent().apply { this.component = ComponentName("fake.task.package", "cls") }
+ val appInfo = mock<ApplicationInfo>()
+ whenever(appInfo.loadLabel(kosmos.packageManager)).thenReturn("Fake Package")
+ whenever(kosmos.packageManager.getApplicationInfo(eq("fake.task.package"), any<Int>()))
+ .thenReturn(appInfo)
+
+ val projectionState =
+ MediaProjectionState.Projecting.SingleTask(
+ "host.package",
+ createTask(taskId = 1, baseIntent = baseIntent)
+ )
+
+ val result =
+ underTest.getDialogMessage(
+ projectionState,
+ R.string.accessibility_home,
+ R.string.cast_to_other_device_stop_dialog_message_specific_app
+ )
+
+ // It'd be nice to use the R.string resources directly, but they include the <b> tags which
+ // aren't in the returned string.
+ assertThat(result.toString()).isEqualTo("You will stop casting Fake Package")
+ }
+
+ @Test
+ fun getDialogMessage_appLabelHasSpecialCharacters_isEscaped() {
+ val baseIntent =
+ Intent().apply { this.component = ComponentName("fake.task.package", "cls") }
+ val appInfo = mock<ApplicationInfo>()
+ whenever(appInfo.loadLabel(kosmos.packageManager)).thenReturn("Fake & Package <Here>")
+ whenever(kosmos.packageManager.getApplicationInfo(eq("fake.task.package"), any<Int>()))
+ .thenReturn(appInfo)
+
+ val projectionState =
+ MediaProjectionState.Projecting.SingleTask(
+ "host.package",
+ createTask(taskId = 1, baseIntent = baseIntent)
+ )
+
+ val result =
+ underTest.getDialogMessage(
+ projectionState,
+ R.string.accessibility_home,
+ R.string.cast_to_other_device_stop_dialog_message_specific_app
+ )
+
+ assertThat(result.toString()).isEqualTo("You will stop casting Fake & Package <Here>")
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegateTest.kt
index 1d6e8669274d..4ddca521abd3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegateTest.kt
@@ -16,22 +16,28 @@
package com.android.systemui.statusbar.chips.mediaprojection.ui.view
+import android.content.ComponentName
import android.content.DialogInterface
+import android.content.Intent
+import android.content.packageManager
+import android.content.pm.ApplicationInfo
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
+import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
import com.android.systemui.res.R
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.mediaProjectionChipInteractor
import com.android.systemui.statusbar.phone.SystemUIDialog
-import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
-import org.junit.Before
+import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
@@ -41,22 +47,14 @@ import org.mockito.kotlin.whenever
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
class EndShareToAppDialogDelegateTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = Kosmos().also { it.testCase = this }
private val sysuiDialog = mock<SystemUIDialog>()
- private val sysuiDialogFactory = kosmos.mockSystemUIDialogFactory
- private val underTest =
- EndShareToAppDialogDelegate(
- sysuiDialogFactory,
- kosmos.mediaProjectionChipInteractor,
- )
-
- @Before
- fun setUp() {
- whenever(sysuiDialogFactory.create(eq(underTest), eq(context))).thenReturn(sysuiDialog)
- }
+ private lateinit var underTest: EndShareToAppDialogDelegate
@Test
fun icon() {
+ createAndSetDelegate(SINGLE_TASK)
+
underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
verify(sysuiDialog).setIcon(R.drawable.ic_screenshot_share)
@@ -64,20 +62,51 @@ class EndShareToAppDialogDelegateTest : SysuiTestCase() {
@Test
fun title() {
+ createAndSetDelegate(ENTIRE_SCREEN)
+
underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
verify(sysuiDialog).setTitle(R.string.share_to_app_stop_dialog_title)
}
@Test
- fun message() {
+ fun message_entireScreen() {
+ createAndSetDelegate(ENTIRE_SCREEN)
+
underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
- verify(sysuiDialog).setMessage(R.string.share_to_app_stop_dialog_message)
+ verify(sysuiDialog).setMessage(context.getString(R.string.share_to_app_stop_dialog_message))
+ }
+
+ @Test
+ fun message_singleTask() {
+ val baseIntent =
+ Intent().apply { this.component = ComponentName("fake.task.package", "cls") }
+ val appInfo = mock<ApplicationInfo>()
+ whenever(appInfo.loadLabel(kosmos.packageManager)).thenReturn("Fake Package")
+ whenever(kosmos.packageManager.getApplicationInfo(eq("fake.task.package"), any<Int>()))
+ .thenReturn(appInfo)
+
+ createAndSetDelegate(
+ MediaProjectionState.Projecting.SingleTask(
+ HOST_PACKAGE,
+ createTask(taskId = 1, baseIntent = baseIntent)
+ )
+ )
+
+ underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+ // It'd be nice to use R.string.share_to_app_stop_dialog_message_specific_app directly, but
+ // it includes the <b> tags which aren't in the returned string.
+ val result = argumentCaptor<CharSequence>()
+ verify(sysuiDialog).setMessage(result.capture())
+ assertThat(result.firstValue.toString()).isEqualTo("You will stop sharing Fake Package")
}
@Test
fun negativeButton() {
+ createAndSetDelegate(SINGLE_TASK)
+
underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
verify(sysuiDialog).setNegativeButton(R.string.close_dialog_button, null)
@@ -86,6 +115,8 @@ class EndShareToAppDialogDelegateTest : SysuiTestCase() {
@Test
fun positiveButton() =
kosmos.testScope.runTest {
+ createAndSetDelegate(ENTIRE_SCREEN)
+
underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
val clickListener = argumentCaptor<DialogInterface.OnClickListener>()
@@ -105,4 +136,20 @@ class EndShareToAppDialogDelegateTest : SysuiTestCase() {
assertThat(kosmos.fakeMediaProjectionRepository.stopProjectingInvoked).isTrue()
}
+
+ private fun createAndSetDelegate(state: MediaProjectionState.Projecting) {
+ underTest =
+ EndShareToAppDialogDelegate(
+ kosmos.endMediaProjectionDialogHelper,
+ kosmos.mediaProjectionChipInteractor,
+ state,
+ )
+ }
+
+ companion object {
+ private const val HOST_PACKAGE = "fake.host.package"
+ private val ENTIRE_SCREEN = MediaProjectionState.Projecting.EntireScreen(HOST_PACKAGE)
+ private val SINGLE_TASK =
+ MediaProjectionState.Projecting.SingleTask(HOST_PACKAGE, createTask(taskId = 1))
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
index 67129639efaf..65bf0bcf2741 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
@@ -22,6 +22,7 @@ import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.mediaprojection.data.model.MediaProjectionState
import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
@@ -39,8 +40,7 @@ import org.junit.Test
@SmallTest
class OngoingActivityChipsViewModelTest : SysuiTestCase() {
-
- private val kosmos = Kosmos()
+ private val kosmos = Kosmos().also { it.testCase = this }
private val testScope = kosmos.testScope
private val screenRecordState = kosmos.screenRecordRepository.screenRecordState
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
index 057dcb2a156e..6af14e02ed62 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
@@ -35,6 +35,7 @@ import com.android.systemui.statusbar.phone.LetterboxAppearance
import com.android.systemui.statusbar.phone.LetterboxAppearanceCalculator
import com.android.systemui.statusbar.phone.StatusBarBoundsProvider
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
+import com.android.systemui.statusbar.phone.ongoingcall.data.model.OngoingCallModel
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
@@ -390,7 +391,7 @@ class StatusBarModeRepositoryImplTest : SysuiTestCase() {
testScope.runTest {
val latest by collectLastValue(underTest.statusBarAppearance)
- ongoingCallRepository.setHasOngoingCall(true)
+ ongoingCallRepository.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 34))
onSystemBarAttributesChanged(
requestedVisibleTypes = WindowInsets.Type.navigationBars(),
)
@@ -403,7 +404,7 @@ class StatusBarModeRepositoryImplTest : SysuiTestCase() {
testScope.runTest {
val latest by collectLastValue(underTest.statusBarAppearance)
- ongoingCallRepository.setHasOngoingCall(true)
+ ongoingCallRepository.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 789))
onSystemBarAttributesChanged(
requestedVisibleTypes = WindowInsets.Type.statusBars(),
appearance = APPEARANCE_OPAQUE_STATUS_BARS,
@@ -417,7 +418,7 @@ class StatusBarModeRepositoryImplTest : SysuiTestCase() {
testScope.runTest {
val latest by collectLastValue(underTest.statusBarAppearance)
- ongoingCallRepository.setHasOngoingCall(false)
+ ongoingCallRepository.setOngoingCallState(OngoingCallModel.NoCall)
onSystemBarAttributesChanged(
requestedVisibleTypes = WindowInsets.Type.navigationBars(),
appearance = APPEARANCE_OPAQUE_STATUS_BARS,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index 4d6798be9211..feef9431c28c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -32,16 +32,17 @@ import android.view.View
import android.widget.LinearLayout
import androidx.test.filters.SmallTest
import com.android.internal.logging.testing.UiEventLoggerFake
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository
import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
-import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository
+import com.android.systemui.statusbar.phone.ongoingcall.data.model.OngoingCallModel
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.util.concurrency.FakeExecutor
@@ -60,13 +61,13 @@ import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyString
import org.mockito.ArgumentMatchers.nullable
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.eq
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
private const val CALL_UID = 900
@@ -93,8 +94,8 @@ class OngoingCallControllerTest : SysuiTestCase() {
private lateinit var controller: OngoingCallController
private lateinit var notifCollectionListener: NotifCollectionListener
- @Mock private lateinit var mockSwipeStatusBarAwayGestureHandler:
- SwipeStatusBarAwayGestureHandler
+ @Mock
+ private lateinit var mockSwipeStatusBarAwayGestureHandler: SwipeStatusBarAwayGestureHandler
@Mock private lateinit var mockOngoingCallListener: OngoingCallListener
@Mock private lateinit var mockActivityStarter: ActivityStarter
@Mock private lateinit var mockIActivityManager: IActivityManager
@@ -112,21 +113,22 @@ class OngoingCallControllerTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this)
val notificationCollection = mock(CommonNotifCollection::class.java)
- controller = OngoingCallController(
- testScope.backgroundScope,
- context,
- ongoingCallRepository,
- notificationCollection,
- clock,
- mockActivityStarter,
- mainExecutor,
- mockIActivityManager,
- OngoingCallLogger(uiEventLoggerFake),
- DumpManager(),
- mockStatusBarWindowController,
- mockSwipeStatusBarAwayGestureHandler,
- statusBarModeRepository,
- )
+ controller =
+ OngoingCallController(
+ testScope.backgroundScope,
+ context,
+ ongoingCallRepository,
+ notificationCollection,
+ clock,
+ mockActivityStarter,
+ mainExecutor,
+ mockIActivityManager,
+ OngoingCallLogger(uiEventLoggerFake),
+ DumpManager(),
+ mockStatusBarWindowController,
+ mockSwipeStatusBarAwayGestureHandler,
+ statusBarModeRepository,
+ )
controller.start()
controller.addCallback(mockOngoingCallListener)
controller.setChipView(chipView)
@@ -136,7 +138,7 @@ class OngoingCallControllerTest : SysuiTestCase() {
notifCollectionListener = collectionListenerCaptor.value!!
`when`(mockIActivityManager.getUidProcessState(eq(CALL_UID), nullable(String::class.java)))
- .thenReturn(PROC_STATE_INVISIBLE)
+ .thenReturn(PROC_STATE_INVISIBLE)
}
@After
@@ -146,10 +148,14 @@ class OngoingCallControllerTest : SysuiTestCase() {
@Test
fun onEntryUpdated_isOngoingCallNotif_listenerAndRepoNotified() {
- notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+ val notification = NotificationEntryBuilder(createOngoingCallNotifEntry())
+ notification.modifyNotification(context).setWhen(567)
+ notifCollectionListener.onEntryUpdated(notification.build())
verify(mockOngoingCallListener).onOngoingCallStateChanged(anyBoolean())
- assertThat(ongoingCallRepository.hasOngoingCall.value).isTrue()
+ val repoState = ongoingCallRepository.ongoingCallState.value
+ assertThat(repoState).isInstanceOf(OngoingCallModel.InCall::class.java)
+ assertThat((repoState as OngoingCallModel.InCall).startTimeMs).isEqualTo(567)
}
@Test
@@ -164,7 +170,8 @@ class OngoingCallControllerTest : SysuiTestCase() {
notifCollectionListener.onEntryUpdated(createNotCallNotifEntry())
verify(mockOngoingCallListener, never()).onOngoingCallStateChanged(anyBoolean())
- assertThat(ongoingCallRepository.hasOngoingCall.value).isFalse()
+ assertThat(ongoingCallRepository.ongoingCallState.value)
+ .isInstanceOf(OngoingCallModel.NoCall::class.java)
}
@Test
@@ -172,25 +179,27 @@ class OngoingCallControllerTest : SysuiTestCase() {
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
notifCollectionListener.onEntryUpdated(createScreeningCallNotifEntry())
- verify(mockOngoingCallListener, times(2))
- .onOngoingCallStateChanged(anyBoolean())
+ verify(mockOngoingCallListener, times(2)).onOngoingCallStateChanged(anyBoolean())
}
@Test
fun onEntryUpdated_ongoingCallNotifThenScreeningCallNotif_repoUpdated() {
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
- assertThat(ongoingCallRepository.hasOngoingCall.value).isTrue()
+ assertThat(ongoingCallRepository.ongoingCallState.value)
+ .isInstanceOf(OngoingCallModel.InCall::class.java)
notifCollectionListener.onEntryUpdated(createScreeningCallNotifEntry())
- assertThat(ongoingCallRepository.hasOngoingCall.value).isFalse()
+ assertThat(ongoingCallRepository.ongoingCallState.value)
+ .isInstanceOf(OngoingCallModel.NoCall::class.java)
}
/** Regression test for b/191472854. */
@Test
fun onEntryUpdated_notifHasNullContentIntent_noCrash() {
notifCollectionListener.onEntryUpdated(
- createCallNotifEntry(ongoingCallStyle, nullContentIntent = true))
+ createCallNotifEntry(ongoingCallStyle, nullContentIntent = true)
+ )
}
/** Regression test for b/192379214. */
@@ -202,12 +211,12 @@ class OngoingCallControllerTest : SysuiTestCase() {
notifCollectionListener.onEntryUpdated(notification.build())
chipView.measure(
- View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
- View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
)
assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth)
- .isEqualTo(0)
+ .isEqualTo(0)
}
@Test
@@ -218,12 +227,12 @@ class OngoingCallControllerTest : SysuiTestCase() {
notifCollectionListener.onEntryUpdated(notification.build())
chipView.measure(
- View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
- View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
)
assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth)
- .isGreaterThan(0)
+ .isGreaterThan(0)
}
@Test
@@ -233,12 +242,12 @@ class OngoingCallControllerTest : SysuiTestCase() {
notifCollectionListener.onEntryUpdated(notification.build())
chipView.measure(
- View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
- View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
)
assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth)
- .isGreaterThan(0)
+ .isGreaterThan(0)
}
/** Regression test for b/194731244. */
@@ -250,15 +259,14 @@ class OngoingCallControllerTest : SysuiTestCase() {
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
}
- verify(mockIActivityManager, times(1))
- .registerUidObserver(any(), any(), any(), any())
+ verify(mockIActivityManager, times(1)).registerUidObserver(any(), any(), any(), any())
}
/** Regression test for b/216248574. */
@Test
fun entryUpdated_getUidProcessStateThrowsException_noCrash() {
`when`(mockIActivityManager.getUidProcessState(eq(CALL_UID), nullable(String::class.java)))
- .thenThrow(SecurityException())
+ .thenThrow(SecurityException())
// No assert required, just check no crash
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
@@ -267,9 +275,15 @@ class OngoingCallControllerTest : SysuiTestCase() {
/** Regression test for b/216248574. */
@Test
fun entryUpdated_registerUidObserverThrowsException_noCrash() {
- `when`(mockIActivityManager.registerUidObserver(
- any(), any(), any(), nullable(String::class.java)
- )).thenThrow(SecurityException())
+ `when`(
+ mockIActivityManager.registerUidObserver(
+ any(),
+ any(),
+ any(),
+ nullable(String::class.java),
+ )
+ )
+ .thenThrow(SecurityException())
// No assert required, just check no crash
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
@@ -281,9 +295,8 @@ class OngoingCallControllerTest : SysuiTestCase() {
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
val packageNameCaptor = ArgumentCaptor.forClass(String::class.java)
- verify(mockIActivityManager).registerUidObserver(
- any(), any(), any(), packageNameCaptor.capture()
- )
+ verify(mockIActivityManager)
+ .registerUidObserver(any(), any(), any(), packageNameCaptor.capture())
assertThat(packageNameCaptor.value).isNotNull()
}
@@ -313,11 +326,13 @@ class OngoingCallControllerTest : SysuiTestCase() {
fun onEntryRemoved_callNotifAddedThenRemoved_repoUpdated() {
val ongoingCallNotifEntry = createOngoingCallNotifEntry()
notifCollectionListener.onEntryAdded(ongoingCallNotifEntry)
- assertThat(ongoingCallRepository.hasOngoingCall.value).isTrue()
+ assertThat(ongoingCallRepository.ongoingCallState.value)
+ .isInstanceOf(OngoingCallModel.InCall::class.java)
notifCollectionListener.onEntryRemoved(ongoingCallNotifEntry, REASON_USER_STOPPED)
- assertThat(ongoingCallRepository.hasOngoingCall.value).isFalse()
+ assertThat(ongoingCallRepository.ongoingCallState.value)
+ .isInstanceOf(OngoingCallModel.NoCall::class.java)
}
@Test
@@ -360,7 +375,8 @@ class OngoingCallControllerTest : SysuiTestCase() {
notifCollectionListener.onEntryRemoved(removedEntryBuilder.build(), REASON_USER_STOPPED)
- assertThat(ongoingCallRepository.hasOngoingCall.value).isFalse()
+ assertThat(ongoingCallRepository.ongoingCallState.value)
+ .isInstanceOf(OngoingCallModel.NoCall::class.java)
}
@Test
@@ -379,7 +395,8 @@ class OngoingCallControllerTest : SysuiTestCase() {
notifCollectionListener.onEntryRemoved(createNotCallNotifEntry(), REASON_USER_STOPPED)
- assertThat(ongoingCallRepository.hasOngoingCall.value).isTrue()
+ assertThat(ongoingCallRepository.ongoingCallState.value)
+ .isInstanceOf(OngoingCallModel.InCall::class.java)
}
@Test
@@ -404,7 +421,7 @@ class OngoingCallControllerTest : SysuiTestCase() {
@Test
fun hasOngoingCall_ongoingCallNotifSentAndCallAppNotVisible_returnsTrue() {
`when`(mockIActivityManager.getUidProcessState(eq(CALL_UID), nullable(String::class.java)))
- .thenReturn(PROC_STATE_INVISIBLE)
+ .thenReturn(PROC_STATE_INVISIBLE)
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
@@ -414,7 +431,7 @@ class OngoingCallControllerTest : SysuiTestCase() {
@Test
fun hasOngoingCall_ongoingCallNotifSentButCallAppVisible_returnsFalse() {
`when`(mockIActivityManager.getUidProcessState(eq(CALL_UID), nullable(String::class.java)))
- .thenReturn(PROC_STATE_VISIBLE)
+ .thenReturn(PROC_STATE_VISIBLE)
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
@@ -472,10 +489,8 @@ class OngoingCallControllerTest : SysuiTestCase() {
lateinit var newChipView: View
TestableLooper.get(this).runWithLooper {
- newChipView = LayoutInflater.from(mContext).inflate(
- R.layout.ongoing_activity_chip,
- null
- )
+ newChipView =
+ LayoutInflater.from(mContext).inflate(R.layout.ongoing_activity_chip, null)
}
// Change the chip view associated with the controller.
@@ -488,13 +503,13 @@ class OngoingCallControllerTest : SysuiTestCase() {
fun callProcessChangesToVisible_listenerNotified() {
// Start the call while the process is invisible.
`when`(mockIActivityManager.getUidProcessState(eq(CALL_UID), nullable(String::class.java)))
- .thenReturn(PROC_STATE_INVISIBLE)
+ .thenReturn(PROC_STATE_INVISIBLE)
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
reset(mockOngoingCallListener)
val captor = ArgumentCaptor.forClass(IUidObserver.Stub::class.java)
- verify(mockIActivityManager).registerUidObserver(
- captor.capture(), any(), any(), nullable(String::class.java))
+ verify(mockIActivityManager)
+ .registerUidObserver(captor.capture(), any(), any(), nullable(String::class.java))
val uidObserver = captor.value
// Update the process to visible.
@@ -509,13 +524,13 @@ class OngoingCallControllerTest : SysuiTestCase() {
fun callProcessChangesToInvisible_listenerNotified() {
// Start the call while the process is visible.
`when`(mockIActivityManager.getUidProcessState(eq(CALL_UID), nullable(String::class.java)))
- .thenReturn(PROC_STATE_VISIBLE)
+ .thenReturn(PROC_STATE_VISIBLE)
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
reset(mockOngoingCallListener)
val captor = ArgumentCaptor.forClass(IUidObserver.Stub::class.java)
- verify(mockIActivityManager).registerUidObserver(
- captor.capture(), any(), any(), nullable(String::class.java))
+ verify(mockIActivityManager)
+ .registerUidObserver(captor.capture(), any(), any(), nullable(String::class.java))
val uidObserver = captor.value
// Update the process to invisible.
@@ -534,7 +549,7 @@ class OngoingCallControllerTest : SysuiTestCase() {
assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
assertThat(uiEventLoggerFake.eventId(0))
- .isEqualTo(OngoingCallLogger.OngoingCallEvents.ONGOING_CALL_CLICKED.id)
+ .isEqualTo(OngoingCallLogger.OngoingCallEvents.ONGOING_CALL_CLICKED.id)
}
/** Regression test for b/212467440. */
@@ -556,8 +571,9 @@ class OngoingCallControllerTest : SysuiTestCase() {
assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
assertThat(uiEventLoggerFake.eventId(0))
- .isEqualTo(OngoingCallLogger.OngoingCallEvents.ONGOING_CALL_VISIBLE.id)
+ .isEqualTo(OngoingCallLogger.OngoingCallEvents.ONGOING_CALL_VISIBLE.id)
}
+
// Other tests for notifyChipVisibilityChanged are in [OngoingCallLogger], since
// [OngoingCallController.notifyChipVisibilityChanged] just delegates to that class.
@@ -621,8 +637,7 @@ class OngoingCallControllerTest : SysuiTestCase() {
statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = false
testScope.runCurrent()
- verify(mockSwipeStatusBarAwayGestureHandler)
- .removeOnGestureDetectedCallback(anyString())
+ verify(mockSwipeStatusBarAwayGestureHandler).removeOnGestureDetectedCallback(anyString())
}
@Test
@@ -635,8 +650,7 @@ class OngoingCallControllerTest : SysuiTestCase() {
notifCollectionListener.onEntryRemoved(ongoingCallNotifEntry, REASON_USER_STOPPED)
- verify(mockSwipeStatusBarAwayGestureHandler)
- .removeOnGestureDetectedCallback(anyString())
+ verify(mockSwipeStatusBarAwayGestureHandler).removeOnGestureDetectedCallback(anyString())
}
// TODO(b/195839150): Add test
@@ -675,5 +689,9 @@ private val person = Person.Builder().setName("name").build()
private val hangUpIntent = mock(PendingIntent::class.java)
private val ongoingCallStyle = Notification.CallStyle.forOngoingCall(person, hangUpIntent)
-private val screeningCallStyle = Notification.CallStyle.forScreeningCall(
- person, hangUpIntent, /* answerIntent= */ mock(PendingIntent::class.java)) \ No newline at end of file
+private val screeningCallStyle =
+ Notification.CallStyle.forScreeningCall(
+ person,
+ hangUpIntent,
+ /* answerIntent= */ mock(PendingIntent::class.java),
+ )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
index 56aa7d6e3ca4..73a86a1b8511 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone.ongoingcall.data.repository
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.phone.ongoingcall.data.model.OngoingCallModel
import com.google.common.truth.Truth.assertThat
import org.junit.Test
@@ -27,12 +28,13 @@ class OngoingCallRepositoryTest : SysuiTestCase() {
@Test
fun hasOngoingCall_matchesSet() {
- underTest.setHasOngoingCall(true)
+ val inCallModel = OngoingCallModel.InCall(startTimeMs = 654)
+ underTest.setOngoingCallState(inCallModel)
- assertThat(underTest.hasOngoingCall.value).isTrue()
+ assertThat(underTest.ongoingCallState.value).isEqualTo(inCallModel)
- underTest.setHasOngoingCall(false)
+ underTest.setOngoingCallState(OngoingCallModel.NoCall)
- assertThat(underTest.hasOngoingCall.value).isFalse()
+ assertThat(underTest.ongoingCallState.value).isEqualTo(OngoingCallModel.NoCall)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
index cdb2b883078a..b8299e5ef3ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
@@ -29,6 +29,7 @@ import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.assertLogsWtf
@@ -63,7 +64,10 @@ import org.junit.Test
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
class CollapsedStatusBarViewModelImplTest : SysuiTestCase() {
- private val kosmos = Kosmos().apply { testDispatcher = UnconfinedTestDispatcher() }
+ private val kosmos = Kosmos().also {
+ it.testCase = this
+ it.testDispatcher = UnconfinedTestDispatcher()
+ }
private val testScope = kosmos.testScope
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 fac6a4c22178..57ddcde72643 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -86,6 +86,7 @@ import com.android.systemui.statusbar.policy.FakeConfigurationController;
import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.FakeSystemClock;
+import com.android.systemui.volume.domain.interactor.VolumeDialogInteractor;
import com.android.systemui.volume.domain.interactor.VolumePanelNavigationInteractor;
import com.android.systemui.volume.panel.shared.flag.VolumePanelFlag;
import com.android.systemui.volume.ui.binder.VolumeDialogMenuIconBinder;
@@ -153,6 +154,8 @@ public class VolumeDialogImplTest extends SysuiTestCase {
private VolumeDialogMenuIconBinder mVolumeDialogMenuIconBinder;
@Mock
private VolumePanelFlag mVolumePanelFlag;
+ @Mock
+ private VolumeDialogInteractor mVolumeDialogInteractor;
private final CsdWarningDialog.Factory mCsdWarningDialogFactory =
new CsdWarningDialog.Factory() {
@@ -218,7 +221,8 @@ public class VolumeDialogImplTest extends SysuiTestCase {
mLazySecureSettings,
mVibratorHelper,
mVolumeDialogMenuIconBinder,
- new FakeSystemClock());
+ new FakeSystemClock(),
+ mVolumeDialogInteractor);
mDialog.init(0, null);
State state = createShellState();
mDialog.onStateChangedH(state);
@@ -778,6 +782,15 @@ public class VolumeDialogImplTest extends SysuiTestCase {
assertFalse(foundDnDIcon);
}
+ @Test
+ public void testInteractor_onShow() {
+ mDialog.show(SHOW_REASON_UNKNOWN);
+ mTestableLooper.processAllMessages();
+
+ verify(mVolumeDialogInteractor).onDialogShown();
+ verify(mVolumeDialogInteractor).onDialogDismissed(); // dismiss by timeout
+ }
+
/**
* @return true if at least one volume row has the DND icon
*/
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/data/repository/VolumeDialogRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/volume/data/repository/VolumeDialogRepositoryTest.kt
new file mode 100644
index 000000000000..dcac85e7fb5b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/data/repository/VolumeDialogRepositoryTest.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class VolumeDialogRepositoryTest : SysuiTestCase() {
+ private lateinit var underTest: VolumeDialogRepository
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+
+ @Before
+ fun setUp() {
+ underTest = kosmos.volumeDialogRepository
+ }
+
+ @Test
+ fun isDialogVisible_initialValueFalse() {
+ testScope.runTest {
+ val isVisible by collectLastValue(underTest.isDialogVisible)
+ runCurrent()
+
+ assertThat(isVisible).isFalse()
+ }
+ }
+
+ @Test
+ fun isDialogVisible_onChange() {
+ testScope.runTest {
+ val isVisible by collectLastValue(underTest.isDialogVisible)
+ runCurrent()
+
+ underTest.setDialogVisibility(true)
+ assertThat(isVisible).isTrue()
+
+ underTest.setDialogVisibility(false)
+ assertThat(isVisible).isFalse()
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractorTest.kt
new file mode 100644
index 000000000000..5c735e3c7842
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractorTest.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class VolumeDialogInteractorTest : SysuiTestCase() {
+ private lateinit var underTest: VolumeDialogInteractor
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+
+ @Before
+ fun setUp() {
+ underTest = kosmos.volumeDialogInteractor
+ }
+
+ @Test
+ fun onDialogDismissed() {
+ testScope.runTest {
+ val isVisible by collectLastValue(underTest.isDialogVisible)
+ underTest.onDialogDismissed()
+ runCurrent()
+
+ assertThat(isVisible).isFalse()
+ }
+ }
+
+ @Test
+ fun onDialogShown() {
+ testScope.runTest {
+ val isVisible by collectLastValue(underTest.isDialogVisible)
+ underTest.onDialogShown()
+ runCurrent()
+
+ assertThat(isVisible).isTrue()
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/keyguard/KeyguardUnlockAnimationControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/keyguard/KeyguardUnlockAnimationControllerKosmos.kt
new file mode 100644
index 000000000000..1fa681360070
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/keyguard/KeyguardUnlockAnimationControllerKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard
+
+import com.android.systemui.keyguard.KeyguardUnlockAnimationController
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.keyguardUnlockAnimationController by
+ Kosmos.Fixture { mock<KeyguardUnlockAnimationController>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderKosmos.kt
index 8281984d3b4f..79d58a1d4e40 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderKosmos.kt
@@ -22,7 +22,6 @@ import android.view.windowManager
import com.android.systemui.biometrics.domain.interactor.biometricStatusInteractor
import com.android.systemui.biometrics.domain.interactor.displayStateInteractor
import com.android.systemui.biometrics.domain.interactor.sideFpsSensorInteractor
-import com.android.systemui.biometrics.fpsUnlockTracker
import com.android.systemui.keyguard.domain.interactor.deviceEntrySideFpsOverlayInteractor
import com.android.systemui.keyguard.ui.viewmodel.sideFpsProgressBarViewModel
import com.android.systemui.kosmos.Kosmos
@@ -38,7 +37,6 @@ val Kosmos.sideFpsOverlayViewBinder by Fixture {
{ biometricStatusInteractor },
{ displayStateInteractor },
{ deviceEntrySideFpsOverlayInteractor },
- { fpsUnlockTracker },
{ layoutInflater },
{ sideFpsProgressBarViewModel },
{ sideFpsSensorInteractor },
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelKosmos.kt
index 3f3840810946..1ae8449d8b4d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelKosmos.kt
@@ -25,12 +25,14 @@ import com.android.systemui.keyguard.ui.viewmodel.glanceableHubToDreamingTransit
import com.android.systemui.keyguard.ui.viewmodel.glanceableHubToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.lockscreenToGlanceableHubTransitionViewModel
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@OptIn(ExperimentalCoroutinesApi::class)
val Kosmos.communalTransitionViewModel by
Kosmos.Fixture {
CommunalTransitionViewModel(
+ applicationScope = applicationCoroutineScope,
glanceableHubToLockscreenTransitionViewModel =
glanceableHubToLockscreenTransitionViewModel,
lockscreenToGlanceableHubTransitionViewModel =
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index 67f844340096..31cdbc73f6fa 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -68,6 +68,7 @@ import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.wifiIntera
import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvisioningRepository
import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
import com.android.systemui.util.time.systemClock
+import com.android.systemui.volume.domain.interactor.volumeDialogInteractor
import kotlinx.coroutines.ExperimentalCoroutinesApi
/**
@@ -136,6 +137,7 @@ class KosmosJavaAdapter() {
val shadeInteractor by lazy { kosmos.shadeInteractor }
val wifiInteractor by lazy { kosmos.wifiInteractor }
val fakeWifiRepository by lazy { kosmos.fakeWifiRepository }
+ val volumeDialogInteractor by lazy { kosmos.volumeDialogInteractor }
val ongoingActivityChipsViewModel by lazy { kosmos.ongoingActivityChipsViewModel }
val scrimController by lazy { kosmos.scrimController }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorKosmos.kt
index 062b4484044c..9d2281167fbf 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorKosmos.kt
@@ -21,7 +21,7 @@ import com.android.systemui.animation.mockDialogTransitionAnimator
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
-import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
+import com.android.systemui.statusbar.chips.mediaprojection.ui.view.endMediaProjectionDialogHelper
import com.android.systemui.util.time.fakeSystemClock
val Kosmos.mediaProjectionChipInteractor: MediaProjectionChipInteractor by
@@ -31,7 +31,7 @@ val Kosmos.mediaProjectionChipInteractor: MediaProjectionChipInteractor by
mediaProjectionRepository = fakeMediaProjectionRepository,
packageManager = packageManager,
systemClock = fakeSystemClock,
- dialogFactory = mockSystemUIDialogFactory,
+ endMediaProjectionDialogHelper = endMediaProjectionDialogHelper,
dialogTransitionAnimator = mockDialogTransitionAnimator,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperKosmos.kt
new file mode 100644
index 000000000000..4f82662fa673
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperKosmos.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips.mediaprojection.ui.view
+
+import android.content.applicationContext
+import android.content.packageManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
+
+val Kosmos.endMediaProjectionDialogHelper: EndMediaProjectionDialogHelper by
+ Kosmos.Fixture {
+ EndMediaProjectionDialogHelper(
+ dialogFactory = mockSystemUIDialogFactory,
+ packageManager = packageManager,
+ context = applicationContext,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/VolumeDialogRepositoryKosmos.kt
index 6085a1f3353e..2f4eef53830c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/VolumeDialogRepositoryKosmos.kt
@@ -14,9 +14,8 @@
* limitations under the License.
*/
-package com.android.systemui.biometrics
+package com.android.systemui.volume.data.repository
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.util.mockito.mock
-val Kosmos.fpsUnlockTracker by Kosmos.Fixture { mock<FpsUnlockTracker>() }
+val Kosmos.volumeDialogRepository by Kosmos.Fixture { VolumeDialogRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractorKosmos.kt
new file mode 100644
index 000000000000..2e5d0407d522
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractorKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.volume.data.repository.volumeDialogRepository
+
+val Kosmos.volumeDialogInteractor by
+ Kosmos.Fixture { VolumeDialogInteractor(volumeDialogRepository) }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index aa0af7e5075a..b5b998f6cd5e 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -18,6 +18,7 @@ package com.android.server.accessibility.magnification;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN;
+import static android.util.MathUtils.abs;
import static android.util.TypedValue.COMPLEX_UNIT_DIP;
import static android.view.accessibility.MagnificationAnimationCallback.STUB_ANIMATION_CALLBACK;
@@ -263,27 +264,27 @@ public class FullScreenMagnificationController implements
@GuardedBy("mLock")
boolean isAtEdge() {
- return isAtLeftEdge() || isAtRightEdge() || isAtTopEdge() || isAtBottomEdge();
+ return isAtLeftEdge(0f) || isAtRightEdge(0f) || isAtTopEdge(0f) || isAtBottomEdge(0f);
}
@GuardedBy("mLock")
- boolean isAtLeftEdge() {
- return getOffsetX() == getMaxOffsetXLocked();
+ boolean isAtLeftEdge(float slop) {
+ return abs(getOffsetX() - getMaxOffsetXLocked()) <= slop;
}
@GuardedBy("mLock")
- boolean isAtRightEdge() {
- return getOffsetX() == getMinOffsetXLocked();
+ boolean isAtRightEdge(float slop) {
+ return abs(getOffsetX() - getMinOffsetXLocked()) <= slop;
}
@GuardedBy("mLock")
- boolean isAtTopEdge() {
- return getOffsetY() == getMaxOffsetYLocked();
+ boolean isAtTopEdge(float slop) {
+ return abs(getOffsetY() - getMaxOffsetYLocked()) <= slop;
}
@GuardedBy("mLock")
- boolean isAtBottomEdge() {
- return getOffsetY() == getMinOffsetYLocked();
+ boolean isAtBottomEdge(float slop) {
+ return abs(getOffsetY() - getMinOffsetYLocked()) <= slop;
}
@GuardedBy("mLock")
@@ -1298,7 +1299,7 @@ public class FullScreenMagnificationController implements
* Returns whether the user is at one of the edges (left, right, top, bottom)
* of the magnification viewport
*
- * @param displayId
+ * @param displayId The logical display id.
* @return if user is at the edge of the view
*/
public boolean isAtEdge(int displayId) {
@@ -1314,64 +1315,72 @@ public class FullScreenMagnificationController implements
/**
* Returns whether the user is at the left edge of the viewport
*
- * @param displayId
- * @return if user is at left edge of view
+ * @param displayId The logical display id.
+ * @param slop The buffer distance in pixels from the left edge within that will be considered
+ * to be at the edge.
+ * @return if user is considered at left edge of view
*/
- public boolean isAtLeftEdge(int displayId) {
+ public boolean isAtLeftEdge(int displayId, float slop) {
synchronized (mLock) {
final DisplayMagnification display = mDisplays.get(displayId);
if (display == null) {
return false;
}
- return display.isAtLeftEdge();
+ return display.isAtLeftEdge(slop);
}
}
/**
* Returns whether the user is at the right edge of the viewport
*
- * @param displayId
- * @return if user is at right edge of view
+ * @param displayId The logical display id.
+ * @param slop The buffer distance in pixels from the right edge within that will be considered
+ * to be at the edge.
+ * @return if user is considered at right edge of view
*/
- public boolean isAtRightEdge(int displayId) {
+ public boolean isAtRightEdge(int displayId, float slop) {
synchronized (mLock) {
final DisplayMagnification display = mDisplays.get(displayId);
if (display == null) {
return false;
}
- return display.isAtRightEdge();
+ return display.isAtRightEdge(slop);
}
}
/**
* Returns whether the user is at the top edge of the viewport
*
- * @param displayId
- * @return if user is at top edge of view
+ * @param displayId The logical display id.
+ * @param slop The buffer distance in pixels from the top edge within that will be considered
+ * to be at the edge.
+ * @return if user is considered at top edge of view
*/
- public boolean isAtTopEdge(int displayId) {
+ public boolean isAtTopEdge(int displayId, float slop) {
synchronized (mLock) {
final DisplayMagnification display = mDisplays.get(displayId);
if (display == null) {
return false;
}
- return display.isAtTopEdge();
+ return display.isAtTopEdge(slop);
}
}
/**
* Returns whether the user is at the bottom edge of the viewport
*
- * @param displayId
- * @return if user is at bottom edge of view
+ * @param displayId The logical display id.
+ * @param slop The buffer distance in pixels from the bottom edge within that will be considered
+ * to be at the edge.
+ * @return if user is considered at bottom edge of view
*/
- public boolean isAtBottomEdge(int displayId) {
+ public boolean isAtBottomEdge(int displayId, float slop) {
synchronized (mLock) {
final DisplayMagnification display = mDisplays.get(displayId);
if (display == null) {
return false;
}
- return display.isAtBottomEdge();
+ return display.isAtBottomEdge(slop);
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index 6d1ab9f89f78..e9c3fbdf021c 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -171,7 +171,11 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
private final FullScreenMagnificationVibrationHelper mFullScreenMagnificationVibrationHelper;
- @VisibleForTesting final OverscrollHandler mOverscrollHandler;
+ @VisibleForTesting
+ @Nullable
+ final OverscrollHandler mOverscrollHandler;
+
+ private final float mOverscrollEdgeSlop;
private final boolean mIsWatch;
@@ -308,7 +312,11 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
mSinglePanningState = new SinglePanningState(context);
mFullScreenMagnificationVibrationHelper = fullScreenMagnificationVibrationHelper;
mOneFingerPanningSettingsProvider = oneFingerPanningSettingsProvider;
- mOverscrollHandler = new OverscrollHandler();
+ boolean overscrollHandlerSupported = context.getResources().getBoolean(
+ R.bool.config_enable_a11y_fullscreen_magnification_overscroll_handler);
+ mOverscrollHandler = overscrollHandlerSupported ? new OverscrollHandler() : null;
+ mOverscrollEdgeSlop = context.getResources().getDimensionPixelSize(
+ R.dimen.accessibility_fullscreen_magnification_gesture_edge_slop);
mIsWatch = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
if (mDetectShortcutTrigger) {
@@ -523,16 +531,14 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
if (action == ACTION_POINTER_UP
&& event.getPointerCount() == 2 // includes the pointer currently being released
&& mPreviousState == mViewportDraggingState) {
- // if feature flag is enabled, currently only true on watches
- if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()) {
+ if (mOverscrollHandler != null) {
mOverscrollHandler.setScaleAndCenterToEdgeIfNeeded();
mOverscrollHandler.clearEdgeState();
}
persistScaleAndTransitionTo(mViewportDraggingState);
} else if (action == ACTION_UP || action == ACTION_CANCEL) {
onPanningFinished(event);
- // if feature flag is enabled, currently only true on watches
- if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()) {
+ if (mOverscrollHandler != null) {
mOverscrollHandler.setScaleAndCenterToEdgeIfNeeded();
mOverscrollHandler.clearEdgeState();
}
@@ -540,7 +546,6 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
}
}
-
void prepareForState() {
checkShouldDetectPassPersistedScale();
}
@@ -611,7 +616,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
onPan(second);
mFullScreenMagnificationController.offsetMagnifiedRegion(mDisplayId, distanceX,
distanceY, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
- if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()) {
+ if (mOverscrollHandler != null) {
mOverscrollHandler.onScrollStateChanged(first, second);
}
return /* event consumed: */ true;
@@ -1000,7 +1005,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
&& event.getPointerCount() == 2) {
transitionToViewportDraggingStateAndClear(event);
} else if (isActivated() && event.getPointerCount() == 2) {
- if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()
+ if (mOverscrollHandler != null
&& overscrollState(event, mFirstPointerDownLocation)
== OVERSCROLL_VERTICAL_EDGE) {
transitionToDelegatingStateAndClear();
@@ -1011,9 +1016,13 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
} else if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()
&& isActivated()
&& event.getPointerCount() == 1) {
- if (overscrollState(event, mFirstPointerDownLocation)
+ if (mOverscrollHandler != null
+ && overscrollState(event, mFirstPointerDownLocation)
== OVERSCROLL_VERTICAL_EDGE) {
transitionToDelegatingStateAndClear();
+ } else if (overscrollState(event, mFirstPointerDownLocation)
+ != OVERSCROLL_NONE) {
+ transitionToDelegatingStateAndClear();
} else {
transitToSinglePanningStateAndClear();
}
@@ -1255,7 +1264,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
if (isMultiTapTriggered(2 /* taps */) && event.getPointerCount() == 1) {
transitionToViewportDraggingStateAndClear(event);
} else if (isActivated() && event.getPointerCount() == 2) {
- if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()
+ if (mOverscrollHandler != null
&& overscrollState(event, mFirstPointerDownLocation)
== OVERSCROLL_VERTICAL_EDGE) {
transitionToDelegatingStateAndClear();
@@ -1266,9 +1275,13 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
} else if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()
&& isActivated()
&& event.getPointerCount() == 1) {
- if (overscrollState(event, mFirstPointerDownLocation)
+ if (mOverscrollHandler != null
+ && overscrollState(event, mFirstPointerDownLocation)
== OVERSCROLL_VERTICAL_EDGE) {
transitionToDelegatingStateAndClear();
+ } else if (overscrollState(event, mFirstPointerDownLocation)
+ != OVERSCROLL_NONE) {
+ transitionToDelegatingStateAndClear();
} else {
transitToSinglePanningStateAndClear();
}
@@ -1645,22 +1658,36 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
}
float dX = event.getX() - firstPointerDownLocation.x;
float dY = event.getY() - firstPointerDownLocation.y;
- if (mFullScreenMagnificationController.isAtLeftEdge(mDisplayId) && dX > 0) {
+ if (isAtLeftEdge() && dX > 0) {
return OVERSCROLL_LEFT_EDGE;
- } else if (mFullScreenMagnificationController.isAtRightEdge(mDisplayId) && dX < 0) {
+ } else if (isAtRightEdge() && dX < 0) {
return OVERSCROLL_RIGHT_EDGE;
- } else if (mFullScreenMagnificationController.isAtTopEdge(mDisplayId) && dY > 0
- || mFullScreenMagnificationController.isAtBottomEdge(mDisplayId) && dY < 0) {
+ } else if ((isAtTopEdge() && dY > 0) || (isAtBottomEdge() && dY < 0)) {
return OVERSCROLL_VERTICAL_EDGE;
}
return OVERSCROLL_NONE;
}
+ private boolean isAtLeftEdge() {
+ return mFullScreenMagnificationController.isAtLeftEdge(mDisplayId, mOverscrollEdgeSlop);
+ }
+
+ private boolean isAtRightEdge() {
+ return mFullScreenMagnificationController.isAtRightEdge(mDisplayId, mOverscrollEdgeSlop);
+ }
+
+ private boolean isAtTopEdge() {
+ return mFullScreenMagnificationController.isAtTopEdge(mDisplayId, mOverscrollEdgeSlop);
+ }
+
+ private boolean isAtBottomEdge() {
+ return mFullScreenMagnificationController.isAtBottomEdge(mDisplayId, mOverscrollEdgeSlop);
+ }
+
private boolean pointerValid(PointF pointerDownLocation) {
return !(Float.isNaN(pointerDownLocation.x) && Float.isNaN(pointerDownLocation.y));
}
-
private static final class MotionEventInfo {
private static final int MAX_POOL_SIZE = 10;
@@ -1845,7 +1872,6 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
final class SinglePanningState extends SimpleOnGestureListener implements State {
-
private final GestureDetector mScrollGestureDetector;
private MotionEventInfo mEvent;
SinglePanningState(Context context) {
@@ -1860,8 +1886,10 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
onPanningFinished(event);
// fall-through!
case ACTION_CANCEL:
- mOverscrollHandler.setScaleAndCenterToEdgeIfNeeded();
- mOverscrollHandler.clearEdgeState();
+ if (mOverscrollHandler != null) {
+ mOverscrollHandler.setScaleAndCenterToEdgeIfNeeded();
+ mOverscrollHandler.clearEdgeState();
+ }
transitionTo(mDetectingState);
break;
}
@@ -1889,26 +1917,18 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
+ " isAtEdge: "
+ mFullScreenMagnificationController.isAtEdge(mDisplayId));
}
- mOverscrollHandler.onScrollStateChanged(first, second);
- return /* event consumed: */ true;
- }
-
- private void vibrateIfNeeded() {
- if ((mFullScreenMagnificationController.isAtLeftEdge(mDisplayId)
- || mFullScreenMagnificationController.isAtRightEdge(mDisplayId))) {
- mFullScreenMagnificationVibrationHelper.vibrateIfSettingEnabled();
+ if (mOverscrollHandler != null) {
+ mOverscrollHandler.onScrollStateChanged(first, second);
}
+ return /* event consumed: */ true;
}
-
-
@Override
public String toString() {
return "SinglePanningState{"
+ "isEdgeOfView="
+ mFullScreenMagnificationController.isAtEdge(mDisplayId);
}
-
}
/** Overscroll Handler handles the logic when user is at the edge and scrolls past an edge */
@@ -2007,9 +2027,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
if (mOverscrollState != OVERSCROLL_NONE) {
return;
}
- if ((mFullScreenMagnificationController.isAtLeftEdge(mDisplayId)
- || mFullScreenMagnificationController.isAtRightEdge(mDisplayId))
- && !mEdgeCooldown) {
+ if ((isAtLeftEdge() || isAtRightEdge()) && !mEdgeCooldown) {
mFullScreenMagnificationVibrationHelper.vibrateIfSettingEnabled();
}
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 9ad15b2d431f..2bf319e06efa 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -515,7 +515,10 @@ final class AutofillManagerServiceImpl
}
final boolean finished = saveResult.isRemoveSession();
- if (sVerbose) Slog.v(TAG, "finishSessionLocked(): session finished on save? " + finished);
+ if (sVerbose) {
+ Slog.v(TAG, "finishSessionLocked(): session finished? " + finished
+ + ", showing save UI? " + saveResult.isLogSaveShown());
+ }
if (finished) {
session.removeFromServiceLocked();
diff --git a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
index 676abd1491d7..d7da2f0052d3 100644
--- a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
@@ -295,6 +295,38 @@ public final class PresentationStatsEventLogger {
});
}
+ /**
+ * Called for inline suggestions - inflated one at
+ * a time. If InlineSuggestions were filtered,
+ * reset the count be incrementing
+ */
+ public void maybeIncrementCountShown() {
+ mEventInternal.ifPresent(event -> {
+ if (event.shouldResetShownCount) {
+ event.shouldResetShownCount = false;
+ event.mCountShown = 0;
+ }
+
+ if (event.mCountShown == 0) {
+ // The first time suggestions are rendered
+ // set time stamp
+ maybeSetSuggestionPresentedTimestampMs();
+ }
+
+ event.mCountShown += 1;
+ });
+ }
+
+ /**
+ * Call this when UI is hidden. This will set a flag to reset count for inline. We do this
+ * instead of resetting right away in case there are 0 inline presentations after.
+ */
+ public void markShownCountAsResettable() {
+ mEventInternal.ifPresent(event -> {
+ event.shouldResetShownCount = true;
+ });
+ }
+
public void maybeSetCountShown(@Nullable List<Dataset> datasetList,
AutofillId currentViewId) {
mEventInternal.ifPresent(event -> {
@@ -306,6 +338,13 @@ public final class PresentationStatsEventLogger {
});
}
+ public void maybeSetCountShown(int datasets) {
+ mEventInternal.ifPresent(
+ event -> {
+ event.mCountShown = datasets;
+ });
+ }
+
private static CountContainer getDatasetCountForAutofillId(@Nullable List<Dataset> datasetList,
AutofillId currentViewId) {
@@ -567,31 +606,36 @@ public final class PresentationStatsEventLogger {
* <p>If the ViewState contains ViewState.STATE_AUTOFILLED, sets field_autofilled_timestamp_ms
* else, set field_first_modified_timestamp_ms (if unset) and field_last_modified_timestamp_ms
*/
- public void onFieldTextUpdated(ViewState state) {
- mEventInternal.ifPresent(
- event -> {
+ public void onFieldTextUpdated(ViewState state, int length) {
+ mEventInternal.ifPresent(event -> {
int timestamp = getElapsedTime();
// Focused id should be set before this is called
- if (state.id != null && state.id.getViewId() != event.mFocusedId) {
+ if (state == null || state.id == null || state.id.getViewId() != event.mFocusedId) {
// if these don't match, the currently field different than before
Slog.w(
TAG,
- "current id: "
- + state.id.getViewId()
- + " is different than focused id: "
- + event.mFocusedId);
+ "Bad view state for: " + event.mFocusedId);
return;
}
+ // Text changed because filling into form, just log Autofill timestamp
if ((state.getState() & ViewState.STATE_AUTOFILLED) != 0) {
event.mAutofilledTimestampMs = timestamp;
- } else {
- if (event.mFieldModifiedFirstTimestampMs == DEFAULT_VALUE_INT) {
- event.mFieldModifiedFirstTimestampMs = timestamp;
- }
- event.mFieldModifiedLastTimestampMs = timestamp;
+ return;
}
- });
+
+ // Set length variables
+ if (event.mFieldFirstLength == DEFAULT_VALUE_INT) {
+ event.mFieldFirstLength = length;
+ }
+ event.mFieldLastLength = length;
+
+ // Set timestamp variables
+ if (event.mFieldModifiedFirstTimestampMs == DEFAULT_VALUE_INT) {
+ event.mFieldModifiedFirstTimestampMs = timestamp;
+ }
+ event.mFieldModifiedLastTimestampMs = timestamp;
+ });
}
public void setPresentationSelectedTimestamp() {
@@ -661,15 +705,16 @@ public final class PresentationStatsEventLogger {
/** Sets focused_autofill_id using view id */
public void maybeSetFocusedId(AutofillId id) {
- maybeSetFocusedId(id.getViewId());
+ mEventInternal.ifPresent(
+ event -> {
+ event.mFocusedId = id.getViewId();
+ if (id.isVirtualInt()) {
+ event.mFocusedVirtualAutofillId =
+ id.getVirtualChildIntId() % 100;
+ }
+ });
}
- /** Sets focused_autofill_id as long as mEventInternal is present */
- public void maybeSetFocusedId(int id) {
- mEventInternal.ifPresent(event -> {
- event.mFocusedId = id;
- });
- }
/**
* Set views_filled_failure_count using failure count as long as mEventInternal
* presents.
@@ -823,7 +868,7 @@ public final class PresentationStatsEventLogger {
@NotShownReason int mNoPresentationReason = NOT_SHOWN_REASON_UNKNOWN;
boolean mIsDatasetAvailable;
int mAvailableCount;
- int mCountShown;
+ int mCountShown = 0;
int mCountFilteredUserTyping;
int mCountNotShownImePresentationNotDrawn;
int mCountNotShownImeUserNotSeen;
@@ -870,6 +915,9 @@ public final class PresentationStatsEventLogger {
ArraySet<AutofillId> mAutofillIdsAttemptedAutofill;
ArraySet<AutofillId> mAlreadyFilledAutofillIds = new ArraySet<>();
+
+ // Not logged - used for internal logic
+ boolean shouldResetShownCount = false;
PresentationStatsEventInternal() {}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 5b4faa24649c..cdae16b9a3be 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -30,7 +30,6 @@ import static android.service.autofill.Dataset.PICK_REASON_UNKNOWN;
import static android.service.autofill.FillEventHistory.Event.UI_TYPE_CREDMAN_BOTTOM_SHEET;
import static android.service.autofill.FillEventHistory.Event.UI_TYPE_DIALOG;
import static android.service.autofill.FillEventHistory.Event.UI_TYPE_INLINE;
-import static android.service.autofill.FillEventHistory.Event.UI_TYPE_MENU;
import static android.service.autofill.FillEventHistory.Event.UI_TYPE_UNKNOWN;
import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE;
@@ -2661,19 +2660,30 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// AutofillUiCallback
@Override
- public void onShown(int uiType) {
+ public void onShown(int uiType, int numDatasetsShown) {
synchronized (mLock) {
+ mPresentationStatsEventLogger.maybeSetDisplayPresentationType(uiType);
+
if (uiType == UI_TYPE_INLINE) {
- if (mLoggedInlineDatasetShown) {
+ // Inline Suggestions are inflated one at a time
+ // This number will be reset when filtered
+ // This will also call maybeSetSuggestionPresentedTimestampMs
+ mPresentationStatsEventLogger.maybeIncrementCountShown();
+
+ if (!mLoggedInlineDatasetShown) {
// Chip inflation already logged, do not log again.
// This is needed because every chip inflation will call this.
- return;
+ mService.logDatasetShown(this.id, mClientState, uiType);
+ Slog.d(TAG, "onShown(): " + uiType + ", " + numDatasetsShown);
}
mLoggedInlineDatasetShown = true;
+ } else {
+ mPresentationStatsEventLogger.maybeSetCountShown(numDatasetsShown);
+ // Explicitly sets maybeSetSuggestionPresentedTimestampMs
+ mPresentationStatsEventLogger.maybeSetSuggestionPresentedTimestampMs();
+ mService.logDatasetShown(this.id, mClientState, uiType);
+ Slog.d(TAG, "onShown(): " + uiType + ", " + numDatasetsShown);
}
- mService.logDatasetShown(this.id, mClientState, uiType);
- mPresentationStatsEventLogger.maybeSetSuggestionPresentedTimestampMs();
- Slog.d(TAG, "onShown(): " + uiType);
}
}
@@ -2739,6 +2749,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
mInlineSessionController.hideInlineSuggestionsUiLocked(id);
+ mPresentationStatsEventLogger.markShownCountAsResettable();
}
}
@@ -4868,7 +4879,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
currentView.maybeCallOnFillReady(flags);
}
}
- mPresentationStatsEventLogger.onFieldTextUpdated(viewState);
+ if (textValue != null) {
+ mPresentationStatsEventLogger.onFieldTextUpdated(viewState, textValue.length());
+ }
if (viewState.id.equals(this.mCurrentViewId)
&& (viewState.getState() & ViewState.STATE_INLINE_SHOWN) != 0) {
@@ -4965,8 +4978,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
synchronized (mLock) {
final ViewState currentView = mViewStates.get(mCurrentViewId);
currentView.setState(ViewState.STATE_FILL_DIALOG_SHOWN);
- mPresentationStatsEventLogger.maybeSetCountShown(
- response.getDatasets(), mCurrentViewId);
mPresentationStatsEventLogger.maybeSetDisplayPresentationType(UI_TYPE_DIALOG);
}
// Just show fill dialog once, so disabled after shown.
@@ -4987,10 +4998,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// back a response via callback.
final ViewState currentView = mViewStates.get(mCurrentViewId);
currentView.setState(ViewState.STATE_INLINE_SHOWN);
- // TODO(b/234475358): Log more accurate value of number of inline suggestions
- // shown, inflated, and filtered.
- mPresentationStatsEventLogger.maybeSetCountShown(
- response.getDatasets(), mCurrentViewId);
mPresentationStatsEventLogger.maybeSetInlinePresentationAndSuggestionHostUid(
mContext, userId);
return;
@@ -5004,12 +5011,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mService.getMaster().getMaxInputLengthForAutofill());
synchronized (mLock) {
- mPresentationStatsEventLogger.maybeSetCountShown(
- response.getDatasets(), mCurrentViewId);
- mPresentationStatsEventLogger.maybeSetDisplayPresentationType(UI_TYPE_MENU);
- }
-
- synchronized (mLock) {
if (mUiShownTime == 0) {
// Log first time UI is shown.
mUiShownTime = SystemClock.elapsedRealtime();
@@ -5249,7 +5250,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@Override
public void onInflate() {
- Session.this.onShown(UI_TYPE_INLINE);
+ Session.this.onShown(UI_TYPE_INLINE, 1);
}
}, mService.getMaster().getMaxInputLengthForAutofill());
return mInlineSessionController.setInlineFillUiLocked(inlineFillUi);
@@ -5441,9 +5442,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
try {
if (sVerbose) {
- Slog.v(TAG, "updateTrackedIdsLocked(): " + trackedViews + " => " + fillableIds
- + " triggerId: " + saveTriggerId + " saveOnFinish:" + saveOnFinish
- + " flags: " + flags + " hasSaveInfo: " + (saveInfo != null));
+ Slog.v(TAG, "updateTrackedIdsLocked(): trackedViews: " + trackedViews
+ + " fillableIds: " + fillableIds + " triggerId: " + saveTriggerId
+ + " saveOnFinish:" + saveOnFinish + " flags: " + flags
+ + " hasSaveInfo: " + (saveInfo != null));
}
mClient.setTrackedViews(id, toArray(trackedViews), mSaveOnAllViewsInvisible,
saveOnFinish, toArray(fillableIds), saveTriggerId);
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index 8cc666b538ec..2e9a4dcb45aa 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -102,7 +102,7 @@ public final class AutoFillUI {
void cancelSession();
void requestShowSoftInput(AutofillId id);
void requestFallbackFromFillDialog();
- void onShown(int uiType);
+ void onShown(int uiType, int datasetSize);
}
public AutoFillUI(@NonNull Context context) {
@@ -246,9 +246,9 @@ public final class AutoFillUI {
}
@Override
- public void onShown() {
+ public void onShown(int datasetSize) {
if (mCallback != null) {
- mCallback.onShown(UI_TYPE_MENU);
+ mCallback.onShown(UI_TYPE_MENU, datasetSize);
}
}
@@ -462,7 +462,7 @@ public final class AutoFillUI {
@Override
public void onShown() {
- callback.onShown(UI_TYPE_DIALOG);
+ mCallback.onShown(UI_TYPE_DIALOG, response.getDatasets().size());
}
@Override
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 1831ecdaf77d..1bda70deac06 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -16,6 +16,7 @@
package com.android.server.autofill.ui;
import static android.service.autofill.FillResponse.FLAG_CREDENTIAL_MANAGER_RESPONSE;
+
import static com.android.server.autofill.Helper.paramsToString;
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sFullScreenMode;
@@ -90,7 +91,7 @@ final class FillUi {
void onDatasetPicked(@NonNull Dataset dataset);
void onCanceled();
void onDestroy();
- void onShown();
+ void onShown(int datasetSize);
void requestShowFillUi(int width, int height,
IAutofillWindowPresenter windowPresenter);
void requestHideFillUi();
@@ -742,7 +743,8 @@ final class FillUi {
mWm.addView(mContentView, params);
mOverlayControl.hideOverlays();
mShowing = true;
- mCallback.onShown();
+ int numShownDatasets = (mAdapter == null) ? 0 : mAdapter.getCount();
+ mCallback.onShown(numShownDatasets);
} else {
mWm.updateViewLayout(mContentView, params);
}
diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
index 38bbfc4d9108..d3313374243d 100644
--- a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
+++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
@@ -466,12 +466,25 @@ public class ContextualSearchManagerService extends SystemService {
issueToken();
Bundle bundle = new Bundle();
bundle.putParcelable(ContextualSearchManager.EXTRA_TOKEN, mToken);
- try {
- callback.onResult(
+ // We get take the screenshot with the system server's identity because the system
+ // server has READ_FRAME_BUFFER permission to get the screenshot.
+ Binder.withCleanCallingIdentity(() -> {
+ if (mWmInternal != null) {
+ bundle.putParcelable(ContextualSearchManager.EXTRA_SCREENSHOT,
+ mWmInternal.takeAssistScreenshot(Set.of(
+ TYPE_STATUS_BAR,
+ TYPE_NAVIGATION_BAR,
+ TYPE_NAVIGATION_BAR_PANEL,
+ TYPE_POINTER))
+ .asBitmap().asShared());
+ }
+ try {
+ callback.onResult(
new ContextualSearchState(null, null, bundle));
- } catch (RemoteException e) {
- Log.e(TAG, "Error invoking ContextualSearchCallback", e);
- }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error invoking ContextualSearchCallback", e);
+ }
+ });
}
synchronized (mLock) {
mStateCallback = callback;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d750065ee96f..d41de38ce2a8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -306,6 +306,7 @@ import android.content.pm.ServiceInfo;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.TestUtilityService;
import android.content.pm.UserInfo;
+import android.content.pm.UserProperties;
import android.content.pm.VersionedPackage;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -14642,7 +14643,7 @@ public class ActivityManagerService extends IActivityManager.Stub
final StringBuilder sb = new StringBuilder("registerReceiver: ");
sb.append(Binder.getCallingUid()); sb.append('/');
sb.append(receiverId == null ? "null" : receiverId); sb.append('/');
- final int actionsCount = filter.countActions();
+ final int actionsCount = filter.safeCountActions();
if (actionsCount > 0) {
for (int i = 0; i < actionsCount; ++i) {
sb.append(filter.getAction(i));
@@ -18209,7 +18210,9 @@ public class ActivityManagerService extends IActivityManager.Stub
/**
* Stops user but allow delayed locking. Delayed locking keeps user unlocked even after
- * stopping only if {@code config_multiuserDelayUserDataLocking} overlay is set true.
+ * stopping only if {@code config_multiuserDelayUserDataLocking} overlay is set true on the
+ * device or if the user has {@link UserProperties#getAllowStoppingUserWithDelayedLocking()}
+ * set to true.
*
* <p>When delayed locking is not enabled through the overlay, this call becomes the same
* with {@link #stopUserWithCallback(int, IStopUserCallback)} call.
@@ -18221,8 +18224,6 @@ public class ActivityManagerService extends IActivityManager.Stub
* other {@code ActivityManager#USER_OP_*} codes for failure.
*
*/
- // TODO(b/302662311): Add javadoc changes corresponding to the user property that allows
- // delayed locking behavior once the private space flag is finalized.
@Override
public int stopUserWithDelayedLocking(@UserIdInt int userId, IStopUserCallback callback) {
return mUserController.stopUser(userId, /* allowDelayedLocking= */ true,
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index c6c1f9873a45..fa0e2ca3b2d4 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -384,9 +384,11 @@ class UserController implements Handler.Callback {
* postponed until total number of unlocked users in the system reaches mMaxRunningUsers.
* Once total number of unlocked users reach mMaxRunningUsers, least recently used user
* will be locked.
+ *
+ * <p> Note: Even if this is false for the device as a whole, it is possible some users may
+ * individually allow delayed locking, as specified by
+ * {@link UserProperties#getAllowStoppingUserWithDelayedLocking()}.
*/
- // TODO(b/302662311): Add javadoc changes corresponding to the user property that allows
- // delayed locking behavior once the private space flag is finalized.
@GuardedBy("mLock")
private boolean mDelayUserDataLocking;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 5dd1480c2052..684cb245f62b 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -13911,9 +13911,8 @@ public class AudioService extends IAudioService.Stub
final int stream = AudioAttributes.toLegacyStreamType(aa);
final boolean mutingFromVolume = getStreamVolume(stream) == 0;
if (mutingFromVolume) {
- if (DEBUG_VOL) {
- Slog.d(TAG, "notification should not play due to muted stream " + stream);
- }
+ Slog.i(TAG, "shouldNotificationSoundPlay false: muted stream:" + stream
+ + " attr:" + aa);
return false;
}
@@ -13926,10 +13925,8 @@ public class AudioService extends IAudioService.Stub
// is the owner of GAIN_TRANSIENT_EXCLUSIVE focus also recording?
final boolean mutingFromFocusAndRecording = mRecordMonitor.isRecordingActiveForUid(uid);
if (mutingFromFocusAndRecording) {
- if (DEBUG_VOL) {
- Slog.d(TAG, "notification should not play due to exclusive focus owner recording "
- + " uid:" + uid);
- }
+ Slog.i(TAG, "shouldNotificationSoundPlay false: exclusive focus owner recording "
+ + " uid:" + uid + " attr:" + aa);
return false;
}
return true;
diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java
index 469e8b7f26ad..276ab03d9f4b 100644
--- a/services/core/java/com/android/server/biometrics/AuthSession.java
+++ b/services/core/java/com/android/server/biometrics/AuthSession.java
@@ -108,6 +108,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
}
private final Context mContext;
+ private final BiometricManager mBiometricManager;
@NonNull private final BiometricContext mBiometricContext;
private final IStatusBarService mStatusBarService;
@VisibleForTesting final IBiometricSysuiReceiver mSysuiReceiver;
@@ -131,6 +132,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
private final String mOpPackageName;
private final boolean mDebugEnabled;
private final List<FingerprintSensorPropertiesInternal> mFingerprintSensorProperties;
+ private final List<Integer> mSfpsSensorIds;
// The current state, which can be either idle, called, or started
private @SessionState int mState = STATE_AUTH_IDLE;
@@ -220,6 +222,11 @@ public final class AuthSession implements IBinder.DeathRecipient {
mCancelled = false;
mBiometricFrameworkStatsLogger = logger;
mOperationContext = new OperationContextExt(true /* isBP */);
+ mBiometricManager = mContext.getSystemService(BiometricManager.class);
+
+ mSfpsSensorIds = mFingerprintSensorProperties.stream().filter(
+ FingerprintSensorPropertiesInternal::isAnySidefpsType).map(
+ prop -> prop.sensorId).toList();
try {
mClientReceiver.asBinder().linkToDeath(this, 0 /* flags */);
@@ -316,7 +323,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
Slog.w(TAG, "Received cookie but already cancelled (ignoring): " + cookie);
return;
}
- if (hasAuthenticated()) {
+ if (hasAuthenticatedAndConfirmed()) {
Slog.d(TAG, "onCookieReceived after successful auth");
return;
}
@@ -494,6 +501,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
}
case STATE_AUTH_STARTED:
+ case STATE_AUTH_PENDING_CONFIRM:
case STATE_AUTH_STARTED_UI_SHOWING: {
if (isAllowDeviceCredential() && errorLockout) {
// SystemUI handles transition from biometric to device credential.
@@ -539,7 +547,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
}
void onAcquired(int sensorId, int acquiredInfo, int vendorCode) {
- if (hasAuthenticated()) {
+ if (hasAuthenticatedAndConfirmed()) {
Slog.d(TAG, "onAcquired after successful auth");
return;
}
@@ -562,7 +570,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
}
void onSystemEvent(int event) {
- if (hasAuthenticated()) {
+ if (hasAuthenticatedAndConfirmed()) {
Slog.d(TAG, "onSystemEvent after successful auth");
return;
}
@@ -579,12 +587,15 @@ public final class AuthSession implements IBinder.DeathRecipient {
void onDialogAnimatedIn(boolean startFingerprintNow) {
if (mState != STATE_AUTH_STARTED && mState != STATE_ERROR_PENDING_SYSUI
- && mState != STATE_AUTH_PAUSED) {
+ && mState != STATE_AUTH_PAUSED && mState != STATE_AUTH_PENDING_CONFIRM) {
Slog.e(TAG, "onDialogAnimatedIn, unexpected state: " + mState);
return;
}
- mState = STATE_AUTH_STARTED_UI_SHOWING;
+ if (mState != STATE_AUTH_PENDING_CONFIRM) {
+ mState = STATE_AUTH_STARTED_UI_SHOWING;
+ }
+
if (startFingerprintNow) {
startAllPreparedFingerprintSensors();
} else {
@@ -600,6 +611,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
if (mState != STATE_AUTH_STARTED
&& mState != STATE_AUTH_STARTED_UI_SHOWING
&& mState != STATE_AUTH_PAUSED
+ && mState != STATE_AUTH_PENDING_CONFIRM
&& mState != STATE_ERROR_PENDING_SYSUI) {
Slog.w(TAG, "onStartFingerprint, started from unexpected state: " + mState);
}
@@ -608,7 +620,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
}
void onTryAgainPressed() {
- if (hasAuthenticated()) {
+ if (hasAuthenticatedAndConfirmed()) {
Slog.d(TAG, "onTryAgainPressed after successful auth");
return;
}
@@ -625,7 +637,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
}
void onAuthenticationSucceeded(int sensorId, boolean strong, byte[] token) {
- if (hasAuthenticated()) {
+ if (hasAuthenticatedAndConfirmed()) {
Slog.d(TAG, "onAuthenticationSucceeded after successful auth");
return;
}
@@ -656,11 +668,17 @@ public final class AuthSession implements IBinder.DeathRecipient {
Slog.e(TAG, "RemoteException", e);
}
- cancelAllSensors(sensor -> sensor.id != sensorId);
+ if (mState == STATE_AUTH_PENDING_CONFIRM) {
+ // Do not cancel Sfps sensors so auth can continue running
+ cancelAllSensors(
+ sensor -> sensor.id != sensorId && !mSfpsSensorIds.contains(sensor.id));
+ } else {
+ cancelAllSensors(sensor -> sensor.id != sensorId);
+ }
}
void onAuthenticationRejected(int sensorId) {
- if (hasAuthenticated()) {
+ if (hasAuthenticatedAndConfirmed()) {
Slog.d(TAG, "onAuthenticationRejected after successful auth");
return;
}
@@ -678,7 +696,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
}
void onAuthenticationTimedOut(int sensorId, int cookie, int error, int vendorCode) {
- if (hasAuthenticated()) {
+ if (hasAuthenticatedAndConfirmed()) {
Slog.d(TAG, "onAuthenticationTimedOut after successful auth");
return;
}
@@ -703,7 +721,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
}
void onDeviceCredentialPressed() {
- if (hasAuthenticated()) {
+ if (hasAuthenticatedAndConfirmed()) {
Slog.d(TAG, "onDeviceCredentialPressed after successful auth");
return;
}
@@ -739,6 +757,10 @@ public final class AuthSession implements IBinder.DeathRecipient {
return mAuthenticatedSensorId != -1;
}
+ private boolean hasAuthenticatedAndConfirmed() {
+ return mAuthenticatedSensorId != -1 && mState == STATE_AUTHENTICATED_PENDING_SYSUI;
+ }
+
private void logOnDialogDismissed(@BiometricPrompt.DismissedReason int reason) {
if (reason == BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED) {
// Explicit auth, authentication confirmed.
@@ -828,6 +850,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
} else {
Slog.e(TAG, "mTokenEscrow is null");
}
+
mClientReceiver.onAuthenticationSucceeded(
Utils.getAuthenticationTypeForResult(reason));
break;
@@ -861,6 +884,16 @@ public final class AuthSession implements IBinder.DeathRecipient {
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
} finally {
+ if (mTokenEscrow != null && mBiometricManager != null) {
+ final byte[] byteToken = new byte[mTokenEscrow.length];
+ for (int i = 0; i < mTokenEscrow.length; i++) {
+ byteToken[i] = mTokenEscrow[i];
+ }
+ mBiometricManager.resetLockoutTimeBound(mToken,
+ mContext.getOpPackageName(),
+ mAuthenticatedSensorId, mUserId, byteToken);
+ }
+
// ensure everything is cleaned up when dismissed
cancelAllSensors();
}
@@ -874,7 +907,7 @@ public final class AuthSession implements IBinder.DeathRecipient {
* @return true if this AuthSession is finished, e.g. should be set to null
*/
boolean onCancelAuthSession(boolean force) {
- if (hasAuthenticated()) {
+ if (hasAuthenticatedAndConfirmed()) {
Slog.d(TAG, "onCancelAuthSession after successful auth");
return true;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 1e2451c2f916..daaafcb61bc5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -242,14 +242,14 @@ public abstract class AuthenticationClient<T, O extends AuthenticateOptions>
byteToken[i] = hardwareAuthToken.get(i);
}
- if (mIsStrongBiometric) {
- mBiometricManager.resetLockoutTimeBound(getToken(),
- getContext().getOpPackageName(),
- getSensorId(), getTargetUserId(), byteToken);
- }
-
// For BP, BiometricService will add the authToken to Keystore.
- if (!isBiometricPrompt() && mIsStrongBiometric) {
+ if (!isBiometricPrompt()) {
+ if (mIsStrongBiometric) {
+ mBiometricManager.resetLockoutTimeBound(getToken(),
+ getContext().getOpPackageName(),
+ getSensorId(), getTargetUserId(), byteToken);
+ }
+
final int result = KeyStoreAuthorization.getInstance().addAuthToken(byteToken);
if (result != 0) {
Slog.d(TAG, "Error adding auth token : " + result);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 266093229186..72d92b974c1a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -209,7 +209,7 @@ public class FingerprintAuthenticationClient
Slog.d(TAG, "Lockout is implemented by the HAL");
return;
}
- if (authenticated) {
+ if (authenticated && !isBiometricPrompt()) {
getLockoutTracker().resetFailedAttemptsForUser(true /* clearAttemptCounter */,
getTargetUserId());
} else {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index 9e8bf0e2a060..69452310f6cc 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -195,6 +195,18 @@ final class InputMethodBindingController {
}
/**
+ * Returns {@link InputMethodInfo} that is queried from {@link #getSelectedMethodId()}.
+ *
+ * @return {@link InputMethodInfo} whose IME ID is the same as {@link #getSelectedMethodId()}.
+ * {@code null} otherwise
+ */
+ @GuardedBy("ImfLock.class")
+ @Nullable
+ InputMethodInfo getSelectedMethod() {
+ return InputMethodSettingsRepository.get(mUserId).getMethodMap().get(mSelectedMethodId);
+ }
+
+ /**
* The token we have made for the currently active input method, to
* identify it in the future.
*/
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 223d54842b16..c4b1f4010af5 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -270,6 +270,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
private static final int MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE = 7000;
private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;
+
+ private static final int INVALID_SUBTYPE_HASHCODE =
+ InputMethodSettings.INVALID_SUBTYPE_HASHCODE;
+
private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher";
private static final String HANDLER_THREAD_NAME = "android.imms";
private static final String PACKAGE_MONITOR_THREAD_NAME = "android.imms2";
@@ -322,7 +326,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
private final Handler mHandler;
@NonNull
- private final Handler mPackageMonitorHandler;
+ private final Handler mIoHandler;
@MultiUserUnawareField
@UserIdInt
@@ -1237,7 +1241,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
Context context,
boolean experimentalConcurrentMultiUserModeEnabled,
@Nullable ServiceThread serviceThreadForTesting,
- @Nullable ServiceThread packageMonitorThreadForTesting,
+ @Nullable ServiceThread ioThreadForTesting,
@Nullable IntFunction<InputMethodBindingController> bindingControllerForTesting) {
synchronized (ImfLock.class) {
mExperimentalConcurrentMultiUserModeEnabled =
@@ -1258,15 +1262,15 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
thread.start();
mHandler = Handler.createAsync(thread.getLooper(), this);
{
- final ServiceThread packageMonitorThread =
- packageMonitorThreadForTesting != null
- ? packageMonitorThreadForTesting
+ final ServiceThread ioThread =
+ ioThreadForTesting != null
+ ? ioThreadForTesting
: new ServiceThread(
PACKAGE_MONITOR_THREAD_NAME,
Process.THREAD_PRIORITY_FOREGROUND,
true /* allowIo */);
- packageMonitorThread.start();
- mPackageMonitorHandler = Handler.createAsync(packageMonitorThread.getLooper());
+ ioThread.start();
+ mIoHandler = Handler.createAsync(ioThread.getLooper());
}
SystemLocaleWrapper.onStart(context, this::onActionLocaleChanged, mHandler);
mImeTrackerService = new ImeTrackerService(serviceThreadForTesting != null
@@ -1537,7 +1541,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
}
}, "Lazily initialize IMMS#mImeDrawsImeNavBarRes");
- mMyPackageMonitor.register(mContext, UserHandle.ALL, mPackageMonitorHandler);
+ mMyPackageMonitor.register(mContext, UserHandle.ALL, mIoHandler);
mSettingsObserver.registerContentObserverLocked(currentUserId);
final IntentFilter broadcastFilterForAllUsers = new IntentFilter();
@@ -2005,17 +2009,18 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
@GuardedBy("ImfLock.class")
@NonNull
InputBindResult attachNewInputLocked(@StartInputReason int startInputReason, boolean initial) {
+ final int userId = mCurrentUserId;
+ final var bindingController = getInputMethodBindingController(userId);
if (!mBoundToMethod) {
- getCurMethodLocked().bindInput(mCurClient.mBinding);
+ bindingController.getCurMethod().bindInput(mCurClient.mBinding);
mBoundToMethod = true;
}
final boolean restarting = !initial;
final Binder startInputToken = new Binder();
- final var bindingController = getInputMethodBindingController(mCurrentUserId);
final StartInputInfo info = new StartInputInfo(mCurrentUserId,
- getCurTokenLocked(),
- getCurTokenDisplayIdLocked(), bindingController.getCurId(), startInputReason,
+ bindingController.getCurToken(), bindingController.getCurTokenDisplayId(),
+ bindingController.getCurId(), startInputReason,
restarting, UserHandle.getUserId(mCurClient.mUid),
mCurClient.mSelfReportedDisplayId, mImeBindingState.mFocusedWindow, mCurEditorInfo,
mImeBindingState.mFocusedWindowSoftInputMode,
@@ -2028,9 +2033,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
// same-user scenarios.
// That said ignoring cross-user scenario will never affect IMEs that do not have
// INTERACT_ACROSS_USERS(_FULL) permissions, which is actually almost always the case.
- if (mCurrentUserId == UserHandle.getUserId(
- mCurClient.mUid)) {
- mPackageManagerInternal.grantImplicitAccess(mCurrentUserId, null /* intent */,
+ if (userId == UserHandle.getUserId(mCurClient.mUid)) {
+ mPackageManagerInternal.grantImplicitAccess(userId, null /* intent */,
UserHandle.getAppId(bindingController.getCurMethodUid()),
mCurClient.mUid, true /* direct */);
}
@@ -2060,7 +2064,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
}
final var curId = bindingController.getCurId();
- final InputMethodInfo curInputMethodInfo = InputMethodSettingsRepository.get(mCurrentUserId)
+ final InputMethodInfo curInputMethodInfo = InputMethodSettingsRepository.get(userId)
.getMethodMap().get(curId);
final boolean suppressesSpellChecker =
curInputMethodInfo != null && curInputMethodInfo.suppressesSpellChecker();
@@ -3106,6 +3110,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
// If subtype is null, try to find the most applicable one from
// getCurrentInputMethodSubtype.
subtypeId = NOT_A_SUBTYPE_ID;
+ // TODO(b/347083680): The method below has questionable behaviors.
newSubtype = getCurrentInputMethodSubtypeLocked();
if (newSubtype != null) {
for (int i = 0; i < subtypeCount; ++i) {
@@ -4192,10 +4197,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
@GuardedBy("ImfLock.class")
private boolean switchToNextInputMethodLocked(@Nullable IBinder token, boolean onlyCurrentIme) {
- final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+ final int userId = mCurrentUserId;
+ final var currentImi = getInputMethodBindingController(userId).getSelectedMethod();
final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
- onlyCurrentIme, settings.getMethodMap().get(getSelectedMethodIdLocked()),
- mCurrentSubtype);
+ onlyCurrentIme, currentImi, mCurrentSubtype);
if (nextSubtype == null) {
return false;
}
@@ -4210,10 +4215,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
if (!calledWithValidTokenLocked(token)) {
return false;
}
- final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+ final int userId = mCurrentUserId;
+ final var currentImi = getInputMethodBindingController(userId).getSelectedMethod();
final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
- false /* onlyCurrentIme */,
- settings.getMethodMap().get(getSelectedMethodIdLocked()), mCurrentSubtype);
+ false /* onlyCurrentIme */, currentImi, mCurrentSubtype);
return nextSubtype != null;
}
}
@@ -4648,8 +4653,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
if (mCurrentUserId != mSwitchingController.getUserId()) {
return;
}
- final InputMethodInfo imi = InputMethodSettingsRepository.get(mCurrentUserId)
- .getMethodMap().get(getSelectedMethodIdLocked());
+ final var imi = getInputMethodBindingController(mCurrentUserId).getSelectedMethod();
if (imi != null) {
mSwitchingController.onUserActionLocked(imi, mCurrentSubtype);
}
@@ -5443,20 +5447,26 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
mCurrentSubtype);
// Set Subtype here
+ final int newSubtypeHashcode;
+ final InputMethodSubtype newSubtype;
if (imi == null || subtypeId < 0) {
- settings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
- mCurrentSubtype = null;
+ newSubtypeHashcode = INVALID_SUBTYPE_HASHCODE;
+ newSubtype = null;
} else {
if (subtypeId < imi.getSubtypeCount()) {
InputMethodSubtype subtype = imi.getSubtypeAt(subtypeId);
- settings.putSelectedSubtype(subtype.hashCode());
- mCurrentSubtype = subtype;
+ newSubtypeHashcode = subtype.hashCode();
+ newSubtype = subtype;
} else {
- settings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
+ // TODO(b/347093491): Probably this should be determined from the new subtype.
+ newSubtypeHashcode = INVALID_SUBTYPE_HASHCODE;
// If the subtype is not specified, choose the most applicable one
- mCurrentSubtype = getCurrentInputMethodSubtypeLocked();
+ // TODO(b/347083680): The method below has questionable behaviors.
+ newSubtype = getCurrentInputMethodSubtypeLocked();
}
}
+ mCurrentSubtype = newSubtype;
+ settings.putSelectedSubtype(newSubtypeHashcode);
notifyInputMethodSubtypeChangedLocked(settings.getUserId(), imi, mCurrentSubtype);
if (!setSubtypeOnly) {
@@ -5506,6 +5516,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
}
synchronized (ImfLock.class) {
if (mCurrentUserId == userId) {
+ // TODO(b/347083680): The method below has questionable behaviors.
return getCurrentInputMethodSubtypeLocked();
}
@@ -5523,6 +5534,14 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
*
* <p>TODO: Address code duplication between this and
* {@link InputMethodSettings#getCurrentInputMethodSubtypeForNonCurrentUsers()}.</p>
+ *
+ * <p>Also this method has had questionable behaviors:</p>
+ * <ul>
+ * <li>Calling this method can update {@link #mCurrentSubtype}.</li>
+ * <li>This method may return {@link #mCurrentSubtype} as-is, even if it does not belong
+ * to the current IME.</li>
+ * </ul>
+ * <p>TODO(b/347083680): Address above issues.</p>
*/
@GuardedBy("ImfLock.class")
InputMethodSubtype getCurrentInputMethodSubtypeLocked() {
@@ -5906,27 +5925,36 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
synchronized (ImfLock.class) {
final int uid = Binder.getCallingUid();
- if (getSelectedMethodIdLocked() == null) {
+ final int imeUserId = UserHandle.getUserId(uid);
+ if (imeUserId != mCurrentUserId) {
+ // Currently concurrent multi-user is not supported here due to the remaining
+ // dependency on mCurEditorInfo and mCurClient.
+ // TODO(b/341558132): Remove this early-exit once it becomes multi-user ready.
+ Slog.i(TAG, "Ignoring createInputContentUriToken due to user ID mismatch."
+ + " imeUserId=" + imeUserId + " mCurrentUserId=" + mCurrentUserId);
return null;
}
- if (getCurTokenLocked() != token) {
- Slog.e(TAG, "Ignoring createInputContentUriToken mCurToken=" + getCurTokenLocked()
- + " token=" + token);
+ final var bindingController = getInputMethodBindingController(imeUserId);
+ if (bindingController.getSelectedMethodId() == null) {
+ return null;
+ }
+ if (bindingController.getCurToken() != token) {
+ Slog.e(TAG, "Ignoring createInputContentUriToken mCurToken="
+ + bindingController.getCurToken() + " token=" + token);
return null;
}
// We cannot simply distinguish a bad IME that reports an arbitrary package name from
// an unfortunate IME whose internal state is already obsolete due to the asynchronous
// nature of our system. Let's compare it with our internal record.
- final var curPackageName = mCurEditorInfo != null
- ? mCurEditorInfo.packageName : null;
+ // TODO(b/341558132): Use "imeUserId" to query per-user "curEditorInfo"
+ final var curPackageName = mCurEditorInfo != null ? mCurEditorInfo.packageName : null;
if (!TextUtils.equals(curPackageName, packageName)) {
Slog.e(TAG, "Ignoring createInputContentUriToken mCurEditorInfo.packageName="
+ curPackageName + " packageName=" + packageName);
return null;
}
- // This user ID can never bee spoofed.
- final int imeUserId = UserHandle.getUserId(uid);
- // This user ID can never bee spoofed.
+ // This user ID can never be spoofed.
+ // TODO(b/341558132): Use "imeUserId" to query per-user "curClient"
final int appUserId = UserHandle.getUserId(mCurClient.mUid);
// This user ID may be invalid if "contentUri" embedded an invalid user ID.
final int contentUriOwnerUserId = ContentProvider.getUserIdFromUri(contentUri,
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSettings.java b/services/core/java/com/android/server/inputmethod/InputMethodSettings.java
index 7ce4074bb1d0..5569e1e5211e 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodSettings.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSettings.java
@@ -58,7 +58,7 @@ final class InputMethodSettings {
* used {@code -1} here. We cannot change this value as it's already saved into secure settings.
* </p>
*/
- private static final int INVALID_SUBTYPE_HASHCODE = -1;
+ static final int INVALID_SUBTYPE_HASHCODE = -1;
/**
* A string code that represents "no subtype" when a subtype hashcode is used.
*
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSettingsRepository.java b/services/core/java/com/android/server/inputmethod/InputMethodSettingsRepository.java
index 60b9a4cfe840..68924b5f370f 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodSettingsRepository.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSettingsRepository.java
@@ -78,7 +78,7 @@ final class InputMethodSettingsRepository {
userId,
AdditionalSubtypeMapRepository.get(userId),
DirectBootAwareness.AUTO);
- sPerUserMap.put(userId, settings);
+ put(userId, settings);
}
}
});
diff --git a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
index 880787e8664c..12495bb4f2cc 100644
--- a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
+++ b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
@@ -619,7 +619,18 @@ class GnssNetworkConnectivityHandler {
ServiceState state = telephonyManager.getServiceState();
if (state != null && state.isUsingNonTerrestrialNetwork()) {
networkRequestBuilder.removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
- networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_SATELLITE);
+ try {
+ networkRequestBuilder.addTransportType(NetworkCapabilities
+ .TRANSPORT_SATELLITE);
+ networkRequestBuilder.removeCapability(NetworkCapabilities
+ .NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED);
+ } catch (IllegalArgumentException ignored) {
+ // In case TRANSPORT_SATELLITE or NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED
+ // are not recognized, meaning an old connectivity module runs on new
+ // android in which case no network with such capabilities will be brought
+ // up, so it's safe to ignore the exception.
+ // TODO: Can remove the try-catch in next quarter release.
+ }
}
}
}
diff --git a/services/core/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java b/services/core/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java
index 6578853909c9..b532d5a8f8fc 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java
+++ b/services/core/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java
@@ -21,9 +21,9 @@ import android.os.Bundle;
import android.os.PersistableBundle;
import android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService;
import android.util.Slog;
+import android.util.Base64;
import java.io.IOException;
-import java.util.Base64;
import java.util.Comparator;
import java.util.List;
import java.util.TreeSet;
@@ -53,7 +53,7 @@ public class InferenceInfoStore {
String infoBytesBase64String = pb.getString(
OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY);
if (infoBytesBase64String != null) {
- byte[] infoBytes = Base64.getDecoder().decode(infoBytesBase64String);
+ byte[] infoBytes = Base64.decode(infoBytesBase64String, Base64.DEFAULT);
com.android.server.ondeviceintelligence.nano.InferenceInfo inferenceInfo =
com.android.server.ondeviceintelligence.nano.InferenceInfo.parseFrom(
infoBytes);
@@ -84,7 +84,9 @@ public class InferenceInfoStore {
}
private synchronized void add(com.android.server.ondeviceintelligence.nano.InferenceInfo info) {
- while (System.currentTimeMillis() - inferenceInfos.first().getStartTimeMs() > maxAgeMs) {
+ while (!inferenceInfos.isEmpty()
+ && System.currentTimeMillis() - inferenceInfos.first().getStartTimeMs()
+ > maxAgeMs) {
inferenceInfos.pollFirst();
}
inferenceInfos.add(toInferenceInfo(info));
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 06065638dc11..00e9d8d595f1 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -33,6 +33,7 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
import static android.content.pm.PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SPLIT;
import static android.content.pm.PackageManager.INSTALL_FAILED_PRE_APPROVAL_NOT_AVAILABLE;
+import static android.content.pm.PackageManager.INSTALL_FAILED_SESSION_INVALID;
import static android.content.pm.PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
import static android.content.pm.PackageManager.INSTALL_STAGED;
@@ -3459,11 +3460,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
- if (mHasAppMetadataFile && !getStagedAppMetadataFile().exists()) {
- throw new PackageManagerException(INSTALL_FAILED_VERIFICATION_FAILURE,
- "App metadata file expected but not found in " + stageDir.getAbsolutePath());
- }
-
final List<ApkLite> addedFiles = getAddedApkLitesLocked();
if (addedFiles.isEmpty()
&& (removeSplitList.size() == 0 || mHasAppMetadataFile)) {
@@ -3593,6 +3589,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ File stagedAppMetadataFile = isIncrementalInstallation()
+ ? getTmpAppMetadataFile() : getStagedAppMetadataFile();
+ if (mHasAppMetadataFile && !stagedAppMetadataFile.exists()) {
+ throw new PackageManagerException(INSTALL_FAILED_SESSION_INVALID,
+ "App metadata file expected but not found in " + stageDir.getAbsolutePath());
+ }
+
if (isIncrementalInstallation()) {
if (!isIncrementalInstallationAllowed(existingPkgSetting)) {
throw new PackageManagerException(
@@ -3601,8 +3604,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
// Since we moved the staged app metadata file so that incfs can be initialized, lets
// now move it back.
- File appMetadataFile = getTmpAppMetadataFile();
- if (appMetadataFile.exists()) {
+ if (mHasAppMetadataFile) {
+ File appMetadataFile = getTmpAppMetadataFile();
final IncrementalFileStorages incrementalFileStorages =
getIncrementalFileStorages();
try {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 66a93d7ffc47..c0b8034b9a56 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3999,7 +3999,9 @@ public class PackageManagerService implements PackageSender, TestUtilityService
final PackageMetrics.ComponentStateMetrics componentStateMetrics =
new PackageMetrics.ComponentStateMetrics(setting,
UserHandle.getUid(userId, packageSetting.getAppId()),
- packageSetting.getEnabled(userId), callingUid);
+ setting.isComponent() ? computer.getComponentEnabledSettingInternal(
+ setting.getComponentName(), callingUid, userId)
+ : packageSetting.getEnabled(userId), callingUid);
if (!setEnabledSettingInternalLocked(computer, packageSetting, setting, userId,
callingPackage)) {
continue;
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java
index 0052d4f28b93..7f2476979635 100644
--- a/services/core/java/com/android/server/power/ThermalManagerService.java
+++ b/services/core/java/com/android/server/power/ThermalManagerService.java
@@ -543,7 +543,7 @@ public class ThermalManagerService extends SystemService {
if (!mHalReady.get()) {
FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_HEADROOM_CALLED, getCallingUid(),
FrameworkStatsLog.THERMAL_HEADROOM_CALLED__API_STATUS__HAL_NOT_READY,
- Float.NaN);
+ Float.NaN, forecastSeconds);
return Float.NaN;
}
@@ -553,7 +553,7 @@ public class ThermalManagerService extends SystemService {
}
FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_HEADROOM_CALLED, getCallingUid(),
FrameworkStatsLog.THERMAL_HEADROOM_CALLED__API_STATUS__INVALID_ARGUMENT,
- Float.NaN);
+ Float.NaN, forecastSeconds);
return Float.NaN;
}
@@ -1778,7 +1778,7 @@ public class ThermalManagerService extends SystemService {
FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_HEADROOM_CALLED,
Binder.getCallingUid(),
FrameworkStatsLog.THERMAL_HEADROOM_CALLED__API_STATUS__NO_TEMPERATURE,
- Float.NaN);
+ Float.NaN, forecastSeconds);
return Float.NaN;
}
@@ -1789,7 +1789,7 @@ public class ThermalManagerService extends SystemService {
FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_HEADROOM_CALLED,
Binder.getCallingUid(),
THERMAL_HEADROOM_CALLED__API_STATUS__NO_TEMPERATURE_THRESHOLD,
- Float.NaN);
+ Float.NaN, forecastSeconds);
return Float.NaN;
}
@@ -1828,12 +1828,12 @@ public class ThermalManagerService extends SystemService {
FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_HEADROOM_CALLED,
Binder.getCallingUid(),
THERMAL_HEADROOM_CALLED__API_STATUS__NO_TEMPERATURE_THRESHOLD,
- Float.NaN);
+ Float.NaN, forecastSeconds);
} else {
FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_HEADROOM_CALLED,
Binder.getCallingUid(),
FrameworkStatsLog.THERMAL_HEADROOM_CALLED__API_STATUS__SUCCESS,
- maxNormalized);
+ maxNormalized, forecastSeconds);
}
return maxNormalized;
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 5159fc4009f7..d9dc7ba9ad12 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -37,6 +37,7 @@ import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
+import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE;
import static android.app.CameraCompatTaskInfo.cameraCompatControlStateToString;
import static android.app.WaitResult.INVALID_DELAY;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
@@ -46,6 +47,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
@@ -855,7 +857,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
@CameraCompatControlState
private int mCameraCompatControlState = CAMERA_COMPAT_CONTROL_HIDDEN;
-
// The callback that allows to ask the calling View to apply the treatment for stretched
// issues affecting camera viewfinders when the user clicks on the camera compat control.
@Nullable
@@ -6562,10 +6563,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Schedule an idle timeout in case the app doesn't do it for us.
mTaskSupervisor.scheduleIdleTimeout(this);
- mTaskSupervisor.mStoppingActivities.remove(this);
- if (getDisplayArea().allResumedActivitiesComplete()) {
- mRootWindowContainer.executeAppTransitionForAllDisplay();
- }
+ mTaskSupervisor.reportResumedActivityLocked(this);
resumeKeyDispatchingLocked();
final Task rootTask = getRootTask();
@@ -8559,11 +8557,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// and back which can cause visible issues (see b/184078928).
final int parentWindowingMode =
newParentConfiguration.windowConfiguration.getWindowingMode();
+ final boolean isInCameraCompatFreeform = parentWindowingMode == WINDOWING_MODE_FREEFORM
+ && mLetterboxUiController.getFreeformCameraCompatMode()
+ != CAMERA_COMPAT_FREEFORM_NONE;
// Bubble activities should always fill their parent and should not be letterboxed.
final boolean isFixedOrientationLetterboxAllowed = !getLaunchedFromBubble()
&& (parentWindowingMode == WINDOWING_MODE_MULTI_WINDOW
|| parentWindowingMode == WINDOWING_MODE_FULLSCREEN
+ || isInCameraCompatFreeform
// When starting to switch between PiP and fullscreen, the task is pinned
// and the activity is fullscreen. But only allow to apply letterbox if the
// activity is exiting PiP because an entered PiP should fill the task.
@@ -8701,9 +8703,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (!mOptOutEdgeToEdge && (!mResolveConfigHint.mUseOverrideInsetsForConfig
|| getCompatDisplayInsets() != null
|| (isFloating(parentWindowingMode)
- // Check the windowing mode of activity as well in case it is switching
- // between PiP and fullscreen.
- && isFloating(inOutConfig.windowConfiguration.getWindowingMode()))
+ // Check the requested windowing mode of activity as well in case it is
+ // switching between PiP and fullscreen.
+ && (inOutConfig.windowConfiguration.getWindowingMode()
+ == WINDOWING_MODE_UNDEFINED
+ || isFloating(inOutConfig.windowConfiguration.getWindowingMode())))
|| rotation == ROTATION_UNDEFINED)) {
// If the insets configuration decoupled logic is not enabled for the app, or the app
// already has a compat override, or the context doesn't contain enough info to
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index b6e6991656f2..3867d2d229ea 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -2064,6 +2064,21 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
}
}
+ boolean reportResumedActivityLocked(ActivityRecord r) {
+ // A resumed activity cannot be stopping. remove from list
+ mStoppingActivities.remove(r);
+
+ final Task rootTask = r.getRootTask();
+ if (rootTask.getDisplayArea().allResumedActivitiesComplete()) {
+ mRootWindowContainer.ensureActivitiesVisible();
+ // Make sure activity & window visibility should be identical
+ // for all displays in this stage.
+ mRootWindowContainer.executeAppTransitionForAllDisplay();
+ return true;
+ }
+ return false;
+ }
+
// Called when WindowManager has finished animating the launchingBehind activity to the back.
private void handleLaunchTaskBehindCompleteLocked(ActivityRecord r) {
final Task task = r.getTask();
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 0febec9169c0..1ce324f68569 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -19,6 +19,7 @@ package com.android.server.wm;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_APP_PROGRESS_GENERATION_ALLOWED;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
@@ -201,6 +202,8 @@ class BackNavigationController {
infoBuilder.setOnBackInvokedCallback(callbackInfo.getCallback());
infoBuilder.setAnimationCallback(callbackInfo.isAnimationCallback());
infoBuilder.setTouchableRegion(window.getFrame());
+ infoBuilder.setAppProgressAllowed((window.getAttrs().privateFlags
+ & PRIVATE_FLAG_APP_PROGRESS_GENERATION_ALLOWED) != 0);
mNavigationMonitor.startMonitor(window, navigationObserver);
ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation currentTask=%s, "
@@ -1366,8 +1369,6 @@ class BackNavigationController {
? task.getSurfaceControl()
: mAdaptors[0].mTarget.getSurfaceControl());
}
- // remove starting surface.
- mStartingSurface = null;
}
}
@@ -1384,7 +1385,10 @@ class BackNavigationController {
.removeWindowlessStartingSurface(mRequestedStartingSurfaceId,
!openTransitionMatch);
mRequestedStartingSurfaceId = INVALID_TASK_ID;
- mStartingSurface = null;
+ if (mStartingSurface != null && mStartingSurface.isValid()) {
+ mStartingSurface.release();
+ mStartingSurface = null;
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java b/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
new file mode 100644
index 000000000000..0c751cfe4f46
--- /dev/null
+++ b/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
+
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.annotation.NonNull;
+import android.app.CameraCompatTaskInfo;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.ProtoLogGroup;
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.window.flags.Flags;
+
+/**
+ * Policy for camera compatibility freeform treatment.
+ *
+ * <p>The treatment is applied to a fixed-orientation camera activity in freeform windowing mode.
+ * The treatment letterboxes or pillarboxes the activity to the expected orientation and provides
+ * changes to the camera and display orientation signals to match those expected on a portrait
+ * device in that orientation (for example, on a standard phone).
+ */
+final class CameraCompatFreeformPolicy implements CameraStateMonitor.CameraCompatStateListener,
+ ActivityRefresher.Evaluator {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "CameraCompatFreeformPolicy" : TAG_WM;
+
+ @NonNull
+ private final DisplayContent mDisplayContent;
+ @NonNull
+ private final ActivityRefresher mActivityRefresher;
+ @NonNull
+ private final CameraStateMonitor mCameraStateMonitor;
+
+ private boolean mIsCameraCompatTreatmentPending = false;
+
+ CameraCompatFreeformPolicy(@NonNull DisplayContent displayContent,
+ @NonNull CameraStateMonitor cameraStateMonitor,
+ @NonNull ActivityRefresher activityRefresher) {
+ mDisplayContent = displayContent;
+ mCameraStateMonitor = cameraStateMonitor;
+ mActivityRefresher = activityRefresher;
+ }
+
+ void start() {
+ mCameraStateMonitor.addCameraStateListener(this);
+ mActivityRefresher.addEvaluator(this);
+ }
+
+ /** Releases camera callback listener. */
+ void dispose() {
+ mCameraStateMonitor.removeCameraStateListener(this);
+ mActivityRefresher.removeEvaluator(this);
+ }
+
+ // Refreshing only when configuration changes after rotation or camera split screen aspect ratio
+ // treatment is enabled.
+ @Override
+ public boolean shouldRefreshActivity(@NonNull ActivityRecord activity,
+ @NonNull Configuration newConfig,
+ @NonNull Configuration lastReportedConfig) {
+ return isTreatmentEnabledForActivity(activity) && mIsCameraCompatTreatmentPending;
+ }
+
+ /**
+ * Whether activity is eligible for camera compatibility free-form treatment.
+ *
+ * <p>The treatment is applied to a fixed-orientation camera activity in free-form windowing
+ * mode. The treatment letterboxes or pillarboxes the activity to the expected orientation and
+ * provides changes to the camera and display orientation signals to match those expected on a
+ * portrait device in that orientation (for example, on a standard phone).
+ *
+ * <p>The treatment is enabled when the following conditions are met:
+ * <ul>
+ * <li>Property gating the camera compatibility free-form treatment is enabled.
+ * <li>Activity isn't opted out by the device manufacturer with override.
+ * </ul>
+ */
+ @VisibleForTesting
+ boolean shouldApplyFreeformTreatmentForCameraCompat(@NonNull ActivityRecord activity) {
+ return Flags.cameraCompatForFreeform() && !activity.info.isChangeEnabled(
+ ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT);
+ }
+
+ @Override
+ public boolean onCameraOpened(@NonNull ActivityRecord cameraActivity,
+ @NonNull String cameraId) {
+ if (!isTreatmentEnabledForActivity(cameraActivity)) {
+ return false;
+ }
+ final int existingCameraCompatMode =
+ cameraActivity.mLetterboxUiController.getFreeformCameraCompatMode();
+ final int newCameraCompatMode = getCameraCompatMode(cameraActivity);
+ if (newCameraCompatMode != existingCameraCompatMode) {
+ mIsCameraCompatTreatmentPending = true;
+ cameraActivity.mLetterboxUiController.setFreeformCameraCompatMode(newCameraCompatMode);
+ forceUpdateActivityAndTask(cameraActivity);
+ return true;
+ } else {
+ mIsCameraCompatTreatmentPending = false;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onCameraClosed(@NonNull ActivityRecord cameraActivity,
+ @NonNull String cameraId) {
+ if (isActivityForCameraIdRefreshing(cameraId)) {
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_STATES,
+ "Display id=%d is notified that Camera %s is closed but activity is"
+ + " still refreshing. Rescheduling an update.",
+ mDisplayContent.mDisplayId, cameraId);
+ return false;
+ }
+ cameraActivity.mLetterboxUiController.setFreeformCameraCompatMode(
+ CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE);
+ forceUpdateActivityAndTask(cameraActivity);
+ mIsCameraCompatTreatmentPending = false;
+ return true;
+ }
+
+ private void forceUpdateActivityAndTask(ActivityRecord cameraActivity) {
+ cameraActivity.recomputeConfiguration();
+ cameraActivity.updateReportedConfigurationAndSend();
+ Task cameraTask = cameraActivity.getTask();
+ if (cameraTask != null) {
+ cameraTask.dispatchTaskInfoChangedIfNeeded(/* force= */ true);
+ }
+ }
+
+ private static int getCameraCompatMode(@NonNull ActivityRecord topActivity) {
+ return switch (topActivity.getRequestedConfigurationOrientation()) {
+ case ORIENTATION_PORTRAIT -> CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_PORTRAIT;
+ case ORIENTATION_LANDSCAPE -> CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_LANDSCAPE;
+ default -> CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE;
+ };
+ }
+
+ /**
+ * Whether camera compat treatment is applicable for the given activity, ignoring its windowing
+ * mode.
+ *
+ * <p>Conditions that need to be met:
+ * <ul>
+ * <li>Treatment is enabled.
+ * <li>Camera is active for the package.
+ * <li>The app has a fixed orientation.
+ * <li>The app is in freeform windowing mode.
+ * </ul>
+ */
+ private boolean isTreatmentEnabledForActivity(@NonNull ActivityRecord activity) {
+ int orientation = activity.getRequestedConfigurationOrientation();
+ return shouldApplyFreeformTreatmentForCameraCompat(activity)
+ && mCameraStateMonitor.isCameraRunningForActivity(activity)
+ && orientation != ORIENTATION_UNDEFINED
+ && activity.inFreeformWindowingMode()
+ // "locked" and "nosensor" values are often used by camera apps that can't
+ // handle dynamic changes so we shouldn't force-letterbox them.
+ && activity.getRequestedOrientation() != SCREEN_ORIENTATION_NOSENSOR
+ && activity.getRequestedOrientation() != SCREEN_ORIENTATION_LOCKED
+ // TODO(b/332665280): investigate whether we can support activity embedding.
+ && !activity.isEmbedded();
+ }
+
+ private boolean isActivityForCameraIdRefreshing(@NonNull String cameraId) {
+ final ActivityRecord topActivity = mDisplayContent.topRunningActivity(
+ /* considerKeyguardState= */ true);
+ if (topActivity == null || !isTreatmentEnabledForActivity(topActivity)
+ || mCameraStateMonitor.isCameraWithIdRunningForActivity(topActivity, cameraId)) {
+ return false;
+ }
+ return topActivity.mLetterboxUiController.isRefreshRequested();
+ }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 87ee5d8f7f13..ad711cb2af31 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -263,6 +263,7 @@ import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.utils.RegionUtils;
import com.android.server.wm.utils.RotationCache;
import com.android.server.wm.utils.WmDisplayCutout;
+import com.android.window.flags.Flags;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -477,6 +478,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
@Nullable
final DisplayRotationCompatPolicy mDisplayRotationCompatPolicy;
@Nullable
+ final CameraCompatFreeformPolicy mCameraCompatFreeformPolicy;
+ @Nullable
final CameraStateMonitor mCameraStateMonitor;
@Nullable
final ActivityRefresher mActivityRefresher;
@@ -683,7 +686,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
*/
private InputTarget mLastImeInputTarget;
-
/**
* Tracks the windowToken of the input method input target and the corresponding
* {@link WindowContainerListener} for monitoring changes (e.g. the requested visibility
@@ -1233,11 +1235,26 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// without the need to restart the device.
final boolean shouldCreateDisplayRotationCompatPolicy =
mWmService.mLetterboxConfiguration.isCameraCompatTreatmentEnabledAtBuildTime();
- if (shouldCreateDisplayRotationCompatPolicy) {
+ final boolean shouldCreateCameraCompatFreeformPolicy = Flags.cameraCompatForFreeform()
+ && DesktopModeLaunchParamsModifier.canEnterDesktopMode(mWmService.mContext);
+ if (shouldCreateDisplayRotationCompatPolicy || shouldCreateCameraCompatFreeformPolicy) {
mCameraStateMonitor = new CameraStateMonitor(this, mWmService.mH);
mActivityRefresher = new ActivityRefresher(mWmService, mWmService.mH);
- mDisplayRotationCompatPolicy = new DisplayRotationCompatPolicy(
- this, mCameraStateMonitor, mActivityRefresher);
+ if (shouldCreateDisplayRotationCompatPolicy) {
+ mDisplayRotationCompatPolicy = new DisplayRotationCompatPolicy(this,
+ mCameraStateMonitor, mActivityRefresher);
+ mDisplayRotationCompatPolicy.start();
+ } else {
+ mDisplayRotationCompatPolicy = null;
+ }
+
+ if (shouldCreateCameraCompatFreeformPolicy) {
+ mCameraCompatFreeformPolicy = new CameraCompatFreeformPolicy(this,
+ mCameraStateMonitor, mActivityRefresher);
+ mCameraCompatFreeformPolicy.start();
+ } else {
+ mCameraCompatFreeformPolicy = null;
+ }
mCameraStateMonitor.startListeningToCameraState();
} else {
@@ -1245,9 +1262,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mCameraStateMonitor = null;
mActivityRefresher = null;
mDisplayRotationCompatPolicy = null;
+ mCameraCompatFreeformPolicy = null;
}
-
mRotationReversionController = new DisplayRotationReversionController(this);
mInputMonitor = new InputMonitor(mWmService, this);
@@ -3350,6 +3367,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
if (mDisplayRotationCompatPolicy != null) {
mDisplayRotationCompatPolicy.dispose();
}
+
+ if (mCameraCompatFreeformPolicy != null) {
+ mCameraCompatFreeformPolicy.dispose();
+ }
+
if (mCameraStateMonitor != null) {
mCameraStateMonitor.dispose();
}
diff --git a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
index e0cc064fcacc..6ecafdb03d20 100644
--- a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
@@ -80,8 +80,11 @@ final class DisplayRotationCompatPolicy implements CameraStateMonitor.CameraComp
mDisplayContent = displayContent;
mWmService = displayContent.mWmService;
mCameraStateMonitor = cameraStateMonitor;
- mCameraStateMonitor.addCameraStateListener(this);
mActivityRefresher = activityRefresher;
+ }
+
+ void start() {
+ mCameraStateMonitor.addCameraStateListener(this);
mActivityRefresher.addEvaluator(this);
}
@@ -365,7 +368,7 @@ final class DisplayRotationCompatPolicy implements CameraStateMonitor.CameraComp
}
// TODO(b/336474959): Do we need cameraId here?
- private boolean isActivityForCameraIdRefreshing(String cameraId) {
+ private boolean isActivityForCameraIdRefreshing(@NonNull String cameraId) {
final ActivityRecord topActivity = mDisplayContent.topRunningActivity(
/* considerKeyguardState= */ true);
if (!isTreatmentEnabledForActivity(topActivity)
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 5e93e8930bab..9a513752dee2 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.pm.ActivityInfo.FORCE_NON_RESIZE_APP;
import static android.content.pm.ActivityInfo.FORCE_RESIZE_APP;
@@ -103,6 +104,7 @@ import static com.android.server.wm.LetterboxConfiguration.letterboxBackgroundTy
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager.TaskDescription;
+import android.app.CameraCompatTaskInfo.FreeformCameraCompatMode;
import android.content.pm.ActivityInfo.ScreenOrientation;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
@@ -231,6 +233,9 @@ final class LetterboxUiController {
private boolean mDoubleTapEvent;
+ @FreeformCameraCompatMode
+ private int mFreeformCameraCompatMode = CAMERA_COMPAT_FREEFORM_NONE;
+
LetterboxUiController(WindowManagerService wmService, ActivityRecord activityRecord) {
mLetterboxConfiguration = wmService.mLetterboxConfiguration;
// Given activityRecord may not be fully constructed since LetterboxUiController
@@ -711,6 +716,15 @@ final class LetterboxUiController {
.isTreatmentEnabledForActivity(mActivityRecord);
}
+ @FreeformCameraCompatMode
+ int getFreeformCameraCompatMode() {
+ return mFreeformCameraCompatMode;
+ }
+
+ void setFreeformCameraCompatMode(@FreeformCameraCompatMode int freeformCameraCompatMode) {
+ mFreeformCameraCompatMode = freeformCameraCompatMode;
+ }
+
private boolean isCompatChangeEnabled(long overrideChangeId) {
return mActivityRecord.info.isChangeEnabled(overrideChangeId);
}
@@ -871,12 +885,14 @@ final class LetterboxUiController {
// Check if we are in the given pose and in fullscreen mode.
// Note that we check the task rather than the parent as with ActivityEmbedding the parent might
// be a TaskFragment, and its windowing mode is always MULTI_WINDOW, even if the task is
- // actually fullscreen.
+ // actually fullscreen. If display is still in transition e.g. unfolding, don't return true
+ // for HALF_FOLDED state or app will flicker.
private boolean isDisplayFullScreenAndInPosture(boolean isTabletop) {
Task task = mActivityRecord.getTask();
return mActivityRecord.mDisplayContent != null && task != null
&& mActivityRecord.mDisplayContent.getDisplayRotation().isDeviceInPosture(
DeviceStateController.DeviceState.HALF_FOLDED, isTabletop)
+ && !mActivityRecord.mDisplayContent.inTransition()
&& task.getWindowingMode() == WINDOWING_MODE_FULLSCREEN;
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 54ba47eeb441..f5ab38f72b54 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1905,7 +1905,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// Don't do recursive work.
return;
}
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RWC_ensureActivitiesVisible");
mTaskSupervisor.beginActivityVisibilityUpdate();
try {
// First the front root tasks. In case any are not fullscreen and are in front of home.
@@ -1915,7 +1914,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
} finally {
mTaskSupervisor.endActivityVisibilityUpdate();
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 22f718ddbd22..787c5d6f7699 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3515,7 +3515,10 @@ class Task extends TaskFragment {
&& !appCompatTaskInfo.topActivityInSizeCompat
&& top.mLetterboxUiController.shouldEnableUserAspectRatioSettings()
&& !info.isTopActivityTransparent;
- appCompatTaskInfo.topActivityBoundsLetterboxed = top != null && top.areBoundsLetterboxed();
+ appCompatTaskInfo.topActivityBoundsLetterboxed = top != null && top.areBoundsLetterboxed();
+ appCompatTaskInfo.cameraCompatTaskInfo.freeformCameraCompatMode = top == null
+ ? CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE
+ : top.mLetterboxUiController.getFreeformCameraCompatMode();
}
/**
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index b19de189af5a..1f0c827083ac 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -362,6 +362,7 @@ public:
void notifyDropWindow(const sp<IBinder>& token, float x, float y) override;
void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
const std::set<gui::Uid>& uids) override;
+ void notifyFocusedDisplayChanged(ui::LogicalDisplayId displayId) override;
/* --- PointerControllerPolicyInterface implementation --- */
@@ -1108,6 +1109,10 @@ void NativeInputManager::notifyVibratorState(int32_t deviceId, bool isOn) {
checkAndClearExceptionFromCallback(env, "notifyVibratorState");
}
+void NativeInputManager::notifyFocusedDisplayChanged(ui::LogicalDisplayId displayId) {
+ mInputManager->getChoreographer().setFocusedDisplay(displayId);
+}
+
void NativeInputManager::displayRemoved(JNIEnv* env, ui::LogicalDisplayId displayId) {
mInputManager->getDispatcher().displayRemoved(displayId);
}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
index 3b25cb13e66c..42bd75a7a67e 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
@@ -127,7 +127,7 @@ public class InputMethodManagerServiceTestBase {
protected IInputMethodInvoker mMockInputMethodInvoker;
protected InputMethodManagerService mInputMethodManagerService;
protected ServiceThread mServiceThread;
- protected ServiceThread mPackageMonitorThread;
+ protected ServiceThread mIoThread;
protected boolean mIsLargeScreen;
private InputManagerGlobal.TestSession mInputManagerGlobalSession;
@@ -226,14 +226,14 @@ public class InputMethodManagerServiceTestBase {
"immstest1",
Process.THREAD_PRIORITY_FOREGROUND,
true /* allowIo */);
- mPackageMonitorThread =
+ mIoThread =
new ServiceThread(
"immstest2",
Process.THREAD_PRIORITY_FOREGROUND,
true /* allowIo */);
mInputMethodManagerService = new InputMethodManagerService(mContext,
InputMethodManagerService.shouldEnableExperimentalConcurrentMultiUserMode(mContext),
- mServiceThread, mPackageMonitorThread,
+ mServiceThread, mIoThread,
unusedUserId -> mMockInputMethodBindingController);
spyOn(mInputMethodManagerService);
@@ -267,8 +267,8 @@ public class InputMethodManagerServiceTestBase {
mInputMethodManagerService.mInputMethodDeviceConfigs.destroy();
}
- if (mPackageMonitorThread != null) {
- mPackageMonitorThread.quitSafely();
+ if (mIoThread != null) {
+ mIoThread.quitSafely();
}
if (mServiceThread != null) {
diff --git a/services/tests/ondeviceintelligencetests/Android.bp b/services/tests/ondeviceintelligencetests/Android.bp
new file mode 100644
index 000000000000..aa859422f54f
--- /dev/null
+++ b/services/tests/ondeviceintelligencetests/Android.bp
@@ -0,0 +1,62 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // 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_test {
+ name: "FrameworksOnDeviceIntelligenceTests",
+ team: "trendy_team_machine_learning",
+ defaults: [
+ "modules-utils-testable-device-config-defaults",
+ ],
+
+ srcs: [
+ "src/**/*.java",
+ ],
+
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.runner",
+ "androidx.test.ext.truth",
+ "mockito-target-extended-minus-junit4",
+ "platform-test-annotations",
+ "services.core",
+ "servicestests-core-utils",
+ "servicestests-utils-mockito-extended",
+ "truth",
+ "frameworks-base-testutils",
+ "androidx.test.rules",
+ ],
+
+ libs: [
+ "android.test.mock",
+ "android.test.base",
+ "android.test.runner",
+ ],
+
+ certificate: "platform",
+ platform_apis: true,
+ test_suites: ["device-tests"],
+
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/services/tests/ondeviceintelligencetests/AndroidManifest.xml b/services/tests/ondeviceintelligencetests/AndroidManifest.xml
new file mode 100644
index 000000000000..8bd111e96638
--- /dev/null
+++ b/services/tests/ondeviceintelligencetests/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.ondeviceintelligencetests">
+
+ <application android:testOnly="true"
+ android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.frameworks.ondeviceintelligencetests"
+ android:label="Frameworks OnDeviceIntelligence Services Tests" />
+
+</manifest>
diff --git a/services/tests/ondeviceintelligencetests/AndroidTest.xml b/services/tests/ondeviceintelligencetests/AndroidTest.xml
new file mode 100644
index 000000000000..3ae96c5039b2
--- /dev/null
+++ b/services/tests/ondeviceintelligencetests/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs Frameworks OnDeviceIntelligence Services Tests.">
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="install-arg" value="-t" />
+ <option name="test-file-name" value="FrameworksOnDeviceIntelligenceTests.apk" />
+ </target_preparer>
+
+ <option name="test-tag" value="FrameworksOnDeviceIntelligenceTests" />
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.frameworks.ondeviceintelligencetests" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/services/tests/ondeviceintelligencetests/src/com/android/server/ondeviceintelligence/InferenceInfoStoreTest.java b/services/tests/ondeviceintelligencetests/src/com/android/server/ondeviceintelligence/InferenceInfoStoreTest.java
new file mode 100644
index 000000000000..d3943e32ef07
--- /dev/null
+++ b/services/tests/ondeviceintelligencetests/src/com/android/server/ondeviceintelligence/InferenceInfoStoreTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.ondeviceintelligence;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService;
+import android.app.ondeviceintelligence.InferenceInfo;
+import android.util.Base64;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.framework.protobuf.nano.MessageNano;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class InferenceInfoStoreTest {
+ InferenceInfoStore inferenceInfoStore;
+
+ @Before
+ public void setUp() {
+ inferenceInfoStore = new InferenceInfoStore(1000);
+ }
+
+ @Test
+ public void testInferenceInfoParsesFromBundleSuccessfully() throws Exception {
+ Bundle bundle = new Bundle();
+ bundle.putByteArray(OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY,
+ getInferenceInfoBytes(1, 1, 100));
+ inferenceInfoStore.addInferenceInfoFromBundle(bundle);
+ List<InferenceInfo> inferenceInfos = inferenceInfoStore.getLatestInferenceInfo(0);
+ assertThat(inferenceInfos).hasSize(1);
+ assertThat(inferenceInfos.get(0).getUid()).isEqualTo(1);
+ assertThat(inferenceInfos.get(0).getStartTimeMs()).isEqualTo(1);
+ assertThat(inferenceInfos.get(0).getEndTimeMs()).isEqualTo(100);
+ }
+
+ @Test
+ public void testInferenceInfoParsesFromPersistableBundleSuccessfully() throws Exception {
+ PersistableBundle bundle = new PersistableBundle();
+ bundle.putString(OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY,
+ Base64.encodeToString(getInferenceInfoBytes(1, 1, 100), Base64.DEFAULT));
+ inferenceInfoStore.addInferenceInfoFromBundle(bundle);
+ List<InferenceInfo> inferenceInfos = inferenceInfoStore.getLatestInferenceInfo(0);
+ assertThat(inferenceInfos).hasSize(1);
+ assertThat(inferenceInfos.get(0).getUid()).isEqualTo(1);
+ assertThat(inferenceInfos.get(0).getStartTimeMs()).isEqualTo(1);
+ assertThat(inferenceInfos.get(0).getEndTimeMs()).isEqualTo(100);
+ }
+
+
+ @Test
+ public void testEvictionAfterMaxAge() throws Exception {
+ PersistableBundle bundle = new PersistableBundle();
+ long testStartTime = System.currentTimeMillis();
+ bundle.putString(OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY,
+ Base64.encodeToString(getInferenceInfoBytes(1, testStartTime - 10,
+ testStartTime + 100), Base64.DEFAULT));
+ inferenceInfoStore.addInferenceInfoFromBundle(bundle);
+ bundle.putString(OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY,
+ Base64.encodeToString(getInferenceInfoBytes(1, testStartTime - 5,
+ testStartTime + 100), Base64.DEFAULT));
+ inferenceInfoStore.addInferenceInfoFromBundle(bundle);
+ Thread.sleep(1020);
+ List<InferenceInfo> inferenceInfos = inferenceInfoStore.getLatestInferenceInfo(0);
+ assertThat(inferenceInfos).hasSize(2);
+ assertThat(inferenceInfos.get(0).getUid()).isEqualTo(1);
+ assertThat(inferenceInfos.get(0).getStartTimeMs()).isEqualTo(testStartTime - 10);
+ assertThat(inferenceInfos.get(0).getEndTimeMs()).isEqualTo(testStartTime + 100);
+ inferenceInfoStore.addInferenceInfoFromBundle(bundle);
+ List<InferenceInfo> inferenceInfos2 = inferenceInfoStore.getLatestInferenceInfo(0);
+ assertThat(inferenceInfos2).hasSize(1); //previous entries should have been evicted
+ }
+
+ private byte[] getInferenceInfoBytes(int uid, long startTime, long endTime) {
+ com.android.server.ondeviceintelligence.nano.InferenceInfo inferenceInfo =
+ new com.android.server.ondeviceintelligence.nano.InferenceInfo();
+ inferenceInfo.uid = uid;
+ inferenceInfo.startTimeMs = startTime;
+ inferenceInfo.endTimeMs = endTime;
+ return MessageNano.toByteArray(inferenceInfo);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index f482ddc7e03c..3ef81fde6506 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -28,8 +28,11 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat
import static com.android.server.testutils.TestUtils.strictMock;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import static org.mockito.AdditionalMatchers.gt;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -198,6 +201,7 @@ public class FullScreenMagnificationGestureHandlerTest {
private final Scroller mMockScroller = spy(new Scroller(mContext));
private boolean mMockMagnificationConnectionState;
+ private boolean mMockOneFingerPanningEnabled;
private OffsettableClock mClock;
private FullScreenMagnificationGestureHandler mMgh;
@@ -209,8 +213,6 @@ public class FullScreenMagnificationGestureHandlerTest {
static final Rect INITIAL_MAGNIFICATION_BOUNDS = new Rect(0, 0, 800, 800);
- static final Region INITIAL_MAGNIFICATION_REGION = new Region(INITIAL_MAGNIFICATION_BOUNDS);
-
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -282,6 +284,8 @@ public class FullScreenMagnificationGestureHandlerTest {
@NonNull
private FullScreenMagnificationGestureHandler newInstance(boolean detectSingleFingerTripleTap,
boolean detectTwoFingerTripleTap, boolean detectShortcutTrigger) {
+ enableOneFingerPanning(
+ isWatch() || Flags.enableMagnificationOneFingerPanningGesture());
FullScreenMagnificationGestureHandler h =
new FullScreenMagnificationGestureHandler(
mContext,
@@ -297,8 +301,12 @@ public class FullScreenMagnificationGestureHandlerTest {
mMockMagnificationLogger,
ViewConfiguration.get(mContext),
mMockOneFingerPanningSettingsProvider);
+ // OverscrollHandler is only supported on watches.
+ // @See config_enable_a11y_fullscreen_magnification_overscroll_handler
if (isWatch()) {
- enableOneFingerPanning(true);
+ assertNotNull(h.mOverscrollHandler);
+ } else {
+ assertNull(h.mOverscrollHandler);
}
mHandler = new TestHandler(h.mDetectingState, mClock) {
@Override
@@ -836,7 +844,7 @@ public class FullScreenMagnificationGestureHandlerTest {
@Test
public void testActionUpNotAtEdge_singlePanningState_detectingState() {
- assumeTrue(isWatch());
+ assumeTrue(isOneFingerPanningEnabled());
goFromStateIdleTo(STATE_SINGLE_PANNING);
send(upEvent());
@@ -846,8 +854,7 @@ public class FullScreenMagnificationGestureHandlerTest {
}
@Test
- public void testScroll_SinglePanningDisabled_delegatingState() {
- assumeTrue(isWatch());
+ public void testScroll_singlePanningDisabled_delegatingState() {
enableOneFingerPanning(false);
goFromStateIdleTo(STATE_ACTIVATED);
@@ -858,8 +865,54 @@ public class FullScreenMagnificationGestureHandlerTest {
}
@Test
+ public void testSingleFingerOverscrollAtLeftEdge_isNotWatch_transitionToDelegatingState() {
+ assumeTrue(isOneFingerPanningEnabled());
+ assumeFalse(isWatch());
+ goFromStateIdleTo(STATE_ACTIVATED);
+ float centerY =
+ (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.bottom) / 2.0f;
+ mFullScreenMagnificationController.setCenter(
+ DISPLAY_0, INITIAL_MAGNIFICATION_BOUNDS.left, centerY, false, 1);
+ final float swipeMinDistance = ViewConfiguration.get(mContext).getScaledTouchSlop() + 1;
+ PointF initCoords =
+ new PointF(INITIAL_MAGNIFICATION_BOUNDS.centerX(),
+ INITIAL_MAGNIFICATION_BOUNDS.centerY());
+ PointF edgeCoords = new PointF(initCoords.x, initCoords.y);
+ edgeCoords.offset(swipeMinDistance, 0);
+
+ allowEventDelegation();
+ swipeAndHold(initCoords, edgeCoords);
+
+ assertTrue(mMgh.mCurrentState == mMgh.mDelegatingState);
+ assertTrue(isZoomed());
+ }
+
+ @Test
+ public void testSingleFingerOverscrollAtBottomEdge_isNotWatch_transitionToDelegatingState() {
+ assumeTrue(isOneFingerPanningEnabled());
+ assumeFalse(isWatch());
+ goFromStateIdleTo(STATE_ACTIVATED);
+ float centerX =
+ (INITIAL_MAGNIFICATION_BOUNDS.right + INITIAL_MAGNIFICATION_BOUNDS.left) / 2.0f;
+ mFullScreenMagnificationController.setCenter(
+ DISPLAY_0, centerX, INITIAL_MAGNIFICATION_BOUNDS.bottom, false, 1);
+ final float swipeMinDistance = ViewConfiguration.get(mContext).getScaledTouchSlop() + 1;
+ PointF initCoords =
+ new PointF(INITIAL_MAGNIFICATION_BOUNDS.centerX(),
+ INITIAL_MAGNIFICATION_BOUNDS.centerY());
+ PointF edgeCoords = new PointF(initCoords.x, initCoords.y);
+ edgeCoords.offset(0, -swipeMinDistance);
+
+ allowEventDelegation();
+ swipeAndHold(initCoords, edgeCoords);
+
+ assertTrue(mMgh.mCurrentState == mMgh.mDelegatingState);
+ assertTrue(isZoomed());
+ }
+
+ @Test
@FlakyTest
- public void testScroll_singleHorizontalPanningAndAtEdge_leftEdgeOverscroll() {
+ public void testSingleFingerOverscrollAtLeftEdge_isWatch_expectedOverscrollState() {
assumeTrue(isWatch());
goFromStateIdleTo(STATE_SINGLE_PANNING);
float centerY =
@@ -883,7 +936,7 @@ public class FullScreenMagnificationGestureHandlerTest {
@Test
@FlakyTest
- public void testScroll_singleHorizontalPanningAndAtEdge_rightEdgeOverscroll() {
+ public void testSingleFingerOverscrollAtRightEdge_isWatch_expectedOverscrollState() {
assumeTrue(isWatch());
goFromStateIdleTo(STATE_SINGLE_PANNING);
float centerY =
@@ -907,7 +960,7 @@ public class FullScreenMagnificationGestureHandlerTest {
@Test
@FlakyTest
- public void testScroll_singleVerticalPanningAndAtEdge_verticalOverscroll() {
+ public void testSingleFingerOverscrollAtTopEdge_isWatch_expectedOverscrollState() {
assumeTrue(isWatch());
goFromStateIdleTo(STATE_SINGLE_PANNING);
float centerX =
@@ -929,7 +982,7 @@ public class FullScreenMagnificationGestureHandlerTest {
}
@Test
- public void testScroll_singlePanningAndAtEdge_noOverscroll() {
+ public void testSingleFingerScrollAtEdge_isWatch_noOverscroll() {
assumeTrue(isWatch());
goFromStateIdleTo(STATE_SINGLE_PANNING);
float centerY =
@@ -951,7 +1004,7 @@ public class FullScreenMagnificationGestureHandlerTest {
}
@Test
- public void testScroll_singleHorizontalPanningAndAtEdge_vibrate() {
+ public void testSingleFingerHorizontalScrollAtEdge_isWatch_vibrate() {
assumeTrue(isWatch());
goFromStateIdleTo(STATE_SINGLE_PANNING);
mFullScreenMagnificationController.setCenter(
@@ -975,7 +1028,7 @@ public class FullScreenMagnificationGestureHandlerTest {
}
@Test
- public void testScroll_singleVerticalPanningAndAtEdge_doNotVibrate() {
+ public void testSingleFingerVerticalScrollAtEdge_isWatch_doNotVibrate() {
assumeTrue(isWatch());
goFromStateIdleTo(STATE_SINGLE_PANNING);
mFullScreenMagnificationController.setCenter(
@@ -1000,7 +1053,7 @@ public class FullScreenMagnificationGestureHandlerTest {
@Test
@RequiresFlagsEnabled(Flags.FLAG_FULLSCREEN_FLING_GESTURE)
- public void singleFinger_testScrollAfterMagnified_startsFling() {
+ public void testSingleFingerScrollAfterMagnified_startsFling() {
assumeTrue(isWatch());
goFromStateIdleTo(STATE_ACTIVATED);
@@ -1282,9 +1335,14 @@ public class FullScreenMagnificationGestureHandlerTest {
}
private void enableOneFingerPanning(boolean enable) {
+ mMockOneFingerPanningEnabled = enable;
when(mMockOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()).thenReturn(enable);
}
+ private boolean isOneFingerPanningEnabled() {
+ return mMockOneFingerPanningEnabled;
+ }
+
private void assertActionsInOrder(List<MotionEvent> actualEvents,
List<Integer> expectedActions) {
assertTrue(actualEvents.size() == expectedActions.size());
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
index 1eaa170e169b..bc2fd73f22ae 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
@@ -22,10 +22,11 @@ import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_NEG
import static android.hardware.biometrics.BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED;
import static android.hardware.biometrics.BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED;
import static android.hardware.biometrics.BiometricPrompt.DISMISSED_REASON_NEGATIVE;
+import static android.hardware.biometrics.BiometricPrompt.DISMISSED_REASON_USER_CANCEL;
import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_CALLED;
-import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_STARTED;
import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_PAUSED;
+import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_STARTED;
import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_STARTED_UI_SHOWING;
import static com.android.server.biometrics.BiometricServiceStateProto.STATE_ERROR_PENDING_SYSUI;
@@ -50,9 +51,9 @@ import static org.mockito.Mockito.when;
import android.annotation.NonNull;
import android.app.admin.DevicePolicyManager;
import android.app.trust.ITrustManager;
-import android.content.Context;
import android.content.res.Resources;
import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.ComponentInfoInternal;
@@ -70,9 +71,12 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.security.KeyStoreAuthorization;
+import android.testing.TestableContext;
import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.internal.R;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.log.BiometricContext;
@@ -80,6 +84,7 @@ import com.android.server.biometrics.log.BiometricFrameworkStatsLogger;
import com.android.server.biometrics.log.OperationContextExt;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -95,8 +100,12 @@ public class AuthSessionTest {
private static final String TEST_PACKAGE = "test_package";
private static final long TEST_REQUEST_ID = 22;
+ private static final String ACQUIRED_STRING = "test_acquired_info_callback";
+ private static final String ACQUIRED_STRING_VENDOR = "test_acquired_info_callback_vendor";
- @Mock private Context mContext;
+ @Rule
+ public final TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
@Mock private Resources mResources;
@Mock private BiometricContext mBiometricContext;
@Mock private ITrustManager mTrustManager;
@@ -110,6 +119,7 @@ public class AuthSessionTest {
@Mock private AuthSession.ClientDeathReceiver mClientDeathReceiver;
@Mock private BiometricFrameworkStatsLogger mBiometricFrameworkStatsLogger;
@Mock private BiometricCameraManager mBiometricCameraManager;
+ @Mock private BiometricManager mBiometricManager;
private Random mRandom;
private IBinder mToken;
@@ -121,7 +131,11 @@ public class AuthSessionTest {
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- when(mContext.getResources()).thenReturn(mResources);
+ mContext.addMockSystemService(BiometricManager.class, mBiometricManager);
+ mContext.getOrCreateTestableResources().addOverride(R.string.fingerprint_acquired_partial,
+ ACQUIRED_STRING);
+ mContext.getOrCreateTestableResources().addOverride(R.array.fingerprint_acquired_vendor,
+ new String[]{ACQUIRED_STRING_VENDOR});
when(mClientReceiver.asBinder()).thenReturn(mock(Binder.class));
when(mBiometricContext.updateContext(any(), anyBoolean()))
.thenAnswer(invocation -> invocation.getArgument(0));
@@ -499,8 +513,6 @@ public class AuthSessionTest {
@Test
public void testCallbackOnAcquired() throws RemoteException {
- final String acquiredStr = "test_acquired_info_callback";
- final String acquiredStrVendor = "test_acquired_info_callback_vendor";
setupFingerprint(0 /* id */, FingerprintSensorProperties.TYPE_REAR);
final AuthSession session = createAuthSession(mSensors,
@@ -510,18 +522,15 @@ public class AuthSessionTest {
0 /* operationId */,
0 /* userId */);
- when(mContext.getString(com.android.internal.R.string.fingerprint_acquired_partial))
- .thenReturn(acquiredStr);
session.onAcquired(0, FingerprintManager.FINGERPRINT_ACQUIRED_PARTIAL, 0);
- verify(mStatusBarService).onBiometricHelp(anyInt(), eq(acquiredStr));
- verify(mClientReceiver).onAcquired(eq(1), eq(acquiredStr));
+ verify(mStatusBarService).onBiometricHelp(anyInt(), eq(ACQUIRED_STRING));
+ verify(mClientReceiver).onAcquired(eq(1), eq(ACQUIRED_STRING));
- when(mResources.getStringArray(com.android.internal.R.array.fingerprint_acquired_vendor))
- .thenReturn(new String[]{acquiredStrVendor});
session.onAcquired(0, FingerprintManager.FINGERPRINT_ACQUIRED_VENDOR, 0);
- verify(mStatusBarService).onBiometricHelp(anyInt(), eq(acquiredStrVendor));
+ verify(mStatusBarService).onBiometricHelp(anyInt(), eq(ACQUIRED_STRING_VENDOR));
verify(mClientReceiver).onAcquired(
- eq(FingerprintManager.FINGERPRINT_ACQUIRED_VENDOR_BASE), eq(acquiredStrVendor));
+ eq(FingerprintManager.FINGERPRINT_ACQUIRED_VENDOR_BASE),
+ eq(ACQUIRED_STRING_VENDOR));
}
@Test
@@ -665,6 +674,87 @@ public class AuthSessionTest {
verify(mStatusBarService, never()).onBiometricError(anyInt(), anyInt(), anyInt());
}
+ @Test
+ public void onAuthReceivedWhileWaitingForConfirmation_SFPS() throws Exception {
+ setupFingerprint(0 /* id */, FingerprintSensorProperties.TYPE_POWER_BUTTON);
+ setupFace(1 /* id */, false, mock(IBiometricAuthenticator.class));
+ final long operationId = 123;
+ final int userId = 10;
+ final AuthSession session = createAuthSession(mSensors,
+ false /* checkDevicePolicyManager */,
+ Authenticators.BIOMETRIC_STRONG,
+ TEST_REQUEST_ID,
+ operationId,
+ userId);
+ session.goToInitialState();
+ for (BiometricSensor sensor : session.mPreAuthInfo.eligibleSensors) {
+ session.onCookieReceived(
+ session.mPreAuthInfo.eligibleSensors.get(sensor.id).getCookie());
+ }
+ session.onDialogAnimatedIn(true /* startFingerprintNow */);
+
+ // Face succeeds
+ session.onAuthenticationSucceeded(1, true, null);
+ verify(mStatusBarService).onBiometricAuthenticated(TYPE_FACE);
+ for (BiometricSensor sensor : session.mPreAuthInfo.eligibleSensors) {
+ if (sensor.modality == FingerprintSensorProperties.TYPE_POWER_BUTTON) {
+ assertEquals(BiometricSensor.STATE_AUTHENTICATING, sensor.getSensorState());
+ }
+ }
+
+ // SFPS succeeds
+ session.onAuthenticationSucceeded(0, true, null);
+ verify(mStatusBarService).onBiometricAuthenticated(TYPE_FINGERPRINT);
+ }
+
+ @Test
+ public void onDialogDismissedResetLockout_Confirmed() throws Exception {
+ setupFingerprint(0 /* id */, FingerprintSensorProperties.TYPE_POWER_BUTTON);
+ setupFace(1 /* id */, false, mock(IBiometricAuthenticator.class));
+ final long operationId = 123;
+ final int userId = 10;
+ final AuthSession session = createAuthSession(mSensors,
+ false /* checkDevicePolicyManager */,
+ Authenticators.BIOMETRIC_STRONG,
+ TEST_REQUEST_ID,
+ operationId,
+ userId);
+ session.goToInitialState();
+ session.onDialogAnimatedIn(true /* startFingerprintNow */);
+
+ // Face succeeds
+ session.onAuthenticationSucceeded(1, true, new byte[1]);
+
+ // Dismiss through confirmation
+ session.onDialogDismissed(DISMISSED_REASON_BIOMETRIC_CONFIRMED, null);
+
+ verify(mBiometricManager).resetLockoutTimeBound(any(), any(), anyInt(), anyInt(), any());
+ }
+
+ @Test
+ public void onDialogDismissedResetLockout_Cancelled() throws Exception {
+ setupFingerprint(0 /* id */, FingerprintSensorProperties.TYPE_POWER_BUTTON);
+ setupFace(1 /* id */, false, mock(IBiometricAuthenticator.class));
+ final long operationId = 123;
+ final int userId = 10;
+ final AuthSession session = createAuthSession(mSensors,
+ false /* checkDevicePolicyManager */,
+ Authenticators.BIOMETRIC_STRONG,
+ TEST_REQUEST_ID,
+ operationId,
+ userId);
+ session.goToInitialState();
+ session.onDialogAnimatedIn(true /* startFingerprintNow */);
+
+ // Face succeeds
+ session.onAuthenticationSucceeded(1, true, new byte[1]);
+
+ // User cancel after success
+ session.onDialogDismissed(DISMISSED_REASON_USER_CANCEL, null);
+
+ verify(mBiometricManager).resetLockoutTimeBound(any(), any(), anyInt(), anyInt(), any());
+ }
+
// TODO (b/208484275) : Enable these tests
// @Test
// public void testPreAuth_canAuthAndPrivacyDisabled() throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
index 98738052b1de..2d4dbb77343e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
@@ -92,6 +92,7 @@ import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
@@ -445,21 +446,61 @@ public class FaceAuthenticationClientTest {
);
}
+ @Test
+ public void testResetLockoutOnAuthSuccess_nonBiometricPrompt() throws RemoteException {
+ FaceAuthenticationClient client = createClient(false);
+ client.start(mCallback);
+ client.onAuthenticated(new Face("friendly", 1 /* faceId */,
+ 2 /* deviceId */), true /* authenticated */, createHardwareAuthToken());
+
+ verify(mBiometricManager).resetLockoutTimeBound(eq(mToken), eq(mContext.getOpPackageName()),
+ anyInt(), anyInt(), any());
+ }
+
+ @Test
+ public void testNoResetLockoutOnAuthFailure_nonBiometricPrompt() throws RemoteException {
+ FaceAuthenticationClient client = createClient(false);
+ client.start(mCallback);
+ client.onAuthenticated(new Face("friendly", 1 /* faceId */,
+ 2 /* deviceId */), false /* authenticated */, createHardwareAuthToken());
+
+ verify(mBiometricManager, never()).resetLockoutTimeBound(eq(mToken),
+ eq(mContext.getOpPackageName()), anyInt(), anyInt(), any());
+ }
+
+ @Test
+ public void testNoResetLockoutOnAuthSuccess_BiometricPrompt() throws RemoteException {
+ FaceAuthenticationClient client = createClient(true);
+ client.start(mCallback);
+ client.onAuthenticated(new Face("friendly", 1 /* faceId */,
+ 2 /* deviceId */), true /* authenticated */, createHardwareAuthToken());
+
+ verify(mBiometricManager, never()).resetLockoutTimeBound(eq(mToken),
+ eq(mContext.getOpPackageName()), anyInt(), anyInt(), any());
+ }
+
private FaceAuthenticationClient createClient() throws RemoteException {
return createClient(2 /* version */, mClientMonitorCallbackConverter,
- false /* allowBackgroundAuthentication */,
+ false /* allowBackgroundAuthentication */, true /* isBiometricPrompt */,
+ null /* lockoutTracker */);
+ }
+
+ private FaceAuthenticationClient createClient(boolean isBiometricPrompt)
+ throws RemoteException {
+ return createClient(2 /* version */, mClientMonitorCallbackConverter,
+ true /* allowBackgroundAuthentication */, isBiometricPrompt,
null /* lockoutTracker */);
}
private FaceAuthenticationClient createClientWithNullListener() throws RemoteException {
return createClient(2 /* version */, null /* listener */,
- true /* allowBackgroundAuthentication */,
+ false /* allowBackgroundAuthentication */, true /* isBiometricPrompt */,
null /* lockoutTracker */);
}
private FaceAuthenticationClient createClient(int version) throws RemoteException {
return createClient(version, mClientMonitorCallbackConverter,
- false /* allowBackgroundAuthentication */,
+ false /* allowBackgroundAuthentication */, true /* isBiometricPrompt */,
null /* lockoutTracker */);
}
@@ -468,12 +509,14 @@ public class FaceAuthenticationClientTest {
return createClient(0 /* version */,
mClientMonitorCallbackConverter,
true /* allowBackgroundAuthentication */,
+ true /* isBiometricPrompt */,
lockoutTracker);
}
private FaceAuthenticationClient createClient(int version,
ClientMonitorCallbackConverter listener,
boolean allowBackgroundAuthentication,
+ boolean isBiometricPrompt,
LockoutTracker lockoutTracker) throws RemoteException {
when(mHal.getInterfaceVersion()).thenReturn(version);
@@ -488,7 +531,7 @@ public class FaceAuthenticationClientTest {
.build();
return new FaceAuthenticationClient(mContext, () -> aidl, mToken,
2 /* requestId */, listener, OP_ID,
- false /* restricted */, options, 4 /* cookie */,
+ false /* restricted */, options, isBiometricPrompt ? 4 : 0 /* cookie */,
false /* requireConfirmation */,
mBiometricLogger, mBiometricContext, true /* isStrongBiometric */,
mUsageStats, lockoutTracker, allowBackgroundAuthentication,
@@ -500,4 +543,8 @@ public class FaceAuthenticationClientTest {
}
};
}
+
+ private ArrayList<Byte> createHardwareAuthToken() {
+ return new ArrayList<>(Collections.nCopies(69, Byte.valueOf("0")));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
index 182d60328440..ecd799f44552 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
@@ -98,6 +98,7 @@ import org.mockito.junit.MockitoRule;
import java.time.Clock;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
@@ -608,6 +609,45 @@ public class FingerprintAuthenticationClientTest {
}
@Test
+ public void testResetLockoutOnAuthSuccess_nonBiometricPrompt() throws RemoteException {
+ final FingerprintAuthenticationClient client = createClient(1 /* version */,
+ true /* allowBackgroundAuthentication */, false /* isBiometricPrompt */,
+ mClientMonitorCallbackConverter, mLockoutTracker);
+ client.start(mCallback);
+ client.onAuthenticated(new Fingerprint("friendly", 1 /* fingerId */,
+ 2 /* deviceId */), true /* authenticated */, createHardwareAuthToken());
+
+ verify(mBiometricManager).resetLockoutTimeBound(eq(mToken), eq(mContext.getOpPackageName()),
+ anyInt(), anyInt(), any());
+ }
+
+ @Test
+ public void testNoResetLockoutOnAuthFailure_nonBiometricPrompt() throws RemoteException {
+ final FingerprintAuthenticationClient client = createClient(1 /* version */,
+ true /* allowBackgroundAuthentication */, false /* isBiometricPrompt */,
+ mClientMonitorCallbackConverter, mLockoutTracker);
+ client.start(mCallback);
+ client.onAuthenticated(new Fingerprint("friendly", 1 /* fingerId */,
+ 2 /* deviceId */), false /* authenticated */, createHardwareAuthToken());
+
+ verify(mBiometricManager, never()).resetLockoutTimeBound(eq(mToken),
+ eq(mContext.getOpPackageName()), anyInt(), anyInt(), any());
+ }
+
+ @Test
+ public void testNoResetLockoutOnAuthSuccess_BiometricPrompt() throws RemoteException {
+ final FingerprintAuthenticationClient client = createClient(1 /* version */,
+ true /* allowBackgroundAuthentication */, true /* isBiometricPrompt */,
+ mClientMonitorCallbackConverter, mLockoutTracker);
+ client.start(mCallback);
+ client.onAuthenticated(new Fingerprint("friendly", 1 /* fingerId */,
+ 2 /* deviceId */), true /* authenticated */, createHardwareAuthToken());
+
+ verify(mBiometricManager, never()).resetLockoutTimeBound(eq(mToken),
+ eq(mContext.getOpPackageName()), anyInt(), anyInt(), any());
+ }
+
+ @Test
public void testOnAuthenticatedFalseWhenListenerIsNull() throws RemoteException {
final FingerprintAuthenticationClient client = createClientWithNullListener();
client.start(mCallback);
@@ -630,11 +670,11 @@ public class FingerprintAuthenticationClientTest {
@Test
public void testLockoutTracker_authSuccess() throws RemoteException {
final FingerprintAuthenticationClient client = createClient(1 /* version */,
- true /* allowBackgroundAuthentication */, mClientMonitorCallbackConverter,
- mLockoutTracker);
+ true /* allowBackgroundAuthentication */, false /* isBiometricPrompt */,
+ mClientMonitorCallbackConverter, mLockoutTracker);
client.start(mCallback);
client.onAuthenticated(new Fingerprint("friendly", 1 /* fingerId */,
- 2 /* deviceId */), true /* authenticated */, new ArrayList<>());
+ 2 /* deviceId */), true /* authenticated */, createHardwareAuthToken());
verify(mLockoutTracker).resetFailedAttemptsForUser(true, USER_ID);
verify(mLockoutTracker, never()).addFailedAttemptForUser(anyInt());
@@ -643,11 +683,11 @@ public class FingerprintAuthenticationClientTest {
@Test
public void testLockoutTracker_authFailed() throws RemoteException {
final FingerprintAuthenticationClient client = createClient(1 /* version */,
- true /* allowBackgroundAuthentication */, mClientMonitorCallbackConverter,
- mLockoutTracker);
+ true /* allowBackgroundAuthentication */, false /* isBiometricPrompt */,
+ mClientMonitorCallbackConverter, mLockoutTracker);
client.start(mCallback);
client.onAuthenticated(new Fingerprint("friendly", 1 /* fingerId */,
- 2 /* deviceId */), false /* authenticated */, new ArrayList<>());
+ 2 /* deviceId */), false /* authenticated */, createHardwareAuthToken());
verify(mLockoutTracker, never()).resetFailedAttemptsForUser(anyBoolean(), anyInt());
verify(mLockoutTracker).addFailedAttemptForUser(USER_ID);
@@ -655,27 +695,31 @@ public class FingerprintAuthenticationClientTest {
private FingerprintAuthenticationClient createClient() throws RemoteException {
return createClient(100 /* version */, true /* allowBackgroundAuthentication */,
+ true /* isBiometricPrompt */,
mClientMonitorCallbackConverter, null);
}
private FingerprintAuthenticationClient createClientWithoutBackgroundAuth()
throws RemoteException {
return createClient(100 /* version */, false /* allowBackgroundAuthentication */,
- mClientMonitorCallbackConverter, null);
+ true /* isBiometricPrompt */, mClientMonitorCallbackConverter, null);
}
private FingerprintAuthenticationClient createClient(int version) throws RemoteException {
return createClient(version, true /* allowBackgroundAuthentication */,
+ true /* isBiometricPrompt */,
mClientMonitorCallbackConverter, null);
}
private FingerprintAuthenticationClient createClientWithNullListener() throws RemoteException {
return createClient(100 /* version */, true /* allowBackgroundAuthentication */,
- null, /* listener */null);
+ true /* isBiometricPrompt */,
+ /* listener */null, null);
}
private FingerprintAuthenticationClient createClient(int version,
- boolean allowBackgroundAuthentication, ClientMonitorCallbackConverter listener,
+ boolean allowBackgroundAuthentication, boolean isBiometricPrompt,
+ ClientMonitorCallbackConverter listener,
LockoutTracker lockoutTracker)
throws RemoteException {
when(mHal.getInterfaceVersion()).thenReturn(version);
@@ -687,7 +731,8 @@ public class FingerprintAuthenticationClientTest {
.setSensorId(SENSOR_ID)
.build();
return new FingerprintAuthenticationClient(mContext, () -> aidl, mToken, REQUEST_ID,
- listener, OP_ID, false /* restricted */, options, 4 /* cookie */,
+ listener, OP_ID, false /* restricted */, options,
+ isBiometricPrompt ? 4 : 0 /* cookie */,
false /* requireConfirmation */, mBiometricLogger, mBiometricContext,
true /* isStrongBiometric */, null /* taskStackListener */, mUdfpsOverlayController,
mAuthenticationStateListeners, allowBackgroundAuthentication, mSensorProps,
@@ -698,4 +743,8 @@ public class FingerprintAuthenticationClientTest {
}
};
}
+
+ private ArrayList<Byte> createHardwareAuthToken() {
+ return new ArrayList<>(Collections.nCopies(69, Byte.valueOf("0")));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java
index f221b75564a3..148c96850d34 100644
--- a/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java
@@ -18,6 +18,20 @@ package com.android.server.statusbar;
import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
import static android.app.ActivityManager.PROCESS_STATE_TOP;
+import static android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS;
+import static android.app.StatusBarManager.DISABLE2_MASK;
+import static android.app.StatusBarManager.DISABLE2_NONE;
+import static android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE;
+import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
+import static android.app.StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS;
+import static android.app.StatusBarManager.DISABLE_BACK;
+import static android.app.StatusBarManager.DISABLE_CLOCK;
+import static android.app.StatusBarManager.DISABLE_HOME;
+import static android.app.StatusBarManager.DISABLE_MASK;
+import static android.app.StatusBarManager.DISABLE_NONE;
+import static android.app.StatusBarManager.DISABLE_RECENT;
+import static android.app.StatusBarManager.DISABLE_SEARCH;
+import static android.app.StatusBarManager.DISABLE_SYSTEM_INFO;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
@@ -137,6 +151,7 @@ public class StatusBarManagerServiceTest {
LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
when(mMockStatusBar.asBinder()).thenReturn(mMockStatusBar);
+ when(mMockStatusBar.isBinderAlive()).thenReturn(true);
when(mApplicationInfo.loadLabel(any())).thenReturn(APP_NAME);
mockHandleIncomingUser();
@@ -722,6 +737,369 @@ public class StatusBarManagerServiceTest {
verify(mOverlayManager, never()).setEnabledExclusiveInCategory(anyString(), anyInt());
}
+ @Test
+ public void testGetDisableFlags() throws Exception {
+ String packageName = mContext.getPackageName();
+ int userId = 0;
+ mockUidCheck();
+ mockCurrentUserCheck(userId);
+ mStatusBarManagerService.disable(DISABLE_NONE, mMockStatusBar, packageName);
+ assertEquals(DISABLE_NONE,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+ }
+
+ @Test
+ public void testSetHomeDisabled() throws Exception {
+ int expectedFlags = DISABLE_MASK & DISABLE_HOME;
+ String pkg = mContext.getPackageName();
+ int userId = 0;
+ mockUidCheck();
+ mockCurrentUserCheck(userId);
+ // before disabling
+ assertEquals(DISABLE_NONE,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+ // disable
+ mStatusBarManagerService.disable(expectedFlags, mMockStatusBar, pkg);
+ // check that disable works
+ assertEquals(expectedFlags,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+ }
+
+ @Test
+ public void testSetSystemInfoDisabled() throws Exception {
+ int expectedFlags = DISABLE_MASK & DISABLE_SYSTEM_INFO;
+ String pkg = mContext.getPackageName();
+ int userId = 0;
+ mockUidCheck();
+ mockCurrentUserCheck(userId);
+ // before disabling
+ assertEquals(DISABLE_NONE,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+ // disable
+ mStatusBarManagerService.disable(expectedFlags, mMockStatusBar, pkg);
+ // check that right flag is disabled
+ assertEquals(expectedFlags,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+ }
+
+ @Test
+ public void testSetRecentDisabled() throws Exception {
+ int expectedFlags = DISABLE_MASK & DISABLE_RECENT;
+ String pkg = mContext.getPackageName();
+ int userId = 0;
+ mockUidCheck();
+ mockCurrentUserCheck(userId);
+ // before disabling
+ assertEquals(DISABLE_NONE,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+ // disable
+ mStatusBarManagerService.disable(expectedFlags, mMockStatusBar, pkg);
+ // check that right flag is disabled
+ assertEquals(expectedFlags,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+ }
+
+ @Test
+ public void testSetBackDisabled() throws Exception {
+ int expectedFlags = DISABLE_MASK & DISABLE_BACK;
+ String pkg = mContext.getPackageName();
+ int userId = 0;
+ mockUidCheck();
+ mockCurrentUserCheck(userId);
+ // before disabling
+ assertEquals(DISABLE_NONE,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+ // disable
+ mStatusBarManagerService.disable(expectedFlags, mMockStatusBar, pkg);
+ // check that right flag is disabled
+ assertEquals(expectedFlags,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+ }
+
+ @Test
+ public void testSetClockDisabled() throws Exception {
+ int expectedFlags = DISABLE_MASK & DISABLE_CLOCK;
+ String pkg = mContext.getPackageName();
+ int userId = 0;
+ mockUidCheck();
+ mockCurrentUserCheck(userId);
+ // before disabling
+ assertEquals(DISABLE_NONE,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+ // disable home
+ mStatusBarManagerService.disable(expectedFlags, mMockStatusBar, pkg);
+ // check that right flag is disabled
+ assertEquals(expectedFlags,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+ }
+
+ @Test
+ public void testSetSearchDisabled() throws Exception {
+ int expectedFlags = DISABLE_MASK & DISABLE_SEARCH;
+ String pkg = mContext.getPackageName();
+ int userId = 0;
+ mockUidCheck();
+ mockCurrentUserCheck(userId);
+ // before disabling
+ assertEquals(DISABLE_NONE, mStatusBarManagerService.getDisableFlags(mMockStatusBar,
+ userId)[0]);
+ // disable
+ mStatusBarManagerService.disable(expectedFlags, mMockStatusBar, pkg);
+ // check that right flag is disabled
+ assertEquals(expectedFlags,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+ }
+
+ @Test
+ public void testSetQuickSettingsDisabled2() throws Exception {
+ int expectedFlags = DISABLE2_MASK & DISABLE2_QUICK_SETTINGS;
+ String pkg = mContext.getPackageName();
+ int userId = 0;
+ mockUidCheck();
+ mockCurrentUserCheck(userId);
+ // before disabling
+ assertEquals(DISABLE2_NONE,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[1]);
+ // disable
+ mStatusBarManagerService.disable2(expectedFlags, mMockStatusBar, pkg);
+ // check that right flag is disabled
+ assertEquals(expectedFlags,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[1]);
+ }
+
+ @Test
+ public void testSetSystemIconsDisabled2() throws Exception {
+ int expectedFlags = DISABLE2_MASK & DISABLE2_QUICK_SETTINGS;
+ String pkg = mContext.getPackageName();
+ int userId = 0;
+ mockUidCheck();
+ mockCurrentUserCheck(userId);
+ // before disabling
+ assertEquals(DISABLE_NONE, mStatusBarManagerService.getDisableFlags(mMockStatusBar,
+ userId)[1]);
+ // disable
+ mStatusBarManagerService.disable2(expectedFlags, mMockStatusBar, pkg);
+ // check that right flag is disabled
+ assertEquals(expectedFlags,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[1]);
+ }
+
+ @Test
+ public void testSetNotificationShadeDisabled2() throws Exception {
+ int expectedFlags = DISABLE2_MASK & DISABLE2_NOTIFICATION_SHADE;
+ String pkg = mContext.getPackageName();
+ int userId = 0;
+ mockUidCheck();
+ mockCurrentUserCheck(userId);
+ // before disabling
+ assertEquals(DISABLE_NONE, mStatusBarManagerService.getDisableFlags(mMockStatusBar,
+ userId)[1]);
+ // disable
+ mStatusBarManagerService.disable2(expectedFlags, mMockStatusBar, pkg);
+ // check that right flag is disabled
+ assertEquals(expectedFlags,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[1]);
+ }
+
+
+ @Test
+ public void testSetGlobalActionsDisabled2() throws Exception {
+ int expectedFlags = DISABLE2_MASK & DISABLE2_GLOBAL_ACTIONS;
+ String pkg = mContext.getPackageName();
+ int userId = 0;
+ mockUidCheck();
+ mockCurrentUserCheck(userId);
+ // before disabling
+ assertEquals(DISABLE_NONE, mStatusBarManagerService.getDisableFlags(mMockStatusBar,
+ userId)[1]);
+ // disable
+ mStatusBarManagerService.disable2(expectedFlags, mMockStatusBar, pkg);
+ // check that right flag is disabled
+ assertEquals(expectedFlags,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[1]);
+ }
+
+ @Test
+ public void testSetRotateSuggestionsDisabled2() throws Exception {
+ int expectedFlags = DISABLE2_MASK & DISABLE2_ROTATE_SUGGESTIONS;
+ String pkg = mContext.getPackageName();
+ int userId = 0;
+ mockUidCheck();
+ mockCurrentUserCheck(userId);
+ // before disabling
+ assertEquals(DISABLE_NONE,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[1]);
+ // disable
+ mStatusBarManagerService.disable2(expectedFlags, mMockStatusBar, pkg);
+ // check that right flag is disabled
+ assertEquals(expectedFlags,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[1]);
+ }
+
+ @Test
+ public void testSetTwoDisable2Flags() throws Exception {
+ int expectedFlags = DISABLE2_MASK & DISABLE2_ROTATE_SUGGESTIONS & DISABLE2_QUICK_SETTINGS;
+ String pkg = mContext.getPackageName();
+ int userId = 0;
+ mockUidCheck();
+ mockCurrentUserCheck(userId);
+ // before disabling
+ assertEquals(DISABLE_NONE,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[1]);
+ // disable
+ mStatusBarManagerService.disable2(expectedFlags, mMockStatusBar, pkg);
+ // check that right flag is disabled
+ assertEquals(expectedFlags,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[1]);
+ }
+
+ @Test
+ public void testSetTwoDisableFlagsRemoveOne() throws Exception {
+ int twoFlags = DISABLE_MASK & DISABLE_HOME & DISABLE_BACK;
+ int expectedFlag = DISABLE_MASK & DISABLE_HOME;
+
+ String pkg = mContext.getPackageName();
+ int userId = 0;
+ mockUidCheck();
+ mockCurrentUserCheck(userId);
+ // before disabling
+ assertEquals(DISABLE_NONE,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+ // disable
+ mStatusBarManagerService.disable(twoFlags, mMockStatusBar, pkg);
+ mStatusBarManagerService.disable(DISABLE_NONE, mMockStatusBar, pkg);
+ mStatusBarManagerService.disable(expectedFlag, mMockStatusBar, pkg);
+ // check that right flag is disabled
+ assertEquals(expectedFlag,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+ }
+
+ @Test
+ public void testSetTwoDisable2FlagsRemoveOne() throws Exception {
+ int twoFlags = DISABLE2_MASK & DISABLE2_ROTATE_SUGGESTIONS & DISABLE2_QUICK_SETTINGS;
+ int expectedFlag = DISABLE2_MASK & DISABLE2_QUICK_SETTINGS;
+
+ String pkg = mContext.getPackageName();
+ int userId = 0;
+ mockUidCheck();
+ mockCurrentUserCheck(userId);
+ // before disabling
+ assertEquals(DISABLE_NONE, mStatusBarManagerService.getDisableFlags(mMockStatusBar,
+ userId)[1]);
+ // disable
+ mStatusBarManagerService.disable2(twoFlags, mMockStatusBar, pkg);
+ mStatusBarManagerService.disable2(DISABLE2_NONE, mMockStatusBar, pkg);
+ mStatusBarManagerService.disable2(expectedFlag, mMockStatusBar, pkg);
+ // check that right flag is disabled
+ assertEquals(expectedFlag,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[1]);
+ }
+
+ @Test
+ public void testDisableBothFlags() throws Exception {
+ int disableFlags = DISABLE_MASK & DISABLE_BACK & DISABLE_HOME;
+ int disable2Flags = DISABLE2_MASK & DISABLE2_QUICK_SETTINGS & DISABLE2_ROTATE_SUGGESTIONS;
+
+ String pkg = mContext.getPackageName();
+ int userId = 0;
+ mockUidCheck();
+ mockCurrentUserCheck(userId);
+ // before disabling
+ assertEquals(DISABLE_NONE, mStatusBarManagerService.getDisableFlags(mMockStatusBar,
+ userId)[0]);
+ assertEquals(DISABLE2_NONE,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[1]);
+ // disable
+ mStatusBarManagerService.disable(disableFlags, mMockStatusBar, pkg);
+ mStatusBarManagerService.disable2(disable2Flags, mMockStatusBar, pkg);
+ // check that right flag is disabled
+ assertEquals(disableFlags,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+ assertEquals(disable2Flags,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[1]);
+ }
+
+ @Test
+ public void testDisableBothFlagsEnable1Flags() throws Exception {
+ int disableFlags = DISABLE_MASK & DISABLE_BACK & DISABLE_HOME;
+ int disable2Flags = DISABLE2_MASK & DISABLE2_QUICK_SETTINGS & DISABLE2_ROTATE_SUGGESTIONS;
+
+ String pkg = mContext.getPackageName();
+ int userId = 0;
+ mockUidCheck();
+ mockCurrentUserCheck(userId);
+ // before disabling
+ assertEquals(DISABLE_NONE,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+ assertEquals(DISABLE2_NONE,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[1]);
+ // disable
+ mStatusBarManagerService.disable(disableFlags, mMockStatusBar, pkg);
+ mStatusBarManagerService.disable2(disable2Flags, mMockStatusBar, pkg);
+ // re-enable one
+ mStatusBarManagerService.disable(DISABLE_NONE, mMockStatusBar, pkg);
+ // check that right flag is disabled
+ assertEquals(DISABLE_NONE,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+ assertEquals(disable2Flags,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[1]);
+ }
+
+ @Test
+ public void testDisableBothFlagsEnable2Flags() throws Exception {
+ int disableFlags = DISABLE_MASK & DISABLE_BACK & DISABLE_HOME;
+ int disable2Flags = DISABLE2_MASK & DISABLE2_QUICK_SETTINGS & DISABLE2_ROTATE_SUGGESTIONS;
+
+ String pkg = mContext.getPackageName();
+ int userId = 0;
+ mockUidCheck();
+ mockCurrentUserCheck(userId);
+ // before disabling
+ assertEquals(DISABLE_NONE,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+ assertEquals(DISABLE2_NONE,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[1]);
+ // disable
+ mStatusBarManagerService.disable(disableFlags, mMockStatusBar, pkg);
+ mStatusBarManagerService.disable2(disable2Flags, mMockStatusBar, pkg);
+ // re-enable one
+ mStatusBarManagerService.disable2(DISABLE_NONE, mMockStatusBar, pkg);
+ // check that right flag is disabled
+ assertEquals(disableFlags,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+ assertEquals(DISABLE2_NONE,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[1]);
+ }
+
+ @Test
+ public void testDifferentUsersDisable() throws Exception {
+ int user1Id = 0;
+ mockUidCheck();
+ mockCurrentUserCheck(user1Id);
+ int user2Id = 14;
+ mockComponentInfo(user2Id);
+ mockEverything(user2Id);
+
+ int expectedUser1Flags = DISABLE_MASK & DISABLE_BACK;
+ int expectedUser2Flags = DISABLE_MASK & DISABLE_HOME;
+ String pkg = mContext.getPackageName();
+
+ // before disabling
+ assertEquals(DISABLE_NONE,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, user1Id)[0]);
+ assertEquals(DISABLE_NONE,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, user2Id)[0]);
+ // disable
+ mStatusBarManagerService.disableForUser(expectedUser1Flags, mMockStatusBar, pkg, user1Id);
+ mStatusBarManagerService.disableForUser(expectedUser2Flags, mMockStatusBar, pkg, user2Id);
+ // check that right flag is disabled
+ assertEquals(expectedUser1Flags,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, user1Id)[0]);
+ assertEquals(expectedUser2Flags,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, user2Id)[0]);
+ }
+
+
private void mockUidCheck() {
mockUidCheck(TEST_PACKAGE);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
new file mode 100644
index 000000000000..b3f150241115
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
@@ -0,0 +1,357 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.window.flags.Flags.FLAG_CAMERA_COMPAT_FOR_FREEFORM;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.CameraCompatTaskInfo;
+import android.app.WindowConfiguration.WindowingMode;
+import android.app.servertransaction.RefreshCallbackItem;
+import android.app.servertransaction.ResumeActivityItem;
+import android.compat.testing.PlatformCompatChangeRule;
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo.ScreenOrientation;
+import android.content.res.Configuration;
+import android.content.res.Configuration.Orientation;
+import android.graphics.Rect;
+import android.hardware.camera2.CameraManager;
+import android.os.Handler;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Tests for {@link CameraCompatFreeformPolicy}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:CameraCompatFreeformPolicyTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class CameraCompatFreeformPolicyTests extends WindowTestsBase {
+ @Rule
+ public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
+ // Main activity package name needs to be the same as the process to test overrides.
+ private static final String TEST_PACKAGE_1 = "com.android.frameworks.wmtests";
+ private static final String TEST_PACKAGE_2 = "com.test.package.two";
+ private static final String CAMERA_ID_1 = "camera-1";
+ private static final String CAMERA_ID_2 = "camera-2";
+ private CameraManager mMockCameraManager;
+ private Handler mMockHandler;
+ private LetterboxConfiguration mLetterboxConfiguration;
+
+ private CameraManager.AvailabilityCallback mCameraAvailabilityCallback;
+ private CameraCompatFreeformPolicy mCameraCompatFreeformPolicy;
+ private ActivityRecord mActivity;
+ private Task mTask;
+ private ActivityRefresher mActivityRefresher;
+
+ @Before
+ public void setUp() throws Exception {
+ mLetterboxConfiguration = mDisplayContent.mWmService.mLetterboxConfiguration;
+ spyOn(mLetterboxConfiguration);
+ when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled())
+ .thenReturn(true);
+ when(mLetterboxConfiguration.isCameraCompatRefreshEnabled())
+ .thenReturn(true);
+ when(mLetterboxConfiguration.isCameraCompatRefreshCycleThroughStopEnabled())
+ .thenReturn(true);
+
+ mMockCameraManager = mock(CameraManager.class);
+ doAnswer(invocation -> {
+ mCameraAvailabilityCallback = invocation.getArgument(1);
+ return null;
+ }).when(mMockCameraManager).registerAvailabilityCallback(
+ any(Executor.class), any(CameraManager.AvailabilityCallback.class));
+
+ when(mContext.getSystemService(CameraManager.class)).thenReturn(mMockCameraManager);
+
+ mDisplayContent.setIgnoreOrientationRequest(true);
+
+ mMockHandler = mock(Handler.class);
+
+ when(mMockHandler.postDelayed(any(Runnable.class), anyLong())).thenAnswer(
+ invocation -> {
+ ((Runnable) invocation.getArgument(0)).run();
+ return null;
+ });
+
+ mActivityRefresher = new ActivityRefresher(mDisplayContent.mWmService, mMockHandler);
+ mSetFlagsRule.enableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM);
+ CameraStateMonitor cameraStateMonitor =
+ new CameraStateMonitor(mDisplayContent, mMockHandler);
+ mCameraCompatFreeformPolicy =
+ new CameraCompatFreeformPolicy(mDisplayContent, cameraStateMonitor,
+ mActivityRefresher);
+
+ mCameraCompatFreeformPolicy.start();
+ cameraStateMonitor.startListeningToCameraState();
+ }
+
+ @Test
+ public void testFullscreen_doesNotActivateCameraCompatMode() {
+ configureActivity(SCREEN_ORIENTATION_PORTRAIT, WINDOWING_MODE_FULLSCREEN);
+ doReturn(false).when(mActivity).inFreeformWindowingMode();
+
+ mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+
+ assertNotInCameraCompatMode();
+ }
+
+ @Test
+ public void testOrientationUnspecified_doesNotActivateCameraCompatMode() {
+ configureActivity(SCREEN_ORIENTATION_UNSPECIFIED);
+
+ assertNotInCameraCompatMode();
+ }
+
+ @Test
+ public void testNoCameraConnection_doesNotActivateCameraCompatMode() {
+ configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+ assertNotInCameraCompatMode();
+ }
+
+ @Test
+ public void testCameraConnected_activatesCameraCompatMode() throws Exception {
+ configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+ mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+
+ assertInCameraCompatMode();
+ assertActivityRefreshRequested(/* refreshRequested */ false);
+ }
+
+ @Test
+ public void testCameraReconnected_cameraCompatModeAndRefresh() throws Exception {
+ configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+
+ mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+ mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1);
+ mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+ callOnActivityConfigurationChanging(mActivity);
+
+ assertInCameraCompatMode();
+ assertActivityRefreshRequested(/* refreshRequested */ true);
+ }
+
+ @Test
+ public void testReconnectedToDifferentCamera_activatesCameraCompatModeAndRefresh()
+ throws Exception {
+ configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+
+ mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+ mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1);
+ mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_2, TEST_PACKAGE_1);
+ callOnActivityConfigurationChanging(mActivity);
+
+ assertInCameraCompatMode();
+ assertActivityRefreshRequested(/* refreshRequested */ true);
+ }
+
+ @Test
+ public void testCameraDisconnected_deactivatesCameraCompatMode() {
+ configureActivityAndDisplay(SCREEN_ORIENTATION_PORTRAIT, ORIENTATION_LANDSCAPE,
+ WINDOWING_MODE_FREEFORM);
+ // Open camera and test for compat treatment
+ mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+ assertInCameraCompatMode();
+
+ // Close camera and test for revert
+ mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1);
+
+ assertNotInCameraCompatMode();
+ }
+
+ @Test
+ public void testCameraOpenedForDifferentPackage_notInCameraCompatMode() {
+ configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+
+ mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_2);
+
+ assertNotInCameraCompatMode();
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT})
+ public void testShouldApplyCameraCompatFreeformTreatment_overrideEnabled_returnsFalse() {
+ configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+
+ assertTrue(mActivity.info
+ .isChangeEnabled(OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT));
+ assertFalse(mCameraCompatFreeformPolicy
+ .shouldApplyFreeformTreatmentForCameraCompat(mActivity));
+ }
+
+ @Test
+ public void testShouldApplyCameraCompatFreeformTreatment_notDisabledByOverride_returnsTrue() {
+ configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+
+ assertTrue(mCameraCompatFreeformPolicy
+ .shouldApplyFreeformTreatmentForCameraCompat(mActivity));
+ }
+
+ @Test
+ public void testOnActivityConfigurationChanging_refreshDisabledViaFlag_noRefresh()
+ throws Exception {
+ configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+
+ doReturn(false).when(
+ mActivity.mLetterboxUiController).shouldRefreshActivityForCameraCompat();
+
+ mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+ callOnActivityConfigurationChanging(mActivity);
+
+ assertActivityRefreshRequested(/* refreshRequested */ false);
+ }
+
+ @Test
+ public void testOnActivityConfigurationChanging_cycleThroughStopDisabled() throws Exception {
+ when(mLetterboxConfiguration.isCameraCompatRefreshCycleThroughStopEnabled())
+ .thenReturn(false);
+
+ configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+
+ mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+ callOnActivityConfigurationChanging(mActivity);
+
+ assertActivityRefreshRequested(/* refreshRequested */ true, /* cycleThroughStop */ false);
+ }
+
+ @Test
+ public void testOnActivityConfigurationChanging_cycleThroughStopDisabledForApp()
+ throws Exception {
+ configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+ doReturn(true).when(mActivity.mLetterboxUiController)
+ .shouldRefreshActivityViaPauseForCameraCompat();
+
+ mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+ callOnActivityConfigurationChanging(mActivity);
+
+ assertActivityRefreshRequested(/* refreshRequested */ true, /* cycleThroughStop */ false);
+ }
+
+ private void configureActivity(@ScreenOrientation int activityOrientation) {
+ configureActivity(activityOrientation, WINDOWING_MODE_FREEFORM);
+ }
+
+ private void configureActivity(@ScreenOrientation int activityOrientation,
+ @WindowingMode int windowingMode) {
+ configureActivityAndDisplay(activityOrientation, ORIENTATION_PORTRAIT, windowingMode);
+ }
+
+ private void configureActivityAndDisplay(@ScreenOrientation int activityOrientation,
+ @Orientation int naturalOrientation, @WindowingMode int windowingMode) {
+ mTask = new TaskBuilder(mSupervisor)
+ .setDisplay(mDisplayContent)
+ .setWindowingMode(windowingMode)
+ .build();
+
+ mActivity = new ActivityBuilder(mAtm)
+ // Set the component to be that of the test class in order to enable compat changes
+ .setComponent(ComponentName.createRelative(mContext,
+ com.android.server.wm.CameraCompatFreeformPolicyTests.class.getName()))
+ .setScreenOrientation(activityOrientation)
+ .setTask(mTask)
+ .build();
+
+ spyOn(mActivity.mLetterboxUiController);
+ spyOn(mActivity.info);
+
+ doReturn(mActivity).when(mDisplayContent).topRunningActivity(anyBoolean());
+ doReturn(naturalOrientation).when(mDisplayContent).getNaturalOrientation();
+
+ doReturn(true).when(mActivity).inFreeformWindowingMode();
+ }
+
+ private void assertInCameraCompatMode() {
+ assertNotEquals(CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE,
+ mActivity.mLetterboxUiController.getFreeformCameraCompatMode());
+ }
+
+ private void assertNotInCameraCompatMode() {
+ assertEquals(CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE,
+ mActivity.mLetterboxUiController.getFreeformCameraCompatMode());
+ }
+
+ private void assertActivityRefreshRequested(boolean refreshRequested) throws Exception {
+ assertActivityRefreshRequested(refreshRequested, /* cycleThroughStop*/ true);
+ }
+
+ private void assertActivityRefreshRequested(boolean refreshRequested,
+ boolean cycleThroughStop) throws Exception {
+ verify(mActivity.mLetterboxUiController, times(refreshRequested ? 1 : 0))
+ .setIsRefreshRequested(true);
+
+ final RefreshCallbackItem refreshCallbackItem = RefreshCallbackItem.obtain(mActivity.token,
+ cycleThroughStop ? ON_STOP : ON_PAUSE);
+ final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(mActivity.token,
+ /* isForward */ false, /* shouldSendCompatFakeFocus */ false);
+
+ verify(mActivity.mAtmService.getLifecycleManager(), times(refreshRequested ? 1 : 0))
+ .scheduleTransactionAndLifecycleItems(mActivity.app.getThread(),
+ refreshCallbackItem, resumeActivityItem);
+ }
+
+ private void callOnActivityConfigurationChanging(ActivityRecord activity) {
+ mActivityRefresher.onActivityConfigurationChanging(activity,
+ /* newConfig */ createConfiguration(/*letterbox=*/ true),
+ /* lastReportedConfig */ createConfiguration(/*letterbox=*/ false));
+ }
+
+ private Configuration createConfiguration(boolean letterbox) {
+ final Configuration configuration = new Configuration();
+ Rect bounds = letterbox ? new Rect(300, 0, 700, 600) : new Rect(0, 0, 1000, 600);
+ configuration.windowConfiguration.setAppBounds(bounds);
+ return configuration;
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 417ee6be17bc..695750217cac 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -83,6 +83,8 @@ import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_TOKEN_TRANSFO
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
+import static com.android.window.flags.Flags.FLAG_CAMERA_COMPAT_FOR_FREEFORM;
+import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE;
import static com.google.common.truth.Truth.assertThat;
@@ -115,6 +117,8 @@ import android.os.Binder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.Presubmit;
import android.util.ArraySet;
import android.view.Display;
@@ -2822,6 +2826,31 @@ public class DisplayContentTests extends WindowTestsBase {
verify(mWm.mUmInternal, never()).isUserVisible(userId2, displayId);
}
+ @EnableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+ @Test
+ public void cameraCompatFreeformFlagEnabled_cameraCompatFreeformPolicyNotNull() {
+ doReturn(true).when(() ->
+ DesktopModeLaunchParamsModifier.canEnterDesktopMode(any()));
+
+ assertNotNull(createNewDisplay().mCameraCompatFreeformPolicy);
+ }
+
+ @DisableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+ @Test
+ public void cameraCompatFreeformFlagNotEnabled_cameraCompatFreeformPolicyIsNull() {
+ doReturn(true).when(() ->
+ DesktopModeLaunchParamsModifier.canEnterDesktopMode(any()));
+
+ assertNull(createNewDisplay().mCameraCompatFreeformPolicy);
+ }
+
+ @EnableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+ @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ @Test
+ public void desktopWindowingFlagNotEnabled_cameraCompatFreeformPolicyIsNull() {
+ assertNull(createNewDisplay().mCameraCompatFreeformPolicy);
+ }
+
private void removeRootTaskTests(Runnable runnable) {
final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
final Task rootTask1 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
index c76acd7e1d6b..c65371fc3320 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
@@ -142,6 +142,7 @@ public final class DisplayRotationCompatPolicyTests extends WindowTestsBase {
doNothing().when(mDisplayRotationCompatPolicy).showToast(anyInt());
doNothing().when(mDisplayRotationCompatPolicy).showToast(anyInt(), anyString());
+ mDisplayRotationCompatPolicy.start();
cameraStateMonitor.startListeningToCameraState();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 1a366b3e3a4f..ac1aa20b322f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -4121,6 +4121,35 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
+ public void testUpdateResolvedBoundsVerticalPosition_unfoldDisplay_notTabletop() {
+ // Set up a display in portrait with a fixed-orientation LANDSCAPE app.
+ setUpDisplaySizeWithApp(1000, 2000);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mActivity.mWmService.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(
+ 1.0f /*letterboxVerticalPositionMultiplier*/);
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
+
+ // Make the activity full-screen.
+ mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+ // Simulate display unfolding.
+ setFoldablePosture(true /* isHalfFolded */, true /* isTabletop */);
+ doReturn(true).when(mActivity.mDisplayContent).inTransition();
+ resizeDisplay(mTask.mDisplayContent, 1400, 2800);
+
+ // Make sure app doesn't jump to top (default tabletop position) when unfolding.
+ assertEquals(1.0f, mActivity.mLetterboxUiController.getVerticalPositionMultiplier(
+ mActivity.getParent().getConfiguration()), 0);
+
+ // Simulate display fully open after unfolding.
+ setFoldablePosture(false /* isHalfFolded */, false /* isTabletop */);
+ doReturn(false).when(mActivity.mDisplayContent).inTransition();
+
+ assertEquals(1.0f, mActivity.mLetterboxUiController.getVerticalPositionMultiplier(
+ mActivity.getParent().getConfiguration()), 0);
+ }
+
+ @Test
public void testGetFixedOrientationLetterboxAspectRatio_tabletop_centered() {
// Set up a display in portrait with a fixed-orientation LANDSCAPE app
setUpDisplaySizeWithApp(1400, 2800);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index c45c86cec5a7..c962a3f9ea4d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -445,6 +445,9 @@ public class SystemServicesTestRule implements TestRule {
if (dc.mDisplayRotationCompatPolicy != null) {
dc.mDisplayRotationCompatPolicy.dispose();
}
+ if (dc.mCameraCompatFreeformPolicy != null) {
+ dc.mCameraCompatFreeformPolicy.dispose();
+ }
if (dc.mCameraStateMonitor != null) {
dc.mCameraStateMonitor.dispose();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 6ecaea90b85b..e01cea3d62f8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -69,6 +69,7 @@ import static org.mockito.Mockito.never;
import android.app.ActivityManager;
import android.app.ActivityOptions;
+import android.app.CameraCompatTaskInfo;
import android.app.TaskInfo;
import android.app.WindowConfiguration;
import android.content.ComponentName;
@@ -1986,6 +1987,17 @@ public class TaskTests extends WindowTestsBase {
assertNotEquals(activityDifferentPackage, task.getBottomMostActivityInSamePackage());
}
+ @Test
+ public void getTaskInfoPropagatesCameraCompatMode() {
+ final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
+ final ActivityRecord activity = task.getTopMostActivity();
+ activity.mLetterboxUiController
+ .setFreeformCameraCompatMode(CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_PORTRAIT);
+
+ assertEquals(CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_PORTRAIT,
+ task.getTaskInfo().appCompatTaskInfo.cameraCompatTaskInfo.freeformCameraCompatMode);
+ }
+
private Task getTestTask() {
return new TaskBuilder(mSupervisor).setCreateActivity(true).build();
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategory.java
index d551953477d8..c16d18b34360 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategory.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategory.java
@@ -23,6 +23,7 @@ import com.android.asllib.util.XmlUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -40,6 +41,11 @@ public class DataCategory implements AslMarshallable {
this.mDataTypes = dataTypes;
}
+ public DataCategory(String categoryName) {
+ this.mCategoryName = categoryName;
+ this.mDataTypes = new LinkedHashMap<String, DataType>();
+ }
+
public String getCategoryName() {
return mCategoryName;
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategoryFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategoryFactory.java
index 90424fe00504..724416285acd 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategoryFactory.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategoryFactory.java
@@ -26,33 +26,8 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-public class DataCategoryFactory implements AslMarshallableFactory<DataCategory> {
- @Override
- public DataCategory createFromHrElements(List<Element> elements) throws MalformedXmlException {
- String categoryName = null;
- Map<String, DataType> dataTypeMap = new LinkedHashMap<String, DataType>();
- for (Element ele : elements) {
- categoryName = XmlUtils.getStringAttr(ele, XmlUtils.HR_ATTR_DATA_CATEGORY, true);
- String dataTypeName = XmlUtils.getStringAttr(ele, XmlUtils.HR_ATTR_DATA_TYPE, true);
- if (!DataTypeConstants.getValidDataTypes().containsKey(categoryName)) {
- throw new MalformedXmlException(
- String.format("Unrecognized data category %s", categoryName));
- }
- if (!DataTypeConstants.getValidDataTypes().get(categoryName).contains(dataTypeName)) {
- throw new MalformedXmlException(
- String.format(
- "Unrecognized data type name %s for category %s",
- dataTypeName, categoryName));
- }
- dataTypeMap.put(
- dataTypeName, new DataTypeFactory().createFromHrElements(XmlUtils.listOf(ele)));
- }
-
- return new DataCategory(categoryName, dataTypeMap);
- }
-
+public class DataCategoryFactory {
/** Creates an {@link AslMarshallableFactory} from on-device DOM elements */
- @Override
public DataCategory createFromOdElements(List<Element> elements) throws MalformedXmlException {
Element dataCategoryEle = XmlUtils.getSingleElement(elements);
Map<String, DataType> dataTypeMap = new LinkedHashMap<String, DataType>();
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabels.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabels.java
index 4a0d75977d78..ba0e3db52027 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabels.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabels.java
@@ -30,28 +30,17 @@ import java.util.Map;
* DataCategory}
*/
public class DataLabels implements AslMarshallable {
- private final Map<String, DataCategory> mDataAccessed;
private final Map<String, DataCategory> mDataCollected;
private final Map<String, DataCategory> mDataShared;
public DataLabels(
- Map<String, DataCategory> dataAccessed,
Map<String, DataCategory> dataCollected,
Map<String, DataCategory> dataShared) {
- mDataAccessed = dataAccessed;
mDataCollected = dataCollected;
mDataShared = dataShared;
}
/**
- * Returns the data accessed {@link Map} of {@link DataCategoryConstants} to {@link
- * DataCategory}
- */
- public Map<String, DataCategory> getDataAccessed() {
- return mDataAccessed;
- }
-
- /**
* Returns the data collected {@link Map} of {@link DataCategoryConstants} to {@link
* DataCategory}
*/
@@ -72,7 +61,6 @@ public class DataLabels implements AslMarshallable {
Element dataLabelsEle =
XmlUtils.createPbundleEleWithName(doc, XmlUtils.OD_NAME_DATA_LABELS);
- maybeAppendDataUsages(doc, dataLabelsEle, mDataAccessed, XmlUtils.OD_NAME_DATA_ACCESSED);
maybeAppendDataUsages(doc, dataLabelsEle, mDataCollected, XmlUtils.OD_NAME_DATA_COLLECTED);
maybeAppendDataUsages(doc, dataLabelsEle, mDataShared, XmlUtils.OD_NAME_DATA_SHARED);
@@ -83,9 +71,12 @@ public class DataLabels implements AslMarshallable {
@Override
public List<Element> toHrDomElements(Document doc) {
Element dataLabelsEle = doc.createElement(XmlUtils.HR_TAG_DATA_LABELS);
- maybeAppendHrDataUsages(doc, dataLabelsEle, mDataAccessed, XmlUtils.HR_TAG_DATA_ACCESSED);
- maybeAppendHrDataUsages(doc, dataLabelsEle, mDataCollected, XmlUtils.HR_TAG_DATA_COLLECTED);
- maybeAppendHrDataUsages(doc, dataLabelsEle, mDataShared, XmlUtils.HR_TAG_DATA_SHARED);
+ maybeAppendHrDataUsages(
+ doc, dataLabelsEle, mDataCollected, XmlUtils.HR_TAG_DATA_COLLECTED, false);
+ maybeAppendHrDataUsages(
+ doc, dataLabelsEle, mDataCollected, XmlUtils.HR_TAG_DATA_COLLECTED_EPHEMERAL, true);
+ maybeAppendHrDataUsages(
+ doc, dataLabelsEle, mDataShared, XmlUtils.HR_TAG_DATA_SHARED, false);
return XmlUtils.listOf(dataLabelsEle);
}
@@ -115,7 +106,8 @@ public class DataLabels implements AslMarshallable {
Document doc,
Element dataLabelsEle,
Map<String, DataCategory> dataCategoriesMap,
- String dataUsageTypeName) {
+ String dataUsageTypeName,
+ boolean ephemeral) {
if (dataCategoriesMap.isEmpty()) {
return;
}
@@ -123,10 +115,15 @@ public class DataLabels implements AslMarshallable {
DataCategory dataCategory = dataCategoriesMap.get(dataCategoryName);
for (String dataTypeName : dataCategory.getDataTypes().keySet()) {
DataType dataType = dataCategory.getDataTypes().get(dataTypeName);
- // XmlUtils.appendChildren(dataLabelsEle, dataType.toHrDomElements(doc));
+ if (ephemeral
+ != (dataType.getEphemeral() != null ? dataType.getEphemeral() : false)) {
+ continue;
+ }
+
Element hrDataTypeEle = doc.createElement(dataUsageTypeName);
- hrDataTypeEle.setAttribute(XmlUtils.HR_ATTR_DATA_CATEGORY, dataCategoryName);
- hrDataTypeEle.setAttribute(XmlUtils.HR_ATTR_DATA_TYPE, dataTypeName);
+ hrDataTypeEle.setAttribute(
+ XmlUtils.HR_ATTR_DATA_TYPE,
+ dataCategoryName + XmlUtils.DATA_TYPE_SEPARATOR + dataTypeName);
XmlUtils.maybeSetHrBoolAttr(
hrDataTypeEle,
XmlUtils.HR_ATTR_IS_COLLECTION_OPTIONAL,
@@ -135,8 +132,6 @@ public class DataLabels implements AslMarshallable {
hrDataTypeEle,
XmlUtils.HR_ATTR_IS_SHARING_OPTIONAL,
dataType.getIsSharingOptional());
- XmlUtils.maybeSetHrBoolAttr(
- hrDataTypeEle, XmlUtils.HR_ATTR_EPHEMERAL, dataType.getEphemeral());
hrDataTypeEle.setAttribute(
XmlUtils.HR_ATTR_PURPOSES,
String.join(
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabelsFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabelsFactory.java
index 5473e010cc65..c4d88761835a 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabelsFactory.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabelsFactory.java
@@ -18,16 +18,15 @@ package com.android.asllib.marshallable;
import com.android.asllib.util.AslgenUtil;
import com.android.asllib.util.DataCategoryConstants;
+import com.android.asllib.util.DataTypeConstants;
import com.android.asllib.util.MalformedXmlException;
import com.android.asllib.util.XmlUtils;
import org.w3c.dom.Element;
-import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Set;
public class DataLabelsFactory implements AslMarshallableFactory<DataLabels> {
@@ -39,13 +38,46 @@ public class DataLabelsFactory implements AslMarshallableFactory<DataLabels> {
AslgenUtil.logI("Found no DataLabels in hr format.");
return null;
}
- Map<String, DataCategory> dataAccessed =
- getDataCategoriesWithTag(ele, XmlUtils.HR_TAG_DATA_ACCESSED);
Map<String, DataCategory> dataCollected =
- getDataCategoriesWithTag(ele, XmlUtils.HR_TAG_DATA_COLLECTED);
+ getDataCategoriesWithTag(ele, XmlUtils.HR_TAG_DATA_COLLECTED, false);
+ Map<String, DataCategory> dataCollectedEphemeral =
+ getDataCategoriesWithTag(ele, XmlUtils.HR_TAG_DATA_COLLECTED_EPHEMERAL, true);
Map<String, DataCategory> dataShared =
- getDataCategoriesWithTag(ele, XmlUtils.HR_TAG_DATA_SHARED);
- DataLabels dataLabels = new DataLabels(dataAccessed, dataCollected, dataShared);
+ getDataCategoriesWithTag(ele, XmlUtils.HR_TAG_DATA_SHARED, null);
+
+ for (String dataCollectedEphemeralDataCategoryKey : dataCollectedEphemeral.keySet()) {
+ DataCategory dataCategoryEphemeral =
+ dataCollectedEphemeral.get(dataCollectedEphemeralDataCategoryKey);
+ for (String dataCollectedEphemeralDataTypeKey :
+ dataCategoryEphemeral.getDataTypes().keySet()) {
+ if (dataCollected.containsKey(dataCollectedEphemeralDataCategoryKey)
+ && dataCollected
+ .get(dataCollectedEphemeralDataCategoryKey)
+ .getDataTypes()
+ .containsKey(dataCollectedEphemeralDataTypeKey)) {
+ throw new MalformedXmlException(
+ String.format(
+ "Duplicate entries in data-collected and"
+ + " data-collected-ephemeral: %s %s",
+ dataCollectedEphemeralDataCategoryKey,
+ dataCollectedEphemeralDataTypeKey));
+ }
+
+ if (!dataCollected.containsKey(dataCollectedEphemeralDataCategoryKey)) {
+ dataCollected.put(
+ dataCollectedEphemeralDataCategoryKey,
+ new DataCategory(dataCollectedEphemeralDataCategoryKey));
+ }
+ DataType dataTypeEphemeral =
+ dataCategoryEphemeral.getDataTypes().get(dataCollectedEphemeralDataTypeKey);
+ dataCollected
+ .get(dataCollectedEphemeralDataCategoryKey)
+ .getDataTypes()
+ .put(dataCollectedEphemeralDataTypeKey, dataTypeEphemeral);
+ }
+ }
+ DataLabels dataLabels = new DataLabels(dataCollected, dataShared);
+
validateIsXOptional(dataLabels);
return dataLabels;
}
@@ -58,13 +90,11 @@ public class DataLabelsFactory implements AslMarshallableFactory<DataLabels> {
AslgenUtil.logI("Found no DataLabels in od format.");
return null;
}
- Map<String, DataCategory> dataAccessed =
- getOdDataCategoriesWithTag(dataLabelsEle, XmlUtils.OD_NAME_DATA_ACCESSED);
Map<String, DataCategory> dataCollected =
getOdDataCategoriesWithTag(dataLabelsEle, XmlUtils.OD_NAME_DATA_COLLECTED);
Map<String, DataCategory> dataShared =
getOdDataCategoriesWithTag(dataLabelsEle, XmlUtils.OD_NAME_DATA_SHARED);
- DataLabels dataLabels = new DataLabels(dataAccessed, dataCollected, dataShared);
+ DataLabels dataLabels = new DataLabels(dataCollected, dataShared);
validateIsXOptional(dataLabels);
return dataLabels;
}
@@ -88,56 +118,56 @@ public class DataLabelsFactory implements AslMarshallableFactory<DataLabels> {
}
private static Map<String, DataCategory> getDataCategoriesWithTag(
- Element dataLabelsEle, String dataCategoryUsageTypeTag) throws MalformedXmlException {
+ Element dataLabelsEle, String dataCategoryUsageTypeTag, Boolean ephemeral)
+ throws MalformedXmlException {
List<Element> dataUsedElements =
XmlUtils.getChildrenByTagName(dataLabelsEle, dataCategoryUsageTypeTag);
Map<String, DataCategory> dataCategoryMap = new LinkedHashMap<String, DataCategory>();
- Set<String> dataCategoryNames = new HashSet<String>();
for (int i = 0; i < dataUsedElements.size(); i++) {
Element dataUsedEle = dataUsedElements.get(i);
- String dataCategoryName = dataUsedEle.getAttribute(XmlUtils.HR_ATTR_DATA_CATEGORY);
+ String dataCategoryAndTypeCombinedStr =
+ dataUsedEle.getAttribute(XmlUtils.HR_ATTR_DATA_TYPE);
+ String[] strs = dataCategoryAndTypeCombinedStr.split(XmlUtils.DATA_TYPE_SEPARATOR);
+ if (strs.length != 2) {
+ throw new MalformedXmlException(
+ String.format(
+ "Could not parse human-readable data type string (expecting"
+ + " substring of _data_type_): %s",
+ dataCategoryAndTypeCombinedStr));
+ }
+ String dataCategoryName = strs[0];
+ String dataTypeName = strs[1];
+
if (!DataCategoryConstants.getValidDataCategories().contains(dataCategoryName)) {
throw new MalformedXmlException(
String.format("Unrecognized category name: %s", dataCategoryName));
}
- dataCategoryNames.add(dataCategoryName);
- }
- for (String dataCategoryName : dataCategoryNames) {
- var dataCategoryElements =
- dataUsedElements.stream()
- .filter(
- ele ->
- ele.getAttribute(XmlUtils.HR_ATTR_DATA_CATEGORY)
- .equals(dataCategoryName))
- .toList();
- DataCategory dataCategory =
- new DataCategoryFactory().createFromHrElements(dataCategoryElements);
- dataCategoryMap.put(dataCategoryName, dataCategory);
+ if (!DataTypeConstants.getValidDataTypes()
+ .get(dataCategoryName)
+ .contains(dataTypeName)) {
+ throw new MalformedXmlException(
+ String.format(
+ "Unrecognized data type name %s for category %s",
+ dataTypeName, dataCategoryName));
+ }
+
+ if (!dataCategoryMap.containsKey(dataCategoryName)) {
+ dataCategoryMap.put(dataCategoryName, new DataCategory(dataCategoryName));
+ }
+ dataCategoryMap
+ .get(dataCategoryName)
+ .getDataTypes()
+ .put(
+ dataTypeName,
+ new DataTypeFactory().createFromHrElements(dataUsedEle, ephemeral));
}
+
return dataCategoryMap;
}
private void validateIsXOptional(DataLabels dataLabels) throws MalformedXmlException {
// Validate booleans such as isCollectionOptional, isSharingOptional.
- for (DataCategory dataCategory : dataLabels.getDataAccessed().values()) {
- for (DataType dataType : dataCategory.getDataTypes().values()) {
- if (dataType.getIsSharingOptional() != null) {
- throw new MalformedXmlException(
- String.format(
- "isSharingOptional was unexpectedly defined on a DataType"
- + " belonging to data accessed: %s",
- dataType.getDataTypeName()));
- }
- if (dataType.getIsCollectionOptional() != null) {
- throw new MalformedXmlException(
- String.format(
- "isCollectionOptional was unexpectedly defined on a DataType"
- + " belonging to data accessed: %s",
- dataType.getDataTypeName()));
- }
- }
- }
for (DataCategory dataCategory : dataLabels.getDataCollected().values()) {
for (DataType dataType : dataCategory.getDataTypes().values()) {
if (dataType.getIsSharingOptional() != null) {
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataTypeFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataTypeFactory.java
index 488c2595912a..a5559d801349 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataTypeFactory.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataTypeFactory.java
@@ -25,12 +25,22 @@ import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;
-public class DataTypeFactory implements AslMarshallableFactory<DataType> {
+public class DataTypeFactory {
/** Creates a {@link DataType} from the human-readable DOM element. */
- @Override
- public DataType createFromHrElements(List<Element> elements) throws MalformedXmlException {
- Element hrDataTypeEle = XmlUtils.getSingleElement(elements);
- String dataTypeName = hrDataTypeEle.getAttribute(XmlUtils.HR_ATTR_DATA_TYPE);
+ public DataType createFromHrElements(Element hrDataTypeEle, Boolean ephemeral)
+ throws MalformedXmlException {
+ String dataCategoryAndTypeCombinedStr =
+ hrDataTypeEle.getAttribute(XmlUtils.HR_ATTR_DATA_TYPE);
+ String[] strs = dataCategoryAndTypeCombinedStr.split(XmlUtils.DATA_TYPE_SEPARATOR);
+ if (strs.length != 2) {
+ throw new MalformedXmlException(
+ String.format(
+ "Could not parse human-readable data type string (expecting substring"
+ + " of _data_type_): %s",
+ dataCategoryAndTypeCombinedStr));
+ }
+ String dataTypeName = strs[1];
+
List<DataType.Purpose> purposes =
XmlUtils.getPipelineSplitAttr(hrDataTypeEle, XmlUtils.HR_ATTR_PURPOSES, true)
.stream()
@@ -47,13 +57,13 @@ public class DataTypeFactory implements AslMarshallableFactory<DataType> {
XmlUtils.getBoolAttr(hrDataTypeEle, XmlUtils.HR_ATTR_IS_COLLECTION_OPTIONAL, false);
Boolean isSharingOptional =
XmlUtils.getBoolAttr(hrDataTypeEle, XmlUtils.HR_ATTR_IS_SHARING_OPTIONAL, false);
- Boolean ephemeral = XmlUtils.getBoolAttr(hrDataTypeEle, XmlUtils.HR_ATTR_EPHEMERAL, false);
+ // Boolean ephemeral = XmlUtils.getBoolAttr(hrDataTypeEle, XmlUtils.HR_ATTR_EPHEMERAL,
+ // false);
return new DataType(
dataTypeName, purposes, isCollectionOptional, isSharingOptional, ephemeral);
}
/** Creates an {@link AslMarshallableFactory} from on-device DOM elements */
- @Override
public DataType createFromOdElements(List<Element> elements) throws MalformedXmlException {
Element odDataTypeEle = XmlUtils.getSingleElement(elements);
String dataTypeName = odDataTypeEle.getAttribute(XmlUtils.OD_ATTR_NAME);
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabel.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabel.java
index 854c0d0ac3e1..242e7be66d76 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabel.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabel.java
@@ -26,15 +26,10 @@ import java.util.List;
/** Safety Label representation containing zero or more {@link DataCategory} for data shared */
public class SystemAppSafetyLabel implements AslMarshallable {
- private final String mUrl;
+ private final Boolean mDeclaration;
- public SystemAppSafetyLabel(String url) {
- this.mUrl = url;
- }
-
- /** Returns the system app safety label URL. */
- public String getUrl() {
- return mUrl;
+ public SystemAppSafetyLabel(Boolean d) {
+ this.mDeclaration = d;
}
/** Creates an on-device DOM element from the {@link SystemAppSafetyLabel}. */
@@ -43,7 +38,7 @@ public class SystemAppSafetyLabel implements AslMarshallable {
Element systemAppSafetyLabelEle =
XmlUtils.createPbundleEleWithName(doc, XmlUtils.OD_NAME_SYSTEM_APP_SAFETY_LABEL);
systemAppSafetyLabelEle.appendChild(
- XmlUtils.createOdStringEle(doc, XmlUtils.OD_NAME_URL, mUrl));
+ XmlUtils.createOdBooleanEle(doc, XmlUtils.OD_NAME_DECLARATION, mDeclaration));
return XmlUtils.listOf(systemAppSafetyLabelEle);
}
@@ -52,7 +47,8 @@ public class SystemAppSafetyLabel implements AslMarshallable {
public List<Element> toHrDomElements(Document doc) {
Element systemAppSafetyLabelEle =
doc.createElement(XmlUtils.HR_TAG_SYSTEM_APP_SAFETY_LABEL);
- systemAppSafetyLabelEle.setAttribute(XmlUtils.HR_ATTR_URL, mUrl);
+ XmlUtils.maybeSetHrBoolAttr(
+ systemAppSafetyLabelEle, XmlUtils.HR_ATTR_DECLARATION, mDeclaration);
return XmlUtils.listOf(systemAppSafetyLabelEle);
}
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabelFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabelFactory.java
index c8e22b6c42cd..7f4aa7a3b690 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabelFactory.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabelFactory.java
@@ -36,8 +36,9 @@ public class SystemAppSafetyLabelFactory implements AslMarshallableFactory<Syste
return null;
}
- String url = XmlUtils.getStringAttr(systemAppSafetyLabelEle, XmlUtils.HR_ATTR_URL, true);
- return new SystemAppSafetyLabel(url);
+ Boolean declaration =
+ XmlUtils.getBoolAttr(systemAppSafetyLabelEle, XmlUtils.HR_ATTR_DECLARATION, true);
+ return new SystemAppSafetyLabel(declaration);
}
/** Creates an {@link AslMarshallableFactory} from on-device DOM elements */
@@ -49,7 +50,8 @@ public class SystemAppSafetyLabelFactory implements AslMarshallableFactory<Syste
AslgenUtil.logI("No SystemAppSafetyLabel found in od format.");
return null;
}
- String url = XmlUtils.getOdStringEle(systemAppSafetyLabelEle, XmlUtils.OD_NAME_URL, true);
- return new SystemAppSafetyLabel(url);
+ Boolean declaration =
+ XmlUtils.getOdBoolEle(systemAppSafetyLabelEle, XmlUtils.OD_NAME_DECLARATION, true);
+ return new SystemAppSafetyLabel(declaration);
}
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/XmlUtils.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/XmlUtils.java
index 1d54ead0a28d..97cbc3950490 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/XmlUtils.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/XmlUtils.java
@@ -27,6 +27,8 @@ import java.util.Arrays;
import java.util.List;
public class XmlUtils {
+ public static final String DATA_TYPE_SEPARATOR = "_data_type_";
+
public static final String HR_TAG_APP_METADATA_BUNDLES = "app-metadata-bundles";
public static final String HR_TAG_SYSTEM_APP_SAFETY_LABEL = "system-app-safety-label";
public static final String HR_TAG_SAFETY_LABELS = "safety-labels";
@@ -38,6 +40,7 @@ public class XmlUtils {
public static final String HR_TAG_THIRD_PARTY_VERIFICATION = "third-party-verification";
public static final String HR_TAG_DATA_ACCESSED = "data-accessed";
public static final String HR_TAG_DATA_COLLECTED = "data-collected";
+ public static final String HR_TAG_DATA_COLLECTED_EPHEMERAL = "data-collected-ephemeral";
public static final String HR_TAG_DATA_SHARED = "data-shared";
public static final String HR_ATTR_NAME = "name";
public static final String HR_ATTR_EMAIL = "email";
@@ -52,10 +55,11 @@ public class XmlUtils {
public static final String HR_ATTR_IS_SHARING_OPTIONAL = "isSharingOptional";
public static final String HR_ATTR_IS_DATA_DELETABLE = "isDataDeletable";
public static final String HR_ATTR_IS_DATA_ENCRYPTED = "isDataEncrypted";
- public static final String HR_ATTR_EPHEMERAL = "ephemeral";
+ // public static final String HR_ATTR_EPHEMERAL = "ephemeral";
public static final String HR_ATTR_PURPOSES = "purposes";
public static final String HR_ATTR_VERSION = "version";
public static final String HR_ATTR_URL = "url";
+ public static final String HR_ATTR_DECLARATION = "declaration";
public static final String HR_ATTR_TITLE = "title";
public static final String HR_ATTR_DESCRIPTION = "description";
public static final String HR_ATTR_CONTAINS_ADS = "containsAds";
@@ -103,6 +107,7 @@ public class XmlUtils {
public static final String OD_NAME_CATEGORY = "category";
public static final String OD_NAME_VERSION = "version";
public static final String OD_NAME_URL = "url";
+ public static final String OD_NAME_DECLARATION = "declaration";
public static final String OD_NAME_SYSTEM_APP_SAFETY_LABEL = "system_app_safety_label";
public static final String OD_NAME_SECURITY_LABELS = "security_labels";
public static final String OD_NAME_THIRD_PARTY_VERIFICATION = "third_party_verification";
@@ -299,12 +304,13 @@ public class XmlUtils {
.toList();
if (boolEles.size() > 1) {
throw new MalformedXmlException(
- String.format("Found more than one %s in %s.", nameName, ele.getTagName()));
+ String.format(
+ "Found more than one boolean %s in %s.", nameName, ele.getTagName()));
}
if (boolEles.isEmpty()) {
if (required) {
throw new MalformedXmlException(
- String.format("Found no %s in %s.", nameName, ele.getTagName()));
+ String.format("Found no boolean %s in %s.", nameName, ele.getTagName()));
}
return null;
}
@@ -329,12 +335,13 @@ public class XmlUtils {
.toList();
if (longEles.size() > 1) {
throw new MalformedXmlException(
- String.format("Found more than one %s in %s.", nameName, ele.getTagName()));
+ String.format(
+ "Found more than one long %s in %s.", nameName, ele.getTagName()));
}
if (longEles.isEmpty()) {
if (required) {
throw new MalformedXmlException(
- String.format("Found no %s in %s.", nameName, ele.getTagName()));
+ String.format("Found no long %s in %s.", nameName, ele.getTagName()));
}
return null;
}
@@ -359,12 +366,13 @@ public class XmlUtils {
.toList();
if (eles.size() > 1) {
throw new MalformedXmlException(
- String.format("Found more than one %s in %s.", nameName, ele.getTagName()));
+ String.format(
+ "Found more than one string %s in %s.", nameName, ele.getTagName()));
}
if (eles.isEmpty()) {
if (required) {
throw new MalformedXmlException(
- String.format("Found no %s in %s.", nameName, ele.getTagName()));
+ String.format("Found no string %s in %s.", nameName, ele.getTagName()));
}
return null;
}
@@ -386,12 +394,13 @@ public class XmlUtils {
.toList();
if (eles.size() > 1) {
throw new MalformedXmlException(
- String.format("Found more than one %s in %s.", nameName, ele.getTagName()));
+ String.format(
+ "Found more than one pbundle %s in %s.", nameName, ele.getTagName()));
}
if (eles.isEmpty()) {
if (required) {
throw new MalformedXmlException(
- String.format("Found no %s in %s.", nameName, ele.getTagName()));
+ String.format("Found no pbundle %s in %s.", nameName, ele.getTagName()));
}
return null;
}
@@ -456,12 +465,15 @@ public class XmlUtils {
.toList();
if (arrayEles.size() > 1) {
throw new MalformedXmlException(
- String.format("Found more than one %s in %s.", nameName, ele.getTagName()));
+ String.format(
+ "Found more than one string array %s in %s.",
+ nameName, ele.getTagName()));
}
if (arrayEles.isEmpty()) {
if (required) {
throw new MalformedXmlException(
- String.format("Found no %s in %s.", nameName, ele.getTagName()));
+ String.format(
+ "Found no string array %s in %s.", nameName, ele.getTagName()));
}
return null;
}
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/AllTests.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/AllTests.java
index f156484485ec..dbeeb496144a 100644
--- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/AllTests.java
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/AllTests.java
@@ -18,7 +18,6 @@ package com.android.asllib;
import com.android.asllib.marshallable.AndroidSafetyLabelTest;
import com.android.asllib.marshallable.AppInfoTest;
-import com.android.asllib.marshallable.DataCategoryTest;
import com.android.asllib.marshallable.DataLabelsTest;
import com.android.asllib.marshallable.DataTypeEqualityTest;
import com.android.asllib.marshallable.DeveloperInfoTest;
@@ -36,7 +35,7 @@ import org.junit.runners.Suite;
AslgenTests.class,
AndroidSafetyLabelTest.class,
AppInfoTest.class,
- DataCategoryTest.class,
+ // DataCategoryTest.class,
DataLabelsTest.class,
DataTypeEqualityTest.class,
DeveloperInfoTest.class,
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/AslgenTests.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/AslgenTests.java
index d2e0fc338243..5d1d45a9a29b 100644
--- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/AslgenTests.java
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/AslgenTests.java
@@ -34,8 +34,7 @@ import java.util.List;
@RunWith(JUnit4.class)
public class AslgenTests {
private static final String VALID_MAPPINGS_PATH = "com/android/asllib/validmappings";
- private static final List<String> VALID_MAPPINGS_SUBDIRS =
- List.of("location", "contacts", "general");
+ private static final List<String> VALID_MAPPINGS_SUBDIRS = List.of("general");
private static final String HR_XML_FILENAME = "hr.xml";
private static final String OD_XML_FILENAME = "od.xml";
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataCategoryTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataCategoryTest.java
deleted file mode 100644
index ebb31865843f..000000000000
--- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataCategoryTest.java
+++ /dev/null
@@ -1,212 +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.asllib.marshallable;
-
-import com.android.asllib.testutils.TestUtils;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class DataCategoryTest {
- private static final String DATA_CATEGORY_HR_PATH = "com/android/asllib/datacategory/hr";
- private static final String DATA_CATEGORY_OD_PATH = "com/android/asllib/datacategory/od";
-
- private static final String VALID_PERSONAL_FILE_NAME = "data-category-personal.xml";
- private static final String VALID_PARTIAL_PERSONAL_FILE_NAME =
- "data-category-personal-partial.xml";
- private static final String VALID_FINANCIAL_FILE_NAME = "data-category-financial.xml";
- private static final String VALID_LOCATION_FILE_NAME = "data-category-location.xml";
- private static final String VALID_EMAIL_TEXT_MESSAGE_FILE_NAME =
- "data-category-email-text-message.xml";
- private static final String VALID_PHOTO_VIDEO_FILE_NAME = "data-category-photo-video.xml";
- private static final String VALID_AUDIO_FILE_NAME = "data-category-audio.xml";
- private static final String VALID_STORAGE_FILE_NAME = "data-category-storage.xml";
- private static final String VALID_HEALTH_FITNESS_FILE_NAME = "data-category-health-fitness.xml";
- private static final String VALID_CONTACTS_FILE_NAME = "data-category-contacts.xml";
- private static final String VALID_CALENDAR_FILE_NAME = "data-category-calendar.xml";
- private static final String VALID_IDENTIFIERS_FILE_NAME = "data-category-identifiers.xml";
- private static final String VALID_APP_PERFORMANCE_FILE_NAME =
- "data-category-app-performance.xml";
- private static final String VALID_ACTIONS_IN_APP_FILE_NAME = "data-category-actions-in-app.xml";
- private static final String VALID_SEARCH_AND_BROWSING_FILE_NAME =
- "data-category-search-and-browsing.xml";
-
- private static final String EMPTY_PURPOSE_PERSONAL_FILE_NAME =
- "data-category-personal-empty-purpose.xml";
- private static final String MISSING_PURPOSE_PERSONAL_FILE_NAME =
- "data-category-personal-missing-purpose.xml";
- private static final String UNRECOGNIZED_TYPE_PERSONAL_FILE_NAME =
- "data-category-personal-unrecognized-type.xml";
- private static final String UNRECOGNIZED_CATEGORY_FILE_NAME = "data-category-unrecognized.xml";
-
- /** Logic for setting up tests (empty if not yet needed). */
- public static void main(String[] params) throws Exception {}
-
- @Before
- public void setUp() throws Exception {
- System.out.println("set up.");
- }
-
- /** Test for data category personal. */
- @Test
- public void testDataCategoryPersonal() throws Exception {
- System.out.println("starting testDataCategoryPersonal.");
- testHrToOdDataCategory(VALID_PERSONAL_FILE_NAME);
- }
-
- /** Test for data category financial. */
- @Test
- public void testDataCategoryFinancial() throws Exception {
- System.out.println("starting testDataCategoryFinancial.");
- testHrToOdDataCategory(VALID_FINANCIAL_FILE_NAME);
- }
-
- /** Test for data category location. */
- @Test
- public void testDataCategoryLocation() throws Exception {
- System.out.println("starting testDataCategoryLocation.");
- testHrToOdDataCategory(VALID_LOCATION_FILE_NAME);
- }
-
- /** Test for data category email text message. */
- @Test
- public void testDataCategoryEmailTextMessage() throws Exception {
- System.out.println("starting testDataCategoryEmailTextMessage.");
- testHrToOdDataCategory(VALID_EMAIL_TEXT_MESSAGE_FILE_NAME);
- }
-
- /** Test for data category photo video. */
- @Test
- public void testDataCategoryPhotoVideo() throws Exception {
- System.out.println("starting testDataCategoryPhotoVideo.");
- testHrToOdDataCategory(VALID_PHOTO_VIDEO_FILE_NAME);
- }
-
- /** Test for data category audio. */
- @Test
- public void testDataCategoryAudio() throws Exception {
- System.out.println("starting testDataCategoryAudio.");
- testHrToOdDataCategory(VALID_AUDIO_FILE_NAME);
- }
-
- /** Test for data category storage. */
- @Test
- public void testDataCategoryStorage() throws Exception {
- System.out.println("starting testDataCategoryStorage.");
- testHrToOdDataCategory(VALID_STORAGE_FILE_NAME);
- }
-
- /** Test for data category health fitness. */
- @Test
- public void testDataCategoryHealthFitness() throws Exception {
- System.out.println("starting testDataCategoryHealthFitness.");
- testHrToOdDataCategory(VALID_HEALTH_FITNESS_FILE_NAME);
- }
-
- /** Test for data category contacts. */
- @Test
- public void testDataCategoryContacts() throws Exception {
- System.out.println("starting testDataCategoryContacts.");
- testHrToOdDataCategory(VALID_CONTACTS_FILE_NAME);
- }
-
- /** Test for data category calendar. */
- @Test
- public void testDataCategoryCalendar() throws Exception {
- System.out.println("starting testDataCategoryCalendar.");
- testHrToOdDataCategory(VALID_CALENDAR_FILE_NAME);
- }
-
- /** Test for data category identifiers. */
- @Test
- public void testDataCategoryIdentifiers() throws Exception {
- System.out.println("starting testDataCategoryIdentifiers.");
- testHrToOdDataCategory(VALID_IDENTIFIERS_FILE_NAME);
- }
-
- /** Test for data category app performance. */
- @Test
- public void testDataCategoryAppPerformance() throws Exception {
- System.out.println("starting testDataCategoryAppPerformance.");
- testHrToOdDataCategory(VALID_APP_PERFORMANCE_FILE_NAME);
- }
-
- /** Test for data category actions in app. */
- @Test
- public void testDataCategoryActionsInApp() throws Exception {
- System.out.println("starting testDataCategoryActionsInApp.");
- testHrToOdDataCategory(VALID_ACTIONS_IN_APP_FILE_NAME);
- }
-
- /** Test for data category search and browsing. */
- @Test
- public void testDataCategorySearchAndBrowsing() throws Exception {
- System.out.println("starting testDataCategorySearchAndBrowsing.");
- testHrToOdDataCategory(VALID_SEARCH_AND_BROWSING_FILE_NAME);
- }
-
- /** Test for data category search and browsing. */
- @Test
- public void testMissingOptionalsAllowed() throws Exception {
- System.out.println("starting testMissingOptionalsAllowed.");
- testHrToOdDataCategory(VALID_PARTIAL_PERSONAL_FILE_NAME);
- }
-
- /** Test for empty purposes. */
- @Test
- public void testEmptyPurposesNotAllowed() throws Exception {
- System.out.println("starting testEmptyPurposesNotAllowed.");
- hrToOdExpectException(EMPTY_PURPOSE_PERSONAL_FILE_NAME);
- }
-
- /** Test for missing purposes. */
- @Test
- public void testMissingPurposesNotAllowed() throws Exception {
- System.out.println("starting testMissingPurposesNotAllowed.");
- hrToOdExpectException(MISSING_PURPOSE_PERSONAL_FILE_NAME);
- }
-
- /** Test for unrecognized type. */
- @Test
- public void testUnrecognizedTypeNotAllowed() throws Exception {
- System.out.println("starting testUnrecognizedTypeNotAllowed.");
- hrToOdExpectException(UNRECOGNIZED_TYPE_PERSONAL_FILE_NAME);
- }
-
- /** Test for unrecognized category. */
- @Test
- public void testUnrecognizedCategoryNotAllowed() throws Exception {
- System.out.println("starting testUnrecognizedCategoryNotAllowed.");
- hrToOdExpectException(UNRECOGNIZED_CATEGORY_FILE_NAME);
- }
-
- private void hrToOdExpectException(String fileName) {
- TestUtils.hrToOdExpectException(new DataCategoryFactory(), DATA_CATEGORY_HR_PATH, fileName);
- }
-
- private void testHrToOdDataCategory(String fileName) throws Exception {
- TestUtils.testHrToOd(
- TestUtils.document(),
- new DataCategoryFactory(),
- DATA_CATEGORY_HR_PATH,
- DATA_CATEGORY_OD_PATH,
- fileName);
- }
-}
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataLabelsTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataLabelsTest.java
index 26617264b2e9..ff4374166dd3 100644
--- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataLabelsTest.java
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataLabelsTest.java
@@ -34,6 +34,10 @@ public class DataLabelsTest {
"data-labels-accessed-invalid-bool.xml";
private static final String COLLECTED_VALID_BOOL_FILE_NAME =
"data-labels-collected-valid-bool.xml";
+ private static final String COLLECTED_EPHEMERAL_FILE_NAME =
+ "data-labels-collected-ephemeral.xml";
+ private static final String COLLECTED_EPHEMERAL_COLLISION_FILE_NAME =
+ "data-labels-collected-ephemeral-collision.xml";
private static final String COLLECTED_INVALID_BOOL_FILE_NAME =
"data-labels-collected-invalid-bool.xml";
private static final String SHARED_VALID_BOOL_FILE_NAME = "data-labels-shared-valid-bool.xml";
@@ -69,27 +73,27 @@ public class DataLabelsTest {
System.out.println("set up.");
}
- /** Test for data labels accessed valid bool. */
+ /** Test for data labels collected valid bool. */
@Test
- public void testDataLabelsAccessedValidBool() throws Exception {
- System.out.println("starting testDataLabelsAccessedValidBool.");
- testHrToOdDataLabels(ACCESSED_VALID_BOOL_FILE_NAME);
- testOdToHrDataLabels(ACCESSED_VALID_BOOL_FILE_NAME);
+ public void testDataLabelsCollectedValidBool() throws Exception {
+ System.out.println("starting testDataLabelsCollectedValidBool.");
+ testHrToOdDataLabels(COLLECTED_VALID_BOOL_FILE_NAME);
+ testOdToHrDataLabels(COLLECTED_VALID_BOOL_FILE_NAME);
}
- /** Test for data labels accessed invalid bool. */
+ /** Test for data labels collected ephemeral. */
@Test
- public void testDataLabelsAccessedInvalidBool() throws Exception {
- System.out.println("starting testDataLabelsAccessedInvalidBool.");
- hrToOdExpectException(ACCESSED_INVALID_BOOL_FILE_NAME);
+ public void testDataLabelsCollectedEphemeral() throws Exception {
+ System.out.println("starting testDataLabelsCollectedEphemeral.");
+ testHrToOdDataLabels(COLLECTED_EPHEMERAL_FILE_NAME);
+ testOdToHrDataLabels(COLLECTED_EPHEMERAL_FILE_NAME);
}
- /** Test for data labels collected valid bool. */
+ /** Test for data labels ephemeral collision. */
@Test
- public void testDataLabelsCollectedValidBool() throws Exception {
- System.out.println("starting testDataLabelsCollectedValidBool.");
- testHrToOdDataLabels(COLLECTED_VALID_BOOL_FILE_NAME);
- testOdToHrDataLabels(COLLECTED_VALID_BOOL_FILE_NAME);
+ public void testDataLabelsCollectedEphemeralCollision() throws Exception {
+ System.out.println("starting testDataLabelsCollectedEphemeralCollision.");
+ hrToOdExpectException(COLLECTED_EPHEMERAL_COLLISION_FILE_NAME);
}
/** Test for data labels collected invalid bool. */
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SystemAppSafetyLabelTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SystemAppSafetyLabelTest.java
index 33c276487c64..87d3e4499f5c 100644
--- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SystemAppSafetyLabelTest.java
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SystemAppSafetyLabelTest.java
@@ -31,7 +31,7 @@ public class SystemAppSafetyLabelTest {
"com/android/asllib/systemappsafetylabel/od";
private static final String VALID_FILE_NAME = "valid.xml";
- private static final String MISSING_URL_FILE_NAME = "missing-url.xml";
+ private static final String MISSING_BOOL_FILE_NAME = "missing-bool.xml";
/** Logic for setting up tests (empty if not yet needed). */
public static void main(String[] params) throws Exception {}
@@ -49,12 +49,12 @@ public class SystemAppSafetyLabelTest {
testOdToHrSystemAppSafetyLabel(VALID_FILE_NAME);
}
- /** Tests missing url. */
+ /** Tests missing bool. */
@Test
- public void testMissingUrl() throws Exception {
- System.out.println("starting testMissingUrl.");
- hrToOdExpectException(MISSING_URL_FILE_NAME);
- odToHrExpectException(MISSING_URL_FILE_NAME);
+ public void testMissingBool() throws Exception {
+ System.out.println("starting testMissingBool.");
+ hrToOdExpectException(MISSING_BOOL_FILE_NAME);
+ odToHrExpectException(MISSING_BOOL_FILE_NAME);
}
private void hrToOdExpectException(String fileName) {
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/with-system-app-safety-label.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/with-system-app-safety-label.xml
index 7bcde4547933..afb048632bc6 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/with-system-app-safety-label.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/with-system-app-safety-label.xml
@@ -1,4 +1,4 @@
<app-metadata-bundles version="123456">
-<system-app-safety-label url="www.example.com">
+<system-app-safety-label declaration="true">
</system-app-safety-label>
</app-metadata-bundles> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/with-system-app-safety-label.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/with-system-app-safety-label.xml
index ef0f549fc46b..e8640c4f0934 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/with-system-app-safety-label.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/with-system-app-safety-label.xml
@@ -1,6 +1,6 @@
<bundle>
<long name="version" value="123456"/>
<pbundle_as_map name="system_app_safety_label">
- <string name="url" value="www.example.com"/>
+ <boolean name="declaration" value="true"/>
</pbundle_as_map>
</bundle> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-actions-in-app.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-actions-in-app.xml
index 68e191e8ebe3..680e01a97af3 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-actions-in-app.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-actions-in-app.xml
@@ -1,17 +1,12 @@
<data-labels>
- <data-shared dataCategory="actions_in_app"
- dataType="user_interaction"
+ <data-shared dataType="actions_in_app_data_type_user_interaction"
purposes="analytics" />
- <data-shared dataCategory="actions_in_app"
- dataType="in_app_search_history"
+ <data-shared dataType="actions_in_app_data_type_in_app_search_history"
purposes="analytics" />
- <data-shared dataCategory="actions_in_app"
- dataType="installed_apps"
+ <data-shared dataType="actions_in_app_data_type_installed_apps"
purposes="analytics" />
- <data-shared dataCategory="actions_in_app"
- dataType="user_generated_content"
+ <data-shared dataType="actions_in_app_data_type_user_generated_content"
purposes="analytics" />
- <data-shared dataCategory="actions_in_app"
- dataType="other"
+ <data-shared dataType="actions_in_app_data_type_other"
purposes="analytics" />
</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-app-performance.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-app-performance.xml
index a6bd17d63704..db114bfe15db 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-app-performance.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-app-performance.xml
@@ -1,11 +1,8 @@
<data-labels>
- <data-shared dataCategory="app_performance"
- dataType="crash_logs"
+ <data-shared dataType="app_performance_data_type_crash_logs"
purposes="analytics" />
- <data-shared dataCategory="app_performance"
- dataType="performance_diagnostics"
+ <data-shared dataType="app_performance_data_type_performance_diagnostics"
purposes="analytics" />
- <data-shared dataCategory="app_performance"
- dataType="other"
+ <data-shared dataType="app_performance_data_type_other"
purposes="analytics" />
</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-audio.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-audio.xml
index 6274604f3431..cf273f4dada0 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-audio.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-audio.xml
@@ -1,11 +1,8 @@
<data-labels>
- <data-shared dataCategory="audio"
- dataType="sound_recordings"
+ <data-shared dataType="audio_data_type_sound_recordings"
purposes="analytics" />
- <data-shared dataCategory="audio"
- dataType="music_files"
+ <data-shared dataType="audio_data_type_music_files"
purposes="analytics" />
- <data-shared dataCategory="audio"
- dataType="other"
+ <data-shared dataType="audio_data_type_other"
purposes="analytics" />
</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-calendar.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-calendar.xml
index f7201f625629..16f9d9b699e4 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-calendar.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-calendar.xml
@@ -1,5 +1,4 @@
<data-labels>
- <data-shared dataCategory="calendar"
- dataType="calendar"
+ <data-shared dataType="calendar_data_type_calendar"
purposes="analytics" />
</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-contacts.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-contacts.xml
index e8d40be91d90..6d7a4e8c976d 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-contacts.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-contacts.xml
@@ -1,5 +1,4 @@
<data-labels>
- <data-shared dataCategory="contacts"
- dataType="contacts"
+ <data-shared dataType="contacts_data_type_contacts"
purposes="analytics" />
</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-email-text-message.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-email-text-message.xml
index 69e9b87db287..7a9e978afd10 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-email-text-message.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-email-text-message.xml
@@ -1,11 +1,8 @@
<data-labels>
- <data-shared dataCategory="email_text_message"
- dataType="emails"
+ <data-shared dataType="email_text_message_data_type_emails"
purposes="analytics" />
- <data-shared dataCategory="email_text_message"
- dataType="text_messages"
+ <data-shared dataType="email_text_message_data_type_text_messages"
purposes="analytics" />
- <data-shared dataCategory="email_text_message"
- dataType="other"
+ <data-shared dataType="email_text_message_data_type_other"
purposes="analytics" />
</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-financial.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-financial.xml
index fdd84569a5df..24385b64fe6c 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-financial.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-financial.xml
@@ -1,14 +1,10 @@
<data-labels>
- <data-shared dataCategory="financial"
- dataType="card_bank_account"
+ <data-shared dataType="financial_data_type_card_bank_account"
purposes="analytics" />
- <data-shared dataCategory="financial"
- dataType="purchase_history"
+ <data-shared dataType="financial_data_type_purchase_history"
purposes="analytics" />
- <data-shared dataCategory="financial"
- dataType="credit_score"
+ <data-shared dataType="financial_data_type_credit_score"
purposes="analytics" />
- <data-shared dataCategory="financial"
- dataType="other"
+ <data-shared dataType="financial_data_type_other"
purposes="analytics" />
</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-health-fitness.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-health-fitness.xml
index bac58e6fb7df..faf30b059eef 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-health-fitness.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-health-fitness.xml
@@ -1,8 +1,6 @@
<data-labels>
- <data-shared dataCategory="health_fitness"
- dataType="health"
+ <data-shared dataType="health_fitness_data_type_health"
purposes="analytics" />
- <data-shared dataCategory="health_fitness"
- dataType="fitness"
+ <data-shared dataType="health_fitness_data_type_fitness"
purposes="analytics" />
</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-identifiers.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-identifiers.xml
index ee45f269eb7f..510190660bef 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-identifiers.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-identifiers.xml
@@ -1,5 +1,4 @@
<data-labels>
- <data-shared dataCategory="identifiers"
- dataType="other"
+ <data-shared dataType="identifiers_data_type_other"
purposes="analytics" />
</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-location.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-location.xml
index e8e59118f69c..72cda7e40deb 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-location.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-location.xml
@@ -1,8 +1,6 @@
<data-labels>
- <data-shared dataCategory="location"
- dataType="approx_location"
+ <data-shared dataType="location_data_type_approx_location"
purposes="analytics" />
- <data-shared dataCategory="location"
- dataType="precise_location"
+ <data-shared dataType="location_data_type_precise_location"
purposes="analytics" />
</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-empty-purpose.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-empty-purpose.xml
index 0b220f43e5af..25586814c966 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-empty-purpose.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-empty-purpose.xml
@@ -1,5 +1,4 @@
<data-labels>
- <data-shared dataCategory="personal"
- dataType="email_address"
+ <data-shared dataType="personal_data_type_email_address"
purposes="" />
</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-missing-purpose.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-missing-purpose.xml
index ac221f208577..c5a54755387b 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-missing-purpose.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-missing-purpose.xml
@@ -1,4 +1,3 @@
<data-labels>
- <data-shared dataCategory="personal"
- dataType="email_address" />
+ <data-shared dataType="personal_data_type_email_address" />
</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-partial.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-partial.xml
index 11b73689230b..6ccf336f8556 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-partial.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-partial.xml
@@ -1,8 +1,6 @@
<data-labels>
- <data-shared dataCategory="personal"
- dataType="name"
+ <data-shared dataType="personal_data_type_name"
purposes="analytics|developer_communications" />
- <data-shared dataCategory="personal"
- dataType="email_address"
+ <data-shared dataType="personal_data_type_email_address"
purposes="analytics" />
</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-unrecognized-type.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-unrecognized-type.xml
index f1fbd56571b2..bd88adafea60 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-unrecognized-type.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-unrecognized-type.xml
@@ -1,5 +1,4 @@
<data-labels>
- <data-shared dataCategory="personal"
- dataType="unrecognized"
+ <data-shared dataType="personal_data_type_unrecognized"
purposes="analytics" />
</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal.xml
index 59074628de70..742ed86ed4e5 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal.xml
@@ -1,31 +1,21 @@
<data-labels>
- <data-shared dataCategory="personal"
- dataType="name"
- ephemeral="true"
+ <data-shared dataType="personal_data_type_name"
isSharingOptional="true"
purposes="analytics|developer_communications" />
- <data-shared dataCategory="personal"
- dataType="email_address"
+ <data-shared dataType="personal_data_type_email_address"
purposes="analytics" />
- <data-shared dataCategory="personal"
- dataType="physical_address"
+ <data-shared dataType="personal_data_type_physical_address"
purposes="analytics" />
- <data-shared dataCategory="personal"
- dataType="phone_number"
+ <data-shared dataType="personal_data_type_phone_number"
purposes="analytics" />
- <data-shared dataCategory="personal"
- dataType="race_ethnicity"
+ <data-shared dataType="personal_data_type_race_ethnicity"
purposes="analytics" />
- <data-shared dataCategory="personal"
- dataType="political_or_religious_beliefs"
+ <data-shared dataType="personal_data_type_political_or_religious_beliefs"
purposes="analytics" />
- <data-shared dataCategory="personal"
- dataType="sexual_orientation_or_gender_identity"
+ <data-shared dataType="personal_data_type_sexual_orientation_or_gender_identity"
purposes="analytics" />
- <data-shared dataCategory="personal"
- dataType="personal_identifiers"
+ <data-shared dataType="personal_data_type_personal_identifiers"
purposes="analytics" />
- <data-shared dataCategory="personal"
- dataType="other"
+ <data-shared dataType="personal_data_type_other"
purposes="analytics" />
</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-photo-video.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-photo-video.xml
index 05fe159a1d7c..d416063b87da 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-photo-video.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-photo-video.xml
@@ -1,8 +1,6 @@
<data-labels>
- <data-shared dataCategory="photo_video"
- dataType="photos"
+ <data-shared dataType="photo_video_data_type_photos"
purposes="analytics" />
- <data-shared dataCategory="photo_video"
- dataType="videos"
+ <data-shared dataType="photo_video_data_type_videos"
purposes="analytics" />
</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-search-and-browsing.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-search-and-browsing.xml
index a5de7bed4152..3d932d6d01bf 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-search-and-browsing.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-search-and-browsing.xml
@@ -1,5 +1,4 @@
<data-labels>
- <data-shared dataCategory="search_and_browsing"
- dataType="web_browsing_history"
+ <data-shared dataType="search_and_browsing_data_type_web_browsing_history"
purposes="analytics" />
</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-storage.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-storage.xml
index f01e2df8d99a..704cb1c4674e 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-storage.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-storage.xml
@@ -1,5 +1,4 @@
<data-labels>
- <data-shared dataCategory="storage"
- dataType="files_docs"
+ <data-shared dataType="storage_data_type_files_docs"
purposes="analytics" />
</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-unrecognized-type.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-unrecognized-type.xml
index f1fbd56571b2..bd88adafea60 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-unrecognized-type.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-unrecognized-type.xml
@@ -1,5 +1,4 @@
<data-labels>
- <data-shared dataCategory="personal"
- dataType="unrecognized"
+ <data-shared dataType="personal_data_type_unrecognized"
purposes="analytics" />
</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-unrecognized.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-unrecognized.xml
index c5be68424226..a578d7343f28 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-unrecognized.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-unrecognized.xml
@@ -1,5 +1,4 @@
<data-labels>
- <data-shared dataCategory="unrecognized"
- dataType="email_address"
+ <data-shared dataType="unrecognized_data_type_email_address"
purposes="analytics" />
</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-collected-shared.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-collected-shared.xml
index 161057a51b79..c0bd6529eac0 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-collected-shared.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-collected-shared.xml
@@ -1,11 +1,8 @@
<data-labels>
- <data-accessed dataCategory="location"
- dataType="approx_location"
+ <data-accessed dataType="location_data_type_approx_location"
purposes="app_functionality" />
- <data-collected dataCategory="location"
- dataType="precise_location"
+ <data-collected dataType="location_data_type_precise_location"
purposes="app_functionality" />
- <data-shared dataCategory="personal"
- dataType="name"
+ <data-shared dataType="personal_data_type_name"
purposes="app_functionality" />
</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-invalid-bool.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-invalid-bool.xml
index bb45f426e083..d09fc3b48f51 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-invalid-bool.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-invalid-bool.xml
@@ -1,7 +1,5 @@
<data-labels>
- <data-accessed dataCategory="location"
- dataType="approx_location"
- ephemeral="false"
+ <data-accessed dataType="location_data_type_approx_location"
isSharingOptional="false"
purposes="app_functionality" />
</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-valid-bool.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-valid-bool.xml
index f927bba838fd..6e7f81257116 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-valid-bool.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-valid-bool.xml
@@ -1,6 +1,4 @@
<data-labels>
- <data-accessed dataCategory="location"
- dataType="approx_location"
- ephemeral="false"
+ <data-accessed dataType="location_data_type_approx_location"
purposes="app_functionality" />
</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-ephemeral-collision.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-ephemeral-collision.xml
new file mode 100644
index 000000000000..ee362feb2ca5
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-ephemeral-collision.xml
@@ -0,0 +1,14 @@
+<data-labels>
+ <data-collected dataType="photo_video_data_type_photos"
+ isCollectionOptional="true"
+ purposes="app_functionality" />
+ <data-collected-ephemeral dataType="location_data_type_approx_location"
+ isCollectionOptional="false"
+ purposes="app_functionality" />
+ <data-collected dataType="location_data_type_approx_location"
+ isCollectionOptional="true"
+ purposes="app_functionality" />
+ <data-collected-ephemeral dataType="contacts_data_type_contacts"
+ isCollectionOptional="true"
+ purposes="app_functionality" />
+</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-ephemeral.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-ephemeral.xml
new file mode 100644
index 000000000000..79c900027666
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-ephemeral.xml
@@ -0,0 +1,14 @@
+<data-labels>
+ <data-collected dataType="photo_video_data_type_photos"
+ isCollectionOptional="true"
+ purposes="app_functionality" />
+ <data-collected dataType="location_data_type_precise_location"
+ isCollectionOptional="true"
+ purposes="app_functionality" />
+ <data-collected-ephemeral dataType="location_data_type_approx_location"
+ isCollectionOptional="false"
+ purposes="app_functionality" />
+ <data-collected-ephemeral dataType="contacts_data_type_contacts"
+ isCollectionOptional="true"
+ purposes="app_functionality" />
+</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-invalid-bool.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-invalid-bool.xml
index ba11afbdce5f..801fadaadcdd 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-invalid-bool.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-invalid-bool.xml
@@ -1,7 +1,5 @@
<data-labels>
- <data-collected dataCategory="location"
- dataType="approx_location"
- ephemeral="false"
+ <data-collected dataType="location_data_type_approx_location"
isSharingOptional="false"
purposes="app_functionality" />
</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-valid-bool.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-valid-bool.xml
index 4b6d39776aeb..1ada12db0af8 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-valid-bool.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-valid-bool.xml
@@ -1,7 +1,5 @@
<data-labels>
- <data-collected dataCategory="location"
- dataType="approx_location"
- ephemeral="false"
+ <data-collected dataType="location_data_type_approx_location"
isCollectionOptional="false"
purposes="app_functionality" />
</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-shared-invalid-bool.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-shared-invalid-bool.xml
index 7840b9876ad8..b327d88b2b8a 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-shared-invalid-bool.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-shared-invalid-bool.xml
@@ -1,7 +1,5 @@
<data-labels>
- <data-shared dataCategory="location"
- dataType="approx_location"
- ephemeral="false"
+ <data-shared dataType="location_data_type_approx_location"
isCollectionOptional="false"
purposes="app_functionality" />
</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-shared-valid-bool.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-shared-valid-bool.xml
index ccf77b0e03be..34bd0de2bf51 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-shared-valid-bool.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-shared-valid-bool.xml
@@ -1,7 +1,5 @@
<data-labels>
- <data-shared dataCategory="location"
- dataType="approx_location"
- ephemeral="false"
+ <data-shared dataType="location_data_type_approx_location"
isSharingOptional="false"
purposes="app_functionality" />
</data-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal-partial.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal-partial.xml
index 14f9ef2b74ad..974ea6922fce 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal-partial.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal-partial.xml
@@ -1,17 +1,17 @@
<pbundle_as_map name="data_labels">
<pbundle_as_map name="data_shared">
<pbundle_as_map name="personal">
- <pbundle_as_map name="name">
- <int-array name="purposes" num="2">
- <item value="2" />
- <item value="3" />
- </int-array>
- </pbundle_as_map>
- <pbundle_as_map name="email_address">
- <int-array name="purposes" num="1">
- <item value="2" />
- </int-array>
- </pbundle_as_map>
-</pbundle_as_map>
+ <pbundle_as_map name="name">
+ <int-array name="purposes" num="2">
+ <item value="2" />
+ <item value="3" />
+ </int-array>
+ </pbundle_as_map>
+ <pbundle_as_map name="email_address">
+ <int-array name="purposes" num="1">
+ <item value="2" />
+ </int-array>
+ </pbundle_as_map>
+ </pbundle_as_map>
</pbundle_as_map>
</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal.xml
index 1c87de9e67a7..62c26ab20c68 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal.xml
@@ -7,7 +7,6 @@
<item value="3" />
</int-array>
<boolean name="is_sharing_optional" value="true" />
- <boolean name="ephemeral" value="true" />
</pbundle_as_map>
<pbundle_as_map name="email_address">
<int-array name="purposes" num="1">
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-accessed-valid-bool.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-accessed-valid-bool.xml
index ddefc18f62e3..df000aa2a882 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-accessed-valid-bool.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-accessed-valid-bool.xml
@@ -5,7 +5,6 @@
<int-array name="purposes" num="1">
<item value="1"/>
</int-array>
- <boolean name="ephemeral" value="false"/>
</pbundle_as_map>
</pbundle_as_map>
</pbundle_as_map>
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-collected-ephemeral.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-collected-ephemeral.xml
new file mode 100644
index 000000000000..c671c4be0f5a
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-collected-ephemeral.xml
@@ -0,0 +1,38 @@
+<pbundle_as_map name="data_labels">
+ <pbundle_as_map name="data_collected">
+ <pbundle_as_map name="photo_video">
+ <pbundle_as_map name="photos">
+ <int-array name="purposes" num="1">
+ <item value="1"/>
+ </int-array>
+ <boolean name="is_collection_optional" value="true"/>
+ <boolean name="ephemeral" value="false"/>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ <pbundle_as_map name="location">
+ <pbundle_as_map name="precise_location">
+ <int-array name="purposes" num="1">
+ <item value="1"/>
+ </int-array>
+ <boolean name="is_collection_optional" value="true"/>
+ <boolean name="ephemeral" value="false"/>
+ </pbundle_as_map>
+ <pbundle_as_map name="approx_location">
+ <int-array name="purposes" num="1">
+ <item value="1"/>
+ </int-array>
+ <boolean name="is_collection_optional" value="false"/>
+ <boolean name="ephemeral" value="true"/>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ <pbundle_as_map name="contacts">
+ <pbundle_as_map name="contacts">
+ <int-array name="purposes" num="1">
+ <item value="1"/>
+ </int-array>
+ <boolean name="is_collection_optional" value="true"/>
+ <boolean name="ephemeral" value="true"/>
+ </pbundle_as_map>
+ </pbundle_as_map>
+ </pbundle_as_map>
+</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-shared-valid-bool.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-shared-valid-bool.xml
index 3864f986d20d..0edd8fa264c7 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-shared-valid-bool.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-shared-valid-bool.xml
@@ -6,7 +6,6 @@
<item value="1"/>
</int-array>
<boolean name="is_sharing_optional" value="false"/>
- <boolean name="ephemeral" value="false"/>
</pbundle_as_map>
</pbundle_as_map>
</pbundle_as_map>
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-data-labels.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-data-labels.xml
index 8997f4f30c33..84456daedbd5 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-data-labels.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-data-labels.xml
@@ -1,9 +1,7 @@
<safety-labels version="12345">
<data-labels>
- <data-shared dataCategory="location"
- dataType="approx_location"
+ <data-shared dataType="location_data_type_approx_location"
isSharingOptional="false"
- ephemeral="false"
purposes="app_functionality" />
</data-labels>
</safety-labels> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-data-labels.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-data-labels.xml
index a966fdaf9fe0..fa2a3f841e09 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-data-labels.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-data-labels.xml
@@ -8,7 +8,6 @@
<item value="1"/>
</int-array>
<boolean name="is_sharing_optional" value="false"/>
- <boolean name="ephemeral" value="false"/>
</pbundle_as_map>
</pbundle_as_map>
</pbundle_as_map>
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/hr/missing-url.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/hr/missing-bool.xml
index ff26c05abdb0..ff26c05abdb0 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/hr/missing-url.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/hr/missing-bool.xml
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/hr/valid.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/hr/valid.xml
index 6fe86c33523b..f01d7d299d4a 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/hr/valid.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/hr/valid.xml
@@ -1 +1 @@
-<system-app-safety-label url="www.example.com"></system-app-safety-label> \ No newline at end of file
+<system-app-safety-label declaration="true"></system-app-safety-label> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/missing-url.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/missing-bool.xml
index 33b796552463..33b796552463 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/missing-url.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/missing-bool.xml
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/valid.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/valid.xml
index f96535b4b49b..fad631b37e38 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/valid.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/valid.xml
@@ -1,3 +1,3 @@
<pbundle_as_map name="system_app_safety_label">
- <string name="url" value="www.example.com"/>
+ <boolean name="declaration" value="true"/>
</pbundle_as_map> \ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/hr.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/hr.xml
index 8f854ad1107e..41b32b5ed7b0 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/hr.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/hr.xml
@@ -1,15 +1,13 @@
<app-metadata-bundles version="123">
<safety-labels version="12345">
<data-labels>
- <data-shared dataCategory="location"
- dataType="approx_location"
+ <data-shared
+ dataType="location_data_type_approx_location"
isSharingOptional="false"
- ephemeral="false"
purposes="app_functionality" />
- <data-shared dataCategory="location"
- dataType="precise_location"
+ <data-shared
+ dataType="location_data_type_precise_location"
isSharingOptional="true"
- ephemeral="true"
purposes="app_functionality|analytics" />
</data-labels>
<security-labels
@@ -19,7 +17,7 @@
<third-party-verification url="www.example.com">
</third-party-verification>
</safety-labels>
- <system-app-safety-label url="www.example.com">
+ <system-app-safety-label declaration="true">
</system-app-safety-label>
<transparency-info>
<developer-info
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/od.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/od.xml
index 8f1dc6475b78..c11ac4359ecd 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/od.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/od.xml
@@ -10,7 +10,6 @@
<item value="1"/>
</int-array>
<boolean name="is_sharing_optional" value="false"/>
- <boolean name="ephemeral" value="false"/>
</pbundle_as_map>
<pbundle_as_map name="precise_location">
<int-array name="purposes" num="2">
@@ -18,7 +17,6 @@
<item value="2"/>
</int-array>
<boolean name="is_sharing_optional" value="true"/>
- <boolean name="ephemeral" value="true"/>
</pbundle_as_map>
</pbundle_as_map>
</pbundle_as_map>
@@ -32,7 +30,7 @@
</pbundle_as_map>
</pbundle_as_map>
<pbundle_as_map name="system_app_safety_label">
- <string name="url" value="www.example.com"/>
+ <boolean name="declaration" value="true"/>
</pbundle_as_map>
<pbundle_as_map name="transparency_info">
<pbundle_as_map name="developer_info">