summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp4
-rw-r--r--core/api/current.txt30
-rw-r--r--core/api/module-lib-current.txt1
-rw-r--r--core/api/system-current.txt15
-rw-r--r--core/api/test-current.txt9
-rw-r--r--core/java/Android.bp10
-rw-r--r--core/java/android/app/ActivityThread.java56
-rw-r--r--core/java/android/app/ForegroundServiceTypePolicy.java8
-rw-r--r--core/java/android/app/SystemServiceRegistry.java1
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceManager.java2
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceParams.java2
-rw-r--r--core/java/android/content/Intent.java9
-rw-r--r--core/java/android/content/pm/ActivityInfo.java29
-rw-r--r--core/java/android/content/res/FontScaleConverter.java12
-rw-r--r--core/java/android/hardware/display/DisplayManager.java12
-rw-r--r--core/java/android/hardware/display/DisplayManagerGlobal.java12
-rw-r--r--core/java/android/hardware/display/IDisplayManager.aidl2
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintManager.java56
-rw-r--r--core/java/android/hardware/location/ContextHubManager.java29
-rw-r--r--core/java/android/hardware/location/IContextHubService.aidl4
-rw-r--r--core/java/android/nfc/AvailableNfcAntenna.java20
-rw-r--r--core/java/android/os/Trace.java92
-rw-r--r--core/java/android/service/voice/HotwordAudioStream.java19
-rw-r--r--core/java/android/text/Highlights.java148
-rw-r--r--core/java/android/text/Layout.java149
-rw-r--r--core/java/android/util/RotationUtils.java24
-rw-r--r--core/java/android/view/ViewRootImpl.java44
-rw-r--r--core/java/android/widget/AdapterView.java3
-rw-r--r--core/java/android/widget/Editor.java39
-rw-r--r--core/java/android/widget/Spinner.java15
-rw-r--r--core/java/android/widget/TextView.java95
-rw-r--r--core/java/com/android/internal/content/om/OverlayConfig.java8
-rw-r--r--core/jni/AndroidRuntime.cpp6
-rw-r--r--core/jni/android_content_res_ApkAssets.cpp2
-rw-r--r--core/jni/android_hardware_display_DisplayViewport.cpp3
-rw-r--r--core/jni/android_view_MotionEvent.cpp7
-rw-r--r--core/res/OWNERS1
-rw-r--r--core/res/res/values/attrs_manifest.xml16
-rw-r--r--core/res/res/values/config.xml7
-rw-r--r--core/res/res/values/public-staging.xml2
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImplTest.java4
-rw-r--r--core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java3
-rw-r--r--core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java3
-rw-r--r--core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/BroadcastRadioServiceHidlTest.java8
-rw-r--r--core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/RadioModuleHidlTest.java3
-rw-r--r--core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java3
-rw-r--r--core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java3
-rw-r--r--core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt67
-rw-r--r--core/tests/coretests/src/android/util/RotationUtilsTest.java24
-rw-r--r--core/tests/coretests/src/com/android/internal/content/res/OverlayConfigTest.java90
-rw-r--r--core/tests/fuzzers/FuzzService/Android.bp28
-rw-r--r--core/tests/fuzzers/FuzzService/FuzzBinder.java38
-rw-r--r--core/tests/fuzzers/FuzzService/random_parcel_jni.cpp37
-rw-r--r--core/tests/fuzzers/FuzzService/random_parcel_jni.h26
-rw-r--r--core/tests/fuzzers/OWNERS2
-rw-r--r--core/tests/fuzzers/java_service_fuzzer/Android.bp40
-rw-r--r--core/tests/fuzzers/java_service_fuzzer/ServiceFuzzer.java32
-rw-r--r--core/tests/fuzzers/java_service_fuzzer/TestService.java25
-rw-r--r--core/tests/fuzzers/java_service_fuzzer/fuzztest/ITestService.aidl20
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java48
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java156
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java45
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java46
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModelTests.java217
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java63
-rw-r--r--libs/androidfw/AssetManager2.cpp4
-rw-r--r--libs/androidfw/AssetsProvider.cpp45
-rw-r--r--libs/androidfw/Idmap.cpp32
-rw-r--r--libs/androidfw/include/androidfw/AssetManager2.h2
-rw-r--r--libs/androidfw/include/androidfw/AssetsProvider.h25
-rw-r--r--libs/androidfw/include/androidfw/Idmap.h4
-rw-r--r--libs/androidfw/include/androidfw/Util.h94
-rw-r--r--libs/androidfw/misc.cpp12
-rw-r--r--libs/hwui/Android.bp1
-rw-r--r--libs/hwui/CanvasTransform.cpp1
-rw-r--r--libs/hwui/DeferredLayerUpdater.h1
-rw-r--r--libs/hwui/FrameInfo.h1
-rw-r--r--libs/hwui/Layer.cpp2
-rw-r--r--libs/hwui/RecordingCanvas.cpp1
-rw-r--r--libs/hwui/RecordingCanvas.h1
-rw-r--r--libs/hwui/SkiaCanvas.h1
-rw-r--r--libs/hwui/apex/android_paint.cpp1
-rw-r--r--libs/hwui/hwui/BlurDrawLooper.cpp1
-rw-r--r--libs/hwui/hwui/Canvas.h1
-rw-r--r--libs/hwui/jni/ColorFilter.cpp1
-rw-r--r--libs/hwui/jni/MaskFilter.cpp1
-rw-r--r--libs/hwui/jni/RenderEffect.cpp1
-rw-r--r--libs/hwui/pipeline/skia/RenderNodeDrawable.h1
-rw-r--r--libs/hwui/pipeline/skia/StretchMask.cpp4
-rw-r--r--libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp2
-rw-r--r--libs/hwui/renderthread/CacheManager.cpp1
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp58
-rw-r--r--libs/hwui/renderthread/CanvasContext.h48
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.cpp159
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.h29
-rw-r--r--libs/hwui/renderthread/HintSessionWrapper.cpp162
-rw-r--r--libs/hwui/renderthread/HintSessionWrapper.h57
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp14
-rw-r--r--libs/hwui/tests/common/CallCountingCanvas.h2
-rw-r--r--libs/hwui/tests/common/TestListViewSceneBase.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/BitmapFillrate.cpp1
-rw-r--r--libs/hwui/tests/common/scenes/ClippingAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp1
-rw-r--r--libs/hwui/tests/common/scenes/HwLayerAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/HwLayerSizeAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/JankyScene.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp1
-rw-r--r--libs/hwui/tests/common/scenes/OvalAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/PathClippingAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/RectGridAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp1
-rw-r--r--libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/ShapeAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp1
-rw-r--r--libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp1
-rw-r--r--libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp1
-rw-r--r--libs/hwui/tests/common/scenes/TextAnimation.cpp2
-rw-r--r--libs/hwui/tests/microbench/DisplayListCanvasBench.cpp2
-rw-r--r--libs/hwui/tests/microbench/RenderNodeBench.cpp2
-rw-r--r--libs/hwui/tests/unit/CanvasContextTests.cpp2
-rw-r--r--libs/hwui/tests/unit/CanvasOpTests.cpp1
-rw-r--r--libs/hwui/tests/unit/RenderNodeDrawableTests.cpp11
-rw-r--r--libs/hwui/tests/unit/RenderNodeTests.cpp4
-rw-r--r--libs/hwui/tests/unit/SkiaCanvasTests.cpp9
-rw-r--r--libs/hwui/tests/unit/SkiaDisplayListTests.cpp4
-rw-r--r--libs/hwui/tests/unit/SkiaPipelineTests.cpp1
-rw-r--r--libs/hwui/utils/PaintUtils.h1
-rw-r--r--libs/input/MouseCursorController.cpp19
-rw-r--r--media/java/android/media/RouteListingPreference.java83
-rw-r--r--media/java/android/media/tv/tuner/filter/AvSettings.java7
-rw-r--r--media/java/android/media/tv/tuner/filter/Filter.java9
-rw-r--r--media/java/android/media/tv/tuner/filter/RecordSettings.java61
-rw-r--r--media/jni/android_media_tv_Tuner.cpp6
-rw-r--r--native/android/performance_hint.cpp45
-rw-r--r--packages/CredentialManager/res/values-af/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-am/strings.xml101
-rw-r--r--packages/CredentialManager/res/values-ar/strings.xml62
-rw-r--r--packages/CredentialManager/res/values-as/strings.xml62
-rw-r--r--packages/CredentialManager/res/values-az/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-b+sr+Latn/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-be/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-bg/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-bn/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-bs/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-ca/strings.xml62
-rw-r--r--packages/CredentialManager/res/values-cs/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-da/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-de/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-el/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-en-rAU/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-en-rCA/strings.xml59
-rw-r--r--packages/CredentialManager/res/values-en-rGB/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-en-rIN/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-en-rXC/strings.xml59
-rw-r--r--packages/CredentialManager/res/values-es-rUS/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-es/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-et/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-eu/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-fa/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-fi/strings.xml62
-rw-r--r--packages/CredentialManager/res/values-fr-rCA/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-fr/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-gl/strings.xml62
-rw-r--r--packages/CredentialManager/res/values-gu/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-hi/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-hr/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-hu/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-hy/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-in/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-is/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-it/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-iw/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-ja/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-ka/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-kk/strings.xml62
-rw-r--r--packages/CredentialManager/res/values-km/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-kn/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-ko/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-ky/strings.xml62
-rw-r--r--packages/CredentialManager/res/values-lo/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-lt/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-lv/strings.xml62
-rw-r--r--packages/CredentialManager/res/values-mk/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-ml/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-mn/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-mr/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-ms/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-my/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-nb/strings.xml62
-rw-r--r--packages/CredentialManager/res/values-ne/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-nl/strings.xml62
-rw-r--r--packages/CredentialManager/res/values-or/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-pa/strings.xml62
-rw-r--r--packages/CredentialManager/res/values-pl/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-pt-rBR/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-pt-rPT/strings.xml59
-rw-r--r--packages/CredentialManager/res/values-pt/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-ro/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-ru/strings.xml62
-rw-r--r--packages/CredentialManager/res/values-si/strings.xml62
-rw-r--r--packages/CredentialManager/res/values-sk/strings.xml62
-rw-r--r--packages/CredentialManager/res/values-sl/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-sq/strings.xml62
-rw-r--r--packages/CredentialManager/res/values-sr/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-sv/strings.xml62
-rw-r--r--packages/CredentialManager/res/values-ta/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-te/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-th/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-tl/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-tr/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-uk/strings.xml62
-rw-r--r--packages/CredentialManager/res/values-ur/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-uz/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-vi/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-zh-rCN/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-zh-rHK/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-zh-rTW/strings.xml61
-rw-r--r--packages/CredentialManager/res/values-zu/strings.xml61
-rw-r--r--packages/PackageInstaller/Android.bp2
-rw-r--r--packages/SettingsLib/res/values/strings.xml2
-rw-r--r--packages/SystemUI/animation/Android.bp5
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt12
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt12
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt2
-rw-r--r--packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/ExpandableController.kt2
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt11
-rw-r--r--packages/SystemUI/ktfmt_includes.txt2
-rw-r--r--packages/SystemUI/proguard.flags5
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml1
-rw-r--r--packages/SystemUI/res-keyguard/values-h650dp/dimens.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values/dimens.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values/strings.xml2
-rw-r--r--packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml45
-rw-r--r--packages/SystemUI/res/layout/dream_overlay_home_controls_chip.xml29
-rw-r--r--packages/SystemUI/res/layout/media_smartspace_recommendations.xml2
-rw-r--r--packages/SystemUI/res/values-h700dp/dimens.xml20
-rw-r--r--packages/SystemUI/res/values-h800dp/dimens.xml3
-rw-r--r--packages/SystemUI/res/values-sw720dp-land/dimens.xml5
-rw-r--r--packages/SystemUI/res/values/colors.xml3
-rw-r--r--packages/SystemUI/res/values/dimens.xml5
-rw-r--r--packages/SystemUI/res/values/strings.xml2
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java19
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowIconDrawable.kt2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/EmergencyButton.java12
-rw-r--r--packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt11
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java8
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java11
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java7
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java14
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java11
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java10
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java11
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java141
-rw-r--r--packages/SystemUI/src/com/android/systemui/CoreStartable.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java100
-rw-r--r--packages/SystemUI/src/com/android/systemui/ScreenDecorations.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java237
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEllipseDetection.kt92
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayParams.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/dagger/UdfpsModule.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetector.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetector.kt71
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/udfps/InteractionEvent.kt50
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt65
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/udfps/OverlapDetector.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessor.kt164
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/udfps/TouchProcessor.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/udfps/TouchProcessorResult.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ControlsSettingsRepository.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ControlsSettingsRepositoryImpl.kt90
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt59
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt52
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java61
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationModule.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamHomeControlsComplicationComponent.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt120
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncer.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt184
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceProviderClientFactory.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt129
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManager.kt159
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt65
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt170
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt149
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingToAodTransitionInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GoneAodTransitionInteractor.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt95
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockSource.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakeSleepReason.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessState.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt122
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerUI.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java81
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt55
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java57
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java60
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java56
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepository.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModel.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/AirplaneTableLog.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/UserModule.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/domain/model/ShowDialogRequestModel.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/ui/dialog/DialogShowerImpl.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitchDialog.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java31
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java333
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java31
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java105
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt109
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt90
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt506
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/ControlsSettingsRepositoryImplTest.kt187
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/FakeControlsSettingsRepository.kt37
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java125
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java58
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt108
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManagerTest.kt)6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt219
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt104
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt160
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt167
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt154
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt264
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt65
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java27
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java31
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java134
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt173
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryImplTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelTest.kt)6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt)5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt139
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt144
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java32
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/udfps/FakeOverlapDetector.kt27
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/Flow.kt40
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceProviderClientFactory.kt34
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt43
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeLightRevealScrimRepository.kt33
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt10
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt3
-rw-r--r--services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java12
-rw-r--r--services/core/java/com/android/server/BootReceiver.java3
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java6
-rw-r--r--services/core/java/com/android/server/am/UserController.java31
-rw-r--r--services/core/java/com/android/server/app/GameManagerService.java57
-rw-r--r--services/core/java/com/android/server/app/GameManagerSettings.java30
-rw-r--r--services/core/java/com/android/server/app/GameManagerShellCommand.java184
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceService.java7
-rw-r--r--services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java19
-rw-r--r--services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java2
-rw-r--r--services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java53
-rw-r--r--services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java24
-rw-r--r--services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java10
-rw-r--r--services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java19
-rw-r--r--services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java101
-rw-r--r--services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java40
-rw-r--r--services/core/java/com/android/server/cpu/CpuAvailabilityInfo.java63
-rw-r--r--services/core/java/com/android/server/cpu/CpuAvailabilityMonitoringConfig.java109
-rw-r--r--services/core/java/com/android/server/cpu/CpuInfoReader.java453
-rw-r--r--services/core/java/com/android/server/cpu/CpuMonitorInternal.java81
-rw-r--r--services/core/java/com/android/server/cpu/CpuMonitorService.java173
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceConfig.java4
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java44
-rw-r--r--services/core/java/com/android/server/display/DisplayModeDirector.java120
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java36
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController2.java35
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerControllerInterface.java5
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerState.java11
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplay.java89
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplayMapper.java101
-rw-r--r--services/core/java/com/android/server/display/utils/SensorUtils.java3
-rw-r--r--services/core/java/com/android/server/input/KeyboardLayoutManager.java22
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubService.java47
-rw-r--r--services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java26
-rw-r--r--services/core/java/com/android/server/media/MediaButtonReceiverHolder.java4
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java9
-rw-r--r--services/core/java/com/android/server/pm/dex/ArtManagerService.java6
-rw-r--r--services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java2
-rw-r--r--services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java4
-rw-r--r--services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java26
-rw-r--r--services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java14
-rw-r--r--services/core/java/com/android/server/powerstats/PowerStatsService.java76
-rw-r--r--services/core/java/com/android/server/utils/EventLogger.java23
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java11
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java7
-rw-r--r--services/core/java/com/android/server/wm/BLASTSyncEngine.java7
-rw-r--r--services/core/java/com/android/server/wm/DisplayArea.java6
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java5
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java1
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java26
-rw-r--r--services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java43
-rw-r--r--services/core/java/com/android/server/wm/Transition.java27
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java23
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java3
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp4
-rw-r--r--services/tests/PackageManagerServiceTests/server/src/com/android/server/PackageParserTest.java10
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java52
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/cpu/CpuMonitorServiceTest.java121
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java6
-rw-r--r--services/tests/servicestests/Android.bp1
-rw-r--r--services/tests/servicestests/AndroidManifest.xml3
-rw-r--r--services/tests/servicestests/AndroidTest.xml1
-rw-r--r--services/tests/servicestests/src/com/android/server/am/UserControllerTest.java39
-rw-r--r--services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java70
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java484
-rw-r--r--services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java186
-rw-r--r--services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/media/MediaButtonReceiverHolderTest.java45
-rw-r--r--services/tests/servicestests/src/com/android/server/media/OWNERS2
-rw-r--r--services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/utils/EventLoggerTest.java13
-rw-r--r--services/tests/servicestests/test-apps/MediaButtonReceiverHolderTestHelperApp/Android.bp37
-rw-r--r--services/tests/servicestests/test-apps/MediaButtonReceiverHolderTestHelperApp/AndroidManifest.xml31
-rw-r--r--services/tests/servicestests/test-apps/MediaButtonReceiverHolderTestHelperApp/OWNERS2
-rw-r--r--services/tests/servicestests/test-apps/MediaButtonReceiverHolderTestHelperApp/src/FakeMediaButtonBroadcastReceiver.java32
-rw-r--r--services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp4.xml2
-rw-r--r--services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp6.xml2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java38
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java18
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java42
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransitionTests.java2
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamCopier.java (renamed from services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamManager.java)120
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java13
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/TrustedHotwordDetectorSession.java13
-rw-r--r--startop/view_compiler/TEST_MAPPING15
-rw-r--r--telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl1
-rw-r--r--telephony/java/android/telephony/ims/feature/MmTelFeature.java11
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java25
-rw-r--r--tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java1
-rw-r--r--tools/lint/common/src/main/java/com/google/android/lint/PermissionMethodUtils.kt10
-rw-r--r--tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt2
-rw-r--r--tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt2
-rw-r--r--tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt57
-rw-r--r--tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetector.kt46
-rw-r--r--tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetectorTest.kt375
-rw-r--r--tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/Stubs.kt6
557 files changed, 17736 insertions, 3851 deletions
diff --git a/Android.bp b/Android.bp
index 3d25bc1d17db..c0a2abb02292 100644
--- a/Android.bp
+++ b/Android.bp
@@ -102,7 +102,7 @@ filegroup {
":android.hardware.keymaster-V4-java-source",
":android.hardware.security.keymint-V3-java-source",
":android.hardware.security.secureclock-V1-java-source",
- ":android.hardware.tv.tuner-V1-java-source",
+ ":android.hardware.tv.tuner-V2-java-source",
":android.security.apc-java-source",
":android.security.authorization-java-source",
":android.security.legacykeystore-java-source",
@@ -205,7 +205,7 @@ java_library {
"android.hardware.contexthub-V1.0-java",
"android.hardware.contexthub-V1.1-java",
"android.hardware.contexthub-V1.2-java",
- "android.hardware.contexthub-V1-java",
+ "android.hardware.contexthub-V2-java",
"android.hardware.gnss-V1.0-java",
"android.hardware.gnss-V2.1-java",
"android.hardware.health-V1.0-java-constants",
diff --git a/core/api/current.txt b/core/api/current.txt
index f550952fc4cf..f26f8399a6d7 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1261,6 +1261,7 @@ package android {
field public static final int requireDeviceUnlock = 16843756; // 0x10103ec
field public static final int required = 16843406; // 0x101028e
field public static final int requiredAccountType = 16843734; // 0x10103d6
+ field public static final int requiredDisplayCategory;
field public static final int requiredFeature = 16844116; // 0x1010554
field public static final int requiredForAllUsers = 16843728; // 0x10103d0
field public static final int requiredNotFeature = 16844117; // 0x1010555
@@ -1508,7 +1509,6 @@ package android {
field public static final int targetCellWidth = 16844340; // 0x1010634
field public static final int targetClass = 16842799; // 0x101002f
field @Deprecated public static final int targetDescriptions = 16843680; // 0x10103a0
- field public static final int targetDisplayCategory;
field public static final int targetId = 16843740; // 0x10103dc
field public static final int targetName = 16843853; // 0x101044d
field public static final int targetPackage = 16842785; // 0x1010021
@@ -11201,10 +11201,10 @@ package android.content.pm {
field public String parentActivityName;
field public String permission;
field public int persistableMode;
+ field @Nullable public String requiredDisplayCategory;
field public int screenOrientation;
field public int softInputMode;
field public String targetActivity;
- field @Nullable public String targetDisplayCategory;
field public String taskAffinity;
field public int theme;
field public int uiOptions;
@@ -23953,11 +23953,17 @@ package android.media {
}
public static final class RouteListingPreference.Item implements android.os.Parcelable {
- ctor public RouteListingPreference.Item(@NonNull String);
+ ctor public RouteListingPreference.Item(@NonNull String, int, int);
method public int describeContents();
+ method public int getDisableReason();
+ method public int getFlags();
method @NonNull public String getRouteId();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.media.RouteListingPreference.Item> CREATOR;
+ field public static final int DISABLE_REASON_NONE = 0; // 0x0
+ field public static final int DISABLE_REASON_SUBSCRIPTION_REQUIRED = 1; // 0x1
+ field public static final int FLAG_ONGOING_SESSION = 1; // 0x1
+ field public static final int FLAG_SUGGESTED_ROUTE = 2; // 0x2
}
public final class RoutingSessionInfo implements android.os.Parcelable {
@@ -45670,6 +45676,19 @@ package android.text {
method public int previousStartBoundary(@IntRange(from=0) int);
}
+ public class Highlights {
+ method @NonNull public android.graphics.Paint getPaint(int);
+ method @NonNull public int[] getRanges(int);
+ method public int getSize();
+ }
+
+ public static final class Highlights.Builder {
+ ctor public Highlights.Builder();
+ method @NonNull public android.text.Highlights.Builder addRange(@NonNull android.graphics.Paint, int, int);
+ method @NonNull public android.text.Highlights.Builder addRanges(@NonNull android.graphics.Paint, @NonNull int...);
+ method @NonNull public android.text.Highlights build();
+ }
+
public class Html {
method public static String escapeHtml(CharSequence);
method @Deprecated public static android.text.Spanned fromHtml(String);
@@ -45761,6 +45780,9 @@ package android.text {
ctor protected Layout(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float);
method public void draw(android.graphics.Canvas);
method public void draw(android.graphics.Canvas, android.graphics.Path, android.graphics.Paint, int);
+ method public void draw(@NonNull android.graphics.Canvas, @Nullable java.util.List<android.graphics.Path>, @Nullable java.util.List<android.graphics.Paint>, @Nullable android.graphics.Path, @Nullable android.graphics.Paint, int);
+ method public void drawBackground(@NonNull android.graphics.Canvas);
+ method public void drawText(@NonNull android.graphics.Canvas);
method public void fillCharacterBounds(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull float[], @IntRange(from=0) int);
method public final android.text.Layout.Alignment getAlignment();
method public abstract int getBottomPadding();
@@ -58574,6 +58596,7 @@ package android.widget {
method public boolean getFreezesText();
method public int getGravity();
method @ColorInt public int getHighlightColor();
+ method @Nullable public android.text.Highlights getHighlights();
method public CharSequence getHint();
method public final android.content.res.ColorStateList getHintTextColors();
method public int getHyphenationFrequency();
@@ -58703,6 +58726,7 @@ package android.widget {
method public void setGravity(int);
method public void setHeight(int);
method public void setHighlightColor(@ColorInt int);
+ method public void setHighlights(@Nullable android.text.Highlights);
method public final void setHint(CharSequence);
method public final void setHint(@StringRes int);
method public final void setHintTextColor(@ColorInt int);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 3228ce6cf939..286a80051809 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -393,7 +393,6 @@ package android.os {
method public static void traceBegin(long, @NonNull String);
method public static void traceCounter(long, @NonNull String, int);
method public static void traceEnd(long);
- field public static final long TRACE_TAG_AIDL = 16777216L; // 0x1000000L
field public static final long TRACE_TAG_NETWORK = 2097152L; // 0x200000L
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 13914c54a813..66423c8ede34 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -7521,6 +7521,7 @@ package android.media.tv.tuner.filter {
field public static final int VIDEO_STREAM_TYPE_VC1 = 7; // 0x7
field public static final int VIDEO_STREAM_TYPE_VP8 = 8; // 0x8
field public static final int VIDEO_STREAM_TYPE_VP9 = 9; // 0x9
+ field public static final int VIDEO_STREAM_TYPE_VVC = 13; // 0xd
}
public static class AvSettings.Builder {
@@ -7713,6 +7714,7 @@ package android.media.tv.tuner.filter {
field public static final int INDEX_TYPE_SC = 1; // 0x1
field public static final int INDEX_TYPE_SC_AVC = 3; // 0x3
field public static final int INDEX_TYPE_SC_HEVC = 2; // 0x2
+ field public static final int INDEX_TYPE_SC_VVC = 4; // 0x4
field public static final int MPT_INDEX_AUDIO = 262144; // 0x40000
field public static final int MPT_INDEX_MPT = 65536; // 0x10000
field public static final int MPT_INDEX_TIMESTAMP_TARGET_AUDIO = 1048576; // 0x100000
@@ -7735,6 +7737,13 @@ package android.media.tv.tuner.filter {
field public static final int SC_INDEX_SEQUENCE = 8; // 0x8
field public static final int SC_INDEX_SI_SLICE = 128; // 0x80
field public static final int SC_INDEX_SP_SLICE = 256; // 0x100
+ field public static final int SC_VVC_INDEX_AUD = 64; // 0x40
+ field public static final int SC_VVC_INDEX_SLICE_CRA = 4; // 0x4
+ field public static final int SC_VVC_INDEX_SLICE_GDR = 8; // 0x8
+ field public static final int SC_VVC_INDEX_SLICE_IDR_N_LP = 2; // 0x2
+ field public static final int SC_VVC_INDEX_SLICE_IDR_W_RADL = 1; // 0x1
+ field public static final int SC_VVC_INDEX_SPS = 32; // 0x20
+ field public static final int SC_VVC_INDEX_VPS = 16; // 0x10
field public static final int TS_INDEX_ADAPTATION_EXTENSION_FLAG = 4096; // 0x1000
field public static final int TS_INDEX_CHANGE_TO_EVEN_SCRAMBLED = 8; // 0x8
field public static final int TS_INDEX_CHANGE_TO_NOT_SCRAMBLED = 4; // 0x4
@@ -10012,6 +10021,10 @@ package android.os {
field public static final int STATUS_WAITING_REBOOT = 5; // 0x5
}
+ public final class Trace {
+ field public static final long TRACE_TAG_AIDL = 16777216L; // 0x1000000L
+ }
+
public class UpdateEngine {
ctor public UpdateEngine();
method @NonNull @WorkerThread public android.os.UpdateEngine.AllocateSpaceResult allocateSpace(@NonNull String, @NonNull String[]);
@@ -12277,6 +12290,7 @@ package android.service.voice {
method @Nullable public android.media.AudioTimestamp getTimestamp();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.voice.HotwordAudioStream> CREATOR;
+ field public static final String KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES = "android.service.voice.key.AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES";
}
public static final class HotwordAudioStream.Builder {
@@ -15902,6 +15916,7 @@ package android.telephony.ims.stub {
public class ImsSmsImplBase {
ctor public ImsSmsImplBase();
method public void acknowledgeSms(int, @IntRange(from=0, to=65535) int, int);
+ method public void acknowledgeSms(int, @IntRange(from=0, to=65535) int, int, @NonNull byte[]);
method public void acknowledgeSmsReport(int, @IntRange(from=0, to=65535) int, int);
method public String getSmsFormat();
method public void onReady();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 984e82215169..002032ae10c3 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1316,6 +1316,14 @@ package android.hardware.lights {
}
+package android.hardware.location {
+
+ public final class ContextHubManager {
+ method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public long[] getPreloadedNanoAppIds(@NonNull android.hardware.location.ContextHubInfo);
+ }
+
+}
+
package android.hardware.soundtrigger {
public class KeyphraseEnrollmentInfo {
@@ -3274,6 +3282,7 @@ package android.widget {
public class Spinner extends android.widget.AbsSpinner implements android.content.DialogInterface.OnClickListener {
method public boolean isPopupShowing();
+ method public void onClick(int);
}
@android.widget.RemoteViews.RemoteView public class TextClock extends android.widget.TextView {
diff --git a/core/java/Android.bp b/core/java/Android.bp
index af0fa392e337..a4a12d743442 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -417,6 +417,16 @@ filegroup {
],
}
+// This file group is used by service fuzzer
+filegroup {
+ name: "framework-core-sources-for-fuzzers",
+ srcs: [
+ "android/os/IInterface.java",
+ "android/os/Binder.java",
+ "android/os/IBinder.java",
+ ],
+}
+
aidl_interface {
name: "android.os.statsbootstrap_aidl",
unstable: true,
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 96ced41f36ca..897cd1f27e11 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -6683,34 +6683,6 @@ public final class ActivityThread extends ClientTransactionHandler
StrictMode.initThreadDefaults(data.appInfo);
StrictMode.initVmDefaults(data.appInfo);
- if (data.debugMode != ApplicationThreadConstants.DEBUG_OFF) {
- // XXX should have option to change the port.
- Debug.changeDebugPort(8100);
- if (data.debugMode == ApplicationThreadConstants.DEBUG_WAIT) {
- Slog.w(TAG, "Application " + data.info.getPackageName()
- + " is waiting for the debugger on port 8100...");
-
- IActivityManager mgr = ActivityManager.getService();
- try {
- mgr.showWaitingForDebugger(mAppThread, true);
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
-
- Debug.waitForDebugger();
-
- try {
- mgr.showWaitingForDebugger(mAppThread, false);
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
-
- } else {
- Slog.w(TAG, "Application " + data.info.getPackageName()
- + " can be debugged on port 8100...");
- }
- }
-
// Allow binder tracing, and application-generated systrace messages if we're profileable.
boolean isAppDebuggable = (data.appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
boolean isAppProfileable = isAppDebuggable || data.appInfo.isProfileable();
@@ -6818,6 +6790,34 @@ public final class ActivityThread extends ClientTransactionHandler
throw ex.rethrowFromSystemServer();
}
+ // Wait for debugger after we have notified the system to finish attach application
+ if (data.debugMode != ApplicationThreadConstants.DEBUG_OFF) {
+ // XXX should have option to change the port.
+ Debug.changeDebugPort(8100);
+ if (data.debugMode == ApplicationThreadConstants.DEBUG_WAIT) {
+ Slog.w(TAG, "Application " + data.info.getPackageName()
+ + " is waiting for the debugger on port 8100...");
+
+ try {
+ mgr.showWaitingForDebugger(mAppThread, true);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+
+ Debug.waitForDebugger();
+
+ try {
+ mgr.showWaitingForDebugger(mAppThread, false);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+
+ } else {
+ Slog.w(TAG, "Application " + data.info.getPackageName()
+ + " can be debugged on port 8100...");
+ }
+ }
+
try {
// If the app is being launched for full backup or restore, bring it up in
// a restricted environment with the base application class.
diff --git a/core/java/android/app/ForegroundServiceTypePolicy.java b/core/java/android/app/ForegroundServiceTypePolicy.java
index 63fdc2e1b686..332aaddef950 100644
--- a/core/java/android/app/ForegroundServiceTypePolicy.java
+++ b/core/java/android/app/ForegroundServiceTypePolicy.java
@@ -58,6 +58,7 @@ import android.hardware.usb.UsbManager;
import android.healthconnect.HealthConnectManager;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.permission.PermissionCheckerManager;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -880,11 +881,12 @@ public abstract class ForegroundServiceTypePolicy {
int checkPermission(@NonNull Context context, @NonNull String name, int callerUid,
int callerPid, String packageName, boolean allowWhileInUse) {
// Simple case, check if it's already granted.
- if (PermissionChecker.checkPermissionForPreflight(context, name,
- callerPid, callerUid, packageName) == PERMISSION_GRANTED) {
+ @PackageManager.PermissionResult int result;
+ if ((result = PermissionChecker.checkPermissionForPreflight(context, name,
+ callerPid, callerUid, packageName)) == PERMISSION_GRANTED) {
return PERMISSION_GRANTED;
}
- if (allowWhileInUse) {
+ if (allowWhileInUse && result == PermissionCheckerManager.PERMISSION_SOFT_DENIED) {
// Check its appops
final int opCode = AppOpsManager.permissionToOpCode(name);
final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 5d87012ec7e7..762ac23b6efe 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -1617,6 +1617,7 @@ public final class SystemServiceRegistry {
case Context.INCREMENTAL_SERVICE:
case Context.ETHERNET_SERVICE:
case Context.CONTEXTHUB_SERVICE:
+ case Context.VIRTUALIZATION_SERVICE:
return null;
}
Slog.wtf(TAG, "Manager wrapper not available: " + name);
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 01b42bfa661c..568185a0330a 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -416,7 +416,7 @@ public final class VirtualDeviceManager {
* @param densityDpi The density of the virtual display in dpi, must be greater than 0.
* @param displayCategories The categories of the virtual display, indicating the type of
* activities allowed to run on the display. Activities can declare their type using
- * {@link android.content.pm.ActivityInfo#targetDisplayCategory}.
+ * {@link android.content.pm.ActivityInfo#requiredDisplayCategory}.
* @param surface The surface to which the content of the virtual display should
* be rendered, or null if there is none initially. The surface can also be set later using
* {@link VirtualDisplay#setSurface(Surface)}.
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index e2b5c5d74efe..1cbe910d131a 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -617,7 +617,7 @@ public final class VirtualDeviceParams implements Parcelable {
/**
* Specifies a policy for this virtual device.
*
- * Policies define the system behavior that may be specific for this virtual device. A
+ * <p>Policies define the system behavior that may be specific for this virtual device. A
* policy can be defined for each {@code PolicyType}, but they are all optional.
*
* @param policyType the type of policy, i.e. which behavior to specify a policy for.
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 9d82274d3f99..65b3ca42fc66 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3889,7 +3889,7 @@ public class Intent implements Parcelable, Cloneable {
"android.intent.action.USER_INITIALIZE";
/**
- * Sent when a user switch is happening, causing the process's user to be
+ * Sent after a user switch is complete, if the switch caused the process's user to be
* brought to the foreground. This is only sent to receivers registered
* through {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
* Context.registerReceiver}. It is sent to the user that is going to the
@@ -3901,7 +3901,7 @@ public class Intent implements Parcelable, Cloneable {
"android.intent.action.USER_FOREGROUND";
/**
- * Sent when a user switch is happening, causing the process's user to be
+ * Sent after a user switch is complete, if the switch caused the process's user to be
* sent to the background. This is only sent to receivers registered
* through {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
* Context.registerReceiver}. It is sent to the user that is going to the
@@ -4042,6 +4042,11 @@ public class Intent implements Parcelable, Cloneable {
* the current state of the user.
* @hide
*/
+ /*
+ * This broadcast is sent after the user switch is complete. In case a task needs to be done
+ * while the switch is happening (i.e. while the screen is frozen to hide UI jank), please use
+ * ActivityManagerService.registerUserSwitchObserver method.
+ */
@SystemApi
public static final String ACTION_USER_SWITCHED =
"android.intent.action.USER_SWITCHED";
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index fda4119aff99..dab57fda6106 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -221,21 +221,20 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
public String launchToken;
/**
- * Specifies the category of the target display the activity is expected to run on. Set from
- * the {@link android.R.attr#targetDisplayCategory} attribute. Upon creation, a virtual display
- * can specify which display categories it supports and one of the category must be present in
- * the activity's manifest to allow this activity to run. The default value is {@code null},
- * which indicates the activity does not belong to a restricted display category and thus can
- * only run on a display that didn't specify any display categories. Each activity can only
- * specify one category it targets to but a virtual display can support multiple restricted
- * categories.
- *
+ * Specifies the required display category of the activity. Set from the
+ * {@link android.R.attr#requiredDisplayCategory} attribute. Upon creation, a display can
+ * specify which display categories it supports and one of the category must be present
+ * in the {@code <activity>} element to allow this activity to run. The default value is
+ * {@code null}, which indicates the activity does not have a required display category and
+ * thus can only run on a display that didn't specify any display categories. Each activity
+ * can only specify one required category but a display can support multiple display categories.
+ * <p>
* This field should be formatted as a Java-language-style free form string(for example,
* com.google.automotive_entertainment), which may contain uppercase or lowercase letters ('A'
* through 'Z'), numbers, and underscores ('_') but may only start with letters.
*/
@Nullable
- public String targetDisplayCategory;
+ public String requiredDisplayCategory;
/**
* Activity can not be resized and always occupies the fullscreen area with all windows fully
@@ -1330,7 +1329,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
mMaxAspectRatio = orig.mMaxAspectRatio;
mMinAspectRatio = orig.mMinAspectRatio;
supportsSizeChanges = orig.supportsSizeChanges;
- targetDisplayCategory = orig.targetDisplayCategory;
+ requiredDisplayCategory = orig.requiredDisplayCategory;
}
/**
@@ -1669,8 +1668,8 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
if (mKnownActivityEmbeddingCerts != null) {
pw.println(prefix + "knownActivityEmbeddingCerts=" + mKnownActivityEmbeddingCerts);
}
- if (targetDisplayCategory != null) {
- pw.println(prefix + "targetDisplayCategory=" + targetDisplayCategory);
+ if (requiredDisplayCategory != null) {
+ pw.println(prefix + "requiredDisplayCategory=" + requiredDisplayCategory);
}
super.dumpBack(pw, prefix, dumpFlags);
}
@@ -1718,7 +1717,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
dest.writeFloat(mMinAspectRatio);
dest.writeBoolean(supportsSizeChanges);
sForStringSet.parcel(mKnownActivityEmbeddingCerts, dest, flags);
- dest.writeString8(targetDisplayCategory);
+ dest.writeString8(requiredDisplayCategory);
}
/**
@@ -1844,7 +1843,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
if (mKnownActivityEmbeddingCerts.isEmpty()) {
mKnownActivityEmbeddingCerts = null;
}
- targetDisplayCategory = source.readString8();
+ requiredDisplayCategory = source.readString8();
}
/**
diff --git a/core/java/android/content/res/FontScaleConverter.java b/core/java/android/content/res/FontScaleConverter.java
index c7fdb16682e3..457225d26610 100644
--- a/core/java/android/content/res/FontScaleConverter.java
+++ b/core/java/android/content/res/FontScaleConverter.java
@@ -36,11 +36,6 @@ import java.util.Arrays;
* @hide
*/
public class FontScaleConverter {
- /**
- * How close the given SP should be to a canonical SP in the array before they are considered
- * the same for lookup purposes.
- */
- private static final float THRESHOLD_FOR_MATCHING_SP = 0.02f;
@VisibleForTesting
final float[] mFromSpValues;
@@ -78,10 +73,11 @@ public class FontScaleConverter {
public float convertSpToDp(float sp) {
final float spPositive = Math.abs(sp);
// TODO(b/247861374): find a match at a higher index?
- final int spRounded = Math.round(spPositive);
final float sign = Math.signum(sp);
- final int index = Arrays.binarySearch(mFromSpValues, spRounded);
- if (index >= 0 && Math.abs(spRounded - spPositive) < THRESHOLD_FOR_MATCHING_SP) {
+ // We search for exact matches only, even if it's just a little off. The interpolation will
+ // handle any non-exact matches.
+ final int index = Arrays.binarySearch(mFromSpValues, spPositive);
+ if (index >= 0) {
// exact match, return the matching dp
return sign * mToDpValues[index];
} else {
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 441fd88f15ee..f7675e835eb2 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -588,18 +588,20 @@ public final class DisplayManager {
* @see #DISPLAY_CATEGORY_PRESENTATION
*/
public Display[] getDisplays(String category) {
- final int[] displayIds = mGlobal.getDisplayIds();
+ boolean includeDisabled = (category != null
+ && category.equals(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED));
+ final int[] displayIds = mGlobal.getDisplayIds(includeDisabled);
synchronized (mLock) {
try {
- if (category == null
- || DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED.equals(category)) {
- addAllDisplaysLocked(mTempDisplays, displayIds);
- } else if (category.equals(DISPLAY_CATEGORY_PRESENTATION)) {
+ if (DISPLAY_CATEGORY_PRESENTATION.equals(category)) {
addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_WIFI);
addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_EXTERNAL);
addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_OVERLAY);
addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_VIRTUAL);
addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_INTERNAL);
+ } else if (category == null
+ || DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED.equals(category)) {
+ addAllDisplaysLocked(mTempDisplays, displayIds);
}
return mTempDisplays.toArray(new Display[mTempDisplays.size()]);
} finally {
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index cc397d57d838..f038c66d3b51 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -212,6 +212,16 @@ public final class DisplayManagerGlobal {
*/
@UnsupportedAppUsage
public int[] getDisplayIds() {
+ return getDisplayIds(/* includeDisabled= */ false);
+ }
+
+ /**
+ * Gets all currently valid logical display ids.
+ *
+ * @param includeDisabled True if the returned list of displays includes disabled displays.
+ * @return An array containing all display ids.
+ */
+ public int[] getDisplayIds(boolean includeDisabled) {
try {
synchronized (mLock) {
if (USE_CACHE) {
@@ -220,7 +230,7 @@ public final class DisplayManagerGlobal {
}
}
- int[] displayIds = mDm.getDisplayIds();
+ int[] displayIds = mDm.getDisplayIds(includeDisabled);
if (USE_CACHE) {
mDisplayIdCache = displayIds;
}
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 6b5594b1a3dd..28bb35f7d9cf 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -37,7 +37,7 @@ import android.view.Surface;
interface IDisplayManager {
@UnsupportedAppUsage
DisplayInfo getDisplayInfo(int displayId);
- int[] getDisplayIds();
+ int[] getDisplayIds(boolean includeDisabled);
boolean isUidPresentOnDisplay(int uid, int displayId);
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index fb5ac5a4efcb..6f63dbfc3ba9 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -956,7 +956,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
public void onPointerDown(long requestId, int sensorId, int x, int y,
float minor, float major) {
if (mService == null) {
- Slog.w(TAG, "onFingerDown: no fingerprint service");
+ Slog.w(TAG, "onPointerDown: no fingerprint service");
return;
}
@@ -979,7 +979,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
public void onPointerUp(long requestId, int sensorId) {
if (mService == null) {
- Slog.w(TAG, "onFingerDown: no fingerprint service");
+ Slog.w(TAG, "onPointerUp: no fingerprint service");
return;
}
@@ -993,6 +993,58 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
/**
+ * TODO(b/218388821): The parameter list should be replaced with PointerContext.
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public void onPointerDown(
+ long requestId,
+ int sensorId,
+ int pointerId,
+ float x,
+ float y,
+ float minor,
+ float major,
+ float orientation,
+ long time,
+ long gestureStart,
+ boolean isAod) {
+ if (mService == null) {
+ Slog.w(TAG, "onPointerDown: no fingerprint service");
+ return;
+ }
+
+ // TODO(b/218388821): Propagate all the parameters to FingerprintService.
+ Slog.e(TAG, "onPointerDown: not implemented!");
+ }
+
+ /**
+ * TODO(b/218388821): The parameter list should be replaced with PointerContext.
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public void onPointerUp(
+ long requestId,
+ int sensorId,
+ int pointerId,
+ float x,
+ float y,
+ float minor,
+ float major,
+ float orientation,
+ long time,
+ long gestureStart,
+ boolean isAod) {
+ if (mService == null) {
+ Slog.w(TAG, "onPointerUp: no fingerprint service");
+ return;
+ }
+
+ // TODO(b/218388821): Propagate all the parameters to FingerprintService.
+ Slog.e(TAG, "onPointerUp: not implemented!");
+ }
+
+ /**
* @hide
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index b54da6c6eaea..ac23af4396de 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -24,6 +24,7 @@ import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.app.ActivityThread;
import android.app.PendingIntent;
import android.content.Context;
@@ -966,6 +967,34 @@ public final class ContextHubManager {
}
/**
+ * Queries for the list of preloaded nanoapp IDs on the system.
+ *
+ * @param hubInfo The Context Hub to query a list of nanoapp IDs from.
+ *
+ * @return The list of 64-bit IDs of the preloaded nanoapps.
+ *
+ * @throws NullPointerException if hubInfo is null
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
+ @NonNull public long[] getPreloadedNanoAppIds(@NonNull ContextHubInfo hubInfo) {
+ Objects.requireNonNull(hubInfo, "hubInfo cannot be null");
+
+ long[] nanoappIds = null;
+ try {
+ nanoappIds = mService.getPreloadedNanoAppIds(hubInfo);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ if (nanoappIds == null) {
+ nanoappIds = new long[0];
+ }
+ return nanoappIds;
+ }
+
+ /**
* Unregister a callback for receive messages from the context hub.
*
* @see Callback
diff --git a/core/java/android/hardware/location/IContextHubService.aidl b/core/java/android/hardware/location/IContextHubService.aidl
index ced75c4d0247..490267f36b7d 100644
--- a/core/java/android/hardware/location/IContextHubService.aidl
+++ b/core/java/android/hardware/location/IContextHubService.aidl
@@ -109,4 +109,8 @@ interface IContextHubService {
// Queries for a list of nanoapps
@EnforcePermission("ACCESS_CONTEXT_HUB")
void queryNanoApps(int contextHubId, in IContextHubTransactionCallback transactionCallback);
+
+ // Queries for a list of preloaded nanoapps
+ @EnforcePermission("ACCESS_CONTEXT_HUB")
+ long[] getPreloadedNanoAppIds(in ContextHubInfo hubInfo);
}
diff --git a/core/java/android/nfc/AvailableNfcAntenna.java b/core/java/android/nfc/AvailableNfcAntenna.java
index 946ba67b2397..6e6512a04971 100644
--- a/core/java/android/nfc/AvailableNfcAntenna.java
+++ b/core/java/android/nfc/AvailableNfcAntenna.java
@@ -27,13 +27,15 @@ import android.os.Parcelable;
*/
public final class AvailableNfcAntenna implements Parcelable {
/**
- * Location on the antenna on the Y axis in millimeters.
- * 0 is the bottom-left when the user is facing the screen.
+ * Location of the antenna on the Y axis in millimeters.
+ * 0 is the bottom-left when the user is facing the screen
+ * and the device orientation is Portrait.
*/
private final int mLocationX;
/**
- * Location on the antenna on the Y axis in millimeters.
- * 0 is the bottom-left when the user is facing the screen.
+ * Location of the antenna on the Y axis in millimeters.
+ * 0 is the bottom-left when the user is facing the screen
+ * and the device orientation is Portrait.
*/
private final int mLocationY;
@@ -43,16 +45,18 @@ public final class AvailableNfcAntenna implements Parcelable {
}
/**
- * Location on the antenna on the X axis in millimeters.
- * 0 is the bottom-left when the user is facing the screen.
+ * Location of the antenna on the X axis in millimeters.
+ * 0 is the bottom-left when the user is facing the screen
+ * and the device orientation is Portrait.
*/
public int getLocationX() {
return mLocationX;
}
/**
- * Location on the antenna on the Y axis in millimeters.
- * 0 is the bottom-left when the user is facing the screen.
+ * Location of the antenna on the Y axis in millimeters.
+ * 0 is the bottom-left when the user is facing the screen
+ * and the device orientation is Portrait.
*/
public int getLocationY() {
return mLocationY;
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index fb197f5be2a8..cdde18aed604 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -100,7 +100,7 @@ public final class Trace {
/** @hide */
public static final long TRACE_TAG_VIBRATOR = 1L << 23;
/** @hide */
- @SystemApi(client = MODULE_LIBRARIES)
+ @SystemApi
public static final long TRACE_TAG_AIDL = 1L << 24;
/** @hide */
public static final long TRACE_TAG_NNAPI = 1L << 25;
@@ -241,9 +241,16 @@ public final class Trace {
/**
* Writes a trace message to indicate that a given section of code has
* begun. Must be followed by a call to {@link #asyncTraceEnd} using the same
- * tag. Unlike {@link #traceBegin(long, String)} and {@link #traceEnd(long)},
- * asynchronous events do not need to be nested. The name and cookie used to
- * begin an event must be used to end it.
+ * tag, name and cookie.
+ *
+ * If two events with the same methodName overlap in time then they *must* have
+ * different cookie values. If they do not, the trace can become corrupted
+ * in unpredictable ways.
+ *
+ * Unlike {@link #traceBegin(long, String)} and {@link #traceEnd(long)},
+ * asynchronous events cannot be not nested. Consider using
+ * {@link #asyncTraceForTrackBegin(long, String, String, int)}
+ * if nested asynchronous events are needed.
*
* @param traceTag The trace tag.
* @param methodName The method name to appear in the trace.
@@ -264,6 +271,9 @@ public final class Trace {
* Must be called exactly once for each call to {@link #asyncTraceBegin(long, String, int)}
* using the same tag, name and cookie.
*
+ * See the documentation for {@link #asyncTraceBegin(long, String, int)}.
+ * for inteded usage of this method.
+ *
* @param traceTag The trace tag.
* @param methodName The method name to appear in the trace.
* @param cookie Unique identifier for distinguishing simultaneous events
@@ -283,14 +293,73 @@ public final class Trace {
* Writes a trace message to indicate that a given section of code has
* begun. Must be followed by a call to {@link #asyncTraceForTrackEnd} using the same
* track name and cookie.
- * This function operates exactly like {@link #asyncTraceBegin(long, String, int)},
- * except with the inclusion of a track name argument for where this method should appear.
- * The cookie must be unique on the trackName level, not the methodName level
+ *
+ * Events with the same trackName and cookie nest inside each other in the
+ * same way as calls to {@link #traceBegin(long, String)} and
+ * {@link #traceEnd(long)}.
+ *
+ * If two events with the same trackName overlap in time but do not nest
+ * correctly, then they *must* have different cookie values. If they do not,
+ * the trace can become corrupted in unpredictable ways.
+ *
+ * Good Example:
+ *
+ * public void parent() {
+ * asyncTraceForTrackBegin(TRACE_TAG_ALWAYS, "Track", "parent", mId);
+ * child()
+ * asyncTraceForTrackEnd(TRACE_TAG_ALWAYS, "Track", mId);
+ * }
+ *
+ * public void child() {
+ * asyncTraceForTrackBegin(TRACE_TAG_ALWAYS, "Track", "child", mId);
+ * // Some code here.
+ * asyncTraceForTrackEnd(TRACE_TAG_ALWAYS, "Track", mId);
+ * }
+ *
+ * This would be visualized as so:
+ * [ Parent ]
+ * [ Child ]
+ *
+ * Bad Example:
+ *
+ * public static void processData(String dataToProcess) {
+ * asyncTraceForTrackBegin(TRACE_TAG_ALWAYS, "processDataInParallel", "processData", 0);
+ * // Some code here.
+ * asyncTraceForTrackEnd(TRACE_TAG_ALWAYS, "processDataInParallel", 0);
+ * }
+ *
+ * public static void processDataInParallel({@code List<String>} data) {
+ * ExecutorService executor = Executors.newCachedThreadPool();
+ * for (String s : data) {
+ * pool.execute(() -> processData(s));
+ * }
+ * }
+ *
+ * This is invalid because it's possible for processData to be run many times
+ * in parallel (i.e. processData events overlap) but the same cookie is
+ * provided each time.
+ *
+ * To fix this, specify a different id in each invocation of processData:
+ *
+ * public static void processData(String dataToProcess, int id) {
+ * asyncTraceForTrackBegin(TRACE_TAG_ALWAYS, "processDataInParallel", "processData", id);
+ * // Some code here.
+ * asyncTraceForTrackEnd(TRACE_TAG_ALWAYS, "processDataInParallel", id);
+ * }
+ *
+ * public static void processDataInParallel({@code List<String>} data) {
+ * ExecutorService executor = Executors.newCachedThreadPool();
+ * for (int i = 0; i < data.size(); ++i) {
+ * pool.execute(() -> processData(data.get(i), i));
+ * }
+ * }
*
* @param traceTag The trace tag.
* @param trackName The track where the event should appear in the trace.
* @param methodName The method name to appear in the trace.
- * @param cookie Unique identifier for distinguishing simultaneous events
+ * @param cookie Unique identifier used for nesting events on a single
+ * track. Events which overlap without nesting on the same
+ * track must have different values for cookie.
*
* @hide
*/
@@ -307,9 +376,14 @@ public final class Trace {
* {@link #asyncTraceForTrackBegin(long, String, String, int)}
* using the same tag, track name, and cookie.
*
+ * See the documentation for {@link #asyncTraceForTrackBegin(long, String, String, int)}.
+ * for inteded usage of this method.
+ *
* @param traceTag The trace tag.
* @param trackName The track where the event should appear in the trace.
- * @param cookie Unique identifier for distinguishing simultaneous events
+ * @param cookie Unique identifier used for nesting events on a single
+ * track. Events which overlap without nesting on the same
+ * track must have different values for cookie.
*
* @hide
*/
diff --git a/core/java/android/service/voice/HotwordAudioStream.java b/core/java/android/service/voice/HotwordAudioStream.java
index 1c57700d38e1..6ae952c045cb 100644
--- a/core/java/android/service/voice/HotwordAudioStream.java
+++ b/core/java/android/service/voice/HotwordAudioStream.java
@@ -47,6 +47,21 @@ import java.util.Objects;
public final class HotwordAudioStream implements Parcelable {
/**
+ * Key for int value to be read from {@link #getMetadata()}. The value is read by the system and
+ * is the length (in bytes) of the byte buffers created to copy bytes in the
+ * {@link #getAudioStreamParcelFileDescriptor()} written by the {@link HotwordDetectionService}.
+ * The buffer length should be chosen such that no additional latency is introduced. Typically,
+ * this should be <em>at least</em> the size of byte chunks written by the
+ * {@link HotwordDetectionService}.
+ *
+ * <p>If no value specified in the metadata for the buffer length, or if the value is less than
+ * 1, or if it is greater than 65,536, or if it is not an int, the default value of 2,560 will
+ * be used.</p>
+ */
+ public static final String KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES =
+ "android.service.voice.key.AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES";
+
+ /**
* The {@link AudioFormat} of the audio stream.
*/
@NonNull
@@ -414,10 +429,10 @@ public final class HotwordAudioStream implements Parcelable {
}
@DataClass.Generated(
- time = 1669184301563L,
+ time = 1669916341034L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/service/voice/HotwordAudioStream.java",
- inputSignatures = "private final @android.annotation.NonNull android.media.AudioFormat mAudioFormat\nprivate final @android.annotation.NonNull android.os.ParcelFileDescriptor mAudioStreamParcelFileDescriptor\nprivate final @android.annotation.Nullable android.media.AudioTimestamp mTimestamp\nprivate final @android.annotation.NonNull android.os.PersistableBundle mMetadata\nprivate static android.media.AudioTimestamp defaultTimestamp()\nprivate static android.os.PersistableBundle defaultMetadata()\npublic android.service.voice.HotwordAudioStream.Builder buildUpon()\nclass HotwordAudioStream extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genParcelable=true, genToString=true)")
+ inputSignatures = "public static final java.lang.String KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES\nprivate final @android.annotation.NonNull android.media.AudioFormat mAudioFormat\nprivate final @android.annotation.NonNull android.os.ParcelFileDescriptor mAudioStreamParcelFileDescriptor\nprivate final @android.annotation.Nullable android.media.AudioTimestamp mTimestamp\nprivate final @android.annotation.NonNull android.os.PersistableBundle mMetadata\nprivate static android.media.AudioTimestamp defaultTimestamp()\nprivate static android.os.PersistableBundle defaultMetadata()\npublic android.service.voice.HotwordAudioStream.Builder buildUpon()\nclass HotwordAudioStream extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genParcelable=true, genToString=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/text/Highlights.java b/core/java/android/text/Highlights.java
new file mode 100644
index 000000000000..356dfcaeb93b
--- /dev/null
+++ b/core/java/android/text/Highlights.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+import android.graphics.Paint;
+import android.util.Pair;
+
+import androidx.annotation.IntRange;
+import androidx.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A class that represents of the highlight of the text.
+ */
+public class Highlights {
+ private final List<Pair<Paint, int[]>> mHighlights;
+
+ private Highlights(List<Pair<Paint, int[]>> highlights) {
+ mHighlights = highlights;
+ }
+
+ /**
+ * Returns a number of highlight.
+ *
+ * @return a number of highlight.
+ *
+ * @see Builder#addRange(Paint, int, int)
+ * @see Builder#addRanges(Paint, int...)
+ */
+ public @IntRange(from = 0) int getSize() {
+ return mHighlights.size();
+ }
+
+ /**
+ * Returns a paint used for the i-th highlight.
+ *
+ * @param index an index of the highlight. Must be between 0 and {@link #getSize()}
+ * @return a paint object
+ *
+ * @see Builder#addRange(Paint, int, int)
+ * @see Builder#addRanges(Paint, int...)
+ */
+ public @NonNull Paint getPaint(@IntRange(from = 0) int index) {
+ return mHighlights.get(index).first;
+ }
+
+ /**
+ * Returns ranges of the i-th highlight.
+ *
+ * Ranges are represented of flattened inclusive start and exclusive end integers array. The
+ * inclusive start offset of the {@code i}-th range is stored in {@code 2 * i}-th of the array.
+ * The exclusive end offset of the {@code i}-th range is stored in {@code 2* i + 1}-th of the
+ * array. For example, the two ranges: (1, 2) and (3, 4) are flattened into single int array
+ * [1, 2, 3, 4].
+ *
+ * @param index an index of the highlight. Must be between 0 and {@link #getSize()}
+ * @return a paint object
+ *
+ * @see Builder#addRange(Paint, int, int)
+ * @see Builder#addRanges(Paint, int...)
+ */
+ public @NonNull int[] getRanges(int index) {
+ return mHighlights.get(index).second;
+ }
+
+ /**
+ * A builder for the Highlights.
+ */
+ public static final class Builder {
+ private final List<Pair<Paint, int[]>> mHighlights = new ArrayList<>();
+
+ /**
+ * Add single range highlight.
+ *
+ * @param paint a paint object used for drawing highlight path.
+ * @param start an inclusive offset of the text.
+ * @param end an exclusive offset of the text.
+ * @return this builder instance.
+ */
+ public @NonNull Builder addRange(@NonNull Paint paint, @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end) {
+ if (start > end) {
+ throw new IllegalArgumentException("start must not be larger than end: "
+ + start + ", " + end);
+ }
+ Objects.requireNonNull(paint);
+
+ int[] range = new int[] {start, end};
+ mHighlights.add(new Pair<>(paint, range));
+ return this;
+ }
+
+ /**
+ * Add multiple ranges highlight.
+ *
+ * @param paint a paint object used for drawing highlight path.
+ * @param ranges a flatten ranges. The {@code 2 * i}-th element is an inclusive start offset
+ * of the {@code i}-th character. The {@code 2 * i + 1}-th element is an
+ * exclusive end offset of the {@code i}-th character.
+ * @return this builder instance.
+ */
+ public @NonNull Builder addRanges(@NonNull Paint paint, @NonNull int... ranges) {
+ if (ranges.length % 2 == 1) {
+ throw new IllegalArgumentException(
+ "Flatten ranges must have even numbered elements");
+ }
+ for (int j = 0; j < ranges.length / 2; ++j) {
+ int start = ranges[j * 2];
+ int end = ranges[j * 2 + 1];
+ if (start > end) {
+ throw new IllegalArgumentException(
+ "Reverse range found in the flatten range: " + Arrays.toString(
+ ranges));
+ }
+ }
+ Objects.requireNonNull(paint);
+ mHighlights.add(new Pair<>(paint, ranges));
+ return this;
+ }
+
+ /**
+ * Build a new Highlights instance.
+ *
+ * @return a new Highlights instance.
+ */
+ public @NonNull Highlights build() {
+ return new Highlights(mHighlights);
+ }
+ }
+}
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 54ec07edd3ee..64dc16de3e4d 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -44,6 +44,7 @@ import com.android.internal.util.GrowingArrayUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
+import java.util.List;
/**
* A base class that manages text layout in visual elements on
@@ -347,9 +348,13 @@ public abstract class Layout {
/**
* Draw this Layout on the specified Canvas.
+ *
+ * This API draws background first, then draws text on top of it.
+ *
+ * @see #draw(Canvas, List, List, Path, Paint, int)
*/
public void draw(Canvas c) {
- draw(c, null, null, 0);
+ draw(c, (Path) null, (Paint) null, 0);
}
/**
@@ -357,23 +362,142 @@ public abstract class Layout {
* between the background and the text.
*
* @param canvas the canvas
- * @param highlight the path of the highlight or cursor; can be null
- * @param highlightPaint the paint for the highlight
+ * @param selectionHighlight the path of the selection highlight or cursor; can be null
+ * @param selectionHighlightPaint the paint for the selection highlight
* @param cursorOffsetVertical the amount to temporarily translate the
* canvas while rendering the highlight
+ *
+ * @see #draw(Canvas, List, List, Path, Paint, int)
*/
- public void draw(Canvas canvas, Path highlight, Paint highlightPaint,
+ public void draw(
+ Canvas canvas, Path selectionHighlight,
+ Paint selectionHighlightPaint, int cursorOffsetVertical) {
+ draw(canvas, null, null, selectionHighlight, selectionHighlightPaint, cursorOffsetVertical);
+ }
+
+ /**
+ * Draw this layout on the specified canvas.
+ *
+ * This API draws background first, then draws highlight paths on top of it, then draws
+ * selection or cursor, then finally draws text on top of it.
+ *
+ * @see #drawBackground(Canvas)
+ * @see #drawText(Canvas)
+ *
+ * @param canvas the canvas
+ * @param highlightPaths the path of the highlights. The highlightPaths and highlightPaints must
+ * have the same length and aligned in the same order. For example, the
+ * paint of the n-th of the highlightPaths should be stored at the n-th of
+ * highlightPaints.
+ * @param highlightPaints the paints for the highlights. The highlightPaths and highlightPaints
+ * must have the same length and aligned in the same order. For example,
+ * the paint of the n-th of the highlightPaths should be stored at the
+ * n-th of highlightPaints.
+ * @param selectionPath the selection or cursor path
+ * @param selectionPaint the paint for the selection or cursor.
+ * @param cursorOffsetVertical the amount to temporarily translate the canvas while rendering
+ * the highlight
+ */
+ public void draw(@NonNull Canvas canvas,
+ @Nullable List<Path> highlightPaths,
+ @Nullable List<Paint> highlightPaints,
+ @Nullable Path selectionPath,
+ @Nullable Paint selectionPaint,
int cursorOffsetVertical) {
final long lineRange = getLineRangeForDraw(canvas);
int firstLine = TextUtils.unpackRangeStartFromLong(lineRange);
int lastLine = TextUtils.unpackRangeEndFromLong(lineRange);
if (lastLine < 0) return;
- drawBackground(canvas, highlight, highlightPaint, cursorOffsetVertical,
- firstLine, lastLine);
+ drawWithoutText(canvas, highlightPaths, highlightPaints, selectionPath, selectionPaint,
+ cursorOffsetVertical, firstLine, lastLine);
+ drawText(canvas, firstLine, lastLine);
+ }
+
+ /**
+ * Draw text part of this layout.
+ *
+ * Different from {@link #draw(Canvas, List, List, Path, Paint, int)} API, this API only draws
+ * text part, not drawing highlights, selections, or backgrounds.
+ *
+ * @see #draw(Canvas, List, List, Path, Paint, int)
+ * @see #drawBackground(Canvas)
+ *
+ * @param canvas the canvas
+ */
+ public void drawText(@NonNull Canvas canvas) {
+ final long lineRange = getLineRangeForDraw(canvas);
+ int firstLine = TextUtils.unpackRangeStartFromLong(lineRange);
+ int lastLine = TextUtils.unpackRangeEndFromLong(lineRange);
+ if (lastLine < 0) return;
drawText(canvas, firstLine, lastLine);
}
+ /**
+ * Draw background of this layout.
+ *
+ * Different from {@link #draw(Canvas, List, List, Path, Paint, int)} API, this API only draws
+ * background, not drawing text, highlights or selections. The background here is drawn by
+ * {@link LineBackgroundSpan} attached to the text.
+ *
+ * @see #draw(Canvas, List, List, Path, Paint, int)
+ * @see #drawText(Canvas)
+ *
+ * @param canvas the canvas
+ */
+ public void drawBackground(@NonNull Canvas canvas) {
+ final long lineRange = getLineRangeForDraw(canvas);
+ int firstLine = TextUtils.unpackRangeStartFromLong(lineRange);
+ int lastLine = TextUtils.unpackRangeEndFromLong(lineRange);
+ if (lastLine < 0) return;
+ drawBackground(canvas, firstLine, lastLine);
+ }
+
+ /**
+ * @hide public for Editor.java
+ */
+ public void drawWithoutText(
+ @NonNull Canvas canvas,
+ @Nullable List<Path> highlightPaths,
+ @Nullable List<Paint> highlightPaints,
+ @Nullable Path selectionPath,
+ @Nullable Paint selectionPaint,
+ int cursorOffsetVertical,
+ int firstLine,
+ int lastLine) {
+ drawBackground(canvas, firstLine, lastLine);
+ if (highlightPaths == null && highlightPaints == null) {
+ return;
+ }
+ if (cursorOffsetVertical != 0) canvas.translate(0, cursorOffsetVertical);
+ try {
+ if (highlightPaths != null) {
+ if (highlightPaints == null) {
+ throw new IllegalArgumentException(
+ "if highlight is specified, highlightPaint must be specified.");
+ }
+ if (highlightPaints.size() != highlightPaths.size()) {
+ throw new IllegalArgumentException(
+ "The highlight path size is different from the size of highlight"
+ + " paints");
+ }
+ for (int i = 0; i < highlightPaths.size(); ++i) {
+ final Path highlight = highlightPaths.get(i);
+ final Paint highlightPaint = highlightPaints.get(i);
+ if (highlight != null) {
+ canvas.drawPath(highlight, highlightPaint);
+ }
+ }
+ }
+
+ if (selectionPath != null) {
+ canvas.drawPath(selectionPath, selectionPaint);
+ }
+ } finally {
+ if (cursorOffsetVertical != 0) canvas.translate(0, -cursorOffsetVertical);
+ }
+ }
+
private boolean isJustificationRequired(int lineNum) {
if (mJustificationMode == JUSTIFICATION_MODE_NONE) return false;
final int lineEnd = getLineEnd(lineNum);
@@ -635,8 +759,9 @@ public abstract class Layout {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public void drawBackground(Canvas canvas, Path highlight, Paint highlightPaint,
- int cursorOffsetVertical, int firstLine, int lastLine) {
+ public void drawBackground(
+ @NonNull Canvas canvas,
+ int firstLine, int lastLine) {
// First, draw LineBackgroundSpans.
// LineBackgroundSpans know nothing about the alignment, margins, or
// direction of the layout or line. XXX: Should they?
@@ -700,14 +825,6 @@ public abstract class Layout {
}
mLineBackgroundSpans.recycle();
}
-
- // There can be a highlight even without spans if we are drawing
- // a non-spanned transformation of a spanned editing buffer.
- if (highlight != null) {
- if (cursorOffsetVertical != 0) canvas.translate(0, cursorOffsetVertical);
- canvas.drawPath(highlight, highlightPaint);
- if (cursorOffsetVertical != 0) canvas.translate(0, -cursorOffsetVertical);
- }
}
/**
diff --git a/core/java/android/util/RotationUtils.java b/core/java/android/util/RotationUtils.java
index c54d9b604ba2..3e7c67e72031 100644
--- a/core/java/android/util/RotationUtils.java
+++ b/core/java/android/util/RotationUtils.java
@@ -25,6 +25,7 @@ import android.annotation.Dimension;
import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.view.Surface.Rotation;
import android.view.SurfaceControl;
@@ -193,6 +194,29 @@ public class RotationUtils {
}
/**
+ * Same as {@link #rotatePoint}, but for float coordinates.
+ */
+ public static void rotatePointF(PointF inOutPoint, @Rotation int rotation,
+ float parentW, float parentH) {
+ float origX = inOutPoint.x;
+ switch (rotation) {
+ case ROTATION_0:
+ return;
+ case ROTATION_90:
+ inOutPoint.x = inOutPoint.y;
+ inOutPoint.y = parentW - origX;
+ return;
+ case ROTATION_180:
+ inOutPoint.x = parentW - inOutPoint.x;
+ inOutPoint.y = parentH - inOutPoint.y;
+ return;
+ case ROTATION_270:
+ inOutPoint.x = parentH - inOutPoint.y;
+ inOutPoint.y = origX;
+ }
+ }
+
+ /**
* Sets a matrix such that given a rotation, it transforms physical display
* coordinates to that rotation's logical coordinates.
*
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 0f970bf4f91c..09a9d46ba257 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -853,7 +853,12 @@ public final class ViewRootImpl implements ViewParent,
private SurfaceSyncGroup mSyncGroup;
private SurfaceSyncGroup.TransactionReadyCallback mTransactionReadyCallback;
- private int mNumSyncsInProgress = 0;
+
+ private static final Object sSyncProgressLock = new Object();
+ // The count needs to be static since it's used to enable or disable RT animations which is
+ // done at a global level per process. If any VRI syncs are in progress, we can't enable RT
+ // animations until all are done.
+ private static int sNumSyncsInProgress = 0;
private HashSet<ScrollCaptureCallback> mRootScrollCaptureCallbacks;
@@ -11229,13 +11234,6 @@ public final class ViewRootImpl implements ViewParent,
});
}
- private final Executor mPostAtFrontExecutor = new Executor() {
- @Override
- public void execute(Runnable command) {
- mHandler.postAtFrontOfQueue(command);
- }
- };
-
public final SurfaceSyncGroup.SyncTarget mSyncTarget = new SurfaceSyncGroup.SyncTarget() {
@Override
public void onAddedToSyncGroup(SurfaceSyncGroup parentSyncGroup,
@@ -11245,9 +11243,6 @@ public final class ViewRootImpl implements ViewParent,
// Always sync the buffer if the sync request did not come from VRI.
mSyncBuffer = true;
}
- if (mAttachInfo.mThreadedRenderer != null) {
- HardwareRenderer.setRtAnimationsEnabled(false);
- }
if (mTransactionReadyCallback != null) {
Log.d(mTag, "Already set sync for the next draw.");
@@ -11261,16 +11256,29 @@ public final class ViewRootImpl implements ViewParent,
scheduleTraversals();
}
}
+ };
+
+ private final Executor mSimpleExecutor = Runnable::run;
+
+ private void updateSyncInProgressCount(SurfaceSyncGroup syncGroup) {
+ if (mAttachInfo.mThreadedRenderer == null) {
+ return;
+ }
+
+ synchronized (sSyncProgressLock) {
+ if (sNumSyncsInProgress++ == 0) {
+ HardwareRenderer.setRtAnimationsEnabled(false);
+ }
+ }
- private void updateSyncInProgressCount(SurfaceSyncGroup parentSyncGroup) {
- mNumSyncsInProgress++;
- parentSyncGroup.addSyncCompleteCallback(mPostAtFrontExecutor, () -> {
- if (--mNumSyncsInProgress == 0 && mAttachInfo.mThreadedRenderer != null) {
+ syncGroup.addSyncCompleteCallback(mSimpleExecutor, () -> {
+ synchronized (sSyncProgressLock) {
+ if (--sNumSyncsInProgress == 0) {
HardwareRenderer.setRtAnimationsEnabled(true);
}
- });
- }
- };
+ }
+ });
+ }
@Override
public SurfaceSyncGroup.SyncTarget getSyncTarget() {
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index 52658404548c..e6379cfb27cf 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -968,7 +968,8 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup {
final int position = getSelectedItemPosition();
if (position >= 0) {
// we fire selection events here not in View
- sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
+ // posting the event should delay it long enough for UI changes to take effect.
+ post(() -> sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED));
}
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 5740f86b3486..558d96089bb7 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -2054,7 +2054,10 @@ public class Editor {
}
}
- void onDraw(Canvas canvas, Layout layout, Path highlight, Paint highlightPaint,
+ void onDraw(Canvas canvas, Layout layout,
+ List<Path> highlightPaths,
+ List<Paint> highlightPaints,
+ Path selectionHighlight, Paint selectionHighlightPaint,
int cursorOffsetVertical) {
final int selectionStart = mTextView.getSelectionStart();
final int selectionEnd = mTextView.getSelectionEnd();
@@ -2078,37 +2081,40 @@ public class Editor {
mCorrectionHighlighter.draw(canvas, cursorOffsetVertical);
}
- if (highlight != null && selectionStart == selectionEnd && mDrawableForCursor != null) {
+ if (selectionHighlight != null && selectionStart == selectionEnd
+ && mDrawableForCursor != null) {
drawCursor(canvas, cursorOffsetVertical);
// Rely on the drawable entirely, do not draw the cursor line.
// Has to be done after the IMM related code above which relies on the highlight.
- highlight = null;
+ selectionHighlight = null;
}
if (mSelectionActionModeHelper != null) {
mSelectionActionModeHelper.onDraw(canvas);
if (mSelectionActionModeHelper.isDrawingHighlight()) {
- highlight = null;
+ selectionHighlight = null;
}
}
if (mTextView.canHaveDisplayList() && canvas.isHardwareAccelerated()) {
- drawHardwareAccelerated(canvas, layout, highlight, highlightPaint,
- cursorOffsetVertical);
+ drawHardwareAccelerated(canvas, layout, highlightPaths, highlightPaints,
+ selectionHighlight, selectionHighlightPaint, cursorOffsetVertical);
} else {
- layout.draw(canvas, highlight, highlightPaint, cursorOffsetVertical);
+ layout.draw(canvas, highlightPaths, highlightPaints, selectionHighlight,
+ selectionHighlightPaint, cursorOffsetVertical);
}
}
- private void drawHardwareAccelerated(Canvas canvas, Layout layout, Path highlight,
- Paint highlightPaint, int cursorOffsetVertical) {
+ private void drawHardwareAccelerated(Canvas canvas, Layout layout,
+ List<Path> highlightPaths, List<Paint> highlightPaints,
+ Path selectionHighlight, Paint selectionHighlightPaint, int cursorOffsetVertical) {
final long lineRange = layout.getLineRangeForDraw(canvas);
int firstLine = TextUtils.unpackRangeStartFromLong(lineRange);
int lastLine = TextUtils.unpackRangeEndFromLong(lineRange);
if (lastLine < 0) return;
- layout.drawBackground(canvas, highlight, highlightPaint, cursorOffsetVertical,
- firstLine, lastLine);
+ layout.drawWithoutText(canvas, highlightPaths, highlightPaints, selectionHighlight,
+ selectionHighlightPaint, cursorOffsetVertical, firstLine, lastLine);
if (layout instanceof DynamicLayout) {
if (mTextRenderNodes == null) {
@@ -2154,8 +2160,9 @@ public class Editor {
continue;
}
startIndexToFindAvailableRenderNode = drawHardwareAcceleratedInner(canvas, layout,
- highlight, highlightPaint, cursorOffsetVertical, blockEndLines,
- blockIndices, i, numberOfBlocks, startIndexToFindAvailableRenderNode);
+ selectionHighlight, selectionHighlightPaint, cursorOffsetVertical,
+ blockEndLines, blockIndices, i, numberOfBlocks,
+ startIndexToFindAvailableRenderNode);
if (blockEndLines[i] >= lastLine) {
lastIndex = Math.max(indexFirstChangedBlock, i + 1);
break;
@@ -2169,9 +2176,9 @@ public class Editor {
|| mTextRenderNodes[blockIndex] == null
|| mTextRenderNodes[blockIndex].needsToBeShifted) {
startIndexToFindAvailableRenderNode = drawHardwareAcceleratedInner(canvas,
- layout, highlight, highlightPaint, cursorOffsetVertical,
- blockEndLines, blockIndices, block, numberOfBlocks,
- startIndexToFindAvailableRenderNode);
+ layout, selectionHighlight, selectionHighlightPaint,
+ cursorOffsetVertical, blockEndLines, blockIndices, block,
+ numberOfBlocks, startIndexToFindAvailableRenderNode);
}
}
}
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index ba6fa197f164..ad431efc0bd2 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -802,6 +802,21 @@ public class Spinner extends AbsSpinner implements OnClickListener {
dialog.dismiss();
}
+ /**
+ * Sets selection and dismisses the spinner's popup if it can be dismissed.
+ * For ease of use in tests, where publicly obtaining the spinner's popup is difficult.
+ *
+ * @param which index of the item to be selected.
+ * @hide
+ */
+ @TestApi
+ public void onClick(int which) {
+ setSelection(which);
+ if (mPopup != null && mPopup.isShowing()) {
+ mPopup.dismiss();
+ }
+ }
+
@Override
public CharSequence getAccessibilityClassName() {
return Spinner.class.getName();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index bf1a2bd51d91..475a8491de4d 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -102,6 +102,7 @@ import android.text.Editable;
import android.text.GetChars;
import android.text.GraphemeClusterSegmentFinder;
import android.text.GraphicsOperations;
+import android.text.Highlights;
import android.text.InputFilter;
import android.text.InputType;
import android.text.Layout;
@@ -238,6 +239,7 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
@@ -926,6 +928,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
@UnsupportedAppUsage
private boolean mHighlightPathBogus = true;
+ private List<Path> mHighlightPaths;
+ private List<Paint> mHighlightPaints;
+ private Highlights mHighlights;
+ private final List<Path> mPathRecyclePool = new ArrayList<>();
+ private boolean mHighlightPathsBogus = true;
+
// Although these fields are specific to editable text, they are not added to Editor because
// they are defined by the TextView's style and are theme-dependent.
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
@@ -6131,6 +6139,34 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
/**
+ * Set Highlights
+ *
+ * @param highlights A highlight object. Call with null for reset.
+ *
+ * @see #getHighlights()
+ * @see Highlights
+ */
+ public void setHighlights(@Nullable Highlights highlights) {
+ mHighlights = highlights;
+ mHighlightPathsBogus = true;
+ invalidate();
+ }
+
+ /**
+ * Returns highlights
+ *
+ * @return a highlight to be drawn. null if no highlight was set.
+ *
+ * @see #setHighlights(Highlights)
+ * @see Highlights
+ *
+ */
+ @Nullable
+ public Highlights getHighlights() {
+ return mHighlights;
+ }
+
+ /**
* Convenience method to append the specified text to the TextView's
* display buffer, upgrading it to {@link android.widget.TextView.BufferType#EDITABLE}
* if it was not already editable.
@@ -8219,6 +8255,54 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return drawableState;
}
+ private void maybeUpdateHighlightPaths() {
+ if (!mHighlightPathsBogus) {
+ return;
+ }
+
+ if (mHighlightPaths != null) {
+ mPathRecyclePool.addAll(mHighlightPaths);
+ mHighlightPaths.clear();
+ mHighlightPaints.clear();
+ } else {
+ mHighlightPaths = new ArrayList<>();
+ mHighlightPaints = new ArrayList<>();
+ }
+
+ if (mHighlights != null) {
+ for (int i = 0; i < mHighlights.getSize(); ++i) {
+ final int[] ranges = mHighlights.getRanges(i);
+ final Paint paint = mHighlights.getPaint(i);
+
+ final Path path;
+ if (mPathRecyclePool.isEmpty()) {
+ path = new Path();
+ } else {
+ path = mPathRecyclePool.get(mPathRecyclePool.size() - 1);
+ mPathRecyclePool.remove(mPathRecyclePool.size() - 1);
+ path.reset();
+ }
+
+ boolean atLeastOnePathAdded = false;
+ for (int j = 0; j < ranges.length / 2; ++j) {
+ final int start = ranges[2 * j];
+ final int end = ranges[2 * j + 1];
+ if (start < end) {
+ mLayout.getSelection(start, end, (left, top, right, bottom, layout) ->
+ path.addRect(left, top, right, bottom, Path.Direction.CW)
+ );
+ atLeastOnePathAdded = true;
+ }
+ }
+ if (atLeastOnePathAdded) {
+ mHighlightPaths.add(path);
+ mHighlightPaints.add(paint);
+ }
+ }
+ }
+ mHighlightPathsBogus = false;
+ }
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private Path getUpdatedHighlightPath() {
Path highlight = null;
@@ -8418,17 +8502,21 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
final int cursorOffsetVertical = voffsetCursor - voffsetText;
+ maybeUpdateHighlightPaths();
Path highlight = getUpdatedHighlightPath();
if (mEditor != null) {
- mEditor.onDraw(canvas, layout, highlight, mHighlightPaint, cursorOffsetVertical);
+ mEditor.onDraw(canvas, layout, mHighlightPaths, mHighlightPaints, highlight,
+ mHighlightPaint, cursorOffsetVertical);
} else {
- layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical);
+ layout.draw(canvas, mHighlightPaths, mHighlightPaints, highlight, mHighlightPaint,
+ cursorOffsetVertical);
}
if (mMarquee != null && mMarquee.shouldDrawGhost()) {
final float dx = mMarquee.getGhostOffset();
canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f);
- layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical);
+ layout.draw(canvas, mHighlightPaths, mHighlightPaints, highlight, mHighlightPaint,
+ cursorOffsetVertical);
}
canvas.restore();
@@ -9750,6 +9838,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
mOldMaxMode = mMaxMode;
mHighlightPathBogus = true;
+ mHighlightPathsBogus = true;
if (wantWidth < 0) {
wantWidth = 0;
diff --git a/core/java/com/android/internal/content/om/OverlayConfig.java b/core/java/com/android/internal/content/om/OverlayConfig.java
index 828e5cf1fd4c..2d04bdbda846 100644
--- a/core/java/com/android/internal/content/om/OverlayConfig.java
+++ b/core/java/com/android/internal/content/om/OverlayConfig.java
@@ -140,7 +140,6 @@ public class OverlayConfig {
ArrayMap<Integer, List<String>> activeApexesPerPartition = getActiveApexes(partitions);
- boolean foundConfigFile = false;
final Map<String, ParsedOverlayInfo> packageManagerOverlayInfos =
packageProvider == null ? null : getOverlayPackageInfos(packageProvider);
@@ -154,7 +153,6 @@ public class OverlayConfig {
activeApexesPerPartition.getOrDefault(partition.type,
Collections.emptyList()));
if (partitionOverlays != null) {
- foundConfigFile = true;
overlays.addAll(partitionOverlays);
continue;
}
@@ -191,12 +189,6 @@ public class OverlayConfig {
overlays.addAll(partitionConfigs);
}
- if (!foundConfigFile) {
- // If no overlay configuration files exist, disregard partition precedence and allow
- // android:priority to reorder overlays across partition boundaries.
- overlays.sort(sStaticOverlayComparator);
- }
-
for (int i = 0, n = overlays.size(); i < n; i++) {
// Add the configurations to a map so definitions of an overlay in an earlier
// partition can be replaced by an overlay with the same package name in a later
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 9e563dec3e4e..6ceffde68e87 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -645,7 +645,6 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool p
char jitmaxsizeOptsBuf[sizeof("-Xjitmaxsize:")-1 + PROPERTY_VALUE_MAX];
char jitinitialsizeOptsBuf[sizeof("-Xjitinitialsize:")-1 + PROPERTY_VALUE_MAX];
char jitthresholdOptsBuf[sizeof("-Xjitthreshold:")-1 + PROPERTY_VALUE_MAX];
- char useJitProfilesOptsBuf[sizeof("-Xjitsaveprofilinginfo:")-1 + PROPERTY_VALUE_MAX];
char jitprithreadweightOptBuf[sizeof("-Xjitprithreadweight:")-1 + PROPERTY_VALUE_MAX];
char jittransitionweightOptBuf[sizeof("-Xjittransitionweight:")-1 + PROPERTY_VALUE_MAX];
char hotstartupsamplesOptsBuf[sizeof("-Xps-hot-startup-method-samples:")-1 + PROPERTY_VALUE_MAX];
@@ -860,10 +859,7 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool p
parseRuntimeOption("dalvik.vm.jitpthreadpriority",
jitpthreadpriorityOptsBuf,
"-Xjitpthreadpriority:");
- property_get("dalvik.vm.usejitprofiles", useJitProfilesOptsBuf, "");
- if (strcmp(useJitProfilesOptsBuf, "true") == 0) {
- addOption("-Xjitsaveprofilinginfo");
- }
+ addOption("-Xjitsaveprofilinginfo");
parseRuntimeOption("dalvik.vm.jitprithreadweight",
jitprithreadweightOptBuf,
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
index 29560dce1cd8..e9ada235b388 100644
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -96,7 +96,7 @@ class LoaderAssetsProvider : public AssetsProvider {
}
bool ForEachFile(const std::string& /* root_path */,
- const std::function<void(StringPiece, FileType)>& /* f */) const {
+ android::base::function_ref<void(StringPiece, FileType)> /* f */) const {
return true;
}
diff --git a/core/jni/android_hardware_display_DisplayViewport.cpp b/core/jni/android_hardware_display_DisplayViewport.cpp
index 03432e9ce746..7f630cb27972 100644
--- a/core/jni/android_hardware_display_DisplayViewport.cpp
+++ b/core/jni/android_hardware_display_DisplayViewport.cpp
@@ -61,7 +61,8 @@ status_t android_hardware_display_DisplayViewport_toNative(JNIEnv* env, jobject
viewport->displayId = env->GetIntField(viewportObj, gDisplayViewportClassInfo.displayId);
viewport->isActive = env->GetBooleanField(viewportObj, gDisplayViewportClassInfo.isActive);
- viewport->orientation = env->GetIntField(viewportObj, gDisplayViewportClassInfo.orientation);
+ jint orientation = env->GetIntField(viewportObj, gDisplayViewportClassInfo.orientation);
+ viewport->orientation = static_cast<ui::Rotation>(orientation);
viewport->deviceWidth = env->GetIntField(viewportObj, gDisplayViewportClassInfo.deviceWidth);
viewport->deviceHeight = env->GetIntField(viewportObj, gDisplayViewportClassInfo.deviceHeight);
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 80df0ead4bcd..403c5836d9dd 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -763,7 +763,12 @@ static void android_view_MotionEvent_nativeScale(jlong nativePtr, jfloat scale)
static jint android_view_MotionEvent_nativeGetSurfaceRotation(jlong nativePtr) {
MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return jint(event->getSurfaceRotation());
+ auto rotation = event->getSurfaceRotation();
+ if (rotation) {
+ return static_cast<jint>(rotation.value());
+ } else {
+ return -1;
+ }
}
// ----------------------------------------------------------------------------
diff --git a/core/res/OWNERS b/core/res/OWNERS
index 6d05e0785ec1..a2ef4005f5cb 100644
--- a/core/res/OWNERS
+++ b/core/res/OWNERS
@@ -8,7 +8,6 @@ hackbod@android.com
hackbod@google.com
ilyamaty@google.com
jaggies@google.com
-jdemeulenaere@google.com
jsharkey@android.com
jsharkey@google.com
juliacr@google.com
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index eb7034437423..abbff582748b 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -3165,20 +3165,20 @@
<attr name="canDisplayOnRemoteDevices" format="boolean"/>
<attr name="allowUntrustedActivityEmbedding" />
<attr name="knownActivityEmbeddingCerts" />
- <!-- Specifies the category of the target display the activity is expected to run on. Upon
- creation, a virtual display can specify which display categories it supports and one of
- the category must be present in the activity's manifest to allow this activity to run.
- The default value is {@code null}, which indicates the activity does not belong to a
- restricted display category and thus can only run on a display that didn't specify any
- display categories. Each activity can only specify one category it targets to but a
- virtual display can accommodate multiple restricted categories.
+ <!-- Specifies the required display category of the activity. Upon creation, a display can
+ specify which display categories it supports and one of the categories must be present
+ in the {@code <activity>} element to allow this activity to run. The default value is
+ {@code null}, which indicates the activity does not have a required display category
+ and thus can only run on a display that didn't specify any display categories. Each
+ activity can only specify one required category but a display can accommodate multiple
+ display categories.
<p> This field should be formatted as a Java-language-style free form string(for
example, com.google.automotive_entertainment), which may contain uppercase or lowercase
letters ('A' through 'Z'), numbers, and underscores ('_') but may only start with
letters.
-->
- <attr name="targetDisplayCategory" format="string"/>
+ <attr name="requiredDisplayCategory" format="string"/>
</declare-styleable>
<!-- The <code>activity-alias</code> tag declares a new
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 2ab5b75ea25a..9a585a194ed1 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2078,6 +2078,13 @@
<!-- Flag indicating whether the current device supports "Ask every time" for sms-->
<bool name="config_sms_ask_every_time_support">true</bool>
+ <!-- Flag indicating whether the current device allows acknowledgement of SIM operation like
+ SM-PP or saving SMS to SIM can be done via the IMS interfaces.
+ If true,this means that the device supports sending of sim operation response via the
+ IMS interface APIs. This can be overridden to false for devices which can't send
+ sim operation acknowledgements via IMS interface APIs. -->
+ <bool name="config_smppsim_response_via_ims">false</bool>
+
<!-- Flag indicating whether the current device allows data.
If true, this means that the device supports data connectivity through
the telephony network.
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index bc5878a0b2ed..a9bec7a9fe28 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -116,7 +116,7 @@
<public name="handwritingBoundsOffsetBottom" />
<public name="accessibilityDataPrivate" />
<public name="enableTextStylingShortcuts" />
- <public name="targetDisplayCategory"/>
+ <public name="requiredDisplayCategory"/>
<public name="maxConcurrentSessionsCount" />
</staging-public-group>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 168806ae87d0..ace7e4c31b4a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -315,6 +315,7 @@
<java-symbol type="bool" name="config_sms_ask_every_time_support" />
<java-symbol type="bool" name="config_sms_capable" />
<java-symbol type="bool" name="config_sms_utf8_support" />
+ <java-symbol type="bool" name="config_smppsim_response_via_ims" />
<java-symbol type="bool" name="config_mobile_data_capable" />
<java-symbol type="bool" name="config_suspendWhenScreenOffDueToProximity" />
<java-symbol type="bool" name="config_swipeDisambiguation" />
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImplTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImplTest.java
index 36aa915a67ec..1cc0a98512bf 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImplTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImplTest.java
@@ -210,9 +210,9 @@ public final class BroadcastRadioServiceImplTest extends ExtendedRadioMockitoTes
any(IServiceCallback.class)));
doReturn(mFmRadioModuleMock).when(() -> RadioModule.tryLoadingModule(
- eq(FM_RADIO_MODULE_ID), anyString(), any(IBinder.class), any(Object.class)));
+ eq(FM_RADIO_MODULE_ID), anyString(), any(IBinder.class)));
doReturn(mDabRadioModuleMock).when(() -> RadioModule.tryLoadingModule(
- eq(DAB_RADIO_MODULE_ID), anyString(), any(IBinder.class), any(Object.class)));
+ eq(DAB_RADIO_MODULE_ID), anyString(), any(IBinder.class)));
when(mFmRadioModuleMock.getProperties()).thenReturn(mFmModuleMock);
when(mDabRadioModuleMock.getProperties()).thenReturn(mDabModuleMock);
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java
index 7a8475fe4d8f..a0346538ddc5 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java
@@ -58,14 +58,13 @@ public final class RadioModuleTest {
@Mock
private android.hardware.broadcastradio.ICloseHandle mHalCloseHandleMock;
- private final Object mLock = new Object();
// RadioModule under test
private RadioModule mRadioModule;
private android.hardware.broadcastradio.IAnnouncementListener mHalListener;
@Before
public void setup() throws RemoteException {
- mRadioModule = new RadioModule(mBroadcastRadioMock, TEST_MODULE_PROPERTIES, mLock);
+ mRadioModule = new RadioModule(mBroadcastRadioMock, TEST_MODULE_PROPERTIES);
// TODO(b/241118988): test non-null image for getImage method
when(mBroadcastRadioMock.getImage(anyInt())).thenReturn(null);
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
index 87d0ea473665..993ca7728374 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
@@ -83,7 +83,6 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase {
@Mock private IBroadcastRadio mBroadcastRadioMock;
private android.hardware.radio.ITunerCallback[] mAidlTunerCallbackMocks;
- private final Object mLock = new Object();
// RadioModule under test
private RadioModule mRadioModule;
@@ -104,7 +103,7 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase {
doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
mRadioModule = new RadioModule(mBroadcastRadioMock,
- AidlTestUtils.makeDefaultModuleProperties(), mLock);
+ AidlTestUtils.makeDefaultModuleProperties());
doAnswer(invocation -> {
mHalTunerCallback = (ITunerCallback) invocation.getArguments()[0];
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/BroadcastRadioServiceHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/BroadcastRadioServiceHidlTest.java
index 0b7bbeaab28e..c9224bfbe1c7 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/BroadcastRadioServiceHidlTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/BroadcastRadioServiceHidlTest.java
@@ -59,8 +59,6 @@ public final class BroadcastRadioServiceHidlTest extends ExtendedRadioMockitoTes
new ArrayList<>(Arrays.asList("FmService", "DabService"));
private static final int[] TEST_ENABLED_TYPES = new int[]{Announcement.TYPE_TRAFFIC};
- private final Object mLock = new Object();
-
private BroadcastRadioService mBroadcastRadioService;
private DeathRecipient mFmDeathRecipient;
@@ -200,7 +198,7 @@ public final class BroadcastRadioServiceHidlTest extends ExtendedRadioMockitoTes
mockServiceManager();
mBroadcastRadioService = new BroadcastRadioService(/* nextModuleId= */ FM_RADIO_MODULE_ID,
- mLock, mServiceManagerMock);
+ mServiceManagerMock);
}
private void mockServiceManager() throws RemoteException {
@@ -221,9 +219,9 @@ public final class BroadcastRadioServiceHidlTest extends ExtendedRadioMockitoTes
}).thenReturn(true);
doReturn(mFmRadioModuleMock).when(() -> RadioModule.tryLoadingModule(
- eq(FM_RADIO_MODULE_ID), anyString(), any(Object.class)));
+ eq(FM_RADIO_MODULE_ID), anyString()));
doReturn(mDabRadioModuleMock).when(() -> RadioModule.tryLoadingModule(
- eq(DAB_RADIO_MODULE_ID), anyString(), any(Object.class)));
+ eq(DAB_RADIO_MODULE_ID), anyString()));
when(mFmRadioModuleMock.getProperties()).thenReturn(mFmModuleMock);
when(mDabRadioModuleMock.getProperties()).thenReturn(mDabModuleMock);
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/RadioModuleHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/RadioModuleHidlTest.java
index 48f5a461d631..1f5e77038728 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/RadioModuleHidlTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/RadioModuleHidlTest.java
@@ -62,13 +62,12 @@ public final class RadioModuleHidlTest {
@Mock
private android.hardware.broadcastradio.V2_0.ICloseHandle mHalCloseHandleMock;
- private final Object mLock = new Object();
private RadioModule mRadioModule;
private android.hardware.broadcastradio.V2_0.IAnnouncementListener mHalListener;
@Before
public void setup() throws RemoteException {
- mRadioModule = new RadioModule(mBroadcastRadioMock, TEST_MODULE_PROPERTIES, mLock);
+ mRadioModule = new RadioModule(mBroadcastRadioMock, TEST_MODULE_PROPERTIES);
when(mBroadcastRadioMock.getImage(anyInt())).thenReturn(new ArrayList<Byte>(0));
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
index e3c9faa601e7..7d604d497984 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
@@ -64,7 +64,6 @@ public class StartProgramListUpdatesFanoutTest extends ExtendedRadioMockitoTestC
@Mock ITunerSession mHalTunerSessionMock;
private android.hardware.radio.ITunerCallback[] mAidlTunerCallbackMocks;
- private final Object mLock = new Object();
// RadioModule under test
private RadioModule mRadioModule;
@@ -100,7 +99,7 @@ public class StartProgramListUpdatesFanoutTest extends ExtendedRadioMockitoTestC
doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
mRadioModule = new RadioModule(mBroadcastRadioMock,
- TestUtils.makeDefaultModuleProperties(), mLock);
+ TestUtils.makeDefaultModuleProperties());
doAnswer((Answer) invocation -> {
mHalTunerCallback = (ITunerCallback) invocation.getArguments()[0];
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
index b7da5d038f77..ff988a21473a 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
@@ -84,7 +84,6 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase {
new RadioManager.FmBandConfig(FM_BAND_DESCRIPTOR);
private static final int UNSUPPORTED_CONFIG_FLAG = 0;
- private final Object mLock = new Object();
private final ArrayMap<Integer, Boolean> mHalConfigMap = new ArrayMap<>();
private RadioModule mRadioModule;
private ITunerCallback mHalTunerCallback;
@@ -105,7 +104,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase {
doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
mRadioModule = new RadioModule(mBroadcastRadioMock,
- TestUtils.makeDefaultModuleProperties(), mLock);
+ TestUtils.makeDefaultModuleProperties());
doAnswer(invocation -> {
mHalTunerCallback = (ITunerCallback) invocation.getArguments()[0];
diff --git a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
index cfca0375bb96..625c318d9efd 100644
--- a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
+++ b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
@@ -18,8 +18,12 @@ package android.content.res
import androidx.core.util.forEach
import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import kotlin.math.ceil
+import kotlin.math.floor
import org.junit.Test
import org.junit.runner.RunWith
@@ -72,7 +76,70 @@ class FontScaleConverterFactoryTest {
}
}
+ @LargeTest
+ @Test
+ fun allFeasibleScalesAndConversionsDoNotCrash() {
+ generateSequenceOfFractions(-10000f..10000f, step = 0.01f)
+ .mapNotNull{ FontScaleConverterFactory.forScale(it) }
+ .flatMap{ table ->
+ generateSequenceOfFractions(-10000f..10000f, step = 0.01f)
+ .map{ Pair(table, it) }
+ }
+ .forEach { (table, sp) ->
+ try {
+ assertWithMessage(
+ "convertSpToDp(%s) on table: %s",
+ sp.toString(),
+ table.toString()
+ )
+ .that(table.convertSpToDp(sp))
+ .isFinite()
+ } catch (e: Exception) {
+ throw AssertionError("Exception during convertSpToDp($sp) on table: $table", e)
+ }
+ }
+ }
+
+ @Test
+ fun testGenerateSequenceOfFractions() {
+ val fractions = generateSequenceOfFractions(-1000f..1000f, step = 0.1f)
+ .toList()
+ fractions.forEach {
+ assertThat(it).isAtLeast(-1000f)
+ assertThat(it).isAtMost(1000f)
+ }
+
+ assertThat(fractions).isInStrictOrder()
+ assertThat(fractions).hasSize(1000 * 2 * 10 + 1) // Don't forget the 0 in the middle!
+
+ assertThat(fractions).contains(100f)
+ assertThat(fractions).contains(500.1f)
+ assertThat(fractions).contains(500.2f)
+ assertThat(fractions).contains(0.2f)
+ assertThat(fractions).contains(0f)
+ assertThat(fractions).contains(-10f)
+ assertThat(fractions).contains(-10f)
+ assertThat(fractions).contains(-10.3f)
+
+ assertThat(fractions).doesNotContain(-10.31f)
+ assertThat(fractions).doesNotContain(0.35f)
+ assertThat(fractions).doesNotContain(0.31f)
+ assertThat(fractions).doesNotContain(-.35f)
+ }
+
companion object {
private const val CONVERSION_TOLERANCE = 0.05f
}
}
+
+fun generateSequenceOfFractions(
+ range: ClosedFloatingPointRange<Float>,
+ step: Float
+): Sequence<Float> {
+ val multiplier = 1f / step
+ val start = floor(range.start * multiplier).toInt()
+ val endInclusive = ceil(range.endInclusive * multiplier).toInt()
+ return generateSequence(start) { it + 1 }
+ .takeWhile { it <= endInclusive }
+ .map{ it.toFloat() / multiplier }
+}
diff --git a/core/tests/coretests/src/android/util/RotationUtilsTest.java b/core/tests/coretests/src/android/util/RotationUtilsTest.java
index 826eb3070c7e..1b1ee4fb1a1c 100644
--- a/core/tests/coretests/src/android/util/RotationUtilsTest.java
+++ b/core/tests/coretests/src/android/util/RotationUtilsTest.java
@@ -18,6 +18,7 @@ package android.util;
import static android.util.RotationUtils.rotateBounds;
import static android.util.RotationUtils.rotatePoint;
+import static android.util.RotationUtils.rotatePointF;
import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
@@ -25,6 +26,7 @@ import static android.view.Surface.ROTATION_90;
import static org.junit.Assert.assertEquals;
import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.Rect;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -79,4 +81,26 @@ public class RotationUtilsTest {
rotatePoint(testResult, ROTATION_270, parentW, parentH);
assertEquals(new Point(560, 60), testResult);
}
+
+ @Test
+ public void testRotatePointF() {
+ float parentW = 1000f;
+ float parentH = 600f;
+ PointF testPt = new PointF(60f, 40f);
+
+ PointF testResult = new PointF(testPt);
+ rotatePointF(testResult, ROTATION_90, parentW, parentH);
+ assertEquals(40f, testResult.x, .1f);
+ assertEquals(940f, testResult.y, .1f);
+
+ testResult.set(testPt.x, testPt.y);
+ rotatePointF(testResult, ROTATION_180, parentW, parentH);
+ assertEquals(940f, testResult.x, .1f);
+ assertEquals(560f, testResult.y, .1f);
+
+ testResult.set(testPt.x, testPt.y);
+ rotatePointF(testResult, ROTATION_270, parentW, parentH);
+ assertEquals(560f, testResult.x, .1f);
+ assertEquals(60f, testResult.y, .1f);
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigTest.java b/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigTest.java
index caec3651210a..0f30cfead4f7 100644
--- a/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigTest.java
+++ b/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigTest.java
@@ -286,6 +286,39 @@ public class OverlayConfigTest {
}
@Test
+ public void testPartialConfigPartitionPrecedence() throws IOException {
+ createFile("/odm/overlay/config/config.xml",
+ "<config>"
+ + " <overlay package=\"two\" enabled=\"true\" />"
+ + "</config>");
+
+ mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one", "android", 0, true,
+ 1);
+ mScannerRule.addOverlay(createFile("/odm/overlay/two.apk"), "two");
+ mScannerRule.addOverlay(createFile("/product/overlay/three.apk"), "three", "android", 0,
+ true, 0);
+
+ final OverlayConfig overlayConfig = createConfigImpl();
+ assertConfig(overlayConfig, "one", false, true, 0);
+ assertConfig(overlayConfig, "two", true, true, 1);
+ assertConfig(overlayConfig, "three", false, true, 2);
+ }
+
+ @Test
+ public void testNoConfigPartitionPrecedence() throws IOException {
+ mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one", "android", 0, true,
+ 1);
+ mScannerRule.addOverlay(createFile("/odm/overlay/two.apk"), "two", "android", 0, true, 2);
+ mScannerRule.addOverlay(createFile("/product/overlay/three.apk"), "three", "android", 0,
+ true, 0);
+
+ final OverlayConfig overlayConfig = createConfigImpl();
+ assertConfig(overlayConfig, "one", false, true, 0);
+ assertConfig(overlayConfig, "two", false, true, 1);
+ assertConfig(overlayConfig, "three", false, true, 2);
+ }
+
+ @Test
public void testImmutable() throws IOException {
createFile("/product/overlay/config/config.xml",
"<config>"
@@ -507,37 +540,6 @@ public class OverlayConfigTest {
}
@Test
- public void testNoConfigsAllowPartitionReordering() throws IOException {
- mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one", "android", 0, true,
- 1);
- mScannerRule.addOverlay(createFile("/product/overlay/two.apk"), "two", "android", 0, true,
- 0);
-
- final OverlayConfig overlayConfig = createConfigImpl();
- assertConfig(overlayConfig, "one", false, true, 1);
- assertConfig(overlayConfig, "two", false, true, 0);
- }
-
- @Test
- public void testConfigDisablesPartitionReordering() throws IOException {
- createFile("/odm/overlay/config/config.xml",
- "<config>"
- + " <overlay package=\"two\" enabled=\"true\" />"
- + "</config>");
-
- mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one", "android", 0, true,
- 1);
- mScannerRule.addOverlay(createFile("/odm/overlay/two.apk"), "two");
- mScannerRule.addOverlay(createFile("/product/overlay/three.apk"), "three", "android", 0,
- true, 0);
-
- final OverlayConfig overlayConfig = createConfigImpl();
- assertConfig(overlayConfig, "one", false, true, 0);
- assertConfig(overlayConfig, "two", true, true, 1);
- assertConfig(overlayConfig, "three", false, true, 2);
- }
-
- @Test
public void testStaticOverlayOutsideOverlayDir() throws IOException {
mScannerRule.addOverlay(createFile("/product/app/one.apk"), "one", "android", 0, true, 0);
@@ -550,7 +552,7 @@ public class OverlayConfigTest {
@Test
public void testSortStaticOverlaysDifferentTargets() throws IOException {
mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one", "other", 0, true, 0);
- mScannerRule.addOverlay(createFile("/product/overlay/two.apk"), "two", "android", 0, true,
+ mScannerRule.addOverlay(createFile("/vendor/overlay/two.apk"), "two", "android", 0, true,
0);
final OverlayConfig overlayConfig = createConfigImpl();
@@ -559,15 +561,33 @@ public class OverlayConfigTest {
}
@Test
+ public void testSortStaticOverlaysDifferentPartitions() throws IOException {
+ mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one", "android", 0, true,
+ 2);
+ mScannerRule.addOverlay(createFile("/vendor/overlay/two.apk"), "two", "android", 0, true,
+ 3);
+ mScannerRule.addOverlay(createFile("/product/overlay/three.apk"), "three", "android", 0,
+ true, 0);
+ mScannerRule.addOverlay(createFile("/product/overlay/four.apk"), "four", "android", 0,
+ true, 1);
+
+ final OverlayConfig overlayConfig = createConfigImpl();
+ assertConfig(overlayConfig, "one", false, true, 0);
+ assertConfig(overlayConfig, "two", false, true, 1);
+ assertConfig(overlayConfig, "three", false, true, 2);
+ assertConfig(overlayConfig, "four", false, true, 3);
+ }
+
+ @Test
public void testSortStaticOverlaysSamePriority() throws IOException {
mScannerRule.addOverlay(createFile("/vendor/overlay/one.apk"), "one", "android", 0, true,
0);
- mScannerRule.addOverlay(createFile("/product/overlay/two.apk"), "two", "android", 0, true,
+ mScannerRule.addOverlay(createFile("/vendor/overlay/two.apk"), "two", "android", 0, true,
0);
final OverlayConfig overlayConfig = createConfigImpl();
- assertConfig(overlayConfig, "one", false, true, 1);
- assertConfig(overlayConfig, "two", false, true, 0);
+ assertConfig(overlayConfig, "one", false, true, 0);
+ assertConfig(overlayConfig, "two", false, true, 1);
}
@Test
diff --git a/core/tests/fuzzers/FuzzService/Android.bp b/core/tests/fuzzers/FuzzService/Android.bp
new file mode 100644
index 000000000000..5093185688df
--- /dev/null
+++ b/core/tests/fuzzers/FuzzService/Android.bp
@@ -0,0 +1,28 @@
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+java_library {
+ name: "random_parcel_lib",
+ srcs: ["FuzzBinder.java"],
+}
+
+cc_library_shared {
+ name: "librandom_parcel_jni",
+ defaults: ["service_fuzzer_defaults"],
+ srcs: [
+ "random_parcel_jni.cpp",
+ ],
+ shared_libs: [
+ "libandroid_runtime",
+ "libbase",
+ "liblog",
+ ],
+ static_libs: [
+ "libnativehelper_lazy",
+ "libbinder_random_parcel",
+ ],
+ cflags: [
+ "-Wno-unused-parameter",
+ ],
+}
diff --git a/core/tests/fuzzers/FuzzService/FuzzBinder.java b/core/tests/fuzzers/FuzzService/FuzzBinder.java
new file mode 100644
index 000000000000..7096f52ab392
--- /dev/null
+++ b/core/tests/fuzzers/FuzzService/FuzzBinder.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package randomparcel;
+import android.os.IBinder;
+
+public class FuzzBinder {
+ static {
+ System.loadLibrary("random_parcel_jni");
+ }
+
+ // DO NOT REUSE: This API should be called from fuzzer to setup JNI dependencies from
+ // libandroid_runtime. THIS IS WORKAROUND. Please file a bug if you need to use this.
+ public static void init() {
+ System.loadLibrary("android_runtime");
+ registerNatives();
+ }
+
+ // This API automatically fuzzes provided service
+ public static void fuzzService(IBinder binder, byte[] data) {
+ fuzzServiceInternal(binder, data);
+ }
+
+ private static native void fuzzServiceInternal(IBinder binder, byte[] data);
+ private static native int registerNatives();
+}
diff --git a/core/tests/fuzzers/FuzzService/random_parcel_jni.cpp b/core/tests/fuzzers/FuzzService/random_parcel_jni.cpp
new file mode 100644
index 000000000000..c0528d5c7b9a
--- /dev/null
+++ b/core/tests/fuzzers/FuzzService/random_parcel_jni.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "random_parcel_jni.h"
+#include <android_util_Binder.h>
+#include <fuzzbinder/libbinder_driver.h>
+#include <fuzzer/FuzzedDataProvider.h>
+using namespace android;
+
+// JNI interface for fuzzService
+JNIEXPORT void JNICALL Java_randomparcel_FuzzBinder_fuzzServiceInternal(JNIEnv *env, jobject thiz, jobject javaBinder, jbyteArray fuzzData) {
+ size_t len = static_cast<size_t>(env->GetArrayLength(fuzzData));
+ uint8_t data[len];
+ env->GetByteArrayRegion(fuzzData, 0, len, reinterpret_cast<jbyte*>(data));
+
+ FuzzedDataProvider provider(data, len);
+ sp<IBinder> binder = android::ibinderForJavaObject(env, javaBinder);
+ fuzzService(binder, std::move(provider));
+}
+
+// API used by AIDL fuzzers to access JNI functions from libandroid_runtime.
+JNIEXPORT jint JNICALL Java_randomparcel_FuzzBinder_registerNatives(JNIEnv* env) {
+ return registerFrameworkNatives(env);
+}
diff --git a/core/tests/fuzzers/FuzzService/random_parcel_jni.h b/core/tests/fuzzers/FuzzService/random_parcel_jni.h
new file mode 100644
index 000000000000..20a4c9d46aa6
--- /dev/null
+++ b/core/tests/fuzzers/FuzzService/random_parcel_jni.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <jni.h>
+
+extern "C" {
+ JNIEXPORT void JNICALL Java_randomparcel_FuzzBinder_fuzzServiceInternal(JNIEnv *env, jobject thiz, jobject javaBinder, jbyteArray fuzzData);
+
+ // Function to register libandroid_runtime JNI functions with java env.
+ JNIEXPORT jint JNICALL Java_randomparcel_FuzzBinder_registerNatives(JNIEnv* env);
+
+ // Function from AndroidRuntime
+ jint registerFrameworkNatives(JNIEnv* env);
+}
diff --git a/core/tests/fuzzers/OWNERS b/core/tests/fuzzers/OWNERS
new file mode 100644
index 000000000000..b972ac0f74e6
--- /dev/null
+++ b/core/tests/fuzzers/OWNERS
@@ -0,0 +1,2 @@
+smoreland@google.com
+waghpawan@google.com
diff --git a/core/tests/fuzzers/java_service_fuzzer/Android.bp b/core/tests/fuzzers/java_service_fuzzer/Android.bp
new file mode 100644
index 000000000000..625de143a685
--- /dev/null
+++ b/core/tests/fuzzers/java_service_fuzzer/Android.bp
@@ -0,0 +1,40 @@
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+aidl_interface {
+ name: "fuzzTestInterface",
+ srcs: ["fuzztest/ITestService.aidl"],
+ unstable: true,
+ backend: {
+ java: {
+ enabled: true,
+ },
+ },
+}
+
+java_fuzz {
+ name: "java_binder_service_fuzzer",
+ srcs: [
+ "ServiceFuzzer.java",
+ "TestService.java",
+ ":framework-core-sources-for-fuzzers",
+ ],
+ static_libs: [
+ "jazzer",
+ "fuzzTestInterface-java",
+ "random_parcel_lib",
+ ],
+ jni_libs: [
+ "librandom_parcel_jni",
+ "libc++",
+ "libandroid_runtime",
+ ],
+ libs: [
+ "framework",
+ "unsupportedappusage",
+ "ext",
+ "framework-res",
+ ],
+ native_bridge_supported: true,
+}
diff --git a/core/tests/fuzzers/java_service_fuzzer/ServiceFuzzer.java b/core/tests/fuzzers/java_service_fuzzer/ServiceFuzzer.java
new file mode 100644
index 000000000000..a6e09865fcad
--- /dev/null
+++ b/core/tests/fuzzers/java_service_fuzzer/ServiceFuzzer.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import com.code_intelligence.jazzer.api.FuzzedDataProvider;
+
+import randomparcel.FuzzBinder;
+
+public class ServiceFuzzer {
+
+ static {
+ // Initialize fuzzService and JNI dependencies
+ FuzzBinder.init();
+ }
+
+ public static void fuzzerTestOneInput(FuzzedDataProvider data) {
+ TestService service = new TestService();
+ FuzzBinder.fuzzService(service, data.consumeRemainingAsBytes());
+ }
+}
diff --git a/core/tests/fuzzers/java_service_fuzzer/TestService.java b/core/tests/fuzzers/java_service_fuzzer/TestService.java
new file mode 100644
index 000000000000..4404386bd06c
--- /dev/null
+++ b/core/tests/fuzzers/java_service_fuzzer/TestService.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import fuzztest.ITestService;
+
+public class TestService extends ITestService.Stub {
+
+ @Override
+ public boolean repeatData(boolean token) {
+ return token;
+ }
+}
diff --git a/core/tests/fuzzers/java_service_fuzzer/fuzztest/ITestService.aidl b/core/tests/fuzzers/java_service_fuzzer/fuzztest/ITestService.aidl
new file mode 100644
index 000000000000..b766c9f85a53
--- /dev/null
+++ b/core/tests/fuzzers/java_service_fuzzer/fuzztest/ITestService.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package fuzztest;
+
+interface ITestService {
+ boolean repeatData(boolean token);
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 1fd91debe3f6..9674b69baa00 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -475,8 +475,13 @@ public class BubbleController implements ConfigurationChangeListener {
@VisibleForTesting
public void onStatusBarStateChanged(boolean isShade) {
+ boolean didChange = mIsStatusBarShade != isShade;
+ if (DEBUG_BUBBLE_CONTROLLER) {
+ Log.d(TAG, "onStatusBarStateChanged isShade=" + isShade + " didChange=" + didChange);
+ }
mIsStatusBarShade = isShade;
- if (!mIsStatusBarShade) {
+ if (!mIsStatusBarShade && didChange) {
+ // Only collapse stack on change
collapseStack();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index e8b0f0265394..214b304df07c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -219,7 +219,11 @@ public class DividerView extends FrameLayout implements View.OnTouchListener {
insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
// Only insets the divider bar with task bar when it's expanded so that the rounded corners
// will be drawn against task bar.
- if (taskBarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) {
+ // But there is no need to do it when IME showing because there are no rounded corners at
+ // the bottom. This also avoids the problem of task bar height not changing when IME
+ // floating.
+ if (!insetsState.getSourceOrDefaultVisibility(InsetsState.ITYPE_IME)
+ && taskBarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) {
mTempRect.inset(taskBarInsetsSource.calculateVisibleInsets(mTempRect));
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index ae496165028f..45b234a6398a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -122,6 +122,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
private int mDensity;
private final boolean mDimNonImeSide;
+ private ValueAnimator mDividerFlingAnimator;
public SplitLayout(String windowName, Context context, Configuration configuration,
SplitLayoutHandler splitLayoutHandler,
@@ -395,6 +396,10 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
mSplitWindowManager.release(t);
mDisplayImeController.removePositionProcessor(mImePositionProcessor);
mImePositionProcessor.reset();
+ if (mDividerFlingAnimator != null) {
+ mDividerFlingAnimator.cancel();
+ }
+ resetDividerPosition();
}
public void release() {
@@ -577,13 +582,18 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
CUJ_SPLIT_SCREEN_RESIZE);
return;
}
- ValueAnimator animator = ValueAnimator
+
+ if (mDividerFlingAnimator != null) {
+ mDividerFlingAnimator.cancel();
+ }
+
+ mDividerFlingAnimator = ValueAnimator
.ofInt(from, to)
.setDuration(duration);
- animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
- animator.addUpdateListener(
+ mDividerFlingAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ mDividerFlingAnimator.addUpdateListener(
animation -> updateDivideBounds((int) animation.getAnimatedValue()));
- animator.addListener(new AnimatorListenerAdapter() {
+ mDividerFlingAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (flingFinishedCallback != null) {
@@ -591,14 +601,15 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
}
InteractionJankMonitorUtils.endTracing(
CUJ_SPLIT_SCREEN_RESIZE);
+ mDividerFlingAnimator = null;
}
@Override
public void onAnimationCancel(Animator animation) {
- setDividePosition(to, true /* applyLayoutChange */);
+ mDividerFlingAnimator = null;
}
});
- animator.start();
+ mDividerFlingAnimator.start();
}
/** Switch both surface position with animation. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index 90b35a5a55e1..44bcdb2d5de5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -82,7 +82,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener {
mTasks.put(taskInfo.taskId, state);
if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- mWindowDecorationViewModel.createWindowDecoration(taskInfo, leash, t, t);
+ mWindowDecorationViewModel.onTaskOpening(taskInfo, leash, t, t);
t.apply();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
index 168f6d79a390..6e710f7caeda 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
@@ -120,7 +120,7 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
TransitionInfo.Change change,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
- mWindowDecorViewModel.createWindowDecoration(
+ mWindowDecorViewModel.onTaskOpening(
change.getTaskInfo(), change.getLeash(), startT, finishT);
}
@@ -128,31 +128,23 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
TransitionInfo.Change change,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
- mWindowDecorViewModel.setupWindowDecorationForTransition(
- change.getTaskInfo(), startT, finishT);
+ mWindowDecorViewModel.onTaskClosing(change.getTaskInfo(), startT, finishT);
}
private void onChangeTransitionReady(
TransitionInfo.Change change,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
- mWindowDecorViewModel.setupWindowDecorationForTransition(
- change.getTaskInfo(), startT, finishT);
+ mWindowDecorViewModel.onTaskChanging(
+ change.getTaskInfo(), change.getLeash(), startT, finishT);
}
private void onToFrontTransitionReady(
TransitionInfo.Change change,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
- boolean exists = mWindowDecorViewModel.setupWindowDecorationForTransition(
- change.getTaskInfo(),
- startT,
- finishT);
- if (!exists) {
- // Window caption does not exist, create it
- mWindowDecorViewModel.createWindowDecoration(
- change.getTaskInfo(), change.getLeash(), startT, finishT);
- }
+ mWindowDecorViewModel.onTaskChanging(
+ change.getTaskInfo(), change.getLeash(), startT, finishT);
}
@Override
@@ -188,4 +180,4 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
mWindowDecorViewModel.destroyWindowDecoration(taskInfo.get(i));
}
}
-}
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
index 75a4091c7d78..6623f5ca84ee 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
@@ -103,7 +103,7 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
if (mWindowDecorViewModelOptional.isPresent()) {
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
createdWindowDecor = mWindowDecorViewModelOptional.get()
- .createWindowDecoration(taskInfo, leash, t, t);
+ .onTaskOpening(taskInfo, leash, t, t);
t.apply();
}
if (!createdWindowDecor) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
index 17d7f5d0d567..5376ae372de2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
@@ -97,6 +97,8 @@ public class PipBoundsState {
private int mShelfHeight;
/** Whether the user has resized the PIP manually. */
private boolean mHasUserResizedPip;
+ /** Whether the user has moved the PIP manually. */
+ private boolean mHasUserMovedPip;
/**
* Areas defined by currently visible apps that they prefer to keep clear from overlays such as
* the PiP. Restricted areas may only move the PiP a limited amount from its anchor position.
@@ -279,6 +281,7 @@ public class PipBoundsState {
if (changed) {
clearReentryState();
setHasUserResizedPip(false);
+ setHasUserMovedPip(false);
}
}
@@ -442,6 +445,16 @@ public class PipBoundsState {
mHasUserResizedPip = hasUserResizedPip;
}
+ /** Returns whether the user has moved the PIP. */
+ public boolean hasUserMovedPip() {
+ return mHasUserMovedPip;
+ }
+
+ /** Set whether the user has moved the PIP. */
+ public void setHasUserMovedPip(boolean hasUserMovedPip) {
+ mHasUserMovedPip = hasUserMovedPip;
+ }
+
/**
* Registers a callback when the minimal size of PIP that is set by the app changes.
*/
@@ -577,6 +590,8 @@ public class PipBoundsState {
pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing);
pw.println(innerPrefix + "mShelfHeight=" + mShelfHeight);
+ pw.println(innerPrefix + "mHasUserMovedPip=" + mHasUserMovedPip);
+ pw.println(innerPrefix + "mHasUserResizedPip=" + mHasUserResizedPip);
if (mPipReentryState == null) {
pw.println(innerPrefix + "mPipReentryState=null");
} else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
index 84071e08d472..690505e03fce 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
@@ -19,6 +19,7 @@ package com.android.wm.shell.pip.phone;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
+import android.os.SystemProperties;
import android.util.ArraySet;
import android.view.Gravity;
@@ -34,6 +35,10 @@ import java.util.Set;
*/
public class PhonePipKeepClearAlgorithm implements PipKeepClearAlgorithm {
+ private boolean mKeepClearAreaGravityEnabled =
+ SystemProperties.getBoolean(
+ "persist.wm.debug.enable_pip_keep_clear_algorithm_gravity", false);
+
protected int mKeepClearAreasPadding;
public PhonePipKeepClearAlgorithm(Context context) {
@@ -53,31 +58,36 @@ public class PhonePipKeepClearAlgorithm implements PipKeepClearAlgorithm {
Rect startingBounds = pipBoundsState.getBounds().isEmpty()
? pipBoundsAlgorithm.getEntryDestinationBoundsIgnoringKeepClearAreas()
: pipBoundsState.getBounds();
- float snapFraction = pipBoundsAlgorithm.getSnapFraction(startingBounds);
- int verticalGravity = Gravity.BOTTOM;
- int horizontalGravity;
- if (snapFraction >= 0.5f && snapFraction < 2.5f) {
- horizontalGravity = Gravity.RIGHT;
- } else {
- horizontalGravity = Gravity.LEFT;
- }
- // push the bounds based on the gravity
Rect insets = new Rect();
pipBoundsAlgorithm.getInsetBounds(insets);
if (pipBoundsState.isImeShowing()) {
insets.bottom -= pipBoundsState.getImeHeight();
}
- Rect pushedBounds = new Rect(startingBounds);
- if (verticalGravity == Gravity.BOTTOM) {
- pushedBounds.offsetTo(pushedBounds.left,
- insets.bottom - pushedBounds.height());
- }
- if (horizontalGravity == Gravity.RIGHT) {
- pushedBounds.offsetTo(insets.right - pushedBounds.width(), pushedBounds.top);
- } else {
- pushedBounds.offsetTo(insets.left, pushedBounds.top);
+ Rect pipBounds = new Rect(startingBounds);
+
+ // move PiP towards corner if user hasn't moved it manually or the flag is on
+ if (mKeepClearAreaGravityEnabled
+ || (!pipBoundsState.hasUserMovedPip() && !pipBoundsState.hasUserResizedPip())) {
+ float snapFraction = pipBoundsAlgorithm.getSnapFraction(startingBounds);
+ int verticalGravity = Gravity.BOTTOM;
+ int horizontalGravity;
+ if (snapFraction >= 0.5f && snapFraction < 2.5f) {
+ horizontalGravity = Gravity.RIGHT;
+ } else {
+ horizontalGravity = Gravity.LEFT;
+ }
+ if (verticalGravity == Gravity.BOTTOM) {
+ pipBounds.offsetTo(pipBounds.left,
+ insets.bottom - pipBounds.height());
+ }
+ if (horizontalGravity == Gravity.RIGHT) {
+ pipBounds.offsetTo(insets.right - pipBounds.width(), pipBounds.top);
+ } else {
+ pipBounds.offsetTo(insets.left, pipBounds.top);
+ }
}
- return findUnoccludedPosition(pushedBounds, pipBoundsState.getRestrictedKeepClearAreas(),
+
+ return findUnoccludedPosition(pipBounds, pipBoundsState.getRestrictedKeepClearAreas(),
pipBoundsState.getUnrestrictedKeepClearAreas(), insets);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index d28a9f3cf8ff..efe938f0a274 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -612,12 +612,21 @@ public class PipController implements PipTransitionController.PipTransitionCallb
new DisplayInsetsController.OnInsetsChangedListener() {
@Override
public void insetsChanged(InsetsState insetsState) {
+ DisplayLayout pendingLayout =
+ mDisplayController.getDisplayLayout(mPipBoundsState.getDisplayId());
+ if (mIsInFixedRotation
+ || pendingLayout.rotation()
+ != mPipBoundsState.getDisplayLayout().rotation()) {
+ // bail out if there is a pending rotation or fixed rotation change
+ return;
+ }
int oldMaxMovementBound = mPipBoundsState.getMovementBounds().bottom;
onDisplayChanged(
mDisplayController.getDisplayLayout(mPipBoundsState.getDisplayId()),
false /* saveRestoreSnapFraction */);
int newMaxMovementBound = mPipBoundsState.getMovementBounds().bottom;
if (!mEnablePipKeepClearAlgorithm) {
+ // offset PiP to adjust for bottom inset change
int pipTop = mPipBoundsState.getBounds().top;
int diff = newMaxMovementBound - oldMaxMovementBound;
if (diff < 0 && pipTop > newMaxMovementBound) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index a9a97beb9180..83bc7c0e6e7d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -875,6 +875,8 @@ public class PipTouchHandler {
}
if (touchState.isDragging()) {
+ mPipBoundsState.setHasUserMovedPip(true);
+
// Move the pinned stack freely
final PointF lastDelta = touchState.getLastTouchDelta();
float lastX = mStartPosition.x + mDelta.x;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index acb71a80ee8a..4cb76230606f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -669,6 +669,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSplitLayout.init();
mSplitLayout.setDivideRatio(splitRatio);
+ // Apply surface bounds before animation start.
+ SurfaceControl.Transaction startT = mTransactionPool.acquire();
+ updateSurfaceBounds(mSplitLayout, startT, false /* applyResizingOffset */);
+ startT.apply();
+ mTransactionPool.release(startT);
+
// Set false to avoid record new bounds with old task still on top;
mShouldUpdateRecents = false;
mIsDividerRemoteAnimating = true;
@@ -742,7 +748,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSyncQueue.queue(wct);
mSyncQueue.runInSync(t -> {
setDividerVisibility(true, t);
- updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
});
setEnterInstanceId(instanceId);
@@ -1035,7 +1040,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mIsDividerRemoteAnimating = false;
mSplitLayout.getInvisibleBounds(mTempRect1);
- if (childrenToTop == null) {
+ if (childrenToTop == null || childrenToTop.getTopVisibleChildTaskId() == INVALID_TASK_ID) {
mSideStage.removeAllTasks(wct, false /* toTop */);
mMainStage.deactivate(wct, false /* toTop */);
wct.reorder(mRootTaskInfo.token, false /* onTop */);
@@ -1294,13 +1299,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
}
- private void onStageChildTaskEnterPip() {
- // When the exit split-screen is caused by one of the task enters auto pip,
- // we want both tasks to be put to bottom instead of top, otherwise it will end up
- // a fullscreen plus a pinned task instead of pinned only at the end of the transition.
- exitSplitScreen(null, EXIT_REASON_CHILD_TASK_ENTER_PIP);
- }
-
private void updateRecentTasksSplitPair() {
if (!mShouldUpdateRecents) {
return;
@@ -2063,7 +2061,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// Update divider state after animation so that it is still around and positioned
// properly for the animation itself.
mSplitLayout.release();
- mSplitLayout.resetDividerPosition();
mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
}
}
@@ -2340,11 +2337,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
@Override
- public void onChildTaskEnterPip() {
- StageCoordinator.this.onStageChildTaskEnterPip();
- }
-
- @Override
public void onRootTaskVanished() {
reset();
StageCoordinator.this.onRootTaskVanished();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index bcf900b99c69..358f712f76b5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -18,7 +18,6 @@ package com.android.wm.shell.splitscreen;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
@@ -74,8 +73,6 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
void onChildTaskStatusChanged(int taskId, boolean present, boolean visible);
- void onChildTaskEnterPip();
-
void onRootTaskVanished();
void onNoLongerSupportMultiWindow();
@@ -257,9 +254,6 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
// Status is managed/synchronized by the transition lifecycle.
return;
}
- if (taskInfo.getWindowingMode() == WINDOWING_MODE_PINNED) {
- mCallbacks.onChildTaskEnterPip();
- }
sendStatusChanged();
} else {
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 8369569b4163..e40db4e4dcf2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -55,6 +55,8 @@ import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
import com.android.wm.shell.transition.Transitions;
+import java.util.function.Supplier;
+
/**
* View model for the window decoration with a caption and shadows. Works with
* {@link CaptionWindowDecoration}.
@@ -62,6 +64,8 @@ import com.android.wm.shell.transition.Transitions;
public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
private static final String TAG = "CaptionViewModel";
+ private final CaptionWindowDecoration.Factory mCaptionWindowDecorFactory;
+ private final Supplier<InputManager> mInputManagerSupplier;
private final ActivityTaskManager mActivityTaskManager;
private final ShellTaskOrganizer mTaskOrganizer;
private final Context mContext;
@@ -77,6 +81,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
private final SparseArray<CaptionWindowDecoration> mWindowDecorByTaskId = new SparseArray<>();
private final DragStartListenerImpl mDragStartListener = new DragStartListenerImpl();
+ private EventReceiverFactory mEventReceiverFactory = new EventReceiverFactory();
public CaptionWindowDecorViewModel(
Context context,
@@ -86,6 +91,29 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
DisplayController displayController,
SyncTransactionQueue syncQueue,
DesktopModeController desktopModeController) {
+ this(
+ context,
+ mainHandler,
+ mainChoreographer,
+ taskOrganizer,
+ displayController,
+ syncQueue,
+ desktopModeController,
+ new CaptionWindowDecoration.Factory(),
+ InputManager::getInstance);
+ }
+
+ public CaptionWindowDecorViewModel(
+ Context context,
+ Handler mainHandler,
+ Choreographer mainChoreographer,
+ ShellTaskOrganizer taskOrganizer,
+ DisplayController displayController,
+ SyncTransactionQueue syncQueue,
+ DesktopModeController desktopModeController,
+ CaptionWindowDecoration.Factory captionWindowDecorFactory,
+ Supplier<InputManager> inputManagerSupplier) {
+
mContext = context;
mMainHandler = mainHandler;
mMainChoreographer = mainChoreographer;
@@ -94,7 +122,13 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
mDisplayController = displayController;
mSyncQueue = syncQueue;
mDesktopModeController = desktopModeController;
- mTransitionDragActive = false;
+
+ mCaptionWindowDecorFactory = captionWindowDecorFactory;
+ mInputManagerSupplier = inputManagerSupplier;
+ }
+
+ void setEventReceiverFactory(EventReceiverFactory eventReceiverFactory) {
+ mEventReceiverFactory = eventReceiverFactory;
}
@Override
@@ -103,42 +137,13 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
}
@Override
- public boolean createWindowDecoration(
+ public boolean onTaskOpening(
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
if (!shouldShowWindowDecor(taskInfo)) return false;
- CaptionWindowDecoration oldDecoration = mWindowDecorByTaskId.get(taskInfo.taskId);
- if (oldDecoration != null) {
- // close the old decoration if it exists to avoid two window decorations being added
- oldDecoration.close();
- }
- final CaptionWindowDecoration windowDecoration = new CaptionWindowDecoration(
- mContext,
- mDisplayController,
- mTaskOrganizer,
- taskInfo,
- taskSurface,
- mMainHandler,
- mMainChoreographer,
- mSyncQueue);
- mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
-
- TaskPositioner taskPositioner = new TaskPositioner(mTaskOrganizer, windowDecoration,
- mDragStartListener);
- CaptionTouchEventListener touchEventListener =
- new CaptionTouchEventListener(taskInfo, taskPositioner,
- windowDecoration.getDragDetector());
- windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
- windowDecoration.setDragResizeCallback(taskPositioner);
- setupWindowDecorationForTransition(taskInfo, startT, finishT);
- if (mInputMonitor == null) {
- mInputMonitor = InputManager.getInstance().monitorGestureInput(
- "caption-touch", mContext.getDisplayId());
- mEventReceiver = new EventReceiver(
- mInputMonitor.getInputChannel(), Looper.myLooper());
- }
+ createWindowDecoration(taskInfo, taskSurface, startT, finishT);
return true;
}
@@ -151,25 +156,45 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
}
@Override
- public boolean setupWindowDecorationForTransition(
+ public void onTaskChanging(
+ RunningTaskInfo taskInfo,
+ SurfaceControl taskSurface,
+ SurfaceControl.Transaction startT,
+ SurfaceControl.Transaction finishT) {
+ final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
+
+ if (!shouldShowWindowDecor(taskInfo)) {
+ if (decoration != null) {
+ destroyWindowDecoration(taskInfo);
+ }
+ return;
+ }
+
+ if (decoration == null) {
+ createWindowDecoration(taskInfo, taskSurface, startT, finishT);
+ } else {
+ decoration.relayout(taskInfo, startT, finishT);
+ }
+ }
+
+ @Override
+ public void onTaskClosing(
RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
- if (decoration == null) return false;
+ if (decoration == null) return;
decoration.relayout(taskInfo, startT, finishT);
- return true;
}
@Override
- public boolean destroyWindowDecoration(RunningTaskInfo taskInfo) {
+ public void destroyWindowDecoration(RunningTaskInfo taskInfo) {
final CaptionWindowDecoration decoration =
mWindowDecorByTaskId.removeReturnOld(taskInfo.taskId);
- if (decoration == null) return false;
+ if (decoration == null) return;
decoration.close();
- return true;
}
private class CaptionTouchEventListener implements
@@ -217,6 +242,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
decoration.setButtonVisibility();
}
}
+
private void injectBackKey() {
sendBackEvent(KeyEvent.ACTION_DOWN);
sendBackEvent(KeyEvent.ACTION_UP);
@@ -266,7 +292,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
*/
private void handleEventForMove(MotionEvent e) {
RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
- int windowingMode = mDesktopModeController
+ int windowingMode = mDesktopModeController
.getDisplayAreaWindowingMode(taskInfo.displayId);
if (windowingMode == WINDOWING_MODE_FULLSCREEN) {
return;
@@ -302,7 +328,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
}
// InputEventReceiver to listen for touch input outside of caption bounds
- private class EventReceiver extends InputEventReceiver {
+ class EventReceiver extends InputEventReceiver {
EventReceiver(InputChannel channel, Looper looper) {
super(channel, looper);
}
@@ -318,8 +344,15 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
}
}
+ class EventReceiverFactory {
+ EventReceiver create(InputChannel channel, Looper looper) {
+ return new EventReceiver(channel, looper);
+ }
+ }
+
/**
* Handle MotionEvents relevant to focused task's caption that don't directly touch it
+ *
* @param ev the {@link MotionEvent} received by {@link EventReceiver}
*/
private void handleReceivedMotionEvent(MotionEvent ev) {
@@ -401,7 +434,6 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
return focusedDecor;
}
-
private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true;
return DesktopModeStatus.IS_SUPPORTED
@@ -410,7 +442,47 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
.getResources().getConfiguration().smallestScreenWidthDp >= 600;
}
- private class DragStartListenerImpl implements TaskPositioner.DragStartListener{
+ private void createWindowDecoration(
+ ActivityManager.RunningTaskInfo taskInfo,
+ SurfaceControl taskSurface,
+ SurfaceControl.Transaction startT,
+ SurfaceControl.Transaction finishT) {
+ CaptionWindowDecoration oldDecoration = mWindowDecorByTaskId.get(taskInfo.taskId);
+ if (oldDecoration != null) {
+ // close the old decoration if it exists to avoid two window decorations being added
+ oldDecoration.close();
+ }
+ final CaptionWindowDecoration windowDecoration =
+ mCaptionWindowDecorFactory.create(
+ mContext,
+ mDisplayController,
+ mTaskOrganizer,
+ taskInfo,
+ taskSurface,
+ mMainHandler,
+ mMainChoreographer,
+ mSyncQueue);
+ mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
+
+ TaskPositioner taskPositioner =
+ new TaskPositioner(mTaskOrganizer, windowDecoration, mDragStartListener);
+ CaptionTouchEventListener touchEventListener =
+ new CaptionTouchEventListener(
+ taskInfo, taskPositioner, windowDecoration.getDragDetector());
+ windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
+ windowDecoration.setDragResizeCallback(taskPositioner);
+ windowDecoration.relayout(taskInfo, startT, finishT);
+ if (mInputMonitor == null) {
+ InputManager inputManager = mInputManagerSupplier.get();
+ mInputMonitor =
+ inputManager.monitorGestureInput("caption-touch", mContext.getDisplayId());
+ mEventReceiver =
+ mEventReceiverFactory.create(
+ mInputMonitor.getInputChannel(), Looper.myLooper());
+ }
+ }
+
+ private class DragStartListenerImpl implements TaskPositioner.DragStartListener {
@Override
public void onDragStart(int taskId) {
mWindowDecorByTaskId.get(taskId).closeHandleMenu();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index 59576cd3ec15..037ca2031254 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -42,7 +42,8 @@ import com.android.wm.shell.desktopmode.DesktopModeStatus;
/**
* Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with
- * {@link CaptionWindowDecorViewModel}. The caption bar contains a handle, back button, and close button.
+ * {@link CaptionWindowDecorViewModel}. The caption bar contains a handle, back button, and close
+ * button.
*
* The shadow's thickness is 20dp when the window is in focus and 5dp when the window isn't.
*/
@@ -181,12 +182,12 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
if (oldDecorationSurface != mDecorationContainerSurface || mDragResizeListener == null) {
closeDragResizeListener();
mDragResizeListener = new DragResizeInputListener(
- mContext,
- mHandler,
- mChoreographer,
- mDisplay.getDisplayId(),
- mDecorationContainerSurface,
- mDragResizeCallback);
+ mContext,
+ mHandler,
+ mChoreographer,
+ mDisplay.getDisplayId(),
+ mDecorationContainerSurface,
+ mDragResizeCallback);
}
int touchSlop = ViewConfiguration.get(mResult.mRootView.getContext()).getScaledTouchSlop();
@@ -242,7 +243,6 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
/**
* Sets the visibility of buttons and color of caption based on desktop mode status
- *
*/
void setButtonVisibility() {
mDesktopActive = DesktopModeStatus.isActive(mContext);
@@ -313,6 +313,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
/**
* Close an open handle menu if input is outside of menu coordinates
+ *
* @param ev the tapped point to compare against
*/
void closeHandleMenuIfNeeded(MotionEvent ev) {
@@ -329,6 +330,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
/**
* Offset the coordinates of a {@link MotionEvent} to be in the same coordinate space as caption
+ *
* @param ev the {@link MotionEvent} to offset
* @return the point of the input in local space
*/
@@ -343,7 +345,8 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
/**
* Determine if a passed MotionEvent is in a view in caption
- * @param ev the {@link MotionEvent} to check
+ *
+ * @param ev the {@link MotionEvent} to check
* @param layoutId the id of the view
* @return {@code true} if event is inside the specified view, {@code false} if not
*/
@@ -363,6 +366,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
* Check a passed MotionEvent if a click has occurred on any button on this caption
* Note this should only be called when a regular onClick is not possible
* (i.e. the button was clicked through status bar layer)
+ *
* @param ev the MotionEvent to compare
*/
void checkClickEvent(MotionEvent ev) {
@@ -399,4 +403,27 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
closeHandleMenu();
super.close();
}
+
+ static class Factory {
+
+ CaptionWindowDecoration create(
+ Context context,
+ DisplayController displayController,
+ ShellTaskOrganizer taskOrganizer,
+ ActivityManager.RunningTaskInfo taskInfo,
+ SurfaceControl taskSurface,
+ Handler handler,
+ Choreographer choreographer,
+ SyncTransactionQueue syncQueue) {
+ return new CaptionWindowDecoration(
+ context,
+ displayController,
+ taskOrganizer,
+ taskInfo,
+ taskSurface,
+ handler,
+ choreographer,
+ syncQueue);
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
index 2ce4d04377a1..907977c661f8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
@@ -28,7 +28,6 @@ import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
* servers.
*/
public interface WindowDecorViewModel {
-
/**
* Sets the transition starter that starts freeform task transitions.
*
@@ -37,16 +36,16 @@ public interface WindowDecorViewModel {
void setFreeformTaskTransitionStarter(FreeformTaskTransitionStarter transitionStarter);
/**
- * Creates a window decoration for the given task.
- * Can be {@code null} for Fullscreen tasks but not Freeform ones.
+ * Creates a window decoration for the given task. Can be {@code null} for Fullscreen tasks but
+ * not Freeform ones.
*
- * @param taskInfo the initial task info of the task
+ * @param taskInfo the initial task info of the task
* @param taskSurface the surface of the task
- * @param startT the start transaction to be applied before the transition
- * @param finishT the finish transaction to restore states after the transition
+ * @param startT the start transaction to be applied before the transition
+ * @param finishT the finish transaction to restore states after the transition
* @return {@code true} if window decoration was created, {@code false} otherwise
*/
- boolean createWindowDecoration(
+ boolean onTaskOpening(
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
@@ -54,7 +53,7 @@ public interface WindowDecorViewModel {
/**
* Notifies a task info update on the given task, with the window decoration created previously
- * for this task by {@link #createWindowDecoration}.
+ * for this task by {@link #onTaskOpening}.
*
* @param taskInfo the new task info of the task
*/
@@ -62,13 +61,29 @@ public interface WindowDecorViewModel {
/**
* Notifies a transition is about to start about the given task to give the window decoration a
- * chance to prepare for this transition.
+ * chance to prepare for this transition. Unlike {@link #onTaskInfoChanged}, this method creates
+ * a window decoration if one does not exist but is required.
+ *
+ * @param taskInfo the initial task info of the task
+ * @param taskSurface the surface of the task
+ * @param startT the start transaction to be applied before the transition
+ * @param finishT the finish transaction to restore states after the transition
+ */
+ void onTaskChanging(
+ ActivityManager.RunningTaskInfo taskInfo,
+ SurfaceControl taskSurface,
+ SurfaceControl.Transaction startT,
+ SurfaceControl.Transaction finishT);
+
+ /**
+ * Notifies that the given task is about to close to give the window decoration a chance to
+ * prepare for this transition.
*
- * @param startT the start transaction to be applied before the transition
- * @param finishT the finish transaction to restore states after the transition
- * @return {@code true} if window decoration exists, {@code false} otherwise
+ * @param taskInfo the initial task info of the task
+ * @param startT the start transaction to be applied before the transition
+ * @param finishT the finish transaction to restore states after the transition
*/
- boolean setupWindowDecorationForTransition(
+ void onTaskClosing(
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT);
@@ -77,7 +92,6 @@ public interface WindowDecorViewModel {
* Destroys the window decoration of the give task.
*
* @param taskInfo the info of the task
- * @return {@code true} if window decoration was destroyed, {@code false} otherwise
*/
- boolean destroyWindowDecoration(ActivityManager.RunningTaskInfo taskInfo);
-}
+ void destroyWindowDecoration(ActivityManager.RunningTaskInfo taskInfo);
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 7ecb3f3f6355..92154968855f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -251,7 +251,9 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
}
final int captionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId);
- final int captionWidth = loadDimensionPixelSize(resources, params.mCaptionWidthId);
+ final int captionWidth = params.mCaptionWidthId == Resources.ID_NULL
+ ? taskBounds.width()
+ : loadDimensionPixelSize(resources, params.mCaptionWidthId);
startT.setPosition(
mCaptionContainerSurface,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
index 7068a84c3056..48415d47304c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
@@ -103,7 +103,7 @@ public class FreeformTaskTransitionObserverTest {
mTransitionObserver.onTransitionReady(transition, info, startT, finishT);
mTransitionObserver.onTransitionStarting(transition);
- verify(mWindowDecorViewModel).createWindowDecoration(
+ verify(mWindowDecorViewModel).onTaskOpening(
change.getTaskInfo(), change.getLeash(), startT, finishT);
}
@@ -120,7 +120,7 @@ public class FreeformTaskTransitionObserverTest {
mTransitionObserver.onTransitionReady(transition, info, startT, finishT);
mTransitionObserver.onTransitionStarting(transition);
- verify(mWindowDecorViewModel).setupWindowDecorationForTransition(
+ verify(mWindowDecorViewModel).onTaskClosing(
change.getTaskInfo(), startT, finishT);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModelTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModelTests.java
new file mode 100644
index 000000000000..8b134ed1dfe4
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModelTests.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.windowdecor;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.hardware.input.InputManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.Choreographer;
+import android.view.Display;
+import android.view.InputChannel;
+import android.view.InputMonitor;
+import android.view.SurfaceControl;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.rule.GrantPermissionRule;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestRunningTaskInfoBuilder;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.desktopmode.DesktopModeController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Supplier;
+
+/** Tests of {@link CaptionWindowDecorViewModel} */
+@SmallTest
+public class CaptionWindowDecorViewModelTests extends ShellTestCase {
+ @Mock private CaptionWindowDecoration mCaptionWindowDecoration;
+
+ @Mock private CaptionWindowDecoration.Factory mCaptionWindowDecorFactory;
+
+ @Mock private Handler mMainHandler;
+
+ @Mock private Choreographer mMainChoreographer;
+
+ @Mock private ShellTaskOrganizer mTaskOrganizer;
+
+ @Mock private DisplayController mDisplayController;
+
+ @Mock private SyncTransactionQueue mSyncQueue;
+
+ @Mock private DesktopModeController mDesktopModeController;
+
+ @Mock private InputMonitor mInputMonitor;
+
+ @Mock private InputChannel mInputChannel;
+
+ @Mock private CaptionWindowDecorViewModel.EventReceiverFactory mEventReceiverFactory;
+
+ @Mock private CaptionWindowDecorViewModel.EventReceiver mEventReceiver;
+
+ @Mock private InputManager mInputManager;
+
+ private final List<InputManager> mMockInputManagers = new ArrayList<>();
+
+ private CaptionWindowDecorViewModel mCaptionWindowDecorViewModel;
+
+ @Before
+ public void setUp() {
+ mMockInputManagers.add(mInputManager);
+
+ mCaptionWindowDecorViewModel =
+ new CaptionWindowDecorViewModel(
+ mContext,
+ mMainHandler,
+ mMainChoreographer,
+ mTaskOrganizer,
+ mDisplayController,
+ mSyncQueue,
+ mDesktopModeController,
+ mCaptionWindowDecorFactory,
+ new MockObjectSupplier<>(mMockInputManagers, () -> mock(InputManager.class)));
+ mCaptionWindowDecorViewModel.setEventReceiverFactory(mEventReceiverFactory);
+
+ doReturn(mCaptionWindowDecoration)
+ .when(mCaptionWindowDecorFactory)
+ .create(any(), any(), any(), any(), any(), any(), any(), any());
+
+ when(mInputManager.monitorGestureInput(any(), anyInt())).thenReturn(mInputMonitor);
+ when(mEventReceiverFactory.create(any(), any())).thenReturn(mEventReceiver);
+ when(mInputMonitor.getInputChannel()).thenReturn(mInputChannel);
+ }
+
+ @Test
+ public void testDeleteCaptionOnChangeTransitionWhenNecessary() throws Exception {
+ Looper.prepare();
+ final int taskId = 1;
+ final ActivityManager.RunningTaskInfo taskInfo =
+ createTaskInfo(taskId, WINDOWING_MODE_FREEFORM);
+ SurfaceControl surfaceControl = mock(SurfaceControl.class);
+ final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
+ final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
+ GrantPermissionRule.grant(android.Manifest.permission.MONITOR_INPUT);
+
+ mCaptionWindowDecorViewModel.onTaskOpening(taskInfo, surfaceControl, startT, finishT);
+ verify(mCaptionWindowDecorFactory)
+ .create(
+ mContext,
+ mDisplayController,
+ mTaskOrganizer,
+ taskInfo,
+ surfaceControl,
+ mMainHandler,
+ mMainChoreographer,
+ mSyncQueue);
+
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED);
+ taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
+ mCaptionWindowDecorViewModel.onTaskChanging(taskInfo, surfaceControl, startT, finishT);
+ verify(mCaptionWindowDecoration).close();
+ }
+
+ @Test
+ public void testCreateCaptionOnChangeTransitionWhenNecessary() throws Exception {
+ final int taskId = 1;
+ final ActivityManager.RunningTaskInfo taskInfo =
+ createTaskInfo(taskId, WINDOWING_MODE_UNDEFINED);
+ SurfaceControl surfaceControl = mock(SurfaceControl.class);
+ final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
+ final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
+ taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
+
+ mCaptionWindowDecorViewModel.onTaskChanging(taskInfo, surfaceControl, startT, finishT);
+
+ verify(mCaptionWindowDecorFactory, never())
+ .create(
+ mContext,
+ mDisplayController,
+ mTaskOrganizer,
+ taskInfo,
+ surfaceControl,
+ mMainHandler,
+ mMainChoreographer,
+ mSyncQueue);
+
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
+
+ mCaptionWindowDecorViewModel.onTaskChanging(taskInfo, surfaceControl, startT, finishT);
+
+ verify(mCaptionWindowDecorFactory)
+ .create(
+ mContext,
+ mDisplayController,
+ mTaskOrganizer,
+ taskInfo,
+ surfaceControl,
+ mMainHandler,
+ mMainChoreographer,
+ mSyncQueue);
+ }
+
+ private static ActivityManager.RunningTaskInfo createTaskInfo(int taskId, int windowingMode) {
+ ActivityManager.RunningTaskInfo taskInfo =
+ new TestRunningTaskInfoBuilder()
+ .setDisplayId(Display.DEFAULT_DISPLAY)
+ .setVisible(true)
+ .build();
+ taskInfo.taskId = taskId;
+ taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
+ return taskInfo;
+ }
+
+ private static class MockObjectSupplier<T> implements Supplier<T> {
+ private final List<T> mObjects;
+ private final Supplier<T> mDefaultSupplier;
+ private int mNumOfCalls = 0;
+
+ private MockObjectSupplier(List<T> objects, Supplier<T> defaultSupplier) {
+ mObjects = objects;
+ mDefaultSupplier = defaultSupplier;
+ }
+
+ @Override
+ public T get() {
+ final T mock =
+ mNumOfCalls < mObjects.size() ? mObjects.get(mNumOfCalls)
+ : mDefaultSupplier.get();
+ ++mNumOfCalls;
+ return mock;
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index 15181b1549f5..dd9ab9899e13 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -113,6 +113,12 @@ public class WindowDecorationTests extends ShellTestCase {
mMockSurfaceControlFinishT = createMockSurfaceControlTransaction();
mMockSurfaceControlAddWindowT = createMockSurfaceControlTransaction();
+ mRelayoutParams.mLayoutResId = 0;
+ mRelayoutParams.mCaptionHeightId = R.dimen.test_freeform_decor_caption_height;
+ // Caption should have fixed width except in testLayoutResultCalculation_fullWidthCaption()
+ mRelayoutParams.mCaptionWidthId = R.dimen.test_freeform_decor_caption_width;
+ mRelayoutParams.mShadowRadiusId = R.dimen.test_window_decor_shadow_radius;
+
doReturn(mMockSurfaceControlViewHost).when(mMockSurfaceControlViewHostFactory)
.create(any(), any(), any());
}
@@ -435,6 +441,58 @@ public class WindowDecorationTests extends ShellTestCase {
assertThat(additionalWindow.mWindowSurface).isNull();
}
+ @Test
+ public void testLayoutResultCalculation_fullWidthCaption() {
+ final Display defaultDisplay = mock(Display.class);
+ doReturn(defaultDisplay).when(mMockDisplayController)
+ .getDisplay(Display.DEFAULT_DISPLAY);
+
+ final SurfaceControl decorContainerSurface = mock(SurfaceControl.class);
+ final SurfaceControl.Builder decorContainerSurfaceBuilder =
+ createMockSurfaceControlBuilder(decorContainerSurface);
+ mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
+ final SurfaceControl taskBackgroundSurface = mock(SurfaceControl.class);
+ final SurfaceControl.Builder taskBackgroundSurfaceBuilder =
+ createMockSurfaceControlBuilder(taskBackgroundSurface);
+ mMockSurfaceControlBuilders.add(taskBackgroundSurfaceBuilder);
+ final SurfaceControl captionContainerSurface = mock(SurfaceControl.class);
+ final SurfaceControl.Builder captionContainerSurfaceBuilder =
+ createMockSurfaceControlBuilder(captionContainerSurface);
+ mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder);
+
+ final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
+ mMockSurfaceControlTransactions.add(t);
+ final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
+ new ActivityManager.TaskDescription.Builder()
+ .setBackgroundColor(Color.YELLOW);
+ final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setDisplayId(Display.DEFAULT_DISPLAY)
+ .setTaskDescriptionBuilder(taskDescriptionBuilder)
+ .setBounds(TASK_BOUNDS)
+ .setPositionInParent(TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y)
+ .setVisible(true)
+ .build();
+ taskInfo.isFocused = true;
+ taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
+ mRelayoutParams.setOutsets(
+ R.dimen.test_window_decor_left_outset,
+ R.dimen.test_window_decor_top_outset,
+ R.dimen.test_window_decor_right_outset,
+ R.dimen.test_window_decor_bottom_outset);
+ final SurfaceControl taskSurface = mock(SurfaceControl.class);
+ final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface);
+
+ mRelayoutParams.mCaptionWidthId = Resources.ID_NULL;
+ windowDecor.relayout(taskInfo);
+
+ verify(captionContainerSurfaceBuilder).setParent(decorContainerSurface);
+ verify(captionContainerSurfaceBuilder).setContainerLayer();
+ verify(mMockSurfaceControlStartT).setPosition(captionContainerSurface, 20, 40);
+ // Width of the captionContainerSurface should match the width of TASK_BOUNDS
+ verify(mMockSurfaceControlStartT).setWindowCrop(captionContainerSurface, 300, 64);
+ verify(mMockSurfaceControlStartT).show(captionContainerSurface);
+ }
+
private TestWindowDecoration createWindowDecoration(
ActivityManager.RunningTaskInfo taskInfo, SurfaceControl testSurface) {
return new TestWindowDecoration(InstrumentationRegistry.getInstrumentation().getContext(),
@@ -490,11 +548,6 @@ public class WindowDecorationTests extends ShellTestCase {
@Override
void relayout(ActivityManager.RunningTaskInfo taskInfo) {
- mRelayoutParams.mLayoutResId = 0;
- mRelayoutParams.mCaptionHeightId = R.dimen.test_freeform_decor_caption_height;
- mRelayoutParams.mCaptionWidthId = R.dimen.test_freeform_decor_caption_width;
- mRelayoutParams.mShadowRadiusId = R.dimen.test_window_decor_shadow_radius;
-
relayout(mRelayoutParams, mMockSurfaceControlStartT, mMockSurfaceControlFinishT,
mMockWindowContainerTransaction, mMockView, mRelayoutResult);
}
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index cc7e8714c0ac..68f5e4a88c7e 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -1562,11 +1562,11 @@ base::expected<std::monostate, IOError> Theme::SetTo(const Theme& source) {
std::unordered_map<ApkAssetsCookie, SourceToDestinationRuntimePackageMap> src_asset_cookie_id_map;
// Determine which ApkAssets are loaded in both theme AssetManagers.
- const auto src_assets = source.asset_manager_->GetApkAssets();
+ const auto& src_assets = source.asset_manager_->GetApkAssets();
for (size_t i = 0; i < src_assets.size(); i++) {
const ApkAssets* src_asset = src_assets[i];
- const auto dest_assets = asset_manager_->GetApkAssets();
+ const auto& dest_assets = asset_manager_->GetApkAssets();
for (size_t j = 0; j < dest_assets.size(); j++) {
const ApkAssets* dest_asset = dest_assets[j];
if (src_asset != dest_asset) {
diff --git a/libs/androidfw/AssetsProvider.cpp b/libs/androidfw/AssetsProvider.cpp
index b9264c5d0f2d..2d3c06506a1f 100644
--- a/libs/androidfw/AssetsProvider.cpp
+++ b/libs/androidfw/AssetsProvider.cpp
@@ -73,9 +73,6 @@ std::unique_ptr<Asset> AssetsProvider::CreateAssetFromFd(base::unique_fd fd,
(path != nullptr) ? base::unique_fd(-1) : std::move(fd));
}
-ZipAssetsProvider::PathOrDebugName::PathOrDebugName(std::string&& value, bool is_path)
- : value_(std::forward<std::string>(value)), is_path_(is_path) {}
-
const std::string* ZipAssetsProvider::PathOrDebugName::GetPath() const {
return is_path_ ? &value_ : nullptr;
}
@@ -84,10 +81,14 @@ const std::string& ZipAssetsProvider::PathOrDebugName::GetDebugName() const {
return value_;
}
+void ZipAssetsProvider::ZipCloser::operator()(ZipArchive* a) const {
+ ::CloseArchive(a);
+}
+
ZipAssetsProvider::ZipAssetsProvider(ZipArchiveHandle handle, PathOrDebugName&& path,
package_property_t flags, time_t last_mod_time)
- : zip_handle_(handle, ::CloseArchive),
- name_(std::forward<PathOrDebugName>(path)),
+ : zip_handle_(handle),
+ name_(std::move(path)),
flags_(flags),
last_mod_time_(last_mod_time) {}
@@ -110,14 +111,12 @@ std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(std::string path,
// Stat requires execute permissions on all directories path to the file. If the process does
// not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will
// always have to return true.
- LOG(WARNING) << "Failed to stat file '" << path << "': "
- << base::SystemErrorCodeToString(errno);
+ PLOG(WARNING) << "Failed to stat file '" << path << "'";
}
}
return std::unique_ptr<ZipAssetsProvider>(
- new ZipAssetsProvider(handle, PathOrDebugName{std::move(path),
- true /* is_path */}, flags, sb.st_mtime));
+ new ZipAssetsProvider(handle, PathOrDebugName::Path(std::move(path)), flags, sb.st_mtime));
}
std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(base::unique_fd fd,
@@ -150,9 +149,8 @@ std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(base::unique_fd fd,
}
}
- return std::unique_ptr<ZipAssetsProvider>(
- new ZipAssetsProvider(handle, PathOrDebugName{std::move(friendly_name),
- false /* is_path */}, flags, sb.st_mtime));
+ return std::unique_ptr<ZipAssetsProvider>(new ZipAssetsProvider(
+ handle, PathOrDebugName::DebugName(std::move(friendly_name)), flags, sb.st_mtime));
}
std::unique_ptr<Asset> ZipAssetsProvider::OpenInternal(const std::string& path,
@@ -219,8 +217,9 @@ std::unique_ptr<Asset> ZipAssetsProvider::OpenInternal(const std::string& path,
return asset;
}
-bool ZipAssetsProvider::ForEachFile(const std::string& root_path,
- const std::function<void(StringPiece, FileType)>& f) const {
+bool ZipAssetsProvider::ForEachFile(
+ const std::string& root_path,
+ base::function_ref<void(StringPiece, FileType)> f) const {
std::string root_path_full = root_path;
if (root_path_full.back() != '/') {
root_path_full += '/';
@@ -297,7 +296,7 @@ bool ZipAssetsProvider::IsUpToDate() const {
}
DirectoryAssetsProvider::DirectoryAssetsProvider(std::string&& path, time_t last_mod_time)
- : dir_(std::forward<std::string>(path)), last_mod_time_(last_mod_time) {}
+ : dir_(std::move(path)), last_mod_time_(last_mod_time) {}
std::unique_ptr<DirectoryAssetsProvider> DirectoryAssetsProvider::Create(std::string path) {
struct stat sb;
@@ -312,7 +311,7 @@ std::unique_ptr<DirectoryAssetsProvider> DirectoryAssetsProvider::Create(std::st
return nullptr;
}
- if (path[path.size() - 1] != OS_PATH_SEPARATOR) {
+ if (path.back() != OS_PATH_SEPARATOR) {
path += OS_PATH_SEPARATOR;
}
@@ -335,7 +334,7 @@ std::unique_ptr<Asset> DirectoryAssetsProvider::OpenInternal(const std::string&
bool DirectoryAssetsProvider::ForEachFile(
const std::string& /* root_path */,
- const std::function<void(StringPiece, FileType)>& /* f */) const {
+ base::function_ref<void(StringPiece, FileType)> /* f */) const {
return true;
}
@@ -362,8 +361,7 @@ bool DirectoryAssetsProvider::IsUpToDate() const {
MultiAssetsProvider::MultiAssetsProvider(std::unique_ptr<AssetsProvider>&& primary,
std::unique_ptr<AssetsProvider>&& secondary)
- : primary_(std::forward<std::unique_ptr<AssetsProvider>>(primary)),
- secondary_(std::forward<std::unique_ptr<AssetsProvider>>(secondary)) {
+ : primary_(std::move(primary)), secondary_(std::move(secondary)) {
debug_name_ = primary_->GetDebugName() + " and " + secondary_->GetDebugName();
path_ = (primary_->GetDebugName() != kEmptyDebugString) ? primary_->GetPath()
: secondary_->GetPath();
@@ -385,8 +383,9 @@ std::unique_ptr<Asset> MultiAssetsProvider::OpenInternal(const std::string& path
return (asset) ? std::move(asset) : secondary_->Open(path, mode, file_exists);
}
-bool MultiAssetsProvider::ForEachFile(const std::string& root_path,
- const std::function<void(StringPiece, FileType)>& f) const {
+bool MultiAssetsProvider::ForEachFile(
+ const std::string& root_path,
+ base::function_ref<void(StringPiece, FileType)> f) const {
return primary_->ForEachFile(root_path, f) && secondary_->ForEachFile(root_path, f);
}
@@ -424,7 +423,7 @@ std::unique_ptr<Asset> EmptyAssetsProvider::OpenInternal(const std::string& /* p
bool EmptyAssetsProvider::ForEachFile(
const std::string& /* root_path */,
- const std::function<void(StringPiece, FileType)>& /* f */) const {
+ base::function_ref<void(StringPiece, FileType)> /* f */) const {
return true;
}
@@ -447,4 +446,4 @@ bool EmptyAssetsProvider::IsUpToDate() const {
return true;
}
-} // namespace android
+} // namespace android
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index f3d244342b55..89835742c8ff 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -201,7 +201,7 @@ IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const {
const auto& config = configurations_[value.config_index];
values_map[config] = value.value;
}
- return Result(values_map);
+ return Result(std::move(values_map));
}
return {};
}
@@ -250,8 +250,7 @@ std::optional<std::string_view> ReadString(const uint8_t** in_out_data_ptr, size
}
} // namespace
-LoadedIdmap::LoadedIdmap(std::string&& idmap_path,
- const Idmap_header* header,
+LoadedIdmap::LoadedIdmap(std::string&& idmap_path, const Idmap_header* header,
const Idmap_data_header* data_header,
const Idmap_target_entry* target_entries,
const Idmap_target_entry_inline* target_inline_entries,
@@ -259,20 +258,19 @@ LoadedIdmap::LoadedIdmap(std::string&& idmap_path,
const ConfigDescription* configs,
const Idmap_overlay_entry* overlay_entries,
std::unique_ptr<ResStringPool>&& string_pool,
- std::string_view overlay_apk_path,
- std::string_view target_apk_path)
- : header_(header),
- data_header_(data_header),
- target_entries_(target_entries),
- target_inline_entries_(target_inline_entries),
- inline_entry_values_(inline_entry_values),
- configurations_(configs),
- overlay_entries_(overlay_entries),
- string_pool_(std::move(string_pool)),
- idmap_path_(std::move(idmap_path)),
- overlay_apk_path_(overlay_apk_path),
- target_apk_path_(target_apk_path),
- idmap_last_mod_time_(getFileModDate(idmap_path_.data())) {}
+ std::string_view overlay_apk_path, std::string_view target_apk_path)
+ : header_(header),
+ data_header_(data_header),
+ target_entries_(target_entries),
+ target_inline_entries_(target_inline_entries),
+ inline_entry_values_(inline_entry_values),
+ configurations_(configs),
+ overlay_entries_(overlay_entries),
+ string_pool_(std::move(string_pool)),
+ idmap_path_(std::move(idmap_path)),
+ overlay_apk_path_(overlay_apk_path),
+ target_apk_path_(target_apk_path),
+ idmap_last_mod_time_(getFileModDate(idmap_path_.data())) {}
std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(StringPiece idmap_path, StringPiece idmap_data) {
ATRACE_CALL();
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index e4d1218debe6..f10cb9bf480a 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -105,7 +105,7 @@ class AssetManager2 {
// new resource IDs.
bool SetApkAssets(std::vector<const ApkAssets*> apk_assets, bool invalidate_caches = true);
- inline const std::vector<const ApkAssets*> GetApkAssets() const {
+ inline const std::vector<const ApkAssets*>& GetApkAssets() const {
return apk_assets_;
}
diff --git a/libs/androidfw/include/androidfw/AssetsProvider.h b/libs/androidfw/include/androidfw/AssetsProvider.h
index 7891194a65bd..d33c325ff369 100644
--- a/libs/androidfw/include/androidfw/AssetsProvider.h
+++ b/libs/androidfw/include/androidfw/AssetsProvider.h
@@ -20,6 +20,7 @@
#include <memory>
#include <string>
+#include "android-base/function_ref.h"
#include "android-base/macros.h"
#include "android-base/unique_fd.h"
@@ -46,7 +47,7 @@ struct AssetsProvider {
// Iterate over all files and directories provided by the interface. The order of iteration is
// stable.
virtual bool ForEachFile(const std::string& path,
- const std::function<void(StringPiece, FileType)>& f) const = 0;
+ base::function_ref<void(StringPiece, FileType)> f) const = 0;
// Retrieves the path to the contents of the AssetsProvider on disk. The path could represent an
// APk, a directory, or some other file type.
@@ -90,7 +91,7 @@ struct ZipAssetsProvider : public AssetsProvider {
off64_t len = kUnknownLength);
bool ForEachFile(const std::string& root_path,
- const std::function<void(StringPiece, FileType)>& f) const override;
+ base::function_ref<void(StringPiece, FileType)> f) const override;
WARN_UNUSED std::optional<std::string_view> GetPath() const override;
WARN_UNUSED const std::string& GetDebugName() const override;
@@ -108,7 +109,12 @@ struct ZipAssetsProvider : public AssetsProvider {
time_t last_mod_time);
struct PathOrDebugName {
- PathOrDebugName(std::string&& value, bool is_path);
+ static PathOrDebugName Path(std::string value) {
+ return {std::move(value), true};
+ }
+ static PathOrDebugName DebugName(std::string value) {
+ return {std::move(value), false};
+ }
// Retrieves the path or null if this class represents a debug name.
WARN_UNUSED const std::string* GetPath() const;
@@ -117,11 +123,16 @@ struct ZipAssetsProvider : public AssetsProvider {
WARN_UNUSED const std::string& GetDebugName() const;
private:
+ PathOrDebugName(std::string value, bool is_path) : value_(std::move(value)), is_path_(is_path) {
+ }
std::string value_;
bool is_path_;
};
- std::unique_ptr<ZipArchive, void (*)(ZipArchive*)> zip_handle_;
+ struct ZipCloser {
+ void operator()(ZipArchive* a) const;
+ };
+ std::unique_ptr<ZipArchive, ZipCloser> zip_handle_;
PathOrDebugName name_;
package_property_t flags_;
time_t last_mod_time_;
@@ -132,7 +143,7 @@ struct DirectoryAssetsProvider : public AssetsProvider {
static std::unique_ptr<DirectoryAssetsProvider> Create(std::string root_dir);
bool ForEachFile(const std::string& path,
- const std::function<void(StringPiece, FileType)>& f) const override;
+ base::function_ref<void(StringPiece, FileType)> f) const override;
WARN_UNUSED std::optional<std::string_view> GetPath() const override;
WARN_UNUSED const std::string& GetDebugName() const override;
@@ -157,7 +168,7 @@ struct MultiAssetsProvider : public AssetsProvider {
std::unique_ptr<AssetsProvider>&& secondary);
bool ForEachFile(const std::string& root_path,
- const std::function<void(StringPiece, FileType)>& f) const override;
+ base::function_ref<void(StringPiece, FileType)> f) const override;
WARN_UNUSED std::optional<std::string_view> GetPath() const override;
WARN_UNUSED const std::string& GetDebugName() const override;
@@ -184,7 +195,7 @@ struct EmptyAssetsProvider : public AssetsProvider {
static std::unique_ptr<AssetsProvider> Create(std::string path);
bool ForEachFile(const std::string& path,
- const std::function<void(StringPiece, FileType)>& f) const override;
+ base::function_ref<void(StringPiece, FileType)> f) const override;
WARN_UNUSED std::optional<std::string_view> GetPath() const override;
WARN_UNUSED const std::string& GetDebugName() const override;
diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h
index f173e6ef9c16..60689128dffb 100644
--- a/libs/androidfw/include/androidfw/Idmap.h
+++ b/libs/androidfw/include/androidfw/Idmap.h
@@ -93,8 +93,8 @@ class IdmapResMap {
public:
Result() = default;
explicit Result(uint32_t value) : data_(value) {};
- explicit Result(const std::map<ConfigDescription, Res_value> &value)
- : data_(value) { };
+ explicit Result(std::map<ConfigDescription, Res_value> value) : data_(std::move(value)) {
+ }
// Returns `true` if the resource is overlaid.
explicit operator bool() const {
diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h
index ae7c65f2d4e1..a188abb7ecb5 100644
--- a/libs/androidfw/include/androidfw/Util.h
+++ b/libs/androidfw/include/androidfw/Util.h
@@ -22,7 +22,6 @@
#include <cstdlib>
#include <memory>
-#include <sstream>
#include <vector>
#include "androidfw/BigBuffer.h"
@@ -33,7 +32,14 @@
#ifdef __ANDROID__
#define ANDROID_LOG(x) LOG(x)
#else
-#define ANDROID_LOG(x) std::stringstream()
+namespace android {
+// No default logging for aapt2, as it's too noisy for a command line dev tool.
+struct NullLogger {
+ template <class T>
+ friend const NullLogger& operator<<(const NullLogger& l, const T&) { return l; }
+};
+}
+#define ANDROID_LOG(x) (android::NullLogger{})
#endif
namespace android {
@@ -49,76 +55,14 @@ std::unique_ptr<T> make_unique(Args&&... args) {
return std::unique_ptr<T>(new T{std::forward<Args>(args)...});
}
-// Based on std::unique_ptr, but uses free() to release malloc'ed memory
-// without incurring the size increase of holding on to a custom deleter.
-template <typename T>
-class unique_cptr {
- public:
- using pointer = typename std::add_pointer<T>::type;
-
- constexpr unique_cptr() : ptr_(nullptr) {}
- constexpr explicit unique_cptr(std::nullptr_t) : ptr_(nullptr) {}
- explicit unique_cptr(pointer ptr) : ptr_(ptr) {}
- unique_cptr(unique_cptr&& o) noexcept : ptr_(o.ptr_) { o.ptr_ = nullptr; }
-
- ~unique_cptr() { std::free(reinterpret_cast<void*>(ptr_)); }
-
- inline unique_cptr& operator=(unique_cptr&& o) noexcept {
- if (&o == this) {
- return *this;
- }
-
- std::free(reinterpret_cast<void*>(ptr_));
- ptr_ = o.ptr_;
- o.ptr_ = nullptr;
- return *this;
- }
-
- inline unique_cptr& operator=(std::nullptr_t) {
- std::free(reinterpret_cast<void*>(ptr_));
- ptr_ = nullptr;
- return *this;
- }
-
- pointer release() {
- pointer result = ptr_;
- ptr_ = nullptr;
- return result;
+// Based on std::unique_ptr, but uses free() to release malloc'ed memory.
+struct FreeDeleter {
+ void operator()(void* ptr) const {
+ ::free(ptr);
}
-
- inline pointer get() const { return ptr_; }
-
- void reset(pointer ptr = pointer()) {
- if (ptr == ptr_) {
- return;
- }
-
- pointer old_ptr = ptr_;
- ptr_ = ptr;
- std::free(reinterpret_cast<void*>(old_ptr));
- }
-
- inline void swap(unique_cptr& o) { std::swap(ptr_, o.ptr_); }
-
- inline explicit operator bool() const { return ptr_ != nullptr; }
-
- inline typename std::add_lvalue_reference<T>::type operator*() const { return *ptr_; }
-
- inline pointer operator->() const { return ptr_; }
-
- inline bool operator==(const unique_cptr& o) const { return ptr_ == o.ptr_; }
-
- inline bool operator!=(const unique_cptr& o) const { return ptr_ != o.ptr_; }
-
- inline bool operator==(std::nullptr_t) const { return ptr_ == nullptr; }
-
- inline bool operator!=(std::nullptr_t) const { return ptr_ != nullptr; }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(unique_cptr);
-
- pointer ptr_;
};
+template <typename T>
+using unique_cptr = std::unique_ptr<T, FreeDeleter>;
void ReadUtf16StringFromDevice(const uint16_t* src, size_t len, std::string* out);
@@ -152,13 +96,13 @@ inline uint32_t DeviceToHost32(uint32_t value) {
std::vector<std::string> SplitAndLowercase(android::StringPiece str, char sep);
-template <typename T>
-inline bool IsFourByteAligned(const incfs::map_ptr<T>& data) {
- return ((size_t)data.unsafe_ptr() & 0x3U) == 0;
+inline bool IsFourByteAligned(const void* data) {
+ return ((uintptr_t)data & 0x3U) == 0;
}
-inline bool IsFourByteAligned(const void* data) {
- return ((size_t)data & 0x3U) == 0;
+template <typename T>
+inline bool IsFourByteAligned(const incfs::map_ptr<T>& data) {
+ return IsFourByteAligned(data.unsafe_ptr());
}
// Helper method to extract a UTF-16 string from a StringPool. If the string is stored as UTF-8,
diff --git a/libs/androidfw/misc.cpp b/libs/androidfw/misc.cpp
index 7af506638ebc..d3949e9cf69f 100644
--- a/libs/androidfw/misc.cpp
+++ b/libs/androidfw/misc.cpp
@@ -23,10 +23,10 @@
#include "android-base/logging.h"
-#ifndef _WIN32
+#ifdef __linux__
#include <sys/statvfs.h>
#include <sys/vfs.h>
-#endif // _WIN32
+#endif // __linux__
#include <cstring>
#include <cstdio>
@@ -86,15 +86,15 @@ time_t getFileModDate(const char* fileName)
return sb.st_mtime;
}
-#ifdef _WIN32
-// No need to implement these for Windows, the functions only matter on a device.
+#ifndef __linux__
+// No need to implement these on the host, the functions only matter on a device.
bool isReadonlyFilesystem(const char*) {
return false;
}
bool isReadonlyFilesystem(int) {
return false;
}
-#else // _WIN32
+#else // __linux__
bool isReadonlyFilesystem(const char* path) {
struct statfs sfs;
if (::statfs(path, &sfs)) {
@@ -112,6 +112,6 @@ bool isReadonlyFilesystem(int fd) {
}
return (sfs.f_flags & ST_RDONLY) != 0;
}
-#endif // _WIN32
+#endif // __linux__
}; // namespace android
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 88cfed9357d8..fcc0126652bc 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -570,6 +570,7 @@ cc_defaults {
"renderthread/VulkanSurface.cpp",
"renderthread/RenderProxy.cpp",
"renderthread/RenderThread.cpp",
+ "renderthread/HintSessionWrapper.cpp",
"service/GraphicsStatsService.cpp",
"thread/CommonPool.cpp",
"utils/GLUtils.cpp",
diff --git a/libs/hwui/CanvasTransform.cpp b/libs/hwui/CanvasTransform.cpp
index 673041a661ce..cd4fae86aa52 100644
--- a/libs/hwui/CanvasTransform.cpp
+++ b/libs/hwui/CanvasTransform.cpp
@@ -17,6 +17,7 @@
#include "CanvasTransform.h"
#include <SkAndroidFrameworkUtils.h>
+#include <SkBlendMode.h>
#include <SkColorFilter.h>
#include <SkGradientShader.h>
#include <SkHighContrastFilter.h>
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index 9a4c5505fa35..a7f8f6189a8e 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -16,6 +16,7 @@
#pragma once
+#include <SkBlendMode.h>
#include <SkColorFilter.h>
#include <SkImage.h>
#include <SkMatrix.h>
diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h
index 564ee4f53a54..b15b6cb9a9ec 100644
--- a/libs/hwui/FrameInfo.h
+++ b/libs/hwui/FrameInfo.h
@@ -104,6 +104,7 @@ public:
set(FrameInfoIndex::AnimationStart) = vsyncTime;
set(FrameInfoIndex::PerformTraversalsStart) = vsyncTime;
set(FrameInfoIndex::DrawStart) = vsyncTime;
+ set(FrameInfoIndex::FrameStartTime) = vsyncTime;
set(FrameInfoIndex::FrameDeadline) = frameDeadline;
set(FrameInfoIndex::FrameInterval) = frameInterval;
return *this;
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 9053c1240957..fc3118ae32dd 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -20,6 +20,8 @@
#include "utils/Color.h"
#include "utils/MathUtils.h"
+#include <SkBlendMode.h>
+
#include <log/log.h>
namespace android {
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index f5ebfd5d9e23..f070e97dff2a 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -22,6 +22,7 @@
#include <experimental/type_traits>
#include "SkAndroidFrameworkUtils.h"
+#include "SkBlendMode.h"
#include "SkCanvas.h"
#include "SkCanvasPriv.h"
#include "SkColor.h"
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 35bec9335d7c..f37729ebb59c 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -34,6 +34,7 @@
#include <SkRuntimeEffect.h>
#include <vector>
+enum class SkBlendMode;
class SkRRect;
namespace android {
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 51007c52260d..eece77e061b6 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -33,6 +33,7 @@
#include <cassert>
#include <optional>
+enum class SkBlendMode;
class SkRRect;
namespace android {
diff --git a/libs/hwui/apex/android_paint.cpp b/libs/hwui/apex/android_paint.cpp
index 70bd085343ce..cc79cba5e19c 100644
--- a/libs/hwui/apex/android_paint.cpp
+++ b/libs/hwui/apex/android_paint.cpp
@@ -19,6 +19,7 @@
#include "TypeCast.h"
#include <hwui/Paint.h>
+#include <SkBlendMode.h>
using namespace android;
diff --git a/libs/hwui/hwui/BlurDrawLooper.cpp b/libs/hwui/hwui/BlurDrawLooper.cpp
index d4b0198d015d..8b52551fc107 100644
--- a/libs/hwui/hwui/BlurDrawLooper.cpp
+++ b/libs/hwui/hwui/BlurDrawLooper.cpp
@@ -15,6 +15,7 @@
*/
#include "BlurDrawLooper.h"
+#include <SkBlurTypes.h>
#include <SkColorSpace.h>
#include <SkMaskFilter.h>
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 82d23b51b12a..4608088a7cd9 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -30,6 +30,7 @@
#include <SkMatrix.h>
class SkAnimatedImage;
+enum class SkBlendMode;
class SkCanvasState;
class SkRRect;
class SkRuntimeShaderBuilder;
diff --git a/libs/hwui/jni/ColorFilter.cpp b/libs/hwui/jni/ColorFilter.cpp
index cef21f91f3c1..4bd7ef47b871 100644
--- a/libs/hwui/jni/ColorFilter.cpp
+++ b/libs/hwui/jni/ColorFilter.cpp
@@ -17,6 +17,7 @@
#include "GraphicsJNI.h"
+#include "SkBlendMode.h"
#include "SkColorFilter.h"
#include "SkColorMatrixFilter.h"
diff --git a/libs/hwui/jni/MaskFilter.cpp b/libs/hwui/jni/MaskFilter.cpp
index 5383032e0f77..048ce025ce27 100644
--- a/libs/hwui/jni/MaskFilter.cpp
+++ b/libs/hwui/jni/MaskFilter.cpp
@@ -2,6 +2,7 @@
#include "SkMaskFilter.h"
#include "SkBlurMask.h"
#include "SkBlurMaskFilter.h"
+#include "SkBlurTypes.h"
#include "SkTableMaskFilter.h"
static void ThrowIAE_IfNull(JNIEnv* env, void* ptr) {
diff --git a/libs/hwui/jni/RenderEffect.cpp b/libs/hwui/jni/RenderEffect.cpp
index 213f35a81b88..f3db1705e694 100644
--- a/libs/hwui/jni/RenderEffect.cpp
+++ b/libs/hwui/jni/RenderEffect.cpp
@@ -15,6 +15,7 @@
*/
#include "Bitmap.h"
#include "GraphicsJNI.h"
+#include "SkBlendMode.h"
#include "SkImageFilter.h"
#include "SkImageFilters.h"
#include "graphics_jni_helpers.h"
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.h b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
index 6c390c3fce24..c7582e734009 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.h
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
@@ -18,6 +18,7 @@
#include "SkiaUtils.h"
+#include <SkBlendMode.h>
#include <SkCanvas.h>
#include <SkDrawable.h>
#include <SkMatrix.h>
diff --git a/libs/hwui/pipeline/skia/StretchMask.cpp b/libs/hwui/pipeline/skia/StretchMask.cpp
index 2dbeb3adfab3..b169c9200e88 100644
--- a/libs/hwui/pipeline/skia/StretchMask.cpp
+++ b/libs/hwui/pipeline/skia/StretchMask.cpp
@@ -14,8 +14,10 @@
* limitations under the License.
*/
#include "StretchMask.h"
-#include "SkSurface.h"
+
+#include "SkBlendMode.h"
#include "SkCanvas.h"
+#include "SkSurface.h"
#include "TransformCanvas.h"
#include "SkiaDisplayList.h"
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
index 3c7617d35c7c..e168a7b9459a 100644
--- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
@@ -33,6 +33,8 @@
#include "thread/ThreadBase.h"
#include "utils/TimeUtils.h"
+#include <SkBlendMode.h>
+
namespace android {
namespace uirenderer {
namespace skiapipeline {
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index 1d24e718db1a..1c7688464c27 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -98,7 +98,6 @@ void CacheManager::configureContext(GrContextOptions* contextOptions, const void
auto& cache = skiapipeline::ShaderCache::get();
cache.initShaderDiskCache(identity, size);
contextOptions->fPersistentCache = &cache;
- contextOptions->fGpuPathRenderers &= ~GpuPathRenderers::kCoverageCounting;
}
void CacheManager::trimMemory(TrimLevel mode) {
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index d09bc47cf8fd..64839d0147f8 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -71,16 +71,19 @@ CanvasContext* ScopedActiveContext::sActiveContext = nullptr;
} /* namespace */
CanvasContext* CanvasContext::create(RenderThread& thread, bool translucent,
- RenderNode* rootRenderNode, IContextFactory* contextFactory) {
+ RenderNode* rootRenderNode, IContextFactory* contextFactory,
+ int32_t uiThreadId, int32_t renderThreadId) {
auto renderType = Properties::getRenderPipelineType();
switch (renderType) {
case RenderPipelineType::SkiaGL:
return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
- std::make_unique<skiapipeline::SkiaOpenGLPipeline>(thread));
+ std::make_unique<skiapipeline::SkiaOpenGLPipeline>(thread),
+ uiThreadId, renderThreadId);
case RenderPipelineType::SkiaVulkan:
return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
- std::make_unique<skiapipeline::SkiaVulkanPipeline>(thread));
+ std::make_unique<skiapipeline::SkiaVulkanPipeline>(thread),
+ uiThreadId, renderThreadId);
default:
LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t)renderType);
break;
@@ -110,7 +113,8 @@ void CanvasContext::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
IContextFactory* contextFactory,
- std::unique_ptr<IRenderPipeline> renderPipeline)
+ std::unique_ptr<IRenderPipeline> renderPipeline, pid_t uiThreadId,
+ pid_t renderThreadId)
: mRenderThread(thread)
, mGenerationID(0)
, mOpaque(!translucent)
@@ -118,7 +122,8 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode*
, mJankTracker(&thread.globalProfileData())
, mProfiler(mJankTracker.frames(), thread.timeLord().frameIntervalNanos())
, mContentDrawBounds(0, 0, 0, 0)
- , mRenderPipeline(std::move(renderPipeline)) {
+ , mRenderPipeline(std::move(renderPipeline))
+ , mHintSessionWrapper(uiThreadId, renderThreadId) {
mRenderThread.cacheManager().registerCanvasContext(this);
rootRenderNode->makeRoot();
mRenderNodes.emplace_back(rootRenderNode);
@@ -472,16 +477,22 @@ void CanvasContext::notifyFramePending() {
mRenderThread.pushBackFrameCallback(this);
}
-std::optional<nsecs_t> CanvasContext::draw() {
+void CanvasContext::draw() {
if (auto grContext = getGrContext()) {
if (grContext->abandoned()) {
LOG_ALWAYS_FATAL("GrContext is abandoned/device lost at start of CanvasContext::draw");
- return std::nullopt;
+ return;
}
}
SkRect dirty;
mDamageAccumulator.finish(&dirty);
+ // reset syncDelayDuration each time we draw
+ nsecs_t syncDelayDuration = mSyncDelayDuration;
+ nsecs_t idleDuration = mIdleDuration;
+ mSyncDelayDuration = 0;
+ mIdleDuration = 0;
+
if (!Properties::isDrawingEnabled() ||
(dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw())) {
mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
@@ -498,7 +509,7 @@ std::optional<nsecs_t> CanvasContext::draw() {
std::invoke(func, false /* didProduceBuffer */);
}
mFrameCommitCallbacks.clear();
- return std::nullopt;
+ return;
}
ScopedActiveContext activeContext(this);
@@ -650,10 +661,25 @@ std::optional<nsecs_t> CanvasContext::draw() {
}
}
+ int64_t intendedVsync = mCurrentFrameInfo->get(FrameInfoIndex::IntendedVsync);
+ int64_t frameDeadline = mCurrentFrameInfo->get(FrameInfoIndex::FrameDeadline);
+ int64_t dequeueBufferDuration = mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration);
+
+ mHintSessionWrapper.updateTargetWorkDuration(frameDeadline - intendedVsync);
+
+ if (didDraw) {
+ int64_t frameStartTime = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime);
+ int64_t frameDuration = systemTime(SYSTEM_TIME_MONOTONIC) - frameStartTime;
+ int64_t actualDuration = frameDuration -
+ (std::min(syncDelayDuration, mLastDequeueBufferDuration)) -
+ dequeueBufferDuration - idleDuration;
+ mHintSessionWrapper.reportActualWorkDuration(actualDuration);
+ }
+
+ mLastDequeueBufferDuration = dequeueBufferDuration;
+
mRenderThread.cacheManager().onFrameCompleted();
- return didDraw ? std::make_optional(
- mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration))
- : std::nullopt;
+ return;
}
void CanvasContext::reportMetricsWithPresentTime() {
@@ -766,6 +792,8 @@ void CanvasContext::onSurfaceStatsAvailable(void* context, int32_t surfaceContro
// Called by choreographer to do an RT-driven animation
void CanvasContext::doFrame() {
if (!mRenderPipeline->isSurfaceReady()) return;
+ mIdleDuration =
+ systemTime(SYSTEM_TIME_MONOTONIC) - mRenderThread.timeLord().computeFrameTimeNanos();
prepareAndDraw(nullptr);
}
@@ -974,6 +1002,14 @@ void CanvasContext::prepareSurfaceControlForWebview() {
}
}
+void CanvasContext::sendLoadResetHint() {
+ mHintSessionWrapper.sendLoadResetHint();
+}
+
+void CanvasContext::setSyncDelayDuration(nsecs_t duration) {
+ mSyncDelayDuration = duration;
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index db96cfb978e9..e875c42e9eba 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -16,10 +16,26 @@
#pragma once
+#include <SkBitmap.h>
+#include <SkRect.h>
+#include <SkSize.h>
+#include <cutils/compiler.h>
+#include <utils/Functor.h>
+#include <utils/Mutex.h>
+
+#include <functional>
+#include <future>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "ColorMode.h"
#include "DamageAccumulator.h"
#include "FrameInfo.h"
#include "FrameInfoVisualizer.h"
#include "FrameMetricsReporter.h"
+#include "HintSessionWrapper.h"
#include "IContextFactory.h"
#include "IRenderPipeline.h"
#include "JankTracker.h"
@@ -30,21 +46,6 @@
#include "renderthread/RenderTask.h"
#include "renderthread/RenderThread.h"
#include "utils/RingBuffer.h"
-#include "ColorMode.h"
-
-#include <SkBitmap.h>
-#include <SkRect.h>
-#include <SkSize.h>
-#include <cutils/compiler.h>
-#include <utils/Functor.h>
-#include <utils/Mutex.h>
-
-#include <functional>
-#include <future>
-#include <set>
-#include <string>
-#include <utility>
-#include <vector>
namespace android {
namespace uirenderer {
@@ -66,7 +67,8 @@ class Frame;
class CanvasContext : public IFrameCallback {
public:
static CanvasContext* create(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
- IContextFactory* contextFactory);
+ IContextFactory* contextFactory, pid_t uiThreadId,
+ pid_t renderThreadId);
virtual ~CanvasContext();
/**
@@ -138,7 +140,7 @@ public:
bool makeCurrent();
void prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued, RenderNode* target);
// Returns the DequeueBufferDuration.
- std::optional<nsecs_t> draw();
+ void draw();
void destroy();
// IFrameCallback, Choreographer-driven frame callback entry point
@@ -214,9 +216,14 @@ public:
static CanvasContext* getActiveContext();
+ void sendLoadResetHint();
+
+ void setSyncDelayDuration(nsecs_t duration);
+
private:
CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
- IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline);
+ IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline,
+ pid_t uiThreadId, pid_t renderThreadId);
friend class RegisterFrameCallbackTask;
// TODO: Replace with something better for layer & other GL object
@@ -330,6 +337,11 @@ private:
std::function<bool(int64_t, int64_t, int64_t)> mASurfaceTransactionCallback;
std::function<void()> mPrepareSurfaceControlForWebviewCallback;
+
+ HintSessionWrapper mHintSessionWrapper;
+ nsecs_t mLastDequeueBufferDuration = 0;
+ nsecs_t mSyncDelayDuration = 0;
+ nsecs_t mIdleDuration = 0;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index cb306144ffd6..1cc82fd0ff64 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -16,7 +16,6 @@
#include "DrawFrameTask.h"
-#include <dlfcn.h>
#include <gui/TraceUtils.h>
#include <utils/Log.h>
@@ -28,70 +27,11 @@
#include "../RenderNode.h"
#include "CanvasContext.h"
#include "RenderThread.h"
-#include "thread/CommonPool.h"
-#include "utils/TimeUtils.h"
namespace android {
namespace uirenderer {
namespace renderthread {
-namespace {
-
-typedef APerformanceHintManager* (*APH_getManager)();
-typedef APerformanceHintSession* (*APH_createSession)(APerformanceHintManager*, const int32_t*,
- size_t, int64_t);
-typedef void (*APH_updateTargetWorkDuration)(APerformanceHintSession*, int64_t);
-typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_t);
-typedef void (*APH_sendHint)(APerformanceHintSession* session, int32_t);
-typedef void (*APH_closeSession)(APerformanceHintSession* session);
-
-bool gAPerformanceHintBindingInitialized = false;
-APH_getManager gAPH_getManagerFn = nullptr;
-APH_createSession gAPH_createSessionFn = nullptr;
-APH_updateTargetWorkDuration gAPH_updateTargetWorkDurationFn = nullptr;
-APH_reportActualWorkDuration gAPH_reportActualWorkDurationFn = nullptr;
-APH_sendHint gAPH_sendHintFn = nullptr;
-APH_closeSession gAPH_closeSessionFn = nullptr;
-
-void ensureAPerformanceHintBindingInitialized() {
- if (gAPerformanceHintBindingInitialized) return;
-
- void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
- LOG_ALWAYS_FATAL_IF(handle_ == nullptr, "Failed to dlopen libandroid.so!");
-
- gAPH_getManagerFn = (APH_getManager)dlsym(handle_, "APerformanceHint_getManager");
- LOG_ALWAYS_FATAL_IF(gAPH_getManagerFn == nullptr,
- "Failed to find required symbol APerformanceHint_getManager!");
-
- gAPH_createSessionFn = (APH_createSession)dlsym(handle_, "APerformanceHint_createSession");
- LOG_ALWAYS_FATAL_IF(gAPH_createSessionFn == nullptr,
- "Failed to find required symbol APerformanceHint_createSession!");
-
- gAPH_updateTargetWorkDurationFn = (APH_updateTargetWorkDuration)dlsym(
- handle_, "APerformanceHint_updateTargetWorkDuration");
- LOG_ALWAYS_FATAL_IF(
- gAPH_updateTargetWorkDurationFn == nullptr,
- "Failed to find required symbol APerformanceHint_updateTargetWorkDuration!");
-
- gAPH_reportActualWorkDurationFn = (APH_reportActualWorkDuration)dlsym(
- handle_, "APerformanceHint_reportActualWorkDuration");
- LOG_ALWAYS_FATAL_IF(
- gAPH_reportActualWorkDurationFn == nullptr,
- "Failed to find required symbol APerformanceHint_reportActualWorkDuration!");
-
- gAPH_sendHintFn = (APH_sendHint)dlsym(handle_, "APerformanceHint_sendHint");
- LOG_ALWAYS_FATAL_IF(gAPH_sendHintFn == nullptr,
- "Failed to find required symbol APerformanceHint_sendHint!");
-
- gAPH_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSession");
- LOG_ALWAYS_FATAL_IF(gAPH_closeSessionFn == nullptr,
- "Failed to find required symbol APerformanceHint_closeSession!");
-
- gAPerformanceHintBindingInitialized = true;
-}
-
-} // namespace
-
DrawFrameTask::DrawFrameTask()
: mRenderThread(nullptr)
, mContext(nullptr)
@@ -100,13 +40,11 @@ DrawFrameTask::DrawFrameTask()
DrawFrameTask::~DrawFrameTask() {}
-void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode,
- int32_t uiThreadId, int32_t renderThreadId) {
+void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context,
+ RenderNode* targetNode) {
mRenderThread = thread;
mContext = context;
mTargetNode = targetNode;
- mUiThreadId = uiThreadId;
- mRenderThreadId = renderThreadId;
}
void DrawFrameTask::pushLayerUpdate(DeferredLayerUpdater* layer) {
@@ -150,11 +88,11 @@ void DrawFrameTask::postAndWait() {
void DrawFrameTask::run() {
const int64_t vsyncId = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameTimelineVsyncId)];
ATRACE_FORMAT("DrawFrames %" PRId64, vsyncId);
- nsecs_t syncDelayDuration = systemTime(SYSTEM_TIME_MONOTONIC) - mSyncQueued;
+
+ mContext->setSyncDelayDuration(systemTime(SYSTEM_TIME_MONOTONIC) - mSyncQueued);
bool canUnblockUiThread;
bool canDrawThisFrame;
- bool didDraw = false;
{
TreeInfo info(TreeInfo::MODE_FULL, *mContext);
info.forceDrawFrame = mForceDrawFrame;
@@ -175,9 +113,6 @@ void DrawFrameTask::run() {
std::function<void()> frameCompleteCallback = std::move(mFrameCompleteCallback);
mFrameCallback = nullptr;
mFrameCompleteCallback = nullptr;
- int64_t intendedVsync = mFrameInfo[static_cast<int>(FrameInfoIndex::IntendedVsync)];
- int64_t frameDeadline = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameDeadline)];
- int64_t frameStartTime = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameStartTime)];
// From this point on anything in "this" is *UNSAFE TO ACCESS*
if (canUnblockUiThread) {
@@ -188,18 +123,15 @@ void DrawFrameTask::run() {
if (CC_UNLIKELY(frameCallback)) {
context->enqueueFrameWork([frameCallback, context, syncResult = mSyncResult,
frameNr = context->getFrameNumber()]() {
- auto frameCommitCallback = std::move(frameCallback(syncResult, frameNr));
+ auto frameCommitCallback = frameCallback(syncResult, frameNr);
if (frameCommitCallback) {
context->addFrameCommitListener(std::move(frameCommitCallback));
}
});
}
- nsecs_t dequeueBufferDuration = 0;
if (CC_LIKELY(canDrawThisFrame)) {
- std::optional<nsecs_t> drawResult = context->draw();
- didDraw = drawResult.has_value();
- dequeueBufferDuration = drawResult.value_or(0);
+ context->draw();
} else {
// Do a flush in case syncFrameState performed any texture uploads. Since we skipped
// the draw() call, those uploads (or deletes) will end up sitting in the queue.
@@ -218,41 +150,6 @@ void DrawFrameTask::run() {
if (!canUnblockUiThread) {
unblockUiThread();
}
-
- if (!mHintSessionWrapper) mHintSessionWrapper.emplace(mUiThreadId, mRenderThreadId);
-
- constexpr int64_t kSanityCheckLowerBound = 100_us;
- constexpr int64_t kSanityCheckUpperBound = 10_s;
- int64_t targetWorkDuration = frameDeadline - intendedVsync;
- targetWorkDuration = targetWorkDuration * Properties::targetCpuTimePercentage / 100;
- if (targetWorkDuration > kSanityCheckLowerBound &&
- targetWorkDuration < kSanityCheckUpperBound &&
- targetWorkDuration != mLastTargetWorkDuration) {
- mLastTargetWorkDuration = targetWorkDuration;
- mHintSessionWrapper->updateTargetWorkDuration(targetWorkDuration);
- }
-
- if (didDraw) {
- int64_t frameDuration = systemTime(SYSTEM_TIME_MONOTONIC) - frameStartTime;
- int64_t actualDuration = frameDuration -
- (std::min(syncDelayDuration, mLastDequeueBufferDuration)) -
- dequeueBufferDuration;
- if (actualDuration > kSanityCheckLowerBound && actualDuration < kSanityCheckUpperBound) {
- mHintSessionWrapper->reportActualWorkDuration(actualDuration);
- }
- }
-
- mLastDequeueBufferDuration = dequeueBufferDuration;
-}
-
-void DrawFrameTask::sendLoadResetHint() {
- if (!(Properties::useHintManager && Properties::isDrawingEnabled())) return;
- if (!mHintSessionWrapper) mHintSessionWrapper.emplace(mUiThreadId, mRenderThreadId);
- nsecs_t now = systemTime();
- if (now - mLastFrameNotification > kResetHintTimeout) {
- mHintSessionWrapper->sendHint(SessionHint::CPU_LOAD_RESET);
- }
- mLastFrameNotification = now;
}
bool DrawFrameTask::syncFrameState(TreeInfo& info) {
@@ -305,50 +202,6 @@ void DrawFrameTask::unblockUiThread() {
mSignal.signal();
}
-DrawFrameTask::HintSessionWrapper::HintSessionWrapper(int32_t uiThreadId, int32_t renderThreadId) {
- if (!Properties::useHintManager) return;
- if (uiThreadId < 0 || renderThreadId < 0) return;
-
- ensureAPerformanceHintBindingInitialized();
-
- APerformanceHintManager* manager = gAPH_getManagerFn();
- if (!manager) return;
-
- std::vector<int32_t> tids = CommonPool::getThreadIds();
- tids.push_back(uiThreadId);
- tids.push_back(renderThreadId);
-
- // DrawFrameTask code will always set a target duration before reporting actual durations.
- // So this is just a placeholder value that's never used.
- int64_t dummyTargetDurationNanos = 16666667;
- mHintSession =
- gAPH_createSessionFn(manager, tids.data(), tids.size(), dummyTargetDurationNanos);
-}
-
-DrawFrameTask::HintSessionWrapper::~HintSessionWrapper() {
- if (mHintSession) {
- gAPH_closeSessionFn(mHintSession);
- }
-}
-
-void DrawFrameTask::HintSessionWrapper::updateTargetWorkDuration(long targetDurationNanos) {
- if (mHintSession) {
- gAPH_updateTargetWorkDurationFn(mHintSession, targetDurationNanos);
- }
-}
-
-void DrawFrameTask::HintSessionWrapper::reportActualWorkDuration(long actualDurationNanos) {
- if (mHintSession) {
- gAPH_reportActualWorkDurationFn(mHintSession, actualDurationNanos);
- }
-}
-
-void DrawFrameTask::HintSessionWrapper::sendHint(SessionHint hint) {
- if (mHintSession && Properties::isDrawingEnabled()) {
- gAPH_sendHintFn(mHintSession, static_cast<int>(hint));
- }
-}
-
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index 7eae41c07e64..fafab24cbce7 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -16,7 +16,6 @@
#ifndef DRAWFRAMETASK_H
#define DRAWFRAMETASK_H
-#include <android/performance_hint.h>
#include <utils/Condition.h>
#include <utils/Mutex.h>
#include <utils/StrongPointer.h>
@@ -28,7 +27,6 @@
#include "../Rect.h"
#include "../TreeInfo.h"
#include "RenderTask.h"
-#include "utils/TimeUtils.h"
namespace android {
namespace uirenderer {
@@ -62,8 +60,7 @@ public:
DrawFrameTask();
virtual ~DrawFrameTask();
- void setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode,
- int32_t uiThreadId, int32_t renderThreadId);
+ void setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode);
void setContentDrawBounds(int left, int top, int right, int bottom) {
mContentDrawBounds.set(left, top, right, bottom);
}
@@ -91,22 +88,7 @@ public:
void forceDrawNextFrame() { mForceDrawFrame = true; }
- void sendLoadResetHint();
-
private:
- class HintSessionWrapper {
- public:
- HintSessionWrapper(int32_t uiThreadId, int32_t renderThreadId);
- ~HintSessionWrapper();
-
- void updateTargetWorkDuration(long targetDurationNanos);
- void reportActualWorkDuration(long actualDurationNanos);
- void sendHint(SessionHint hint);
-
- private:
- APerformanceHintSession* mHintSession = nullptr;
- };
-
void postAndWait();
bool syncFrameState(TreeInfo& info);
void unblockUiThread();
@@ -117,8 +99,6 @@ private:
RenderThread* mRenderThread;
CanvasContext* mContext;
RenderNode* mTargetNode = nullptr;
- int32_t mUiThreadId = -1;
- int32_t mRenderThreadId = -1;
Rect mContentDrawBounds;
/*********************************************
@@ -135,13 +115,6 @@ private:
std::function<void(bool)> mFrameCommitCallback;
std::function<void()> mFrameCompleteCallback;
- nsecs_t mLastDequeueBufferDuration = 0;
- nsecs_t mLastTargetWorkDuration = 0;
- std::optional<HintSessionWrapper> mHintSessionWrapper;
-
- nsecs_t mLastFrameNotification = 0;
- nsecs_t kResetHintTimeout = 100_ms;
-
bool mForceDrawFrame = false;
};
diff --git a/libs/hwui/renderthread/HintSessionWrapper.cpp b/libs/hwui/renderthread/HintSessionWrapper.cpp
new file mode 100644
index 000000000000..edacef04b50a
--- /dev/null
+++ b/libs/hwui/renderthread/HintSessionWrapper.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "HintSessionWrapper.h"
+
+#include <dlfcn.h>
+#include <utils/Log.h>
+
+#include <vector>
+
+#include "../Properties.h"
+#include "thread/CommonPool.h"
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+namespace {
+
+typedef APerformanceHintManager* (*APH_getManager)();
+typedef APerformanceHintSession* (*APH_createSession)(APerformanceHintManager*, const int32_t*,
+ size_t, int64_t);
+typedef void (*APH_closeSession)(APerformanceHintSession* session);
+typedef void (*APH_updateTargetWorkDuration)(APerformanceHintSession*, int64_t);
+typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_t);
+typedef void (*APH_sendHint)(APerformanceHintSession* session, int32_t);
+
+bool gAPerformanceHintBindingInitialized = false;
+APH_getManager gAPH_getManagerFn = nullptr;
+APH_createSession gAPH_createSessionFn = nullptr;
+APH_closeSession gAPH_closeSessionFn = nullptr;
+APH_updateTargetWorkDuration gAPH_updateTargetWorkDurationFn = nullptr;
+APH_reportActualWorkDuration gAPH_reportActualWorkDurationFn = nullptr;
+APH_sendHint gAPH_sendHintFn = nullptr;
+
+void ensureAPerformanceHintBindingInitialized() {
+ if (gAPerformanceHintBindingInitialized) return;
+
+ void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
+ LOG_ALWAYS_FATAL_IF(handle_ == nullptr, "Failed to dlopen libandroid.so!");
+
+ gAPH_getManagerFn = (APH_getManager)dlsym(handle_, "APerformanceHint_getManager");
+ LOG_ALWAYS_FATAL_IF(gAPH_getManagerFn == nullptr,
+ "Failed to find required symbol APerformanceHint_getManager!");
+
+ gAPH_createSessionFn = (APH_createSession)dlsym(handle_, "APerformanceHint_createSession");
+ LOG_ALWAYS_FATAL_IF(gAPH_createSessionFn == nullptr,
+ "Failed to find required symbol APerformanceHint_createSession!");
+
+ gAPH_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSession");
+ LOG_ALWAYS_FATAL_IF(gAPH_closeSessionFn == nullptr,
+ "Failed to find required symbol APerformanceHint_closeSession!");
+
+ gAPH_updateTargetWorkDurationFn = (APH_updateTargetWorkDuration)dlsym(
+ handle_, "APerformanceHint_updateTargetWorkDuration");
+ LOG_ALWAYS_FATAL_IF(
+ gAPH_updateTargetWorkDurationFn == nullptr,
+ "Failed to find required symbol APerformanceHint_updateTargetWorkDuration!");
+
+ gAPH_reportActualWorkDurationFn = (APH_reportActualWorkDuration)dlsym(
+ handle_, "APerformanceHint_reportActualWorkDuration");
+ LOG_ALWAYS_FATAL_IF(
+ gAPH_reportActualWorkDurationFn == nullptr,
+ "Failed to find required symbol APerformanceHint_reportActualWorkDuration!");
+
+ gAPH_sendHintFn = (APH_sendHint)dlsym(handle_, "APerformanceHint_sendHint");
+ LOG_ALWAYS_FATAL_IF(gAPH_sendHintFn == nullptr,
+ "Failed to find required symbol APerformanceHint_sendHint!");
+
+ gAPerformanceHintBindingInitialized = true;
+}
+
+} // namespace
+
+HintSessionWrapper::HintSessionWrapper(pid_t uiThreadId, pid_t renderThreadId)
+ : mUiThreadId(uiThreadId), mRenderThreadId(renderThreadId) {}
+
+HintSessionWrapper::~HintSessionWrapper() {
+ if (mHintSession) {
+ gAPH_closeSessionFn(mHintSession);
+ }
+}
+
+bool HintSessionWrapper::useHintSession() {
+ if (!Properties::useHintManager || !Properties::isDrawingEnabled()) return false;
+ if (mHintSession) return true;
+ // If session does not exist, create it;
+ // this defers session creation until we try to actually use it.
+ if (!mSessionValid) return false;
+ return init();
+}
+
+bool HintSessionWrapper::init() {
+ if (mUiThreadId < 0 || mRenderThreadId < 0) return false;
+
+ // Assume that if we return before the end, it broke
+ mSessionValid = false;
+
+ ensureAPerformanceHintBindingInitialized();
+
+ APerformanceHintManager* manager = gAPH_getManagerFn();
+ if (!manager) return false;
+
+ std::vector<pid_t> tids = CommonPool::getThreadIds();
+ tids.push_back(mUiThreadId);
+ tids.push_back(mRenderThreadId);
+
+ // Use a placeholder target value to initialize,
+ // this will always be replaced elsewhere before it gets used
+ int64_t defaultTargetDurationNanos = 16666667;
+ mHintSession =
+ gAPH_createSessionFn(manager, tids.data(), tids.size(), defaultTargetDurationNanos);
+
+ mSessionValid = !!mHintSession;
+ return mSessionValid;
+}
+
+void HintSessionWrapper::updateTargetWorkDuration(long targetWorkDurationNanos) {
+ if (!useHintSession()) return;
+ targetWorkDurationNanos = targetWorkDurationNanos * Properties::targetCpuTimePercentage / 100;
+ if (targetWorkDurationNanos != mLastTargetWorkDuration &&
+ targetWorkDurationNanos > kSanityCheckLowerBound &&
+ targetWorkDurationNanos < kSanityCheckUpperBound) {
+ mLastTargetWorkDuration = targetWorkDurationNanos;
+ gAPH_updateTargetWorkDurationFn(mHintSession, targetWorkDurationNanos);
+ }
+ mLastFrameNotification = systemTime();
+}
+
+void HintSessionWrapper::reportActualWorkDuration(long actualDurationNanos) {
+ if (!useHintSession()) return;
+ if (actualDurationNanos > kSanityCheckLowerBound &&
+ actualDurationNanos < kSanityCheckUpperBound) {
+ gAPH_reportActualWorkDurationFn(mHintSession, actualDurationNanos);
+ }
+}
+
+void HintSessionWrapper::sendLoadResetHint() {
+ if (!useHintSession()) return;
+ nsecs_t now = systemTime();
+ if (now - mLastFrameNotification > kResetHintTimeout) {
+ gAPH_sendHintFn(mHintSession, static_cast<int>(SessionHint::CPU_LOAD_RESET));
+ }
+ mLastFrameNotification = now;
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/HintSessionWrapper.h b/libs/hwui/renderthread/HintSessionWrapper.h
new file mode 100644
index 000000000000..fcbc10185255
--- /dev/null
+++ b/libs/hwui/renderthread/HintSessionWrapper.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/performance_hint.h>
+
+#include "utils/TimeUtils.h"
+
+namespace android {
+namespace uirenderer {
+
+namespace renderthread {
+
+class HintSessionWrapper {
+public:
+ HintSessionWrapper(pid_t uiThreadId, pid_t renderThreadId);
+ ~HintSessionWrapper();
+
+ void updateTargetWorkDuration(long targetDurationNanos);
+ void reportActualWorkDuration(long actualDurationNanos);
+ void sendLoadResetHint();
+
+private:
+ bool useHintSession();
+ bool init();
+ APerformanceHintSession* mHintSession = nullptr;
+
+ nsecs_t mLastFrameNotification = 0;
+ nsecs_t mLastTargetWorkDuration = 0;
+
+ pid_t mUiThreadId;
+ pid_t mRenderThreadId;
+
+ bool mSessionValid = true;
+
+ static constexpr nsecs_t kResetHintTimeout = 100_ms;
+ static constexpr int64_t kSanityCheckLowerBound = 100_us;
+ static constexpr int64_t kSanityCheckUpperBound = 10_s;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 03a2bc9f7e51..07f5a78fc1ec 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -42,11 +42,13 @@ namespace renderthread {
RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode,
IContextFactory* contextFactory)
: mRenderThread(RenderThread::getInstance()), mContext(nullptr) {
- mContext = mRenderThread.queue().runSync([&]() -> CanvasContext* {
- return CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory);
+ pid_t uiThreadId = pthread_gettid_np(pthread_self());
+ pid_t renderThreadId = getRenderThreadTid();
+ mContext = mRenderThread.queue().runSync([=, this]() -> CanvasContext* {
+ return CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory,
+ uiThreadId, renderThreadId);
});
- mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode,
- pthread_gettid_np(pthread_self()), getRenderThreadTid());
+ mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode);
}
RenderProxy::~RenderProxy() {
@@ -55,7 +57,7 @@ RenderProxy::~RenderProxy() {
void RenderProxy::destroyContext() {
if (mContext) {
- mDrawFrameTask.setContext(nullptr, nullptr, nullptr, -1, -1);
+ mDrawFrameTask.setContext(nullptr, nullptr, nullptr);
// This is also a fence as we need to be certain that there are no
// outstanding mDrawFrame tasks posted before it is destroyed
mRenderThread.queue().runSync([this]() { delete mContext; });
@@ -237,7 +239,7 @@ void RenderProxy::notifyFramePending() {
}
void RenderProxy::notifyCallbackPending() {
- mDrawFrameTask.sendLoadResetHint();
+ mRenderThread.queue().post([this]() { mContext->sendLoadResetHint(); });
}
void RenderProxy::dumpProfileInfo(int fd, int dumpFlags) {
diff --git a/libs/hwui/tests/common/CallCountingCanvas.h b/libs/hwui/tests/common/CallCountingCanvas.h
index d3c41191eef1..dc36a2e01815 100644
--- a/libs/hwui/tests/common/CallCountingCanvas.h
+++ b/libs/hwui/tests/common/CallCountingCanvas.h
@@ -19,6 +19,8 @@
#include <SkCanvasVirtualEnforcer.h>
#include <SkNoDrawCanvas.h>
+enum class SkBlendMode;
+
namespace android {
namespace uirenderer {
namespace test {
diff --git a/libs/hwui/tests/common/TestListViewSceneBase.cpp b/libs/hwui/tests/common/TestListViewSceneBase.cpp
index 43df4a0b1576..e70d44c9c60a 100644
--- a/libs/hwui/tests/common/TestListViewSceneBase.cpp
+++ b/libs/hwui/tests/common/TestListViewSceneBase.cpp
@@ -19,6 +19,8 @@
#include "TestContext.h"
#include "TestUtils.h"
+#include <SkBlendMode.h>
+
#include <utils/Color.h>
namespace android {
diff --git a/libs/hwui/tests/common/scenes/BitmapFillrate.cpp b/libs/hwui/tests/common/scenes/BitmapFillrate.cpp
index 5af7d43d7f66..19e87f851827 100644
--- a/libs/hwui/tests/common/scenes/BitmapFillrate.cpp
+++ b/libs/hwui/tests/common/scenes/BitmapFillrate.cpp
@@ -19,6 +19,7 @@
#include "utils/Color.h"
#include <SkBitmap.h>
+#include <SkBlendMode.h>
using namespace android;
using namespace android::uirenderer;
diff --git a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
index 2a016ac1b5bc..3a1ea8c29963 100644
--- a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
class ClippingAnimation;
static TestScene::Registrar _RectGrid(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
index 4271d2f04b88..484289a8ef1d 100644
--- a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
@@ -20,6 +20,8 @@
#include <hwui/Paint.h>
#include <minikin/Layout.h>
+#include <SkBlendMode.h>
+
#include <cstdio>
class GlyphStressAnimation;
diff --git a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
index 0d5ca6df9ff3..dfdd0d8727b9 100644
--- a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
+++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
@@ -17,6 +17,7 @@
#include "TestSceneBase.h"
#include "utils/Color.h"
+#include <SkBlendMode.h>
#include <SkColorSpace.h>
#include <SkGradientShader.h>
#include <SkImagePriv.h>
diff --git a/libs/hwui/tests/common/scenes/HwLayerAnimation.cpp b/libs/hwui/tests/common/scenes/HwLayerAnimation.cpp
index cac2fb3d8d5c..2955fb25ec2c 100644
--- a/libs/hwui/tests/common/scenes/HwLayerAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/HwLayerAnimation.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
class HwLayerAnimation;
static TestScene::Registrar _HwLayer(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/HwLayerSizeAnimation.cpp b/libs/hwui/tests/common/scenes/HwLayerSizeAnimation.cpp
index 77a59dfe6ba5..8c9a6147f47d 100644
--- a/libs/hwui/tests/common/scenes/HwLayerSizeAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/HwLayerSizeAnimation.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
class HwLayerSizeAnimation;
static TestScene::Registrar _HwLayerSize(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/JankyScene.cpp b/libs/hwui/tests/common/scenes/JankyScene.cpp
index f5e6b317529a..250b986e7e73 100644
--- a/libs/hwui/tests/common/scenes/JankyScene.cpp
+++ b/libs/hwui/tests/common/scenes/JankyScene.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
#include <unistd.h>
class JankyScene;
diff --git a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp
index 5eaf1853233a..f669dbc9323e 100644
--- a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp
@@ -17,6 +17,7 @@
#include "TestSceneBase.h"
#include "tests/common/TestListViewSceneBase.h"
#include "hwui/Paint.h"
+#include <SkBlendMode.h>
#include <SkGradientShader.h>
class ListOfFadedTextAnimation;
diff --git a/libs/hwui/tests/common/scenes/OvalAnimation.cpp b/libs/hwui/tests/common/scenes/OvalAnimation.cpp
index 402c1ece2146..1a2af8382ad7 100644
--- a/libs/hwui/tests/common/scenes/OvalAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/OvalAnimation.cpp
@@ -17,6 +17,8 @@
#include "TestSceneBase.h"
#include "utils/Color.h"
+#include <SkBlendMode.h>
+
class OvalAnimation;
static TestScene::Registrar _Oval(TestScene::Info{"oval", "Draws 1 oval.",
diff --git a/libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp b/libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp
index fb1b000a995e..25cf4d61bf9d 100644
--- a/libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
class PartialDamageAnimation;
static TestScene::Registrar _PartialDamage(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/PathClippingAnimation.cpp b/libs/hwui/tests/common/scenes/PathClippingAnimation.cpp
index 1e343c1dd283..969514c50d14 100644
--- a/libs/hwui/tests/common/scenes/PathClippingAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/PathClippingAnimation.cpp
@@ -16,6 +16,8 @@
#include <vector>
+#include <SkBlendMode.h>
+
#include "TestSceneBase.h"
class PathClippingAnimation : public TestScene {
diff --git a/libs/hwui/tests/common/scenes/RectGridAnimation.cpp b/libs/hwui/tests/common/scenes/RectGridAnimation.cpp
index f37bcbc3ee1b..99e785887b16 100644
--- a/libs/hwui/tests/common/scenes/RectGridAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/RectGridAnimation.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
class RectGridAnimation;
static TestScene::Registrar _RectGrid(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp b/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp
index e9f353d887f2..2c27969487d3 100644
--- a/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
#include <vector>
class RoundRectClippingAnimation : public TestScene {
diff --git a/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp b/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp
index 252f539ffca9..ee30c131efbd 100644
--- a/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp
+++ b/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp
@@ -16,6 +16,7 @@
#include <hwui/Paint.h>
#include <minikin/Layout.h>
+#include <SkBlendMode.h>
#include <string>
#include "TestSceneBase.h"
diff --git a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
index 31a8ae1d38cd..d5060c758f93 100644
--- a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
class SaveLayerAnimation;
static TestScene::Registrar _SaveLayer(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp b/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp
index c13e80e8c204..827ddab118d9 100644
--- a/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp
+++ b/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
class ShadowGrid2Animation;
static TestScene::Registrar _ShadowGrid2(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp b/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp
index 772b98e32220..a4fb10c5081e 100644
--- a/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
class ShadowGridAnimation;
static TestScene::Registrar _ShadowGrid(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp b/libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp
index 0019da5fd80b..58c03727bc29 100644
--- a/libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
class ShadowShaderAnimation;
static TestScene::Registrar _ShadowShader(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
index 70a1557dcf6a..c0c3dfd9a8c4 100644
--- a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
@@ -17,6 +17,8 @@
#include "TestSceneBase.h"
#include "utils/Color.h"
+#include <SkBlendMode.h>
+
#include <cstdio>
class ShapeAnimation;
diff --git a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp
index 2aeb42cc0e20..40f2ed081626 100644
--- a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp
@@ -16,6 +16,7 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
#include <SkColorFilter.h>
#include <SkColorMatrix.h>
#include <SkGradientShader.h>
diff --git a/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp b/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp
index 57a260c8d234..a9e7a34b5b3f 100644
--- a/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp
@@ -16,6 +16,7 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
#include <SkGradientShader.h>
class SimpleGradientAnimation;
diff --git a/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp b/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp
index 7d3ca9642458..bb95490c1d39 100644
--- a/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp
@@ -15,6 +15,7 @@
*/
#include <SkBitmap.h>
+#include <SkBlendMode.h>
#include <SkCanvas.h>
#include <SkColor.h>
#include <SkFont.h>
diff --git a/libs/hwui/tests/common/scenes/TextAnimation.cpp b/libs/hwui/tests/common/scenes/TextAnimation.cpp
index d30903679bce..78146b8cabf2 100644
--- a/libs/hwui/tests/common/scenes/TextAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/TextAnimation.cpp
@@ -17,6 +17,8 @@
#include "TestSceneBase.h"
#include "hwui/Paint.h"
+#include <SkBlendMode.h>
+
class TextAnimation;
static TestScene::Registrar _Text(TestScene::Info{"text", "Draws a bunch of text.",
diff --git a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
index 9cd10759a834..a55b72534924 100644
--- a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
+++ b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
@@ -22,6 +22,8 @@
#include "pipeline/skia/SkiaDisplayList.h"
#include "tests/common/TestUtils.h"
+#include <SkBlendMode.h>
+
using namespace android;
using namespace android::uirenderer;
using namespace android::uirenderer::skiapipeline;
diff --git a/libs/hwui/tests/microbench/RenderNodeBench.cpp b/libs/hwui/tests/microbench/RenderNodeBench.cpp
index 6aed251481bf..72946c4abdf0 100644
--- a/libs/hwui/tests/microbench/RenderNodeBench.cpp
+++ b/libs/hwui/tests/microbench/RenderNodeBench.cpp
@@ -19,6 +19,8 @@
#include "hwui/Canvas.h"
#include "RenderNode.h"
+#include <SkBlendMode.h>
+
using namespace android;
using namespace android::uirenderer;
diff --git a/libs/hwui/tests/unit/CanvasContextTests.cpp b/libs/hwui/tests/unit/CanvasContextTests.cpp
index 1771c3590e10..88420a5d5c23 100644
--- a/libs/hwui/tests/unit/CanvasContextTests.cpp
+++ b/libs/hwui/tests/unit/CanvasContextTests.cpp
@@ -36,7 +36,7 @@ RENDERTHREAD_TEST(CanvasContext, create) {
auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0));
ASSERT_FALSE(canvasContext->hasSurface());
diff --git a/libs/hwui/tests/unit/CanvasOpTests.cpp b/libs/hwui/tests/unit/CanvasOpTests.cpp
index d2b1ef91a898..1f6edf36af25 100644
--- a/libs/hwui/tests/unit/CanvasOpTests.cpp
+++ b/libs/hwui/tests/unit/CanvasOpTests.cpp
@@ -23,6 +23,7 @@
#include <tests/common/CallCountingCanvas.h>
+#include "SkBlendMode.h"
#include "SkBitmap.h"
#include "SkCanvas.h"
#include "SkColor.h"
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index ec949b80ea55..596bd37e4cf5 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -17,6 +17,7 @@
#include <VectorDrawable.h>
#include <gtest/gtest.h>
+#include <SkBlendMode.h>
#include <SkClipStack.h>
#include <SkSurface_Base.h>
#include <string.h>
@@ -334,7 +335,7 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) {
"A");
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0));
TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
DamageAccumulator damageAccumulator;
info.damageAccumulator = &damageAccumulator;
@@ -398,7 +399,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, emptyReceiver) {
"A");
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0));
TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
DamageAccumulator damageAccumulator;
info.damageAccumulator = &damageAccumulator;
@@ -518,7 +519,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, projectionHwLayer) {
// prepareTree is required to find, which receivers have backward projected nodes
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0));
TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
DamageAccumulator damageAccumulator;
info.damageAccumulator = &damageAccumulator;
@@ -618,7 +619,7 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionChildScroll) {
// prepareTree is required to find, which receivers have backward projected nodes
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0));
TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
DamageAccumulator damageAccumulator;
info.damageAccumulator = &damageAccumulator;
@@ -634,7 +635,7 @@ namespace {
static int drawNode(RenderThread& renderThread, const sp<RenderNode>& renderNode) {
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, renderNode.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, renderNode.get(), &contextFactory, 0, 0));
TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
DamageAccumulator damageAccumulator;
info.damageAccumulator = &damageAccumulator;
diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp
index 61bd646b0a76..80796f4a4111 100644
--- a/libs/hwui/tests/unit/RenderNodeTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeTests.cpp
@@ -274,7 +274,7 @@ RENDERTHREAD_TEST(RenderNode, prepareTree_nullableDisplayList) {
auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0));
TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
DamageAccumulator damageAccumulator;
info.damageAccumulator = &damageAccumulator;
@@ -310,7 +310,7 @@ RENDERTHREAD_TEST(DISABLED_RenderNode, prepareTree_HwLayer_AVD_enqueueDamage) {
});
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0));
canvasContext->setSurface(nullptr);
TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
DamageAccumulator damageAccumulator;
diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
index 50d9f5683a8b..87c52161d68e 100644
--- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp
+++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
@@ -17,10 +17,19 @@
#include "tests/common/TestUtils.h"
#include <hwui/Paint.h>
+#include <SkAlphaType.h>
+#include <SkBitmap.h>
+#include <SkBlendMode.h>
+#include <SkCanvas.h>
#include <SkCanvasStateUtils.h>
+#include <SkColor.h>
#include <SkColorSpace.h>
+#include <SkColorType.h>
+#include <SkImageInfo.h>
#include <SkPicture.h>
#include <SkPictureRecorder.h>
+#include <SkRefCnt.h>
+#include <SkSurface.h>
#include <gtest/gtest.h>
using namespace android;
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index 3d5aca4bf05a..f825d7c5d9cc 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -142,7 +142,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren) {
auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0));
TreeInfo info(TreeInfo::MODE_FULL, *canvasContext.get());
DamageAccumulator damageAccumulator;
info.damageAccumulator = &damageAccumulator;
@@ -201,7 +201,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren_vdOffscr
auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0));
// Set up a Surface so that we can position the VectorDrawable offscreen.
test::TestContext testContext;
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 7419f8fd89f1..4d0595e03da6 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -17,6 +17,7 @@
#include <VectorDrawable.h>
#include <gtest/gtest.h>
+#include <SkBlendMode.h>
#include <SkClipStack.h>
#include <SkSurface_Base.h>
#include <string.h>
diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h
index 94bcb1110e05..f44f9d0fe2d4 100644
--- a/libs/hwui/utils/PaintUtils.h
+++ b/libs/hwui/utils/PaintUtils.h
@@ -19,6 +19,7 @@
#include <GLES2/gl2.h>
#include <utils/Blur.h>
+#include <SkBlendMode.h>
#include <SkColorFilter.h>
#include <SkPaint.h>
#include <SkShader.h>
diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp
index 0e7b7ff4319d..a83516791f33 100644
--- a/libs/input/MouseCursorController.cpp
+++ b/libs/input/MouseCursorController.cpp
@@ -199,8 +199,7 @@ static void getNonRotatedSize(const DisplayViewport& viewport, int32_t& width, i
width = viewport.deviceWidth;
height = viewport.deviceHeight;
- if (viewport.orientation == DISPLAY_ORIENTATION_90 ||
- viewport.orientation == DISPLAY_ORIENTATION_270) {
+ if (viewport.orientation == ui::ROTATION_90 || viewport.orientation == ui::ROTATION_270) {
std::swap(width, height);
}
}
@@ -244,38 +243,42 @@ void MouseCursorController::setDisplayViewport(const DisplayViewport& viewport,
// Undo the previous rotation.
switch (oldViewport.orientation) {
- case DISPLAY_ORIENTATION_90:
+ case ui::ROTATION_90:
temp = x;
x = oldViewport.deviceHeight - y;
y = temp;
break;
- case DISPLAY_ORIENTATION_180:
+ case ui::ROTATION_180:
x = oldViewport.deviceWidth - x;
y = oldViewport.deviceHeight - y;
break;
- case DISPLAY_ORIENTATION_270:
+ case ui::ROTATION_270:
temp = x;
x = y;
y = oldViewport.deviceWidth - temp;
break;
+ case ui::ROTATION_0:
+ break;
}
// Perform the new rotation.
switch (viewport.orientation) {
- case DISPLAY_ORIENTATION_90:
+ case ui::ROTATION_90:
temp = x;
x = y;
y = viewport.deviceHeight - temp;
break;
- case DISPLAY_ORIENTATION_180:
+ case ui::ROTATION_180:
x = viewport.deviceWidth - x;
y = viewport.deviceHeight - y;
break;
- case DISPLAY_ORIENTATION_270:
+ case ui::ROTATION_270:
temp = x;
x = viewport.deviceWidth - y;
y = temp;
break;
+ case ui::ROTATION_0:
+ break;
}
// Apply offsets to convert from the pixel center to the pixel top-left corner position
diff --git a/media/java/android/media/RouteListingPreference.java b/media/java/android/media/RouteListingPreference.java
index 26b190b9fd81..62f233e42989 100644
--- a/media/java/android/media/RouteListingPreference.java
+++ b/media/java/android/media/RouteListingPreference.java
@@ -16,6 +16,7 @@
package android.media;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
@@ -23,6 +24,8 @@ import android.text.TextUtils;
import com.android.internal.util.Preconditions;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -111,6 +114,45 @@ public final class RouteListingPreference implements Parcelable {
/** Holds preference information for a specific route in a media routing listing. */
public static final class Item implements Parcelable {
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ flag = true,
+ prefix = {"FLAG_"},
+ value = {FLAG_ONGOING_SESSION, FLAG_SUGGESTED_ROUTE})
+ public @interface Flags {}
+
+ /**
+ * The corresponding route is already hosting a session with the app that owns this listing
+ * preference.
+ */
+ public static final int FLAG_ONGOING_SESSION = 1;
+
+ /**
+ * The corresponding route is specially likely to be selected by the user.
+ *
+ * <p>A UI reflecting this preference may reserve a specific space for suggested routes,
+ * making it more accessible to the user. If the number of suggested routes exceeds the
+ * number supported by the UI, the routes listed first in {@link
+ * RouteListingPreference#getItems()} will take priority.
+ */
+ public static final int FLAG_SUGGESTED_ROUTE = 1 << 1;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = {"DISABLE_REASON_"},
+ value = {DISABLE_REASON_NONE, DISABLE_REASON_SUBSCRIPTION_REQUIRED})
+ public @interface DisableReason {}
+
+ /** The corresponding route is available for routing. */
+ public static final int DISABLE_REASON_NONE = 0;
+ /**
+ * The corresponding route requires a special subscription in order to be available for
+ * routing.
+ */
+ public static final int DISABLE_REASON_SUBSCRIPTION_REQUIRED = 1;
+
@NonNull
public static final Creator<Item> CREATOR =
new Creator<>() {
@@ -126,21 +168,29 @@ public final class RouteListingPreference implements Parcelable {
};
@NonNull private final String mRouteId;
+ @Flags private final int mFlags;
+ @DisableReason private final int mDisableReason;
/**
* Creates an instance with the given value.
*
* @param routeId See {@link #getRouteId()}. Must not be empty.
+ * @param flags See {@link #getFlags()}.
+ * @param disableReason See {@link #getDisableReason()}.
*/
- public Item(@NonNull String routeId) {
+ public Item(@NonNull String routeId, @Flags int flags, @DisableReason int disableReason) {
Preconditions.checkArgument(!TextUtils.isEmpty(routeId));
mRouteId = routeId;
+ mFlags = flags;
+ mDisableReason = disableReason;
}
private Item(Parcel in) {
String routeId = in.readString();
Preconditions.checkArgument(!TextUtils.isEmpty(routeId));
mRouteId = routeId;
+ mFlags = in.readInt();
+ mDisableReason = in.readInt();
}
/** Returns the id of the route that corresponds to this route listing preference item. */
@@ -149,6 +199,29 @@ public final class RouteListingPreference implements Parcelable {
return mRouteId;
}
+ /**
+ * Returns the flags associated to the route that corresponds to this item.
+ *
+ * @see #FLAG_ONGOING_SESSION
+ * @see #FLAG_SUGGESTED_ROUTE
+ */
+ @Flags
+ public int getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * Returns the reason for the corresponding route to be disabled, or {@link
+ * #DISABLE_REASON_NONE} if the route is not disabled.
+ *
+ * @see #DISABLE_REASON_NONE
+ * @see #DISABLE_REASON_SUBSCRIPTION_REQUIRED
+ */
+ @DisableReason
+ public int getDisableReason() {
+ return mDisableReason;
+ }
+
// Item Parcelable implementation.
@Override
@@ -159,6 +232,8 @@ public final class RouteListingPreference implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(mRouteId);
+ dest.writeInt(mFlags);
+ dest.writeInt(mDisableReason);
}
// Equals and hashCode.
@@ -172,12 +247,14 @@ public final class RouteListingPreference implements Parcelable {
return false;
}
Item item = (Item) other;
- return mRouteId.equals(item.mRouteId);
+ return mRouteId.equals(item.mRouteId)
+ && mFlags == item.mFlags
+ && mDisableReason == item.mDisableReason;
}
@Override
public int hashCode() {
- return Objects.hash(mRouteId);
+ return Objects.hash(mRouteId, mFlags, mDisableReason);
}
}
}
diff --git a/media/java/android/media/tv/tuner/filter/AvSettings.java b/media/java/android/media/tv/tuner/filter/AvSettings.java
index 15811d2c4f11..9144087058a7 100644
--- a/media/java/android/media/tv/tuner/filter/AvSettings.java
+++ b/media/java/android/media/tv/tuner/filter/AvSettings.java
@@ -38,7 +38,8 @@ public class AvSettings extends Settings {
VIDEO_STREAM_TYPE_MPEG1, VIDEO_STREAM_TYPE_MPEG2,
VIDEO_STREAM_TYPE_MPEG4P2, VIDEO_STREAM_TYPE_AVC, VIDEO_STREAM_TYPE_HEVC,
VIDEO_STREAM_TYPE_VC1, VIDEO_STREAM_TYPE_VP8, VIDEO_STREAM_TYPE_VP9,
- VIDEO_STREAM_TYPE_AV1, VIDEO_STREAM_TYPE_AVS, VIDEO_STREAM_TYPE_AVS2})
+ VIDEO_STREAM_TYPE_AV1, VIDEO_STREAM_TYPE_AVS, VIDEO_STREAM_TYPE_AVS2,
+ VIDEO_STREAM_TYPE_VVC})
@Retention(RetentionPolicy.SOURCE)
public @interface VideoStreamType {}
@@ -76,6 +77,10 @@ public class AvSettings extends Settings {
*/
public static final int VIDEO_STREAM_TYPE_HEVC = android.hardware.tv.tuner.VideoStreamType.HEVC;
/*
+ * ITU-T Rec. H.266 and ISO/IEC 23090-3
+ */
+ public static final int VIDEO_STREAM_TYPE_VVC = android.hardware.tv.tuner.VideoStreamType.VVC;
+ /*
* Microsoft VC.1
*/
public static final int VIDEO_STREAM_TYPE_VC1 = android.hardware.tv.tuner.VideoStreamType.VC1;
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index d0973f4eb11f..8568c4349dc4 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -349,6 +349,15 @@ public class Filter implements AutoCloseable {
+ mMainType + ", filter subtype=" + mSubtype + ". config main type="
+ config.getType() + ", config subtype=" + subType);
}
+ // Tuner only support VVC after tuner 3.0
+ if (s instanceof RecordSettings
+ && ((RecordSettings) s).getScIndexType() == RecordSettings.INDEX_TYPE_SC_VVC
+ && !TunerVersionChecker.isHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_3_0)) {
+ Log.e(TAG, "Tuner version " + TunerVersionChecker.getTunerVersion()
+ + " does not support VVC");
+ return Tuner.RESULT_UNAVAILABLE;
+ }
return nativeConfigureFilter(config.getType(), subType, config);
}
}
diff --git a/media/java/android/media/tv/tuner/filter/RecordSettings.java b/media/java/android/media/tv/tuner/filter/RecordSettings.java
index b16d9fb247b7..698bbba2798e 100644
--- a/media/java/android/media/tv/tuner/filter/RecordSettings.java
+++ b/media/java/android/media/tv/tuner/filter/RecordSettings.java
@@ -23,8 +23,10 @@ import android.hardware.tv.tuner.DemuxRecordScIndexType;
import android.hardware.tv.tuner.DemuxScAvcIndex;
import android.hardware.tv.tuner.DemuxScHevcIndex;
import android.hardware.tv.tuner.DemuxScIndex;
+import android.hardware.tv.tuner.DemuxScVvcIndex;
import android.hardware.tv.tuner.DemuxTsIndex;
import android.media.tv.tuner.TunerUtils;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -138,7 +140,8 @@ public class RecordSettings extends Settings {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = "INDEX_TYPE_", value =
- {INDEX_TYPE_NONE, INDEX_TYPE_SC, INDEX_TYPE_SC_HEVC, INDEX_TYPE_SC_AVC})
+ {INDEX_TYPE_NONE, INDEX_TYPE_SC, INDEX_TYPE_SC_HEVC, INDEX_TYPE_SC_AVC,
+ INDEX_TYPE_SC_VVC})
public @interface ScIndexType {}
/**
@@ -157,6 +160,10 @@ public class RecordSettings extends Settings {
* Start Code index for AVC.
*/
public static final int INDEX_TYPE_SC_AVC = DemuxRecordScIndexType.SC_AVC;
+ /**
+ * Start Code index for VVC.
+ */
+ public static final int INDEX_TYPE_SC_VVC = DemuxRecordScIndexType.SC_VVC;
/**
* Indexes can be tagged by Start Code in PES (Packetized Elementary Stream)
@@ -253,6 +260,46 @@ public class RecordSettings extends Settings {
public static final int SC_HEVC_INDEX_SLICE_TRAIL_CRA = DemuxScHevcIndex.SLICE_TRAIL_CRA;
/**
+ * Indexes can be tagged by NAL unit group in VVC according to ISO/IEC 23090-3.
+ *
+ * @hide
+ */
+ @IntDef(value = {SC_VVC_INDEX_SLICE_IDR_W_RADL, SC_VVC_INDEX_SLICE_IDR_N_LP,
+ SC_VVC_INDEX_SLICE_CRA, SC_VVC_INDEX_SLICE_GDR, SC_VVC_INDEX_VPS, SC_VVC_INDEX_SPS,
+ SC_VVC_INDEX_AUD})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ScVvcIndex{}
+
+ /**
+ * SC VVC index SLICE_IDR_W_RADL (nal_unit_type=IDR_W_RADL) for random access key frame.
+ */
+ public static final int SC_VVC_INDEX_SLICE_IDR_W_RADL = DemuxScVvcIndex.SLICE_IDR_W_RADL;
+ /**
+ * SC VVC index SLICE_IDR_N_LP (nal_unit_type=IDR_N_LP) for random access key frame.
+ */
+ public static final int SC_VVC_INDEX_SLICE_IDR_N_LP = DemuxScVvcIndex.SLICE_IDR_N_LP;
+ /**
+ * SC VVC index SLICE_CRA (nal_unit_type=CRA_NUT) for random access key frame.
+ */
+ public static final int SC_VVC_INDEX_SLICE_CRA = DemuxScVvcIndex.SLICE_CRA;
+ /**
+ * SC VVC index SLICE_GDR (nal_unit_type=GDR_NUT) for random access point.
+ */
+ public static final int SC_VVC_INDEX_SLICE_GDR = DemuxScVvcIndex.SLICE_GDR;
+ /**
+ * Optional SC VVC index VPS (nal_unit_type=VPS_NUT) for sequence level info.
+ */
+ public static final int SC_VVC_INDEX_VPS = DemuxScVvcIndex.VPS;
+ /**
+ * SC VVC index SPS (nal_unit_type=SPS_NUT) for sequence level info.
+ */
+ public static final int SC_VVC_INDEX_SPS = DemuxScVvcIndex.SPS;
+ /**
+ * SC VVC index AUD (nal_unit_type=AUD_NUT) for AU (frame) boundary.
+ */
+ public static final int SC_VVC_INDEX_AUD = DemuxScVvcIndex.AUD;
+
+ /**
* @hide
*/
@IntDef(prefix = "SC_",
@@ -261,6 +308,11 @@ public class RecordSettings extends Settings {
SC_INDEX_P_FRAME,
SC_INDEX_B_FRAME,
SC_INDEX_SEQUENCE,
+ SC_INDEX_I_SLICE,
+ SC_INDEX_P_SLICE,
+ SC_INDEX_B_SLICE,
+ SC_INDEX_SI_SLICE,
+ SC_INDEX_SP_SLICE,
SC_HEVC_INDEX_SPS,
SC_HEVC_INDEX_AUD,
SC_HEVC_INDEX_SLICE_CE_BLA_W_LP,
@@ -269,6 +321,13 @@ public class RecordSettings extends Settings {
SC_HEVC_INDEX_SLICE_IDR_W_RADL,
SC_HEVC_INDEX_SLICE_IDR_N_LP,
SC_HEVC_INDEX_SLICE_TRAIL_CRA,
+ SC_VVC_INDEX_SLICE_IDR_W_RADL,
+ SC_VVC_INDEX_SLICE_IDR_N_LP,
+ SC_VVC_INDEX_SLICE_CRA,
+ SC_VVC_INDEX_SLICE_GDR,
+ SC_VVC_INDEX_VPS,
+ SC_VVC_INDEX_SPS,
+ SC_VVC_INDEX_AUD
})
@Retention(RetentionPolicy.SOURCE)
public @interface ScIndexMask {}
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 2afa4d1fdefe..58078cf39227 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -661,6 +661,8 @@ void FilterClientCallbackImpl::getMediaEvent(jobjectArray &arr, const int size,
sc = mediaEvent.scIndexMask.get<DemuxFilterScIndexMask::Tag::scAvc>();
// Java uses the values defined by HIDL HAL. Left shift 4 bits.
sc = sc << 4;
+ } else if (mediaEvent.scIndexMask.getTag() == DemuxFilterScIndexMask::Tag::scVvc) {
+ sc = mediaEvent.scIndexMask.get<DemuxFilterScIndexMask::Tag::scVvc>();
}
jobject obj = env->NewObject(eventClazz, eventInit, streamId, isPtsPresent, pts, isDtsPresent,
@@ -726,6 +728,8 @@ void FilterClientCallbackImpl::getTsRecordEvent(jobjectArray &arr, const int siz
sc = tsRecordEvent.scIndexMask.get<DemuxFilterScIndexMask::Tag::scAvc>();
// Java uses the values defined by HIDL HAL. Left shift 4 bits.
sc = sc << 4;
+ } else if (tsRecordEvent.scIndexMask.getTag() == DemuxFilterScIndexMask::Tag::scVvc) {
+ sc = tsRecordEvent.scIndexMask.get<DemuxFilterScIndexMask::Tag::scVvc>();
}
jint ts = tsRecordEvent.tsIndexMask;
@@ -3819,6 +3823,8 @@ static DemuxFilterRecordSettings getFilterRecordSettings(JNIEnv *env, const jobj
} else if (scIndexType == DemuxRecordScIndexType::SC_AVC) {
// Java uses the values defined by HIDL HAL. Right shift 4 bits.
filterRecordSettings.scIndexMask.set<DemuxFilterScIndexMask::Tag::scAvc>(scIndexMask >> 4);
+ } else if (scIndexType == DemuxRecordScIndexType::SC_VVC) {
+ filterRecordSettings.scIndexMask.set<DemuxFilterScIndexMask::Tag::scVvc>(scIndexMask);
}
return filterRecordSettings;
}
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index 7863a7dba1a7..40eb507a5213 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -71,8 +71,10 @@ private:
const int64_t mPreferredRateNanos;
// Target duration for choosing update rate
int64_t mTargetDurationNanos;
- // Last update timestamp
- int64_t mLastUpdateTimestamp;
+ // First target hit timestamp
+ int64_t mFirstTargetMetTimestamp;
+ // Last target hit timestamp
+ int64_t mLastTargetMetTimestamp;
// Cached samples
std::vector<int64_t> mActualDurationsNanos;
std::vector<int64_t> mTimestampsNanos;
@@ -144,7 +146,8 @@ APerformanceHintSession::APerformanceHintSession(sp<IHintSession> session,
: mHintSession(std::move(session)),
mPreferredRateNanos(preferredRateNanos),
mTargetDurationNanos(targetDurationNanos),
- mLastUpdateTimestamp(elapsedRealtimeNano()) {}
+ mFirstTargetMetTimestamp(0),
+ mLastTargetMetTimestamp(0) {}
APerformanceHintSession::~APerformanceHintSession() {
binder::Status ret = mHintSession->close();
@@ -171,7 +174,8 @@ int APerformanceHintSession::updateTargetWorkDuration(int64_t targetDurationNano
*/
mActualDurationsNanos.clear();
mTimestampsNanos.clear();
- mLastUpdateTimestamp = elapsedRealtimeNano();
+ mFirstTargetMetTimestamp = 0;
+ mLastTargetMetTimestamp = 0;
return 0;
}
@@ -184,25 +188,38 @@ int APerformanceHintSession::reportActualWorkDuration(int64_t actualDurationNano
mActualDurationsNanos.push_back(actualDurationNanos);
mTimestampsNanos.push_back(now);
- /**
- * Cache the hint if the hint is not overtime and the mLastUpdateTimestamp is
- * still in the mPreferredRateNanos duration.
- */
- if (actualDurationNanos < mTargetDurationNanos &&
- now - mLastUpdateTimestamp <= mPreferredRateNanos) {
- return 0;
+ if (actualDurationNanos >= mTargetDurationNanos) {
+ // Reset timestamps if we are equal or over the target.
+ mFirstTargetMetTimestamp = 0;
+ } else {
+ // Set mFirstTargetMetTimestamp for first time meeting target.
+ if (!mFirstTargetMetTimestamp || !mLastTargetMetTimestamp ||
+ (now - mLastTargetMetTimestamp > 2 * mPreferredRateNanos)) {
+ mFirstTargetMetTimestamp = now;
+ }
+ /**
+ * Rate limit the change if the update is over mPreferredRateNanos since first
+ * meeting target and less than mPreferredRateNanos since last meeting target.
+ */
+ if (now - mFirstTargetMetTimestamp > mPreferredRateNanos &&
+ now - mLastTargetMetTimestamp <= mPreferredRateNanos) {
+ return 0;
+ }
+ mLastTargetMetTimestamp = now;
}
binder::Status ret =
mHintSession->reportActualWorkDuration(mActualDurationsNanos, mTimestampsNanos);
- mActualDurationsNanos.clear();
- mTimestampsNanos.clear();
if (!ret.isOk()) {
ALOGE("%s: HintSession reportActualWorkDuration failed: %s", __FUNCTION__,
ret.exceptionMessage().c_str());
+ mFirstTargetMetTimestamp = 0;
+ mLastTargetMetTimestamp = 0;
return EPIPE;
}
- mLastUpdateTimestamp = now;
+ mActualDurationsNanos.clear();
+ mTimestampsNanos.clear();
+
return 0;
}
diff --git a/packages/CredentialManager/res/values-af/strings.xml b/packages/CredentialManager/res/values-af/strings.xml
new file mode 100644
index 000000000000..91771b3ab287
--- /dev/null
+++ b/packages/CredentialManager/res/values-af/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Kanselleer"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Gaan voort"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Skep op ’n ander plek"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Stoor in ’n ander plek"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Gebruik ’n ander toestel"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Stoor op ’n ander toestel"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"’n Maklike manier om veilig aan te meld"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Gebruik jou vingerafdruk, gesig of skermslot om aan te meld met ’n unieke wagwoordsleutel wat nie vergeet of gesteel kan word nie. Kom meer te wete"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Kies waar om <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"stoor jou wagwoord"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"stoor jou aanmeldinligting"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Skep ’n wagwoordsleutel in <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Stoor jou wagwoord in <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Stoor jou aanmeldinligting in <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Jy kan jou <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> op enige toestel gebruik. Dit is in <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> gestoor vir <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"wagwoordsleutel"</string>
+ <string name="password" msgid="6738570945182936667">"wagwoord"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"aanmeldings"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Gebruik <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> vir al jou aanmeldings?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Stel as verstek"</string>
+ <string name="use_once" msgid="9027366575315399714">"Gebruik een keer"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> wagwoorde, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> wagwoordsleutels"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> wagwoorde"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> wagwoordsleutels"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"’n Ander toestel"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Ander wagwoordbestuurders"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Maak sigblad toe"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Gaan terug na die vorige bladsy"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Gebruik jou gestoorde wagwoordsleutel vir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Gebruik jou gestoorde aanmelding vir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Kies ’n gestoorde aanmelding vir <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Meld op ’n ander manier aan"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Nee, dankie"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Gaan voort"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Aanmeldopsies"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Vir <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Geslote wagwoordbestuurders"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Tik om te ontsluit"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Bestuur aanmeldings"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Van ’n ander toestel af"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Gebruik ’n ander toestel"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-am/strings.xml b/packages/CredentialManager/res/values-am/strings.xml
new file mode 100644
index 000000000000..e77b1a788ea4
--- /dev/null
+++ b/packages/CredentialManager/res/values-am/strings.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <!-- no translation found for string_cancel (6369133483981306063) -->
+ <skip />
+ <!-- no translation found for string_continue (1346732695941131882) -->
+ <skip />
+ <!-- no translation found for string_create_in_another_place (1033635365843437603) -->
+ <skip />
+ <!-- no translation found for string_save_to_another_place (7590325934591079193) -->
+ <skip />
+ <!-- no translation found for string_use_another_device (8754514926121520445) -->
+ <skip />
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"ወደ ሌላ መሣሪያ ያስቀምጡ"</string>
+ <!-- no translation found for passkey_creation_intro_title (402553911484409884) -->
+ <skip />
+ <!-- no translation found for passkey_creation_intro_body (7493320456005579290) -->
+ <skip />
+ <!-- no translation found for choose_provider_title (7245243990139698508) -->
+ <skip />
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <!-- no translation found for save_your_password (6597736507991704307) -->
+ <skip />
+ <!-- no translation found for save_your_sign_in_info (7213978049817076882) -->
+ <skip />
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <!-- no translation found for choose_create_option_passkey_title (4146408187146573131) -->
+ <skip />
+ <!-- no translation found for choose_create_option_password_title (8812546498357380545) -->
+ <skip />
+ <!-- no translation found for choose_create_option_sign_in_title (6318246378475961834) -->
+ <skip />
+ <!-- no translation found for choose_create_option_description (4419171903963100257) -->
+ <skip />
+ <!-- no translation found for passkey (632353688396759522) -->
+ <skip />
+ <!-- no translation found for password (6738570945182936667) -->
+ <skip />
+ <!-- no translation found for sign_ins (4710739369149469208) -->
+ <skip />
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <!-- no translation found for use_provider_for_all_title (4201020195058980757) -->
+ <skip />
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <!-- no translation found for set_as_default (4415328591568654603) -->
+ <skip />
+ <!-- no translation found for use_once (9027366575315399714) -->
+ <skip />
+ <!-- no translation found for more_options_usage_passwords_passkeys (4794903978126339473) -->
+ <skip />
+ <!-- no translation found for more_options_usage_passwords (1632047277723187813) -->
+ <skip />
+ <!-- no translation found for more_options_usage_passkeys (5390320437243042237) -->
+ <skip />
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <!-- no translation found for another_device (5147276802037801217) -->
+ <skip />
+ <!-- no translation found for other_password_manager (565790221427004141) -->
+ <skip />
+ <!-- no translation found for close_sheet (1393792015338908262) -->
+ <skip />
+ <!-- no translation found for accessibility_back_arrow_button (3233198183497842492) -->
+ <skip />
+ <!-- no translation found for get_dialog_title_use_passkey_for (6236608872708021767) -->
+ <skip />
+ <!-- no translation found for get_dialog_title_use_sign_in_for (5283099528915572980) -->
+ <skip />
+ <!-- no translation found for get_dialog_title_choose_sign_in_for (1361715440877613701) -->
+ <skip />
+ <!-- no translation found for get_dialog_use_saved_passkey_for (4618100798664888512) -->
+ <skip />
+ <!-- no translation found for get_dialog_button_label_no_thanks (8114363019023838533) -->
+ <skip />
+ <!-- no translation found for get_dialog_button_label_continue (6446201694794283870) -->
+ <skip />
+ <!-- no translation found for get_dialog_title_sign_in_options (2092876443114893618) -->
+ <skip />
+ <!-- no translation found for get_dialog_heading_for_username (3456868514554204776) -->
+ <skip />
+ <!-- no translation found for get_dialog_heading_locked_password_managers (8911514851762862180) -->
+ <skip />
+ <!-- no translation found for locked_credential_entry_label_subtext (9213450912991988691) -->
+ <skip />
+ <!-- no translation found for get_dialog_heading_manage_sign_ins (3522556476480676782) -->
+ <skip />
+ <!-- no translation found for get_dialog_heading_from_another_device (1166697017046724072) -->
+ <skip />
+ <!-- no translation found for get_dialog_option_headline_use_a_different_device (8201578814988047549) -->
+ <skip />
+</resources>
diff --git a/packages/CredentialManager/res/values-ar/strings.xml b/packages/CredentialManager/res/values-ar/strings.xml
new file mode 100644
index 000000000000..4af875ce3519
--- /dev/null
+++ b/packages/CredentialManager/res/values-ar/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"إلغاء"</string>
+ <string name="string_continue" msgid="1346732695941131882">"متابعة"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"الإنشاء في مكان آخر"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"الحفظ في مكان آخر"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"استخدام جهاز آخر"</string>
+ <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+ <skip />
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"طريقة بسيطة لتسجيل الدخول بأمان"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"استخدِم بصمة إصبعك أو وجهك أو قفل الشاشة لتسجيل الدخول باستخدام مفتاح مرور فريد لا يمكن نسيانه أو سرقته. مزيد من المعلومات"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"اختيار مكان <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"حفظ كلمة المرور"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"حفظ معلومات تسجيل الدخول"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"هل تريد إنشاء مفتاح مرور في \"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>\"؟"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"هل تريد حفظ كلمة مرورك في \"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>\"؟"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"هل تريد حفظ معلومات تسجيل الدخول في \"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>\"؟"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"يمكنك استخدام <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> على أي جهاز. يتم حفظه في \"<xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>\" لـ \"<xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>\"."</string>
+ <string name="passkey" msgid="632353688396759522">"مفتاح مرور"</string>
+ <string name="password" msgid="6738570945182936667">"كلمة المرور"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"عمليات تسجيل الدخول"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"هل تريد استخدام \"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>\" لكل عمليات تسجيل الدخول؟"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"ضبط الخيار كتلقائي"</string>
+ <string name="use_once" msgid="9027366575315399714">"الاستخدام مرة واحدة"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"عدد كلمات المرور هو <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>، و عدد مفاتيح المرور هو <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"عدد كلمات المرور: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"عدد مفاتيح المرور: <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"جهاز آخر"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"خدمات مدراء كلمات المرور الأخرى"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"إغلاق ورقة البيانات"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"العودة إلى الصفحة السابقة"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"هل تريد استخدام مفتاح المرور المحفوظ لتطبيق \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"؟"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"هل تريد استخدام بيانات اعتماد تسجيل الدخول المحفوظة لتطبيق \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"؟"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"اختيار بيانات اعتماد تسجيل دخول محفوظة لـ \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"تسجيل الدخول بطريقة أخرى"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"لا، شكرًا"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"متابعة"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"خيارات تسجيل الدخول"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"معلومات تسجيل دخول \"<xliff:g id="USERNAME">%1$s</xliff:g>\""</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"خدمات إدارة كلمات المرور المقفولة"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"انقر لإلغاء القفل."</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"إداراة عمليات تسجيل الدخول"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"من جهاز آخر"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"استخدام جهاز مختلف"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-as/strings.xml b/packages/CredentialManager/res/values-as/strings.xml
new file mode 100644
index 000000000000..c1040987b3d3
--- /dev/null
+++ b/packages/CredentialManager/res/values-as/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"বাতিল কৰক"</string>
+ <string name="string_continue" msgid="1346732695941131882">"অব্যাহত ৰাখক"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"অন্য ঠাইত সৃষ্টি কৰক"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"অন্য ঠাইত ছেভ কৰক"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"অন্য ডিভাইচ ব্যৱহাৰ কৰক"</string>
+ <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+ <skip />
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"সুৰক্ষিতভাৱে ছাইন ইন কৰাৰ এক সৰল উপায়"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"পাহৰি নোযোৱা অথবা চুৰি কৰিব নোৱৰা এটা অদ্বিতীয় পাছকী ব্যৱহাৰ কৰি ছাইন ইন কৰিবলৈ আপোনাৰ ফিংগাৰপ্ৰিণ্ট, মুখাৱয়ব অথবা স্ক্ৰীন লক ব্যৱহাৰ কৰক। অধিক জানক"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"ক’ত <xliff:g id="CREATETYPES">%1$s</xliff:g> সেয়া বাছনি কৰক"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"আপোনাৰ পাছৱৰ্ড ছেভ কৰক"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"আপোনাৰ ছাইন ইন কৰাৰ তথ্য ছেভ কৰক"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>ত পাছকী সৃষ্টি কৰিবনে?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>ত আপোনাৰ পাছৱৰ্ড ছেভ কৰিবনে?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>ত আপোনাৰ ছাইন ইন কৰাৰ তথ্য ছেভ কৰিবনে?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"আপুনি আপোনাৰ <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> যিকোনো ডিভাইচত ব্যৱহাৰ কৰিব পাৰে। এইটো <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>ৰ বাবে <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>ত ছেভ কৰা হৈছে"</string>
+ <string name="passkey" msgid="632353688396759522">"পাছকী"</string>
+ <string name="password" msgid="6738570945182936667">"পাছৱৰ্ড"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"ছাইন-ইন"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"আপোনাৰ আটাইবোৰ ছাইন ইনৰ বাবে <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ব্যৱহাৰ কৰিবনে?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"ডিফ’ল্ট হিচাপে ছেট কৰক"</string>
+ <string name="use_once" msgid="9027366575315399714">"এবাৰ ব্যৱহাৰ কৰক"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> টা পাছৱৰ্ড, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> টা পাছকী"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> টা পাছৱৰ্ড"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> টা পাছকী"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"অন্য এটা ডিভাইচ"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"অন্য পাছৱৰ্ড পৰিচালক"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"শ্বীট বন্ধ কৰক"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"পূৰ্বৱৰ্তী পৃষ্ঠালৈ ঘূৰি যাওক"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ বাবে আপোনাৰ ছেভ হৈ থকা পাছকী ব্যৱহাৰ কৰিবনে?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ বাবে আপোনাৰ ছেভ হৈ থকা ছাইন ইন তথ্য ব্যৱহাৰ কৰিবনে?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ বাবে ছেভ হৈ থকা এটা ছাইন ইন বাছনি কৰক"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"অন্য উপায়েৰে ছাইন ইন কৰক"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"নালাগে, ধন্যবাদ"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"অব্যাহত ৰাখক"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"ছাইন ইনৰ বিকল্প"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g>ৰ বাবে"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"লক হৈ থকা পাছৱৰ্ড পৰিচালক"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"আনলক কৰিবলৈ টিপক"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"ছাইন ইন পৰিচালনা কৰক"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"অন্য এটা ডিভাইচৰ পৰা"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"অন্য এটা ডিভাইচ ব্যৱহাৰ কৰক"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-az/strings.xml b/packages/CredentialManager/res/values-az/strings.xml
new file mode 100644
index 000000000000..4a8fef514785
--- /dev/null
+++ b/packages/CredentialManager/res/values-az/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Ləğv edin"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Davam edin"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Başqa yerdə yaradın"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Başqa yerdə yadda saxlayın"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Digər cihaz istifadə edin"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Başqa cihazda yadda saxlayın"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Təhlükəsiz daxil olmağın sadə yolu"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Unutmaq və ya oğurlamaq mümkün olmayan unikal giriş açarı ilə daxil olmaq üçün barmaq izi, üz və ya ekran kilidindən istifadə edin. Ətraflı məlumat"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> üçün yer seçin"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"parolunuzu yadda saxlayın"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"giriş məlumatınızı yadda saxlayın"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> xidmətində giriş açarı yaradılsın?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Parol <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> xidmətində saxlanılsın?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Giriş məlumatınız <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> xidmətində saxlanılsın?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"<xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> domenini istənilən cihazda istifadə edə bilərsiniz. <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> üçün <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> xidmətində saxlanılıb"</string>
+ <string name="passkey" msgid="632353688396759522">"giriş açarı"</string>
+ <string name="password" msgid="6738570945182936667">"parol"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"girişlər"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Bütün girişlər üçün <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> istifadə edilsin?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Defolt olaraq seçin"</string>
+ <string name="use_once" msgid="9027366575315399714">"Bir dəfə istifadə edin"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> parol, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> giriş açarı"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> parol"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> giriş açarı"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Digər cihaz"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Digər parol menecerləri"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Səhifəni bağlayın"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Əvvəlki səhifəyə qayıdın"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> üçün yadda saxlanmış giriş açarı istifadə edilsin?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> üçün yadda saxlanmış girişdən istifadə edilsin?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> üçün yadda saxlanmış girişi seçin"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Başqa üsulla daxil olun"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Xeyr, təşəkkürlər"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Davam edin"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Giriş seçimləri"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> üçün"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Kilidli parol menecerləri"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Kilidi açmaq üçün tıklayın"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Girişləri idarə edin"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Başqa cihazdan"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Başqa cihaz istifadə edin"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-b+sr+Latn/strings.xml b/packages/CredentialManager/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 000000000000..b46518eafef4
--- /dev/null
+++ b/packages/CredentialManager/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Otkaži"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Nastavi"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Napravi na drugom mestu"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Sačuvaj na drugom mestu"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Koristi drugi uređaj"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Sačuvaj na drugi uređaj"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Jednostavan način da se bezbedno prijavljujete"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Koristite otisak prsta, zaključavanje licem ili zaključavanje ekrana da biste se prijavili pomoću jedinstvenog pristupnog koda koji ne može da se zaboravi ili ukrade. Saznajte više"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Odaberite lokaciju za: <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"sačuvajte lozinku"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"sačuvajte podatke o prijavljivanju"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Želite da napravite pristupni kôd kod korisnika <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Želite da sačuvate lozinku kod korisnika <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Želite da sačuvate podatke o prijavljivanju kod korisnika <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Možete da koristite tip domena <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> na bilo kom uređaju. Čuva se kod korisnika <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> za: <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"pristupni kôd"</string>
+ <string name="password" msgid="6738570945182936667">"lozinka"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"prijavljivanja"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Želite da za sva prijavljivanja koristite: <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Podesi kao podrazumevano"</string>
+ <string name="use_once" msgid="9027366575315399714">"Koristi jednom"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"Lozinki: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>, pristupnih kodova:<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"Lozinki: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Pristupnih kodova: <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Drugi uređaj"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Drugi menadžeri lozinki"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Zatvorite tabelu"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Vratite se na prethodnu stranicu"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Želite da koristite sačuvani pristupni kôd za: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Želite da koristite sačuvane podatke za prijavljivanje za: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Odaberite sačuvano prijavljivanje za: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Prijavite se na drugi način"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Ne, hvala"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Nastavi"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opcije za prijavljivanje"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Za: <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Menadžeri zaključanih lozinki"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Dodirnite da biste otključali"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Upravljajte prijavljivanjima"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Sa drugog uređaja"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Koristi drugi uređaj"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-be/strings.xml b/packages/CredentialManager/res/values-be/strings.xml
new file mode 100644
index 000000000000..afa4d015f918
--- /dev/null
+++ b/packages/CredentialManager/res/values-be/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Скасаваць"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Далей"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Стварыць у іншым месцы"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Захаваць у іншым месцы"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Скарыстаць іншую прыладу"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Захаваць на іншую прыладу"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Просты спосаб бяспечнага ўваходу"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Для ўваходу з унікальным ключом доступу, які нельга згубіць ці ўкрасці, можна скарыстаць адбітак пальца, распазнаванне твару ці разблакіроўку экрана. Даведацца больш"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Выберыце, дзе <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"захаваць пароль"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"захаваць інфармацыю пра спосаб уваходу"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Стварыць ключ доступу ў папцы \"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>\"?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Захаваць пароль у папку \"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>\"?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Захаваць інфармацыю пра спосаб уваходу ў папку \"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>\"?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Вы можаце выкарыстоўваць <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> на любой прыладзе. Даныя для \"<xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>\" захоўваюцца ў папку \"<xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>\""</string>
+ <string name="passkey" msgid="632353688396759522">"ключ доступу"</string>
+ <string name="password" msgid="6738570945182936667">"пароль"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"спосабы ўваходу"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Выкарыстоўваць папку \"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>\" для ўсіх спосабаў уваходу?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Выкарыстоўваць стандартна"</string>
+ <string name="use_once" msgid="9027366575315399714">"Скарыстаць адзін раз"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"Пароляў: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>, ключоў доступу: <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"Пароляў: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Ключоў доступу: <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Іншая прылада"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Іншыя спосабы ўваходу"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Закрыць аркуш"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Вярнуцца да папярэдняй старонкі"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Скарыстаць захаваны ключ доступу для праграмы \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Скарыстаць захаваныя спосабы ўваходу для праграмы \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Выберыце захаваны спосаб уваходу для праграмы \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Увайсці іншым спосабам"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Не, дзякуй"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Далей"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Спосабы ўваходу"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Для карыстальніка <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Заблакіраваныя спосабы ўваходу"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Націсніце, каб разблакіраваць"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Кіраваць спосабамі ўваходу"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"З іншай прылады"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Скарыстаць іншую прыладу"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-bg/strings.xml b/packages/CredentialManager/res/values-bg/strings.xml
new file mode 100644
index 000000000000..1a2f881f6bc5
--- /dev/null
+++ b/packages/CredentialManager/res/values-bg/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Отказ"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Напред"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Създаване другаде"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Запазване на друго място"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Използване на друго устройство"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Запазване на друго устройство"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Лесен начин за безопасно влизане в профил"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Използвайте отпечатъка, лицето или опцията си за заключване на екрана, за да влизате в профила си с помощта на уникален код за достъп, който не може да бъде забравен или откраднат. Научете повече"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Изберете място за <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"запазване на паролата ви"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"запазване на данните ви за вход"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Да се създаде ли код за достъп в(ъв) <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Искате ли да запазите паролата си в(ъв) <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Искате ли да запазите данните си за вход в(ъв) <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Можете да използвате <xliff:g id="TYPE">%2$s</xliff:g> за <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> на всяко устройство. Тези данни се запазват в(ъв) <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> за <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"код за достъп"</string>
+ <string name="password" msgid="6738570945182936667">"парола"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"данни за вход"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Да се използва ли <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> за всичките ви данни за вход?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Задаване като основно"</string>
+ <string name="use_once" msgid="9027366575315399714">"Еднократно използване"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> пароли, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> кода за достъп"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> пароли"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> кода за достъп"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Друго устройство"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Други мениджъри на пароли"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Затваряне на таблицата"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Назад към предишната страница"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Да се използва ли запазеният ви код за достъп за <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Да се използват ли запазените ви данни за вход за <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Изберете запазени данни за вход за <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Влизане в профила по друг начин"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Не, благодаря"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Напред"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Опции за влизане в профила"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"За <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Заключени мениджъри на пароли"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Докоснете, за да отключите"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Управление на данните за вход"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"От друго устройство"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Използване на друго устройство"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-bn/strings.xml b/packages/CredentialManager/res/values-bn/strings.xml
new file mode 100644
index 000000000000..3257b7837c4b
--- /dev/null
+++ b/packages/CredentialManager/res/values-bn/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"বাতিল করুন"</string>
+ <string name="string_continue" msgid="1346732695941131882">"চালিয়ে যান"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"অন্য জায়গায় তৈরি করুন"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"অন্য জায়গায় সেভ করুন"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"অন্য ডিভাইস ব্যবহার করুন"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"অন্য ডিভাইসে সেভ করুন"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"নিরাপদে সাইন-ইন করার একটি সহজ উপায়"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"একটি অনন্য পাসকী ব্যবহার করে সাইন-ইন করতে আপনার ফিঙ্গারপ্রিন্ট, মুখ বা স্ক্রিন লক ব্যবহার করুন যেটি ভুলে যাবেন না বা হারিয়ে যাবেন না। আরও জানুন"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> কোথায় সেভ করবেন তা বেছে নিন"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"আপনার পাসওয়ার্ড সেভ করুন"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"আপনার সাইন-ইন করা সম্পর্কিত তথ্য সেভ করুন"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>-এ পাসকী তৈরি করবেন?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"আপনার পাসওয়ার্ড <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>-এ সেভ করবেন?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"আপনার সাইন-ইন করা সম্পর্কিত তথ্য <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>-এ সেভ করবেন?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"যেকোনও ডিভাইসে নিজের <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> ব্যবহার করতে পারবেন। এটি <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>-এর জন্য <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>-এ সেভ করা আছে"</string>
+ <string name="passkey" msgid="632353688396759522">"পাসকী"</string>
+ <string name="password" msgid="6738570945182936667">"পাসওয়ার্ড"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"সাইন-ইন"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"আপনার সব সাইন-ইনের জন্য <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ব্যবহার করবেন?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"ডিফল্ট হিসেবে সেট করুন"</string>
+ <string name="use_once" msgid="9027366575315399714">"একবার ব্যবহার করুন"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>টি পাসওয়ার্ড, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>টি পাসকী"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>টি পাসওয়ার্ড"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>টি পাসকী"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"অন্য ডিভাইস"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"অন্যান্য Password Manager"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"শিট বন্ধ করুন"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"আগের পৃষ্ঠায় ফিরে যান"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g>-এর জন্য আপনার সেভ করা পাসকী ব্যবহার করবেন?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g>-এর জন্য আপনার সেভ করা সাইন-ইন সম্পর্কিত ক্রেডেনশিয়াল ব্যবহার করবেন?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g>-এর জন্য সাইন-ইন করা সম্পর্কিত ক্রেডেনশিয়াল বেছে নিন"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"অন্যভাবে সাইন-ইন করুন"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"না থাক"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"চালিয়ে যান"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"সাইন-ইন করার বিকল্প"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g>-এর জন্য"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"লক করা Password Manager"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"আনলক করতে ট্যাপ করুন"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"সাইন-ইন করার ক্রেডেনশিয়াল ম্যানেজ করুন"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"অন্য ডিভাইস থেকে"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"আলাদা ডিভাইস ব্যবহার করুন"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-bs/strings.xml b/packages/CredentialManager/res/values-bs/strings.xml
new file mode 100644
index 000000000000..705cfefcf979
--- /dev/null
+++ b/packages/CredentialManager/res/values-bs/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Otkaži"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Nastavi"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Kreirajte na drugom mjestu"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Sačuvajte na drugom mjestu"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Koristite drugi uređaj"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Sačuvajte na drugom uređaju"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Jednostavan način za sigurnu prijavu"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Koristite otisak prsta, lice ili zaključavanje ekrana da se prijavite jedinstvenim pristupnim ključem koji se ne može zaboraviti niti ukrasti. Saznajte više"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Odaberite gdje <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"sačuvajte lozinku"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"sačuvajte informacije za prijavu"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Kreirati pristupni ključ na usluzi <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Sačuvati lozinku na usluzi <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Sačuvati informacije za prijavu na usluzi <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Možete koristiti <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> na bilo kojem uređaju. Sačuvan je na usluzi <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> za <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"pristupni ključ"</string>
+ <string name="password" msgid="6738570945182936667">"lozinka"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"prijave"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Koristiti uslugu <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> za sve vaše prijave?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Postavi kao zadano"</string>
+ <string name="use_once" msgid="9027366575315399714">"Koristi jednom"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"Broj lozinki: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>; broj pristupnih ključeva: <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"Broj lozinki: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Broj pristupnih ključeva: <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Drugi uređaj"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Drugi upravitelji lozinki"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Zatvaranje tabele"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Povratak na prethodnu stranicu"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Koristiti sačuvani pristupni ključ za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Koristiti sačuvanu prijavu za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Odaberite sačuvanu prijavu za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Prijavite se na drugi način"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Ne, hvala"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Nastavi"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opcije prijave"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Za osobu <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Zaključani upravitelji lozinki"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Dodirnite da otključate"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Upravljajte prijavama"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"S drugog uređaja"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Upotrijebite drugi uređaj"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-ca/strings.xml b/packages/CredentialManager/res/values-ca/strings.xml
new file mode 100644
index 000000000000..d9b5a9039d92
--- /dev/null
+++ b/packages/CredentialManager/res/values-ca/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Cancel·la"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Continua"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Crea en un altre lloc"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Desa en un altre lloc"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Utilitza un altre dispositiu"</string>
+ <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+ <skip />
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Una manera senzilla i segura d\'iniciar la sessió"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Utilitza l\'empremta digital, la cara o el bloqueig de pantalla per iniciar la sessió amb una clau d\'accés única que no es pot oblidar ni robar. Més informació"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Tria on vols <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"desar la contrasenya"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"desar la teva informació d\'inici de sessió"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Vols crear una clau d\'accés a <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Vols desar la contrasenya a <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Vols desar la teva informació d\'inici de sessió a <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Pots utilitzar <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> en qualsevol dispositiu. Està desat a <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> per a <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"clau d\'accés"</string>
+ <string name="password" msgid="6738570945182936667">"contrasenya"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"inicis de sessió"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Vols utilitzar <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> per a tots els teus inicis de sessió?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Estableix com a predeterminada"</string>
+ <string name="use_once" msgid="9027366575315399714">"Utilitza un cop"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> contrasenyes, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> claus d\'accés"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> contrasenyes"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> claus d\'accés"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Un altre dispositiu"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Altres gestors de contrasenyes"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Tanca el full"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Torna a la pàgina anterior"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Vols utilitzar la clau d\'accés desada per a <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Vols utilitzar l\'inici de sessió desat per a <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Tria un inici de sessió desat per a <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Inicia la sessió d\'una altra manera"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"No, gràcies"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Continua"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opcions d\'inici de sessió"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Per a <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Gestors de contrasenyes bloquejats"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Toca per desbloquejar"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Gestiona els inicis de sessió"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Des d\'un altre dispositiu"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Utilitza un dispositiu diferent"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-cs/strings.xml b/packages/CredentialManager/res/values-cs/strings.xml
new file mode 100644
index 000000000000..e7fe5365ff26
--- /dev/null
+++ b/packages/CredentialManager/res/values-cs/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Zrušit"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Pokračovat"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Vytvořit na jiném místě"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Uložit na jiné místo"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Použít jiné zařízení"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Uložit do jiného zařízení"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Jednoduchý způsob, jak se bezpečně přihlásit"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Použijte svůj otisk prstu, obličej nebo zámek obrazovky k přihlášení pomocí jedinečného přístupového klíče, který nemůžete zapomenout a který vám nikdo nemůže odcizit. Další informace"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Zvolte, kde <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"uložte si heslo"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"uložte své přihlašovací údaje"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Vytvořit přístupový klíč v <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Uložit heslo do <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Uložit přihlašovací údaje do <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Svoje <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> můžete používat na libovolném zařízení. Ukládá se do <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> pro <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"přístupový klíč"</string>
+ <string name="password" msgid="6738570945182936667">"heslo"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"přihlášení"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Používat <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> pro všechna přihlášení?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Nastavit jako výchozí"</string>
+ <string name="use_once" msgid="9027366575315399714">"Použít jednou"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"Počet hesel: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>, počet přístupových klíčů: <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"Počet hesel: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Počet přístupových klíčů: <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Jiné zařízení"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Další správci hesel"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Zavřít list"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Zpět na předchozí stránku"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Použít uložený přístupový klíč pro aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Použít uložené přihlášení pro aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Vyberte uložené přihlášení pro <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Přihlásit se jinak"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Ne, díky"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Pokračovat"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Možnosti přihlašování"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Pro uživatele <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Uzamčení správci hesel"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Klepnutím odemknete"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Spravovat přihlášení"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Z jiného zařízení"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Použít jiné zařízení"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-da/strings.xml b/packages/CredentialManager/res/values-da/strings.xml
new file mode 100644
index 000000000000..86cf9ff210d2
--- /dev/null
+++ b/packages/CredentialManager/res/values-da/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Annuller"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Fortsæt"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Opret et andet sted"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Gem et andet sted"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Brug en anden enhed"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Gem på en anden enhed"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"En nemmere og mere sikker måde at logge ind på"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Brug dit fingeraftryk, dit ansigt eller din skærmlås for at logge ind med en unik adgangsnøgle, der hverken kan glemmes eller stjæles. Få flere oplysninger"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Vælg, hvor du vil <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"gem din adgangskode"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"gem dine loginoplysninger"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Vil du oprette en adgangsnøgle i <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Vil du gemme din adgangskode i <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Vil du gemme dine loginoplysninger i <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Du kan bruge din <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> på enhver enhed. Den gemmes i <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> for <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"adgangsnøgle"</string>
+ <string name="password" msgid="6738570945182936667">"adgangskode"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"loginmetoder"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Vil du bruge <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> til alle dine loginmetoder?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Angiv som standard"</string>
+ <string name="use_once" msgid="9027366575315399714">"Brug én gang"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> adgangskoder, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> adgangsnøgler"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> adgangskoder"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> adgangsnøgler"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"En anden enhed"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Andre adgangskodeadministratorer"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Luk arket"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Gå tilbage til den forrige side"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Vil du bruge din gemte adgangsnøgle til <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Vil du bruge din gemte loginmetode til <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Vælg en gemt loginmetode til <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Log ind på en anden måde"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Nej tak"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Fortsæt"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Valgmuligheder for login"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"For <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Låste adgangskodeadministratorer"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Tryk for at låse op"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Administrer loginmetoder"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Fra en anden enhed"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Brug en anden enhed"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-de/strings.xml b/packages/CredentialManager/res/values-de/strings.xml
new file mode 100644
index 000000000000..d239dd3d2664
--- /dev/null
+++ b/packages/CredentialManager/res/values-de/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Abbrechen"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Weiter"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"An anderem Speicherort erstellen"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"An anderem Ort speichern"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Anderes Gerät verwenden"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Auf einem anderen Gerät speichern"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Einfach und sicher anmelden"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Verwende deinen Fingerabdruck oder deine Gesichts- bzw. Displaysperre, um dich mit einem eindeutigen Passkey anzumelden, der nicht vergessen oder gestohlen werden kann. Weitere Informationen"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Ort auswählen für: <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"Passwort speichern"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"Anmeldedaten speichern"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Passkey hier erstellen: <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Passwort hier speichern: <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Anmeldedaten hier speichern: <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Du kannst <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> auf jedem beliebigen Gerät verwenden. Gespeichert (<xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>) für <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"Passkey"</string>
+ <string name="password" msgid="6738570945182936667">"Passwort"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"Anmeldungen"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> für alle Anmeldungen verwenden?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Als Standard festlegen"</string>
+ <string name="use_once" msgid="9027366575315399714">"Einmal verwenden"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> Passwörter, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> Passkeys"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> Passwörter"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> Passkeys"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Ein anderes Gerät"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Andere Passwortmanager"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Tabellenblatt schließen"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Zurück zur vorherigen Seite"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Gespeicherten Passkey für <xliff:g id="APP_NAME">%1$s</xliff:g> verwenden?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Gespeicherte Anmeldedaten für <xliff:g id="APP_NAME">%1$s</xliff:g> verwenden?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Gespeicherte Anmeldedaten für <xliff:g id="APP_NAME">%1$s</xliff:g> auswählen"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Andere Anmeldeoption auswählen"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Nein danke"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Weiter"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Anmeldeoptionen"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Für <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Gesperrte Passwortmanager"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Zum Entsperren tippen"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Anmeldedaten verwalten"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Von einem anderen Gerät"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Anderes Gerät verwenden"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-el/strings.xml b/packages/CredentialManager/res/values-el/strings.xml
new file mode 100644
index 000000000000..9b7ccbb24ad1
--- /dev/null
+++ b/packages/CredentialManager/res/values-el/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Ακύρωση"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Συνέχεια"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Δημιουργία σε άλλη θέση"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Αποθήκευση σε άλλη θέση"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Χρήση άλλης συσκευής"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Αποθήκευση σε άλλη συσκευή"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Ένας απλός τρόπος για ασφαλή σύνδεση"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Χρησιμοποιήστε το δακτυλικό αποτύπωμα, το πρόσωπο ή το κλείδωμα οθόνης σας για να συνδεθείτε με ένα μοναδικό κλειδί πρόσβασης που δεν είναι δυνατό να ξεχάσετε ή να κλαπεί. Μάθετε περισσότερα"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Επιλέξτε θέση για <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"αποθήκευση του κωδικού πρόσβασής σας"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"αποθήκευση των στοιχείων σύνδεσής σας"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Να δημιουργηθει κλειδί πρόσβασης στο <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>;"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Να αποθηκευτεί ο κωδικός πρόσβασής σας στο <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>;"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Να αποθηκευτούν τα στοιχεία σύνδεσής σας στο <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>;"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Μπορείτε να χρησιμοποιήσετε το στοιχείο τύπου <xliff:g id="TYPE">%2$s</xliff:g> του τομέα <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> σε οποιαδήποτε συσκευή. Αποθηκεύτηκε στο <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> για <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"κλειδί πρόσβασης"</string>
+ <string name="password" msgid="6738570945182936667">"κωδικός πρόσβασης"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"στοιχεία σύνδεσης"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Να χρησιμοποιηθεί το <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> για όλες τις συνδέσεις σας;"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Ορισμός ως προεπιλογής"</string>
+ <string name="use_once" msgid="9027366575315399714">"Χρήση μία φορά"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> κωδικοί πρόσβασης, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> κλειδιά πρόσβασης"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> κωδικοί πρόσβασης"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> κλειδιά πρόσβασης"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Άλλη συσκευή"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Άλλοι διαχειριστές κωδικών πρόσβασης"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Κλείσιμο φύλλου"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Επιστροφή στην προηγούμενη σελίδα"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Να χρησιμοποιηθεί το αποθηκευμένο κλειδί πρόσβασης για την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g>;"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Να χρησιμοποιηθούν τα αποθηκευμένα στοιχεία σύνδεσης για την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g>;"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Επιλογή αποθηκευμένων στοιχείων σύνδεσης για την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Σύνδεση με άλλον τρόπο"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Όχι, ευχαριστώ"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Συνέχεια"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Επιλογές σύνδεσης"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Για τον χρήστη <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Κλειδωμένοι διαχειριστές κωδικών πρόσβασης"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Πατήστε για ξεκλείδωμα"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Διαχείριση στοιχείων σύνδεσης"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Από άλλη συσκευή"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Χρήση διαφορετικής συσκευής"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-en-rAU/strings.xml b/packages/CredentialManager/res/values-en-rAU/strings.xml
new file mode 100644
index 000000000000..682dffbf4e28
--- /dev/null
+++ b/packages/CredentialManager/res/values-en-rAU/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Cancel"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Continue"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Create in another place"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Save to another place"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Use another device"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Save to another device"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"A simple way to sign in safely"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Use your fingerprint, face or screen lock to sign in with a unique passkey that can’t be forgotten or stolen. Learn more"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Choose where to <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"save your password"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"save your sign-in info"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Create a passkey in <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Save your password to <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Save your sign-in info to <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"You can use your <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> on any device. It is saved to <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> for <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"passkey"</string>
+ <string name="password" msgid="6738570945182936667">"password"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"sign-ins"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Use <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> for all your sign-ins?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Set as default"</string>
+ <string name="use_once" msgid="9027366575315399714">"Use once"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> passwords, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> passkeys"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> passwords"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> passkeys"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Another device"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Other password managers"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Close sheet"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Go back to the previous page"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Use your saved passkey for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Use your saved sign-in for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Choose a saved sign-in for <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Sign in another way"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"No thanks"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Continue"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Sign-in options"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"For <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Locked password managers"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Tap to unlock"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Manage sign-ins"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"From another device"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Use a different device"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-en-rCA/strings.xml b/packages/CredentialManager/res/values-en-rCA/strings.xml
new file mode 100644
index 000000000000..4ec2872f7c0b
--- /dev/null
+++ b/packages/CredentialManager/res/values-en-rCA/strings.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="4539824758261855508">"Credential Manager"</string>
+ <string name="string_cancel" msgid="6369133483981306063">"Cancel"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Continue"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Create in another place"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Save to another place"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Use another device"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Save to another device"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"A simple way to sign in safely"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Use your fingerprint, face or screen lock to sign in with a unique passkey that can’t be forgotten or stolen. Learn more"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Choose where to <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"save your password"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"save your sign-in info"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Create a passkey in <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Save your password to <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Save your sign-in info to <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"You can use your <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> on any device. It is saved to <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> for <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"passkey"</string>
+ <string name="password" msgid="6738570945182936667">"password"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"sign-ins"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Use <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> for all your sign-ins?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Set as default"</string>
+ <string name="use_once" msgid="9027366575315399714">"Use once"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> passwords, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> passkeys"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> passwords"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> passkeys"</string>
+ <string name="passkey_before_subtitle" msgid="2448119456208647444">"Passkey"</string>
+ <string name="another_device" msgid="5147276802037801217">"Another device"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Other password managers"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Close sheet"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Go back to the previous page"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Use your saved passkey for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Use your saved sign-in for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Choose a saved sign-in for <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Sign in another way"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"No thanks"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Continue"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Sign-in options"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"For <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Locked password managers"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Tap to unlock"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Manage sign-ins"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"From another device"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Use a different device"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-en-rGB/strings.xml b/packages/CredentialManager/res/values-en-rGB/strings.xml
new file mode 100644
index 000000000000..682dffbf4e28
--- /dev/null
+++ b/packages/CredentialManager/res/values-en-rGB/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Cancel"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Continue"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Create in another place"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Save to another place"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Use another device"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Save to another device"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"A simple way to sign in safely"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Use your fingerprint, face or screen lock to sign in with a unique passkey that can’t be forgotten or stolen. Learn more"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Choose where to <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"save your password"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"save your sign-in info"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Create a passkey in <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Save your password to <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Save your sign-in info to <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"You can use your <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> on any device. It is saved to <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> for <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"passkey"</string>
+ <string name="password" msgid="6738570945182936667">"password"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"sign-ins"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Use <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> for all your sign-ins?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Set as default"</string>
+ <string name="use_once" msgid="9027366575315399714">"Use once"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> passwords, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> passkeys"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> passwords"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> passkeys"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Another device"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Other password managers"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Close sheet"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Go back to the previous page"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Use your saved passkey for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Use your saved sign-in for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Choose a saved sign-in for <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Sign in another way"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"No thanks"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Continue"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Sign-in options"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"For <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Locked password managers"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Tap to unlock"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Manage sign-ins"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"From another device"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Use a different device"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-en-rIN/strings.xml b/packages/CredentialManager/res/values-en-rIN/strings.xml
new file mode 100644
index 000000000000..682dffbf4e28
--- /dev/null
+++ b/packages/CredentialManager/res/values-en-rIN/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Cancel"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Continue"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Create in another place"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Save to another place"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Use another device"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Save to another device"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"A simple way to sign in safely"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Use your fingerprint, face or screen lock to sign in with a unique passkey that can’t be forgotten or stolen. Learn more"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Choose where to <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"save your password"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"save your sign-in info"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Create a passkey in <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Save your password to <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Save your sign-in info to <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"You can use your <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> on any device. It is saved to <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> for <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"passkey"</string>
+ <string name="password" msgid="6738570945182936667">"password"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"sign-ins"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Use <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> for all your sign-ins?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Set as default"</string>
+ <string name="use_once" msgid="9027366575315399714">"Use once"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> passwords, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> passkeys"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> passwords"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> passkeys"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Another device"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Other password managers"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Close sheet"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Go back to the previous page"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Use your saved passkey for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Use your saved sign-in for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Choose a saved sign-in for <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Sign in another way"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"No thanks"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Continue"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Sign-in options"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"For <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Locked password managers"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Tap to unlock"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Manage sign-ins"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"From another device"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Use a different device"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-en-rXC/strings.xml b/packages/CredentialManager/res/values-en-rXC/strings.xml
new file mode 100644
index 000000000000..d114a4658e64
--- /dev/null
+++ b/packages/CredentialManager/res/values-en-rXC/strings.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="4539824758261855508">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‎‎‎‎‎‎‏‎‏‏‎‎‏‎‏‎‎‏‎‎‎‎‏‎‎‎‏‎‏‎‏‎‏‏‏‎‎‎‏‎‏‏‎‏‎‏‎‎‎‏‎‏‎‎‎Credential Manager‎‏‎‎‏‎"</string>
+ <string name="string_cancel" msgid="6369133483981306063">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‎‎‏‏‏‎‏‏‎‏‎‏‎‎‏‎‏‎‎‎‏‎‎‏‎‏‏‏‏‏‎‎‎‎‎‎‎‏‏‏‏‎‎‎‏‏‎‎‏‏‏‏‎Cancel‎‏‎‎‏‎"</string>
+ <string name="string_continue" msgid="1346732695941131882">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‏‎‎‎‎‏‎‎‎‏‏‏‎‎‏‎‎‎‎‎‎‎‏‏‎‏‏‎‎‏‏‏‏‏‎‎‏‏‎‏‎‏‏‏‎‎‏‏‎‏‎‏‎‎Continue‎‏‎‎‏‎"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‎‏‎‏‏‎‎‎‎‎‏‏‎‏‎‏‏‏‏‎‎‎‏‏‏‎‎‎‏‎‎‎‎‏‏‎‎‎‎‏‎‎‎‎‏‎‎‎‎‎‏‎‎‎‏‏‎Create in another place‎‏‎‎‏‎"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‎‏‎‏‏‎‎‏‎‎‎‎‎‏‎‎‏‏‏‎‎‎‏‏‏‎‎‏‎‎‏‎‏‏‏‏‎‎‎‏‏‏‎‎‏‏‎‎‎‏‏‎‎‏‎Save to another place‎‏‎‎‏‎"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‏‏‏‎‎‏‎‎‏‎‎‎‏‏‏‏‎‎‎‏‎‎‏‎‎‎‎‎‎‏‎‏‎‏‎‎‎‎‎‎‏‏‎‏‎‎‏‏‏‏‎‏‎Use another device‎‏‎‎‏‎"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‎‎‏‏‎‎‎‏‏‏‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‏‏‎‏‏‎‏‎‏‏‎‎‎‏‎‏‏‎‏‎‎Save to another device‎‏‎‎‏‎"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‎‎‏‎‏‏‎‎‎‏‎‏‎‎‎‏‎‏‎‎‏‏‎‎‏‎‏‎‎‎‎‏‏‏‏‏‎‎‎‏‏‏‎‏‎‎‎‎‎‎‏‏‏‎‎‎A simple way to sign in safely‎‏‎‎‏‎"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‎‏‎‎‎‎‎‎‏‏‏‎‎‎‎‎‎‏‏‎‎‎‎‏‎‏‏‏‎‎‏‎‎‎‎‏‏‎‏‎‎Use your fingerprint, face or screen lock to sign in with a unique passkey that can’t be forgotten or stolen. Learn more‎‏‎‎‏‎"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‎‏‏‎‎‎‏‎‎‎‏‏‏‎‎‎‎‎‏‎‏‏‎‏‎‎‏‏‏‎‏‎‎‏‏‏‏‎‏‏‏‏‏‎‏‎‏‎‎‏‏‎‎‎Choose where to ‎‏‎‎‏‏‎<xliff:g id="CREATETYPES">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‏‏‎‎‏‎‏‏‏‎‏‎‎‎‎‎‏‏‏‏‎‎‎‏‏‎‎‎‎‏‎‏‏‏‏‎‎‏‏‎save your password‎‏‎‎‏‎"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‏‏‏‎‏‎‎‏‏‎‎‏‎‏‏‎‏‎‎‎‎‏‎‏‎‎‏‎‎‏‏‎‎‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‏‎‎‏‎‎save your sign-in info‎‏‎‎‏‎"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‎‏‎‏‏‎‎‎‎‎‎‎‎‎‏‎‎‎‏‏‏‎‎‎‏‎‎‎‎‏‎‏‎‎‏‎‎‏‎‎‏‎‏‎‏‎‏‎‎‏‎‏‏‎Create a passkey in ‎‏‎‎‏‏‎<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎‎‎‏‏‏‎‏‎‎‎‏‎‏‏‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‏‎‎‎‏‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎Save your password to ‎‏‎‎‏‏‎<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‎‏‏‏‎‏‏‏‎‏‎‏‏‏‎‎‏‏‎‏‏‎‎‎‏‎‎‎‎‎‏‏‏‎‏‎‏‎‎‏‏‏‏‎‏‏‏‏‎‏‎‏‎‎Save your sign-in info to ‎‏‎‎‏‏‎<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‏‎‏‎‎‎‎‎‎‏‏‎‏‎‏‏‎‏‏‏‎‏‏‎‏‎‎‎‏‎‎‎‏‏‎‏‏‎‏‏‏‎‏‎‎‎‏‏‎‎‎‎‏‎You can use your ‎‏‎‎‏‏‎<xliff:g id="APPDOMAINNAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ ‎‏‎‎‏‏‎<xliff:g id="TYPE">%2$s</xliff:g>‎‏‎‎‏‏‏‎ on any device. It is saved to ‎‏‎‎‏‏‎<xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>‎‏‎‎‏‏‏‎ for ‎‏‎‎‏‏‎<xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="passkey" msgid="632353688396759522">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‏‏‎‎‎‏‏‎‏‎‎‏‎‎‏‎‎‏‎‏‎‏‎‎‎‏‎‎‏‎‎‏‎‏‎‎‏‏‎‎‎‎‎‎‎‎‎‏‏‏‏‎‎‎‏‎‎passkey‎‏‎‎‏‎"</string>
+ <string name="password" msgid="6738570945182936667">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‎‎‎‎‏‎‎‎‎‏‏‎‏‏‎‏‎‎‏‎‎‏‎‎‏‏‏‏‎‎‏‎‏‏‎‎‏‎‏‏‎‏‎‏‏‏‎‎‏‎‏‏‎‏‏‎password‎‏‎‎‏‎"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‎‎‏‏‎‏‎‎‎‎‏‎‎‏‎‎‎‎‏‏‎‎‎‎sign-ins‎‏‎‎‏‎"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‎‎‏‏‎‏‎‎‎‎‎‏‎‏‏‎‎‏‏‏‎‎‎‎‎‎‎‏‎‎‏‎‏‏‏‎‏‏‏‎‎‏‎‏‏‏‏‎‎‏‎‏‎‏‎Use ‎‏‎‎‏‏‎<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ for all your sign-ins?‎‏‎‎‏‎"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‎‎‏‏‎‎‏‏‎‎‏‎‏‏‏‏‏‎‏‎‏‏‏‏‎‏‎‏‏‎‎‎‏‎‎‏‏‎‏‎‏‎‎‎‏‎‎‎‎‏‎‏‏‎Set as default‎‏‎‎‏‎"</string>
+ <string name="use_once" msgid="9027366575315399714">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‏‏‎‏‎‎‏‏‎‎‎‎‏‎‎‏‎‎‎‏‏‏‎‏‎‏‏‏‎‏‎‎‎‏‎‏‏‎‏‎‎‎‎‏‎‎‎‏‎‎Use once‎‏‎‎‏‎"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‎‎‎‏‎‏‎‏‏‏‎‏‎‏‏‏‏‎‎‎‎‏‏‎‏‏‏‏‏‎‎‎‏‎‏‎‎‏‏‏‏‏‎‏‏‎‏‏‎‎‏‎‎‎‏‎‎‏‎‎‏‏‎<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>‎‏‎‎‏‏‏‎ passwords, ‎‏‎‎‏‏‎<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>‎‏‎‎‏‏‏‎ passkeys‎‏‎‎‏‎"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‏‎‏‎‎‏‏‎‎‎‏‏‎‎‏‎‎‏‎‏‏‎‏‎‎‎‏‎‎‏‎‏‎‏‎‏‎‏‎‏‎‏‏‏‏‎‏‎‎‏‏‎‎‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>‎‏‎‎‏‏‏‎ passwords‎‏‎‎‏‎"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‏‎‎‏‏‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‎‎‎‏‏‏‎‎‏‏‏‎‏‏‎‎‏‏‎‎‏‎‏‏‎‎‏‏‎‏‏‏‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>‎‏‎‎‏‏‏‎ passkeys‎‏‎‎‏‎"</string>
+ <string name="passkey_before_subtitle" msgid="2448119456208647444">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‏‏‏‏‏‏‎‎‏‎‏‏‏‎‏‏‏‏‎‏‎‏‏‎‎‏‎‏‏‏‎‏‏‎‎‎‎‏‎‎‏‏‏‏‏‎‏‎‏‎‎‎‏‎‏‎‎‎Passkey‎‏‎‎‏‎"</string>
+ <string name="another_device" msgid="5147276802037801217">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‏‎‏‏‏‎‏‏‎‎‏‏‎‎‏‏‏‏‏‎‏‎‏‏‏‎‎‎‏‎‎‎‎‏‎‎‏‎‎‏‏‎‎‎‎‏‎‎‎‎‎‎‎‏‎Another device‎‏‎‎‏‎"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‏‎‏‏‏‎‎‏‏‎‏‎‏‎‏‏‎‏‏‎‏‏‎‎‎‏‎‏‏‏‏‎‏‎‎‏‎‏‏‏‎‏‏‎‏‎Other password managers‎‏‎‎‏‎"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‎‏‎‏‎‏‏‏‏‎‏‏‏‏‏‎‎‏‏‏‎‏‎‎‎‎‎‎‏‏‎‏‎‎‎‎‏‏‎‏‎‎‏‎‏‎‏‎‎‏‏‎‎‏‏‎‎Close sheet‎‏‎‎‏‎"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‏‏‎‏‏‏‏‎‏‎‏‎‎‎‎‎‏‎‎‏‏‏‏‎‎‎‎‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‏‏‏‏‏‎‎‏‏‏‏‎‎‎Go back to the previous page‎‏‎‎‏‎"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‎‏‏‎‎‏‏‏‎‎‎‏‎‏‎‏‏‏‎‏‎‎‎‎‎‏‏‎‎‏‏‎‎‏‏‏‏‏‎‎‏‏‏‏‎‎‎‎‎‎‏‏‏‎Use your saved passkey for ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‏‎‏‎‎‎‏‎‏‎‏‎‏‏‏‎‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‎‎‎‎‏‎‏‏‏‏‎‏‏‎‎‎‏‏‏‏‎‏‎‎‎Use your saved sign-in for ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‏‏‏‎‎‏‎‏‏‏‎‎‏‎‎‎‏‏‏‏‏‎‏‎‏‎‎‏‎‎‏‎‏‎‏‎‏‏‎‎‏‎‏‏‎‎‏‎‏‎‎‎‎‏‎‏‎Choose a saved sign-in for ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‎‏‎‏‏‎‏‏‎‎‏‎‏‎‎‎‏‏‎‏‎‏‎‏‏‎‏‎‎‏‎‏‎‏‏‏‏‎‎‏‎‏‎‎‎‎‏‏‎‎‎‎‎‎‎Sign in another way‎‏‎‎‏‎"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‏‏‎‎‎‎‎‎‎‎‏‎‎‎‏‎‏‏‎‎‏‎‎‎‏‏‏‏‎‏‏‏‏‎‏‏‎‏‏‎‏‏‎‏‎‏‎‎‎‏‎‏‎No thanks‎‏‎‎‏‎"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‏‏‏‎‏‎‏‏‎‎‎‎‎‏‎‎‏‎‎‏‎‎‏‏‎‏‏‏‎‎‎‏‎‏‏‏‎‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‏‎‎Continue‎‏‎‎‏‎"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‎‎‎‏‎‏‏‎‏‏‎‎‏‎‎‎‎‎‎‏‎‎‏‎‏‎‏‎‏‏‎‏‎‏‎‎‏‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‏‎‎Sign-in options‎‏‎‎‏‎"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎‏‏‏‎‎‏‏‎‎‏‎‏‎‎‎‏‎‏‎‎‏‏‏‏‎‎‎‏‎‏‎‎‏‎‎‏‏‎‏‎‎‎‎For ‎‏‎‎‏‏‎<xliff:g id="USERNAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‎‎‎‎‎‎‏‏‏‏‏‎‎‎‏‎‏‎‏‏‎‏‏‏‎‏‏‏‏‏‎‎‎‏‎‏‏‏‏‎‎‎‎‏‏‎‎‏‎‎‎Locked password managers‎‏‎‎‏‎"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‎‎‎‎‏‏‎‎‏‎‏‎‎‏‎‏‏‎‎‎‎‏‏‎‎‎‏‎‏‏‎‎‏‎‏‏‏‏‎‏‎‎‏‏‎Tap to unlock‎‏‎‎‏‎"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‏‎‎‎‏‎‏‎‏‎‎‎‏‎‎‏‏‏‎‎‏‏‏‎‏‏‏‎‎‏‏‏‏‎‎‎‎‏‏‏‏‎‎‎‏‏‏‎‏‎‏‏‏‎‎Manage sign-ins‎‏‎‎‏‎"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‎‎‏‏‎‎‎‎‏‏‏‏‎‎‎‎‏‏‎‎‎‎‎‏‎‏‏‎‏‏‎‏‏‎‎‏‎‎‏‏‏‏‏‎‏‏‎‏‏‏‏‎‏‎‎‎‎From another device‎‏‎‎‏‎"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‎‎‎‏‏‏‎‏‏‏‎‎‎‏‏‏‏‎‏‎‎‏‏‏‏‎‎‎‏‎‏‏‏‎‏‏‎‏‎‏‎‏‎‎‏‎‏‏‏‏‎‏‎Use a different device‎‏‎‎‏‎"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-es-rUS/strings.xml b/packages/CredentialManager/res/values-es-rUS/strings.xml
new file mode 100644
index 000000000000..96e769754101
--- /dev/null
+++ b/packages/CredentialManager/res/values-es-rUS/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Cancelar"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Continuar"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Crear en otra ubicación"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Guardar en otra ubicación"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Usar otro dispositivo"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Guardar en otro dispositivo"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Un modo simple y seguro de ingresar"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Usa tu huella dactilar, tu rostro o el bloqueo de pantalla para acceder con una llave de acceso única que no olvidarás ni podrán robarte. Más información"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Elige dónde <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"guardar tu contraseña"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"guardar tu información de acceso"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"¿Quieres crear una llave de acceso en <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"¿Quieres guardar tu contraseña de <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"¿Quieres guardar tu información de acceso a <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Puedes usar tu <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> en cualquier dispositivo. Se guardó en <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> para <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>."</string>
+ <string name="passkey" msgid="632353688396759522">"llave de acceso"</string>
+ <string name="password" msgid="6738570945182936667">"contraseña"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"accesos"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"¿Quieres usar <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> para todos tus accesos?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Establecer como predeterminado"</string>
+ <string name="use_once" msgid="9027366575315399714">"Usar una vez"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> llaves de acceso, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> contraseñas"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> contraseñas"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> llaves de acceso"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Otro dispositivo"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Otros administradores de contraseñas"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Cerrar hoja"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Volver a la página anterior"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"¿Quieres usar tu llave de acceso guardada para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"¿Quieres usar tu acceso guardado para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Elige un acceso guardado para <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Acceder de otra forma"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"No, gracias"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Continuar"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opciones de acceso"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Para <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Administradores de contraseñas bloqueados"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Presiona para desbloquear"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Administrar accesos"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Desde otro dispositivo"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Usar otra voz"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-es/strings.xml b/packages/CredentialManager/res/values-es/strings.xml
new file mode 100644
index 000000000000..dce1a8ea7656
--- /dev/null
+++ b/packages/CredentialManager/res/values-es/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Cancelar"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Continuar"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Crear en otro lugar"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Guardar en otro lugar"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Usar otro dispositivo"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Guardar en otro dispositivo"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Una forma sencilla y segura de iniciar sesión"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Usa la huella digital, la cara o el bloqueo de pantalla para iniciar sesión con una llave de acceso única que no se puede olvidar ni robar. Más información"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Elige dónde <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"guardar tu contraseña"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"guardar tu información de inicio de sesión"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"¿Crear una llave de acceso en <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"¿Guardar tu contraseña en <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"¿Guardar tu información de inicio de sesión en <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Puedes usar tu <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> en cualquier dispositivo. Se guarda en <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> para <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>."</string>
+ <string name="passkey" msgid="632353688396759522">"llave de acceso"</string>
+ <string name="password" msgid="6738570945182936667">"contraseña"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"inicios de sesión"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"¿Usar <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> para todos tus inicios de sesión?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Fijar como predeterminado"</string>
+ <string name="use_once" msgid="9027366575315399714">"Usar una vez"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> contraseñas, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> llaves de acceso"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> contraseñas"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> llaves de acceso"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Otro dispositivo"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Otros gestores de contraseñas"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Cerrar hoja"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Volver a la página anterior"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"¿Usar la llave de acceso guardada para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"¿Usar el inicio de sesión guardado para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Elige un inicio de sesión guardado para <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Iniciar sesión de otra manera"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"No, gracias"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Continuar"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opciones de inicio de sesión"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Para <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Gestores de contraseñas bloqueados"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Toca para desbloquear"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Gestionar inicios de sesión"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"De otro dispositivo"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Usar otro dispositivo"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-et/strings.xml b/packages/CredentialManager/res/values-et/strings.xml
new file mode 100644
index 000000000000..00396e007808
--- /dev/null
+++ b/packages/CredentialManager/res/values-et/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Tühista"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Jätka"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Loo teises kohas"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Salvesta teise kohta"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Kasuta teist seadet"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Salvesta teise seadmesse"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Lihtne viis turvaliselt sisselogimiseks"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Kasutage sõrmejälge, nägu või ekraanilukku, et logida sisse unikaalse pääsuvõtmega, mida ei saa unustada ega varastada. Lisateave"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Valige, kus <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"parool salvestada"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"sisselogimisandmed salvestada"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Kas luua teenuses <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> pääsuvõti?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Kas salvestada parool teenusesse <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Kas salvestada sisselogimisteave teenusesse <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Saate rakendust <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> kasutada mis tahes seadmes. Salvestatakse teenusesse <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> – <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"pääsukood"</string>
+ <string name="password" msgid="6738570945182936667">"parool"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"sisselogimisandmed"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Kas kasutada teenust <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> kõigi teie sisselogimisandmete puhul?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Määra vaikeseadeks"</string>
+ <string name="use_once" msgid="9027366575315399714">"Kasuta ühe korra"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> parooli, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> pääsuvõtit"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> parooli"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> pääsuvõtit"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Teine seade"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Muud paroolihaldurid"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Sule leht"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Minge tagasi eelmisele lehele"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Kas kasutada rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> jaoks salvestatud pääsuvõtit?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Kas kasutada rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> jaoks salvestatud sisselogimisandmeid?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Valige rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> jaoks salvestatud sisselogimisandmed"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Logige sisse muul viisil"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Tänan, ei"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Jätka"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Sisselogimise valikud"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Kasutajale <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Lukustatud paroolihaldurid"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Avamiseks puudutage"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Sisselogimisandmete haldamine"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Muus seadmes"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Kasuta teist seadet"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-eu/strings.xml b/packages/CredentialManager/res/values-eu/strings.xml
new file mode 100644
index 000000000000..38f659204830
--- /dev/null
+++ b/packages/CredentialManager/res/values-eu/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Utzi"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Egin aurrera"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Sortu beste toki batean"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Gorde beste toki batean"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Erabili beste gailu bat"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Gorde beste gailu batean"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Segurtasun osoz saioa hasteko modu erraza"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Erabili hatz-marka, aurpegia edo pantailaren blokeoa ahaztu edo lapurtu ezin den sarbide-gako baten bidez saioa hasteko. Lortu informazio gehiago"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Aukeratu non <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"gorde pasahitza"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"gorde kredentzialei buruzko informazioa"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Sarbide-gako bat sortu nahi duzu <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> aplikazioan?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Pasahitza <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> aplikazioan gorde nahi duzu?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Kredentzialei buruzko informazioa <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> aplikazioan gorde nahi duzu?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"<xliff:g id="APPDOMAINNAME">%1$s</xliff:g> aplikazioko <xliff:g id="TYPE">%2$s</xliff:g> edozein gailutan erabil dezakezu. <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> aplikazioan dago gordeta (<xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>)."</string>
+ <string name="passkey" msgid="632353688396759522">"sarbide-gakoa"</string>
+ <string name="password" msgid="6738570945182936667">"pasahitza"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"kredentzialak"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> erabili nahi duzu kredentzial guztietarako?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Ezarri lehenetsi gisa"</string>
+ <string name="use_once" msgid="9027366575315399714">"Erabili behin"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> pasahitz eta <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> sarbide-gako"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> pasahitz"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> sarbide-gako"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Beste gailu bat"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Beste pasahitz-kudeatzaile batzuk"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Itxi orria"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Itzuli aurreko orrira"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikaziorako gorde duzun sarbide-gakoa erabili nahi duzu?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikaziorako gorde dituzun kredentzialak erabili nahi dituzu?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Aukeratu <xliff:g id="APP_NAME">%1$s</xliff:g> aplikaziorako gorde dituzun kredentzialak"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Hasi saioa beste modu batean"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Ez, eskerrik asko"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Egin aurrera"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Saioa hasteko aukerak"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> erabiltzailearenak"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Blokeatutako pasahitz-kudeatzaileak"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Desblokeatzeko, sakatu hau"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Kudeatu kredentzialak"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Beste gailu batean gordetakoak"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Erabili beste gailu bat"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-fa/strings.xml b/packages/CredentialManager/res/values-fa/strings.xml
new file mode 100644
index 000000000000..fdfd1e39eb48
--- /dev/null
+++ b/packages/CredentialManager/res/values-fa/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"لغو"</string>
+ <string name="string_continue" msgid="1346732695941131882">"ادامه"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"ایجاد در مکانی دیگر"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"ذخیره در مکانی دیگر"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"استفاده از دستگاهی دیگر"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"ذخیره در دستگاهی دیگر"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"روشی ساده برای ورود به سیستم ایمن"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"برای ورود به سیستم با گذرکلیدی یکتا که غیرقابل فراموش شدن یا دزدیده شدن باشد، از اثر انگشت، چهره، یا قفل صفحه استفاده کنید. بیشتر بدانید"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"انتخاب محل <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"ذخیره گذرواژه"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"ذخیره اطلاعات ورود به سیستم"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"گذرکلید در <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ایجاد شود؟"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"گذرواژه در <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ذخیره شود؟"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"اطلاعات ورود به سیستم در <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ذخیره شود؟"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"می‌توانید از <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> در هر دستگاهی استفاده کنید. در <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> برای <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> ذخیره می‌شود"</string>
+ <string name="passkey" msgid="632353688396759522">"گذرکلید"</string>
+ <string name="password" msgid="6738570945182936667">"گذرواژه"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"ورود به سیستم‌ها"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"از <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> برای همه ورود به سیستم‌ها استفاده شود؟"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"تنظیم به‌عنوان پیش‌فرض"</string>
+ <string name="use_once" msgid="9027366575315399714">"یک‌بار استفاده"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> گذرواژه، <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> گذرکلید"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> گذرواژه"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> گذرکلید"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"دستگاهی دیگر"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"دیگر مدیران گذرواژه"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"بستن برگ"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"برگشتن به صفحه قبلی"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"گذرکلید ذخیره‌شده برای <xliff:g id="APP_NAME">%1$s</xliff:g> استفاده شود؟"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"ورود به سیستم ذخیره‌شده برای <xliff:g id="APP_NAME">%1$s</xliff:g> استفاده شود؟"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"انتخاب ورود به سیستم ذخیره‌شده برای <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"ورود به سیستم به روشی دیگر"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"نه متشکرم"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"ادامه"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"گزینه‌های ورود به سیستم"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"برای <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"مدیران گذرواژه قفل‌شده"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"برای باز کردن قفل ضربه بزنید"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"مدیریت ورود به سیستم‌ها"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"از دستگاهی دیگر"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"استفاده از دستگاه دیگری"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-fi/strings.xml b/packages/CredentialManager/res/values-fi/strings.xml
new file mode 100644
index 000000000000..26cfbe5fe890
--- /dev/null
+++ b/packages/CredentialManager/res/values-fi/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Peru"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Jatka"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Luo muualla"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Tallenna muualle"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Käytä toista laitetta"</string>
+ <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+ <skip />
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Helppo tapa kirjautua turvallisesti sisään"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Käytä sormenjälkeä, kasvoja tai näytön lukitusta, niin voit kirjautua sisään yksilöllisellä avainkoodilla, jota ei voi unohtaa tai varastaa. Lue lisää"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Valitse paikka: <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"tallenna salasanasi"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"tallenna kirjautumistiedot"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Luodaanko avainkoodi (<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>)?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Tallennetaanko salasanasi tänne: <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Tallennetaanko kirjautumistietosi tänne: <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"<xliff:g id="APPDOMAINNAME">%1$s</xliff:g> (<xliff:g id="TYPE">%2$s</xliff:g>) on käytettävissä millä tahansa laitteella. Se tallennetaan tänne: <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> (<xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>)"</string>
+ <string name="passkey" msgid="632353688396759522">"avainkoodi"</string>
+ <string name="password" msgid="6738570945182936667">"salasana"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"sisäänkirjautumiset"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Otetaanko <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> käyttöön kaikissa sisäänkirjautumisissa?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Aseta oletukseksi"</string>
+ <string name="use_once" msgid="9027366575315399714">"Käytä kerran"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> salasanaa, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> avainkoodia"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> salasanaa"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> avainkoodia"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Toinen laite"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Muut salasanojen ylläpitotyökalut"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Sulje taulukko"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Takaisin edelliselle sivulle"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Käytetäänkö tallennettua avainkoodiasi täällä: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Käytetäänkö tallennettuja kirjautumistietoja täällä: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Valitse tallennetut kirjautumistiedot (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Kirjaudu sisään toisella tavalla"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Ei kiitos"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Jatka"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Kirjautumisvaihtoehdot"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Käyttäjä: <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Lukitut salasanojen ylläpitotyökalut"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Avaa napauttamalla"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Muuta kirjautumistietoja"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Toiselta laitteelta"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Käytä toista laitetta"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-fr-rCA/strings.xml b/packages/CredentialManager/res/values-fr-rCA/strings.xml
new file mode 100644
index 000000000000..ef3d3252c698
--- /dev/null
+++ b/packages/CredentialManager/res/values-fr-rCA/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Annuler"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Continuer"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Créer à un autre emplacement"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Enregistrer à un autre emplacement"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Utiliser un autre appareil"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Enregistrer sur un autre appareil"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Une manière simple de se connecter en toute sécurité"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Utilisez vos empreintes digitales, votre visage ou un écran de verrouillage pour vous connecter avec une clé d\'accès unique qui ne peut pas être oubliée ou volée. En savoir plus"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Choisir où <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"enregistrer votre mot de passe"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"enregistrer vos données de connexion"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Créer une clé d\'accès dans <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Enregistrer votre mot de passe dans <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Enregistrer vos données de connexion dans <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Vous pouvez utiliser votre <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> sur n\'importe quel appareil. Il est enregistré sur <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> pour <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"clé d\'accès"</string>
+ <string name="password" msgid="6738570945182936667">"mot de passe"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"connexions"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Utiliser <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> pour toutes vos connexions?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Définir par défaut"</string>
+ <string name="use_once" msgid="9027366575315399714">"Utiliser une fois"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> mots de passe, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> clés d\'accès"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> mots de passe"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> clés d\'accès"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Un autre appareil"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Autres gestionnaires de mots de passe"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Fermer la feuille"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Retourner à la page précédente"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Utiliser votre clé d\'accès enregistrée pour <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Utiliser votre connexion enregistrée pour <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Choisir une connexion enregistrée pour <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Se connecter d\'une autre manière"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Non merci"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Continuer"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Options de connexion"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Pour <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Gestionnaires de mots de passe verrouillés"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Toucher pour déverrouiller"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Gérer les connexions"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"À partir d\'un autre appareil"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Utiliser un autre appareil"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-fr/strings.xml b/packages/CredentialManager/res/values-fr/strings.xml
new file mode 100644
index 000000000000..f660ddeb70df
--- /dev/null
+++ b/packages/CredentialManager/res/values-fr/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Annuler"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Continuer"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Créer ailleurs"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Enregistrer ailleurs"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Utiliser un autre appareil"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Enregistrer sur un autre appareil"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Une façon simple et sécurisée de vous connecter"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Utilisez votre empreinte digitale, votre visage ou le verrouillage de l\'écran pour vous connecter avec une clé d\'accès unique que vous ne pourrez pas oublier ni vous faire voler. En savoir plus"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Choisir où <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"enregistrer votre mot de passe"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"enregistrer vos informations de connexion"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Créer une clé d\'accès dans <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Enregistrer votre mot de passe dans <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Enregistrer vos informations de connexion dans <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Vous pouvez utiliser votre <xliff:g id="TYPE">%2$s</xliff:g> <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> sur n\'importe quel appareil. Il est enregistré dans <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> pour <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>."</string>
+ <string name="passkey" msgid="632353688396759522">"clé d\'accès"</string>
+ <string name="password" msgid="6738570945182936667">"mot de passe"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"connexions"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Utiliser <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> pour toutes vos connexions ?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Définir par défaut"</string>
+ <string name="use_once" msgid="9027366575315399714">"Utiliser une fois"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> mots de passe, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> clés d\'accès"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> mots de passe"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> clés d\'accès"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Un autre appareil"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Autres gestionnaires de mots de passe"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Fermer la feuille"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Revenir à la page précédente"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Utiliser votre clé d\'accès enregistrée pour <xliff:g id="APP_NAME">%1$s</xliff:g> ?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Utiliser vos informations de connexion enregistrées pour <xliff:g id="APP_NAME">%1$s</xliff:g> ?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Choisir des informations de connexion enregistrées pour <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Se connecter d\'une autre manière"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Non, merci"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Continuer"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Options de connexion"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Pour <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Gestionnaires de mots de passe verrouillés"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Appuyer pour déverrouiller"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Gérer les connexions"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Depuis un autre appareil"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Utiliser un autre appareil"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-gl/strings.xml b/packages/CredentialManager/res/values-gl/strings.xml
new file mode 100644
index 000000000000..cacec21c801e
--- /dev/null
+++ b/packages/CredentialManager/res/values-gl/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Cancelar"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Continuar"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Crear noutro lugar"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Gardar noutro lugar"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Usar outro dispositivo"</string>
+ <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+ <skip />
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Un xeito fácil de iniciar sesión de forma segura"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Usa a impresión dixital, a cara ou o bloqueo de pantalla para iniciar sesión cunha clave de acceso única que non podes esquecer nin cha poden roubar. Máis información"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Escolle onde <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"gardar o contrasinal"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"gardar a información de inicio de sesión"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Queres crear unha clave de acceso en <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Queres gardar o contrasinal en <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Queres gardar a información de inicio de sesión en <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Podes usar o teu <xliff:g id="TYPE">%2$s</xliff:g> de <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> en calquera dispositivo. Está gardado en <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> para <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"clave de acceso"</string>
+ <string name="password" msgid="6738570945182936667">"contrasinal"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"métodos de inicio de sesión"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Queres usar <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> cada vez que inicies sesión?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Establecer como predeterminado"</string>
+ <string name="use_once" msgid="9027366575315399714">"Usar unha vez"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> contrasinais, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> claves de acceso"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> contrasinais"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> claves de acceso"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Outro dispositivo"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Outros xestores de contrasinais"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Pechar folla"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Volver á páxina anterior"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Queres usar a clave de acceso gardada para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Queres usar o método de inicio de sesión gardado para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Escolle un método de inicio de sesión gardado para <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Iniciar sesión doutra forma"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Non, grazas"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Continuar"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opcións de inicio de sesión"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Para <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Xestores de contrasinais bloqueados"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Toca para desbloquear"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Xestionar os métodos de inicio de sesión"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Doutro dispositivo"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Usar outro dispositivo"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-gu/strings.xml b/packages/CredentialManager/res/values-gu/strings.xml
new file mode 100644
index 000000000000..7ac70aa91dcc
--- /dev/null
+++ b/packages/CredentialManager/res/values-gu/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"રદ કરો"</string>
+ <string name="string_continue" msgid="1346732695941131882">"ચાલુ રાખો"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"કોઈ અન્ય સ્થાન પર બનાવો"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"કોઈ અન્ય સ્થાન પર સાચવો"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"કોઈ અન્ય ડિવાઇસનો ઉપયોગ કરો"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"અન્ય ડિવાઇસ પર સાચવો"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"સલામત રીતે સાઇન ઇન કરવાની સરળ રીત"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"ભૂલી ન શકાય કે ચોરાઈ ન જાય, તેવી કોઈ વિશિષ્ટ પાસકી વડે સાઇન ઇન કરવા માટે, તમારી ફિંગરપ્રિન્ટ, ચહેરો અથવા સ્ક્રીન લૉકનો ઉપયોગ કરો. વધુ જાણો"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> ક્યાં સાચવવી છે, તે પસંદ કરો"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"તમારો પાસવર્ડ સાચવો"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"તમારી સાઇન-ઇનની માહિતી સાચવો"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"શું <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>માં કોઈ પાસકી બનાવીએ?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"શું તમારો પાસવર્ડ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>માં સાચવીએ?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"શું તમારી સાઇન-ઇનની માહિતી <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>માં સાચવીએ?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"તમે કોઈપણ ડિવાઇસ પર તમારા <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g>નો ઉપયોગ કરી શકો છો. <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> માટે <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>માં તેને સાચવવામાં આવે છે"</string>
+ <string name="passkey" msgid="632353688396759522">"પાસકી"</string>
+ <string name="password" msgid="6738570945182936667">"પાસવર્ડ"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"સાઇન-ઇન"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"શું તમારા બધા સાઇન-ઇન માટે <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>નો ઉપયોગ કરીએ?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"ડિફૉલ્ટ તરીકે સેટ કરો"</string>
+ <string name="use_once" msgid="9027366575315399714">"એકવાર ઉપયોગ કરો"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> પાસવર્ડ, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> પાસકી"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> પાસવર્ડ"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> પાસકી"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"કોઈ અન્ય ડિવાઇસ"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"અન્ય પાસવર્ડ મેનેજર"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"શીટ બંધ કરો"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"પાછલા પેજ પર પરત જાઓ"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> માટે શું તમારી સાચવેલી પાસકીનો ઉપયોગ કરીએ?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> માટે શું તમારા સાચવેલા સાઇન-ઇનનો ઉપયોગ કરીએ?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> માટે કોઈ સાચવેલું સાઇન-ઇન પસંદ કરો"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"કોઈ અન્ય રીતે સાઇન ઇન કરો"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"ના, આભાર"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"ચાલુ રાખો"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"સાઇન-ઇનના વિકલ્પો"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> માટે"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"લૉક કરેલા પાસવર્ડ મેનેજર"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"અનલૉક કરવા માટે ટૅપ કરો"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"સાઇન-ઇન મેનેજ કરો"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"કોઈ અન્ય ડિવાઇસમાંથી"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"કોઈ અન્ય ડિવાઇસનો ઉપયોગ કરો"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-hi/strings.xml b/packages/CredentialManager/res/values-hi/strings.xml
new file mode 100644
index 000000000000..8d28e0f90abc
--- /dev/null
+++ b/packages/CredentialManager/res/values-hi/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"रद्द करें"</string>
+ <string name="string_continue" msgid="1346732695941131882">"जारी रखें"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"दूसरी जगह पर बनाएं"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"दूसरी जगह पर सेव करें"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"दूसरे डिवाइस का इस्तेमाल करें"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"दूसरे डिवाइस पर सेव करें"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"सुरक्षित तरीके से साइन इन करने का आसान तरीका"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"साइन इन करने के लिए फ़िंगरप्रिंट, फ़ेस या स्क्रीन लॉक जैसी यूनीक पासकी का इस्तेमाल करें. इन्हें, न तो भुलाया जा सकता है न ही चुराया जा सकता है. ज़्यादा जानें"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"चुनें कि <xliff:g id="CREATETYPES">%1$s</xliff:g> कहां पर सेव करना है"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"अपना पासवर्ड सेव करें"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"साइन इन से जुड़ी अपनी जानकारी सेव करें"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"क्या आपको <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> में पासकी बनानी है?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"क्या आपको अपना पासवर्ड <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> में सेव करना है?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"क्या आपको साइन इन करने से जुड़ी जानकारी <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> में सेव करनी है?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"अपना <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> किसी भी डिवाइस पर इस्तेमाल किया जा सकता है. इसे <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> के लिए, <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> में सेव किया जाता है"</string>
+ <string name="passkey" msgid="632353688396759522">"पासकी"</string>
+ <string name="password" msgid="6738570945182936667">"पासवर्ड"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"साइन इन"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"क्या आपको साइन इन से जुड़ी सारी जानकारी सेव करने के लिए, <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> का इस्तेमाल करना है?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"डिफ़ॉल्ट के तौर पर सेट करें"</string>
+ <string name="use_once" msgid="9027366575315399714">"इसका इस्तेमाल एक बार किया जा सकता है"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> पासवर्ड और <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> पासकी"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> पासवर्ड"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> पासकी"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"दूसरा डिवाइस"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"दूसरे पासवर्ड मैनेजर"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"शीट बंद करें"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"पिछले पेज पर वापस जाएं"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"क्या आपको <xliff:g id="APP_NAME">%1$s</xliff:g> पर साइन इन करने के लिए, सेव की गई पासकी का इस्तेमाल करना है?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"क्या आपको <xliff:g id="APP_NAME">%1$s</xliff:g> पर साइन इन करने के लिए, सेव की गई जानकारी का इस्तेमाल करना है?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> पर साइन इन करने के लिए, सेव की गई जानकारी में से चुनें"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"किसी दूसरे तरीके से साइन इन करें"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"रहने दें"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"जारी रखें"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"साइन इन करने के विकल्प"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> के लिए"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"लॉक किए गए पासवर्ड मैनेजर"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"अनलॉक करने के लिए टैप करें"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"साइन इन करने की सुविधा को मैनेज करें"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"किसी दूसरे डिवाइस से"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"दूसरे डिवाइस का इस्तेमाल करें"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-hr/strings.xml b/packages/CredentialManager/res/values-hr/strings.xml
new file mode 100644
index 000000000000..06db58393092
--- /dev/null
+++ b/packages/CredentialManager/res/values-hr/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Odustani"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Nastavi"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Izradi na drugom mjestu"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Spremi na drugom mjestu"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Upotrijebite neki drugi uređaj"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Spremi na drugi uređaj"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Jednostavan način za sigurnu prijavu"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Prijavite se otiskom prsta, licem ili zaključavanjem zaslona kao jedinstvenim pristupnim ključem koji je nemoguće zaboraviti ili ukrasti. Saznajte više"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Odaberite mjesto za sljedeće: <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"spremi zaporku"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"spremi podatke za prijavu"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Želite li izraditi pristupni ključ na usluzi <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Želite li spremiti zaporku na usluzi <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Želite li spremiti podatke o prijavi na usluzi <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Aplikaciju <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> možete upotrijebiti na bilo kojem uređaju. Sprema se na uslugu <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> za: <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"pristupni ključ"</string>
+ <string name="password" msgid="6738570945182936667">"zaporka"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"prijave"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Želite li upotrebljavati uslugu <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> za sve prijave?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Postavi kao zadano"</string>
+ <string name="use_once" msgid="9027366575315399714">"Upotrijebi jednom"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"Broj zaporki: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>, broj pristupnih ključeva: <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"Broj zaporki: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Broj pristupnih ključeva: <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Drugi uređaj"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Drugi upravitelji zaporki"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Zatvaranje lista"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Vratite se na prethodnu stranicu"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Želite li upotrijebiti spremljeni pristupni ključ za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Želite li upotrijebiti spremljene podatke za prijavu za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Odaberite spremljene podatke za prijavu za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Prijavite se na neki drugi način"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Ne, hvala"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Nastavi"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opcije prijave"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Za korisnika <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Upravitelji zaključanih zaporki"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Dodirnite za otključavanje"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Upravljanje prijavama"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Na drugom uređaju"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Upotrijebite drugi uređaj"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-hu/strings.xml b/packages/CredentialManager/res/values-hu/strings.xml
new file mode 100644
index 000000000000..738de3a5482b
--- /dev/null
+++ b/packages/CredentialManager/res/values-hu/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Mégse"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Folytatás"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Létrehozás másik helyen"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Mentés másik helyre"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Másik eszköz használata"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Mentés másik eszközre"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"A biztonságos bejelentkezés egyszerű módja"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Ujjlenyomatát, arcát vagy képernyőzárát használva egyedi azonosítókulccsal jelentkezhet be, amelyet nem lehet elfelejteni vagy ellopni. További információ."</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Válassza ki a(z) <xliff:g id="CREATETYPES">%1$s</xliff:g> helyét"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"jelszó mentése"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"bejelentkezési adatok mentése"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Létrehoz azonosítókulcsot a(z) <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> szolgáltatásban?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Menti jelszavát a(z) <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> szolgáltatásba?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Menti bejelentkezési adatait a(z) <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> szolgáltatásba?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"A(z) <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> bármilyen eszközön használható. A(z) <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> szolgáltatásba van mentve a következő számára: <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"azonosítókulcs"</string>
+ <string name="password" msgid="6738570945182936667">"jelszó"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"bejelentkezési adatok"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Szeretné a következőt használni az összes bejelentkezési adatához: <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Beállítás alapértelmezettként"</string>
+ <string name="use_once" msgid="9027366575315399714">"Egyszeri használat"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> jelszó, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> azonosítókulcs"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> jelszó"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> azonosítókulcs"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Másik eszköz"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Egyéb jelszókezelők"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Munkalap bezárása"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Vissza az előző oldalra"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Szeretné a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazáshoz mentett azonosítókulcsot használni?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Szeretné a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazáshoz mentett bejelentkezési adatait használni?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Mentett bejelentkezési adatok választása a következő számára: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Bejelentkezés más módon"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Most nem"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Folytatás"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Bejelentkezési beállítások"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Zárolt jelszókezelők"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Koppintson a feloldáshoz"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Bejelentkezési adatok kezelése"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Másik eszközről"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Másik eszköz használata"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-hy/strings.xml b/packages/CredentialManager/res/values-hy/strings.xml
new file mode 100644
index 000000000000..7320e6411248
--- /dev/null
+++ b/packages/CredentialManager/res/values-hy/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Չեղարկել"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Շարունակել"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Ստեղծել այլ տեղում"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Պահել այլ տեղում"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Օգտագործել այլ սարք"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Պահել մեկ այլ սարքում"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Մուտք գործելու անվտանգ և պարզ եղանակ"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Օգտագործեք ձեր մատնահետքը, դեմքը կամ էկրանի կողպումը՝ մուտք գործելու հաշիվ եզակի անցաբառի միջոցով, որը հնարավոր չէ կոտրել կամ մոռանալ։ Իմանալ ավելին"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Ընտրեք, թե որտեղ <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"պահել գաղտնաբառը"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"պահել մուտքի տվյալները"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Ստեղծե՞լ անցաբառ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> հավելվածում"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Պահե՞լ ձեր գաղտնաբառը <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> հավելվածում"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Պահե՞լ ձեր մուտքի տվյալները <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> հավելվածում"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Դուք կարող եք օգտագործել <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> ցանկացած սարքում։ <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>-ի տվյալները պահված են <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> հավելվածում։"</string>
+ <string name="passkey" msgid="632353688396759522">"անցաբառ"</string>
+ <string name="password" msgid="6738570945182936667">"գաղտնաբառ"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"մուտք"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Միշտ մուտք գործե՞լ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> հավելվածի միջոցով"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Նշել որպես կանխադրված"</string>
+ <string name="use_once" msgid="9027366575315399714">"Օգտագործել մեկ անգամ"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> գաղտնաբառ, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> անցաբառ"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> գաղտնաբառ"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> անցաբառ"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Այլ սարք"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Գաղտնաբառերի այլ կառավարիչներ"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Փակել թերթը"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Անցնել նախորդ էջ"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Օգտագործե՞լ պահված անցաբառը <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի համար"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Օգտագործե՞լ մուտքի պահված տվյալները <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի համար"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Ընտրեք մուտքի պահված տվյալներ <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի համար"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Մուտք գործել այլ եղանակով"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Ոչ, շնորհակալություն"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Շարունակել"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Մուտքի տարբերակներ"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g>-ի համար"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Գաղտնաբառերի կողպված կառավարիչներ"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Հպեք՝ ապակողպելու համար"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Մուտքի կառավարում"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Մեկ այլ սարքից"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Օգտագործել այլ սարք"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-in/strings.xml b/packages/CredentialManager/res/values-in/strings.xml
new file mode 100644
index 000000000000..827a4ff2b33d
--- /dev/null
+++ b/packages/CredentialManager/res/values-in/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Batal"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Lanjutkan"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Buat di tempat lain"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Simpan ke tempat lain"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Gunakan perangkat lain"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Simpan ke perangkat lain"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Cara mudah untuk login dengan aman"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Gunakan sidik jari, wajah, atau kunci layar untuk login dengan kunci sandi unik yang mudah diingat dan tidak dapat dicuri. Pelajari lebih lanjut"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Pilih tempat untuk <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"menyimpan sandi Anda"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"menyimpan info login Anda"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Buat kunci sandi di <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Simpan sandi ke <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Simpan info login ke <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Anda dapat menggunakan <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> di perangkat mana pun. Disimpan ke <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> untuk <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"kunci sandi"</string>
+ <string name="password" msgid="6738570945182936667">"sandi"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"login"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Gunakan <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> untuk semua info login Anda?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Setel sebagai default"</string>
+ <string name="use_once" msgid="9027366575315399714">"Gunakan sekali"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> sandi, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> kunci sandi"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> sandi"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> kunci sandi"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Perangkat lain"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Pengelola sandi lainnya"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Tutup sheet"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Kembali ke halaman sebelumnya"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Gunakan kunci sandi tersimpan untuk <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Gunakan info login tersimpan untuk <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Pilih info login tersimpan untuk <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Login dengan cara lain"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Lain kali"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Lanjutkan"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opsi login"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Untuk <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Pengelola sandi terkunci"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Ketuk untuk membuka kunci"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Kelola login"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Dari perangkat lain"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Gunakan perangkat lain"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-is/strings.xml b/packages/CredentialManager/res/values-is/strings.xml
new file mode 100644
index 000000000000..c52e5f7815f5
--- /dev/null
+++ b/packages/CredentialManager/res/values-is/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Hætta við"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Áfram"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Búa til annarsstaðar"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Vista annarsstaðar"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Nota annað tæki"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Vista í öðru tæki"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Einföld leið við örugga innskráningu"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Notaðu fingrafar, andlit eða skjálás til að skrá þig inn með einkvæmum aðgangslykli sem ekki er hægt að gleyma eða stela. Nánar"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Veldu hvar á að <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"vistaðu aðgangsorðið"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"vistaðu innskráningarupplýsingarnar"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Búa til aðgangslykil í <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Vista aðgangsorð í <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Vista innskráningarupplýsingar í <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Þú getur notað <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> í hvaða tæki sem er. Það er vistað á <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> fyrir <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"aðgangslykill"</string>
+ <string name="password" msgid="6738570945182936667">"aðgangsorð"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"innskráningar"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Nota <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> fyrir allar innskráningar?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Stilla sem sjálfgefið"</string>
+ <string name="use_once" msgid="9027366575315399714">"Nota einu sinni"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> aðgangsorð, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> aðgangslyklar"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> aðgangsorð"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> aðgangslyklar"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Annað tæki"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Önnur aðgangsorðastjórnun"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Loka blaði"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Fara aftur á fyrri síðu"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Notað vistaðan aðgangslykil fyrir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Nota vistaða innskráningu fyrir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Veldu vistaða innskráningu fyrir <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Skrá inn með öðrum hætti"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Nei takk"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Áfram"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Innskráningarkostir"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Fyrir: <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Læst aðgangsorðastjórnun"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Ýttu til að opna"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Stjórna innskráningu"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Úr öðru tæki"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Nota annað tæki"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-it/strings.xml b/packages/CredentialManager/res/values-it/strings.xml
new file mode 100644
index 000000000000..a06135e0ac0f
--- /dev/null
+++ b/packages/CredentialManager/res/values-it/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Annulla"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Continua"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Crea in un altro luogo"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Salva in un altro luogo"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Usa un altro dispositivo"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Salva su un altro dispositivo"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Un modo semplice per accedere in sicurezza"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Usa l\'impronta digitale, il volto o il blocco schermo per accedere con una passkey unica che non può essere dimenticata o rubata. Scopri di più"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Scegli dove <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"salva la password"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"salva le tue informazioni di accesso"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Vuoi creare una passkey su <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Vuoi salvare la password su <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Vuoi salvare le informazioni di accesso su <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Puoi utilizzare <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> su qualsiasi dispositivo. Questa informazione è salvata su <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> per <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"passkey"</string>
+ <string name="password" msgid="6738570945182936667">"password"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"accessi"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Vuoi usare <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> per tutti gli accessi?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Imposta come valore predefinito"</string>
+ <string name="use_once" msgid="9027366575315399714">"Usa una volta"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> password, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> passkey"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> password"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> passkey"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Un altro dispositivo"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Altri gestori delle password"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Chiudi il foglio"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Torna alla pagina precedente"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Vuoi usare la passkey salvata per <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Vuoi usare l\'accesso salvato per <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Scegli un accesso salvato per <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Accedi in un altro modo"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"No, grazie"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Continua"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opzioni di accesso"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Per <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Gestori delle password bloccati"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Tocca per sbloccare"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Gestisci gli accessi"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Da un altro dispositivo"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Usa un dispositivo diverso"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-iw/strings.xml b/packages/CredentialManager/res/values-iw/strings.xml
new file mode 100644
index 000000000000..e9c6adb744c7
--- /dev/null
+++ b/packages/CredentialManager/res/values-iw/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"ביטול"</string>
+ <string name="string_continue" msgid="1346732695941131882">"המשך"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"יצירה במקום אחר"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"שמירה במקום אחר"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"שימוש במכשיר אחר"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"שמירה במכשיר אחר"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"דרך פשוטה להיכנס לחשבון בבטחה"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"אפשר להשתמש בטביעת אצבע, בזיהוי פנים או בנעילת מסך כדי להיכנס לחשבון עם מפתח גישה ייחודי שאי אפשר לשכוח או לגנוב אותו. מידע נוסף"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"צריך לבחור לאן <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"שמירת הסיסמה שלך"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"שמירת פרטי הכניסה שלך"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"ליצור מפתח גישה ב-<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"לשמור את הסיסמה ב-<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"לשמור את פרטי הכניסה ב-<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"אפשר להשתמש ב-<xliff:g id="APPDOMAINNAME">%1$s</xliff:g> מסוג <xliff:g id="TYPE">%2$s</xliff:g> בכל מכשיר. הוא שמור ב-<xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> ל-<xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"מפתח גישה"</string>
+ <string name="password" msgid="6738570945182936667">"סיסמה"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"פרטי כניסה"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"להשתמש ב-<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> בכל הכניסות?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"הגדרה כברירת מחדל"</string>
+ <string name="use_once" msgid="9027366575315399714">"שימוש פעם אחת"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> סיסמאות, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> מפתחות גישה"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> סיסמאות"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> מפתחות גישה"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"מכשיר אחר"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"מנהלי סיסמאות אחרים"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"סגירת הגיליון"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"חזרה לדף הקודם"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"להשתמש במפתח גישה שנשמר עבור <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"להשתמש בפרטי הכניסה שנשמרו עבור <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"בחירת פרטי כניסה שמורים עבור <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"כניסה בדרך אחרת"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"לא תודה"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"המשך"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"אפשרויות כניסה לחשבון"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"עבור <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"מנהלי סיסמאות נעולים"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"יש להקיש כדי לבטל את הנעילה"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"ניהול כניסות"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"ממכשיר אחר"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"צריך להשתמש במכשיר אחר"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-ja/strings.xml b/packages/CredentialManager/res/values-ja/strings.xml
new file mode 100644
index 000000000000..8e448eb5d420
--- /dev/null
+++ b/packages/CredentialManager/res/values-ja/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"キャンセル"</string>
+ <string name="string_continue" msgid="1346732695941131882">"続行"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"別の場所で作成"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"別の場所に保存"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"別のデバイスを使用"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"他のデバイスに保存"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"安全にログインする簡単な方法"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"忘れたり盗まれたりする可能性がある一意のパスキーと合わせて、ログインに指紋認証、顔認証、画面ロックを使用できます。詳細"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> の保存場所の選択"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"パスワードを保存"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"ログイン情報を保存"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> でパスキーを作成しますか?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> にパスワードを保存しますか?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> にログイン情報を保存しますか?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"<xliff:g id="APPDOMAINNAME">%1$s</xliff:g> の <xliff:g id="TYPE">%2$s</xliff:g> はどのデバイスでも使用できます。<xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> の <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> に保存されます"</string>
+ <string name="passkey" msgid="632353688396759522">"パスキー"</string>
+ <string name="password" msgid="6738570945182936667">"パスワード"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"ログイン"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"ログインのたびに <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> を使用しますか?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"デフォルトに設定"</string>
+ <string name="use_once" msgid="9027366575315399714">"1 回使用"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> 件のパスワード、<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> 件のパスキー"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> 件のパスワード"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> 件のパスキー"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"別のデバイス"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"他のパスワード マネージャー"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"シートを閉じます"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"前のページに戻ります"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> の保存したパスキーを使用しますか?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> の保存したログイン情報を使用しますか?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> の保存したログイン情報の選択"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"別の方法でログイン"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"利用しない"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"続行"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"ログイン オプション"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> 用"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"パスワード マネージャー ロック中"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"タップしてロック解除"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"ログインを管理"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"別のデバイスを使う"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"別のデバイスを使用"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-ka/strings.xml b/packages/CredentialManager/res/values-ka/strings.xml
new file mode 100644
index 000000000000..853ea19fa0e9
--- /dev/null
+++ b/packages/CredentialManager/res/values-ka/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"გაუქმება"</string>
+ <string name="string_continue" msgid="1346732695941131882">"გაგრძელება"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"სხვა სივრცეში შექმნა"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"სხვა სივრცეში შენახვა"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"სხვა მოწყობილობის გამოყენება"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"სხვა მოწყობილობაზე შენახვა"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"უსაფრთხოდ შესვლის მარტივი გზა"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"გამოიყენეთ თქვენი თითის ანაბეჭდი, სახის ამოცნობა და ეკრანის დაბლოკვა სისტემაში უნიკალური წვდომის გასაღებით შესასვლელად, რომლის დავიწყება ან მოპარვა შეუძლებელია. შეიტყვეთ მეტი"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"აირჩიეთ, სად უნდა <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"შეინახეთ თქვენი პაროლი"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"შეინახეთ თქვენი სისტემაში შესვლის ინფორმაცია"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"გსურთ წვდომის გასაღების შექმნა <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>-ში?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"გსურთ თქვენი პაროლის შენახვა <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>-ში?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"გსურთ თქვენი სისტემაში შესვლის მონაცემების შენახვა <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>-ში?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"შეგიძლიათ გამოიყენოთ თქვენი <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> ნებისმიერ მოწყობილობაზე. ის შეინახება <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>-ზე <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>-თვის"</string>
+ <string name="passkey" msgid="632353688396759522">"წვდომის გასაღები"</string>
+ <string name="password" msgid="6738570945182936667">"პაროლი"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"სისტემაში შესვლა"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"გსურთ, გამოიყენოთ<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> სისტემაში ყველა შესვლისთვის?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"ნაგულისხმევად დაყენება"</string>
+ <string name="use_once" msgid="9027366575315399714">"ერთხელ გამოყენება"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> პაროლი, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> წვდომის გასაღები"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> პაროლი"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> წვდომის გასაღები"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"სხვა მოწყობილობა"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"პაროლების სხვა მმართველები"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"ფურცლის დახურვა"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"წინა გვერდზე დაბრუნება"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"გსურთ თქვენი დამახსოვრებული წვდომის გასაღების გამოყენება აპისთვის: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"გსურთ თქვენი დამახსოვრებული სისტემაში შესვლის მონაცემების გამოყენება აპისთვის: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"აირჩიეთ სისტემაში შესვლის ინფორმაცია აპისთვის: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"სხვა ხერხით შესვლა"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"არა, გმადლობთ"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"გაგრძელება"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"სისტემაში შესვლის ვარიანტები"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g>-ისთვის"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"ჩაკეტილი პაროლის მმართველები"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"შეეხეთ განსაბლოკად"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"სისტემაში შესვლის მონაცემების მართვა"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"სხვა მოწყობილობიდან"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"გამოიყენეთ სხვა მოწყობილობა"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-kk/strings.xml b/packages/CredentialManager/res/values-kk/strings.xml
new file mode 100644
index 000000000000..2271533e650d
--- /dev/null
+++ b/packages/CredentialManager/res/values-kk/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Бас тарту"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Жалғастыру"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Басқа орында жасау"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Басқа орынға сақтау"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Басқа құрылғыны пайдалану"</string>
+ <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+ <skip />
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Қауіпсіз кірудің оңай жолы"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Ұмытылмайтын немесе ұрланбайтын бірегей кіру кілтінің көмегімен кіру үшін саусақ ізін, бетті анықтау функциясын немесе экран құлпын пайдаланыңыз. Толық ақпарат"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> таңдау"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"құпия сөзді сақтау"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"тіркелу деректерін сақтау"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> қолданбасында кіру кілті жасалсын ба?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> қолданбасына құпия сөз сақталсын ба?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> қолданбасына тіркелу деректері сақталсын ба?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"<xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> кез келген құрылғыда пайдаланылады. <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> тіркелу деректері <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> қолданбасында сақталады."</string>
+ <string name="passkey" msgid="632353688396759522">"кіру кілті"</string>
+ <string name="password" msgid="6738570945182936667">"құпия сөз"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"кіру әрекеттері"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Барлық кіру әрекеті үшін <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> пайдаланылсын ба?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Әдепкі етіп орнату"</string>
+ <string name="use_once" msgid="9027366575315399714">"Бір рет пайдалану"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> құпия сөз, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> кіру кілті"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> құпия сөз"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> кіру кілті"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Басқа құрылғы"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Басқа құпия сөз менеджерлері"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Парақты жабу"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Алдыңғы бетке оралу"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> үшін сақталған кіру кілті пайдаланылсын ба?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> үшін сақталған тіркелу деректері пайдаланылсын ба?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> үшін сақталған тіркелу деректерін таңдаңыз"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Басқаша кіру"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Жоқ, рақмет"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Жалғастыру"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Кіру опциялары"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> үшін"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Құлыпталған құпия сөз менеджерлері"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Құлыпты ашу үшін түртіңіз."</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Кіру әрекеттерін басқару"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Басқа құрылғыдан жасау"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Басқа құрылғыны пайдалану"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-km/strings.xml b/packages/CredentialManager/res/values-km/strings.xml
new file mode 100644
index 000000000000..d51781009950
--- /dev/null
+++ b/packages/CredentialManager/res/values-km/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"បោះបង់"</string>
+ <string name="string_continue" msgid="1346732695941131882">"បន្ត"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"បង្កើតនៅកន្លែងផ្សេងទៀត"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"រក្សាទុកក្នុងកន្លែងផ្សេងទៀត"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"ប្រើ​ឧបករណ៍​ផ្សេងទៀត"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"រក្សាទុកទៅក្នុងឧបករណ៍ផ្សេង"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"វិធីដ៏សាមញ្ញ ដើម្បីចូលគណនីដោយសុវត្ថិភាព"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"ប្រើស្នាមម្រាមដៃ មុខ ឬការចាក់សោអេក្រង់របស់អ្នក ដើម្បីចូលគណនីដោយប្រើកូដសម្ងាត់ខុសប្លែកពីគេដែលមិនអាចភ្លេច ឬត្រូវគេលួច។ ស្វែងយល់បន្ថែម"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"ជ្រើសរើសកន្លែងដែលត្រូវ <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"រក្សាទុកពាក្យសម្ងាត់របស់អ្នក"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"រក្សាទុកព័ត៌មានចូលគណនីរបស់អ្នក"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"បង្កើតកូដសម្ងាត់នៅក្នុង <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ឬ?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"រក្សាទុកពាក្យសម្ងាត់របស់អ្នកក្នុង <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ឬ?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"រក្សាទុកព័ត៌មានចូលគណនីរបស់អ្នកក្នុង <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ឬ?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"អ្នកអាចប្រើ <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> របស់អ្នកនៅលើឧបករណ៍ណាក៏បាន។ វាត្រូវបានរក្សាទុកក្នុង <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> សម្រាប់ <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"កូដសម្ងាត់"</string>
+ <string name="password" msgid="6738570945182936667">"ពាក្យសម្ងាត់"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"ការចូល​គណនី"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"ប្រើ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> សម្រាប់ការចូលគណនីទាំងអស់របស់អ្នកឬ?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"កំណត់ជាលំនាំដើម"</string>
+ <string name="use_once" msgid="9027366575315399714">"ប្រើម្ដង"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"ពាក្យសម្ងាត់ <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> កូដសម្ងាត់ <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"ពាក្យសម្ងាត់ <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"កូដសម្ងាត់ <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"ឧបករណ៍​ផ្សេងទៀត"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"កម្មវិធីគ្រប់គ្រងពាក្យសម្ងាត់ផ្សេងទៀត"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"បិទសន្លឹក"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"ត្រឡប់ទៅ​ទំព័រ​មុនវិញ"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"ប្រើកូដសម្ងាត់ដែលបានរក្សាទុករបស់អ្នកសម្រាប់ <xliff:g id="APP_NAME">%1$s</xliff:g> ឬ?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"ប្រើការចូល​គណនីដែលបានរក្សាទុករបស់អ្នកសម្រាប់ <xliff:g id="APP_NAME">%1$s</xliff:g> ឬ?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"ជ្រើសរើសការចូលគណនីដែលបានរក្សាទុកសម្រាប់ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"ចូលគណនីដោយប្រើវិធីផ្សេងទៀត"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"ទេ អរគុណ"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"បន្ត"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"ជម្រើស​ចូលគណនី"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"សម្រាប់ <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"កម្មវិធីគ្រប់គ្រងពាក្យសម្ងាត់ដែលបានចាក់សោ"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"ចុចដើម្បីដោះសោ"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"គ្រប់គ្រងការចូល​គណនី"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"ពីឧបករណ៍ផ្សេងទៀត"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"ប្រើឧបករណ៍ផ្សេង"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-kn/strings.xml b/packages/CredentialManager/res/values-kn/strings.xml
new file mode 100644
index 000000000000..763f8eef9203
--- /dev/null
+++ b/packages/CredentialManager/res/values-kn/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"ರದ್ದುಗೊಳಿಸಿ"</string>
+ <string name="string_continue" msgid="1346732695941131882">"ಮುಂದುವರಿಸಿ"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"ಮತ್ತೊಂದು ಸ್ಥಳದಲ್ಲಿ ರಚಿಸಿ"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"ಮತ್ತೊಂದು ಸ್ಥಳದಲ್ಲಿ ಉಳಿಸಿ"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"ಬೇರೊಂದು ಸಾಧನವನ್ನು ಬಳಸಿ"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"ಬೇರೊಂದು ಸಾಧನದಲ್ಲಿ ಉಳಿಸಿ"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"ಸುರಕ್ಷಿತವಾಗಿ ಸೈನ್ ಇನ್ ಮಾಡುವ ಸುಲಭ ವಿಧಾನ"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"ಮರೆಯಲಾಗದ ಅಥವಾ ಕದಿಯಲಾಗದ ಅನನ್ಯ ಪಾಸ್‌ಕೀ ಮೂಲಕ ಸೈನ್ ಇನ್ ಮಾಡಲು ನಿಮ್ಮ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್, ಫೇಸ್ ಲಾಕ್ ಅಥವಾ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಬಳಸಿ. ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> ಅನ್ನು ಎಲ್ಲಿ ಉಳಿಸಬೇಕು ಎಂದು ಆಯ್ಕೆಮಾಡಿ"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"ನಿಮ್ಮ ಪಾಸ್‌ವರ್ಡ್‌ ಉಳಿಸಿ"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"ನಿಮ್ಮ ಸೈನ್-ಇನ್ ಮಾಹಿತಿ ಉಳಿಸಿ"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ನಲ್ಲಿ ಪಾಸ್‌ಕೀ ರಚಿಸಬೇಕೆ?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"ನಿಮ್ಮ ಪಾಸ್‌ವರ್ಡ್ ಅನ್ನು <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ಗೆ ಉಳಿಸಬೇಕೆ?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"ನಿಮ್ಮ ಸೈನ್ ಇನ್ ಮಾಹಿತಿಯನ್ನು <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ಗೆ ಉಳಿಸಬೇಕೆ?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"ನೀವು ಯಾವುದೇ ಸಾಧನದಲ್ಲಿ ನಿಮ್ಮ <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> ಅನ್ನು ಬಳಸಬಹುದು. ಇದನ್ನು <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> ಗಾಗಿ <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> ಗೆ ಉಳಿಸಲಾಗಿದೆ"</string>
+ <string name="passkey" msgid="632353688396759522">"ಪಾಸ್‌ಕೀ"</string>
+ <string name="password" msgid="6738570945182936667">"ಪಾಸ್‌ವರ್ಡ್"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"ಸೈನ್-ಇನ್‌ಗಳು"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"ನಿಮ್ಮ ಎಲ್ಲಾ ಸೈನ್-ಇನ್‌ಗಳಿಗಾಗಿ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ಅನ್ನು ಬಳಸುವುದೇ?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"ಡೀಫಾಲ್ಟ್ ಆಗಿ ಸೆಟ್ ಮಾಡಿ"</string>
+ <string name="use_once" msgid="9027366575315399714">"ಒಂದು ಬಾರಿ ಬಳಸಿ"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> ಪಾಸ್‌ವರ್ಡ್‌ಗಳು, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> ಪಾಸ್‌ಕೀಗಳು"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> ಪಾಸ್‌ವರ್ಡ್‌ಗಳು"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> ಪಾಸ್‌ಕೀಗಳು"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"ಮತ್ತೊಂದು ಸಾಧನ"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"ಇತರ ಪಾಸ್‌ವರ್ಡ್ ನಿರ್ವಾಹಕರು"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"ಶೀಟ್ ಮುಚ್ಚಿರಿ"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"ಹಿಂದಿನ ಪುಟಕ್ಕೆ ಹಿಂದಿರುಗಿ"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಗಾಗಿ ಉಳಿಸಲಾದ ನಿಮ್ಮ ಪಾಸ್‌ಕೀ ಅನ್ನು ಬಳಸಬೇಕೆ?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಗಾಗಿ ಉಳಿಸಲಾದ ನಿಮ್ಮ ಸೈನ್-ಇನ್ ಅನ್ನು ಬಳಸಬೇಕೆ?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಗಾಗಿ ಉಳಿಸಲಾದ ಸೈನ್-ಇನ್ ಮಾಹಿತಿಯನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"ಬೇರೆ ವಿಧಾನದಲ್ಲಿ ಸೈನ್ ಇನ್ ಮಾಡಿ"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"ಬೇಡ"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"ಮುಂದುವರಿಸಿ"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"ಸೈನ್ ಇನ್ ಆಯ್ಕೆಗಳು"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> ಗಾಗಿ"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"ಪಾಸ್‌ವರ್ಡ್ ನಿರ್ವಾಹಕರನ್ನು ಲಾಕ್ ಮಾಡಲಾಗಿದೆ"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"ಅನ್‌ಲಾಕ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"ಸೈನ್-ಇನ್‌ಗಳನ್ನು ನಿರ್ವಹಿಸಿ"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"ಮತ್ತೊಂದು ಸಾಧನದಿಂದ"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"ಬೇರೆ ಸಾಧನವನ್ನು ಬಳಸಿ"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-ko/strings.xml b/packages/CredentialManager/res/values-ko/strings.xml
new file mode 100644
index 000000000000..246790d1c774
--- /dev/null
+++ b/packages/CredentialManager/res/values-ko/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"취소"</string>
+ <string name="string_continue" msgid="1346732695941131882">"계속"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"다른 위치에 만들기"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"다른 위치에 저장"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"다른 기기 사용"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"다른 기기에 저장"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"안전하게 로그인하는 간단한 방법"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"지문, 얼굴 인식 또는 화면 잠금을 통해 잊어버리거나 분실할 염려가 없는 고유한 패스키로 로그인하세요. 자세히 알아보기"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> 작업을 위한 위치 선택"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"비밀번호 저장"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"로그인 정보 저장"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>에 패스키를 만드시겠습니까?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>에 비밀번호를 저장하시겠습니까?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>에 로그인 정보를 저장하시겠습니까?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"기기에서 <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g>을(를) 사용할 수 있습니다. <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>을(를) 위해 <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>에 저장되어 있습니다."</string>
+ <string name="passkey" msgid="632353688396759522">"패스키"</string>
+ <string name="password" msgid="6738570945182936667">"비밀번호"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"로그인 정보"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"모든 로그인에 <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>을(를) 사용하시겠습니까?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"기본값으로 설정"</string>
+ <string name="use_once" msgid="9027366575315399714">"한 번 사용"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"비밀번호 <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>개, 패스키 <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>개"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"비밀번호 <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>개"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"패스키 <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>개"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"다른 기기"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"기타 비밀번호 관리자"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"시트 닫기"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"이전 페이지로 돌아가기"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> 앱용 저장된 패스키를 사용하시겠습니까?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> 앱용 저장된 로그인 정보를 사용하시겠습니까?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> 앱용 저장된 로그인 정보 선택"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"다른 방법으로 로그인"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"아니요"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"계속"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"로그인 옵션"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g>님의 로그인 정보"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"잠긴 비밀번호 관리자"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"잠금 해제하려면 탭하세요."</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"로그인 관리"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"다른 기기에서"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"다른 기기 사용"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-ky/strings.xml b/packages/CredentialManager/res/values-ky/strings.xml
new file mode 100644
index 000000000000..3dc7deaabd42
--- /dev/null
+++ b/packages/CredentialManager/res/values-ky/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Жок"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Улантуу"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Башка жерде түзүү"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Башка жерге сактоо"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Башка түзмөк колдонуу"</string>
+ <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+ <skip />
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Коопсуз кирүүнүн жөнөкөй жолу"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Унутуп калууга же уурдатууга мүмкүн эмес болгон уникалдуу ачкыч менен манжа изин, жүзүнөн таанып ачуу же экранды кулпулоо функцияларын колдонуп өзүңүздү ырастай аласыз. Кененирээк"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> үчүн жер тандаңыз"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"сырсөзүңүздү сактаңыз"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"кирүү маалыматын сактаңыз"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> колдонмосунда мүмкүндүк алуу ачкычын түзөсүзбү?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Сырсөзүңүздү <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> колдонмосунда сактайсызбы?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Кирүү маалыматын <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> колдонмосунда сактайсызбы?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"<xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> каалаган түзмөктө колдонулат. <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> байланыштуу маалыматтар <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> колдонмосунда сакталат"</string>
+ <string name="passkey" msgid="632353688396759522">"мүмкүндүк алуу ачкычы"</string>
+ <string name="password" msgid="6738570945182936667">"сырсөз"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"кирүүлөр"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> бардык аккаунттарга кирүү үчүн колдонулсунбу?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Демейки катары коюу"</string>
+ <string name="use_once" msgid="9027366575315399714">"Бир жолу колдонуу"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> сырсөз, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> мүмкүндүк алуу ачкычы"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> сырсөз"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> мүмкүндүк алуу ачкычы"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Башка түзмөк"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Башка сырсөздөрдү башкаргычтар"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Баракты жабуу"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Мурунку бетке кайтуу"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> үчүн сакталган мүмкүндүк алуу ачкычын колдоносузбу?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> үчүн сакталган кирүү параметрин колдоносузбу?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> үчүн кирүү маалыматын тандаңыз"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Башка жол менен кирүү"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Жок, рахмат"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Улантуу"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Аккаунтка кирүү параметрлери"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> үчүн"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Кулпуланган сырсөздөрдү башкаргычтар"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Кулпусун ачуу үчүн таптаңыз"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Кирүү параметрлерин тескөө"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Башка түзмөктөн"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Башка түзмөктү колдонуу"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-lo/strings.xml b/packages/CredentialManager/res/values-lo/strings.xml
new file mode 100644
index 000000000000..8efc8a8d31f1
--- /dev/null
+++ b/packages/CredentialManager/res/values-lo/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"ຍົກເລີກ"</string>
+ <string name="string_continue" msgid="1346732695941131882">"ສືບຕໍ່"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"ສ້າງໃນບ່ອນອື່ນ"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"ບັນທຶກໃສ່ບ່ອນອື່ນ"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"ໃຊ້ອຸປະກອນອື່ນ"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"ບັນທຶກໃສ່ອຸປະກອນອື່ນ"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"ວິທີງ່າຍໆໃນການເຂົ້າສູ່ລະບົບຢ່າງປອດໄພ"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"ໃຊ້ລາຍນິ້ວມື, ໃບໜ້າ ຫຼື ລັອກໜ້າຈໍຂອງທ່ານເພື່ອເຂົ້າສູ່ລະບົບດ້ວຍກະແຈຜ່ານທີ່ບໍ່ຊ້ຳກັນເພື່ອບໍ່ໃຫ້ລືມ ຫຼື ຖືກລັກໄດ້. ສຶກສາເພີ່ມເຕີມ"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"ເລືອກບ່ອນທີ່ຈະ <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"ບັນທຶກລະຫັດຜ່ານຂອງທ່ານ"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"ບັນທຶກຂໍ້ມູນການເຂົ້າສູ່ລະບົບຂອງທ່ານ"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"ສ້າງກະແຈຜ່ານໃນ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ບໍ?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"ບັນທຶກລະຫັດຜ່ານຂອງທ່ານໃສ່ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ບໍ?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"ບັນທຶກຂໍ້ມູນການເຂົ້າສູ່ລະບົບຂອງທ່ານໃສ່ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ບໍ?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"ທ່ານສາມາດໃຊ້ <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> ຂອງທ່ານຢູ່ອຸປະກອນໃດກໍໄດ້. ມັນຈະຖືກບັນທຶກໃສ່ <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> ສຳລັບ <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"ກະແຈຜ່ານ"</string>
+ <string name="password" msgid="6738570945182936667">"ລະຫັດຜ່ານ"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"ການເຂົ້າສູ່ລະບົບ"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"ໃຊ້ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ສຳລັບການເຂົ້າສູ່ລະບົບທັງໝົດຂອງທ່ານບໍ?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"ຕັ້ງເປັນຄ່າເລີ່ມຕົ້ນ"</string>
+ <string name="use_once" msgid="9027366575315399714">"ໃຊ້ເທື່ອດຽວ"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> ລະຫັດຜ່ານ, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> ກະແຈຜ່ານ"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> ລະຫັດຜ່ານ"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> ກະແຈຜ່ານ"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"ອຸປະກອນອື່ນ"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"ຕົວຈັດການລະຫັດຜ່ານອື່ນໆ"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"ປິດຊີດ"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"ກັບຄືນໄປຫາໜ້າກ່ອນໜ້ານີ້"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"ໃຊ້ກະແຈຜ່ານທີ່ບັນທຶກໄວ້ຂອງທ່ານສຳລັບ <xliff:g id="APP_NAME">%1$s</xliff:g> ບໍ?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"ໃຊ້ການເຂົ້າສູ່ລະບົບທີ່ບັນທຶກໄວ້ຂອງທ່ານສຳລັບ <xliff:g id="APP_NAME">%1$s</xliff:g> ບໍ?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"ເລືອກການເຂົ້າສູ່ລະບົບທີ່ບັນທຶກໄວ້ສຳລັບ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"ເຂົ້າສູ່ລະບົບດ້ວຍວິທີອື່ນ"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"ບໍ່, ຂອບໃຈ"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"ສືບຕໍ່"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"ຕົວເລືອກການເຂົ້າສູ່ລະບົບ"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"ສຳລັບ <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"ຕົວຈັດການລະຫັດຜ່ານທີ່ລັອກໄວ້"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"ແຕະເພື່ອປົດລັອກ"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"ຈັດການການເຂົ້າສູ່ລະບົບ"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"ຈາກອຸປະກອນອື່ນ"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"ໃຊ້ອຸປະກອນອື່ນ"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-lt/strings.xml b/packages/CredentialManager/res/values-lt/strings.xml
new file mode 100644
index 000000000000..02d37837ad43
--- /dev/null
+++ b/packages/CredentialManager/res/values-lt/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Atšaukti"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Tęsti"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Sukurti kitoje vietoje"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Išsaugoti kitoje vietoje"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Naudoti kitą įrenginį"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Išsaugoti kitame įrenginyje"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Paprastas saugaus prisijungimo metodas"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Naudodami piršto atspaudą, veidą ar ekrano užraktą prisijunkite su unikaliu „passkey“, kurio neįmanoma pamiršti ar pavogti. Sužinokite daugiau"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Pasirinkite, kur <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"išsaugoti slaptažodį"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"išsaugoti prisijungimo informaciją"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Sukurti „passkey“ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Išsaugoti slaptažodį <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Išsaugoti prisijungimo informaciją <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Galite naudoti <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> bet kuriame įrenginyje. Jis išsaugomas <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> (<xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>)"</string>
+ <string name="passkey" msgid="632353688396759522">"„passkey“"</string>
+ <string name="password" msgid="6738570945182936667">"slaptažodis"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"prisijungimo informacija"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Naudoti <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> visada prisijungiant?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Nustatyti kaip numatytąjį"</string>
+ <string name="use_once" msgid="9027366575315399714">"Naudoti vieną kartą"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"slaptažodžių: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>, „passkey“: <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"slaptažodžių: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"„passkey“: <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Kitas įrenginys"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Kitos slaptažodžių tvarkyklės"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Uždaryti lapą"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Grįžti į ankstesnį puslapį"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Naudoti išsaugotą „passkey“ programai „<xliff:g id="APP_NAME">%1$s</xliff:g>“?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Naudoti išsaugotą prisijungimo informaciją programai „<xliff:g id="APP_NAME">%1$s</xliff:g>“?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Pasirinkite išsaugotą prisijungimo informaciją programai „<xliff:g id="APP_NAME">%1$s</xliff:g>“"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Prisijungti kitu būdu"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Ne, ačiū"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Tęsti"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Prisijungimo parinktys"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Skirta <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Užrakintos slaptažodžių tvarkyklės"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Palieskite, kad atrakintumėte"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Tvarkyti prisijungimo informaciją"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Naudojant kitą įrenginį"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Naudoti kitą įrenginį"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-lv/strings.xml b/packages/CredentialManager/res/values-lv/strings.xml
new file mode 100644
index 000000000000..cbea91aa917c
--- /dev/null
+++ b/packages/CredentialManager/res/values-lv/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Atcelt"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Turpināt"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Izveidot citur"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Saglabāt citur"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Izmantot citu ierīci"</string>
+ <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+ <skip />
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Vienkāršs veids, kā droši pierakstīties"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Izmantojiet pirksta nospiedumu, autorizāciju pēc sejas vai ekrāna bloķēšanu, lai pierakstītos ar unikālu piekļuves atslēgu, ko nevar aizmirst vai nozagt. Uzziniet vairāk."</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Izvēlieties, kur: <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"saglabāt paroli"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"saglabāt pierakstīšanās informāciju"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Vai izveidot piekļuves atslēgu šeit: <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Vai saglabāt paroli šeit: <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Vai saglabāt pierakstīšanās informāciju šeit: <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Jebkurā ierīcē varat izmantot šo: <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g>. Tas tiek saglabāt šeit: <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>; mērķis: <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>."</string>
+ <string name="passkey" msgid="632353688396759522">"piekļuves atslēga"</string>
+ <string name="password" msgid="6738570945182936667">"parole"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"pierakstīšanās informācija"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Vai vienmēr izmantot <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>, lai pierakstītos?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Iestatīt kā noklusējumu"</string>
+ <string name="use_once" msgid="9027366575315399714">"Izmantot vienreiz"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> paroles, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> piekļuves atslēgas"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> paroles"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> piekļuves atslēgas"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Cita ierīce"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Citi paroļu pārvaldnieki"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Aizvērt lapu"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Atgriezties iepriekšējā lapā"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Vai izmantot saglabāto piekļuves atslēgu lietotnei <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Vai izmantot saglabāto pierakstīšanās informāciju lietotnei <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Saglabātas pierakstīšanās informācijas izvēle lietotnei <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Pierakstīties citā veidā"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Nē, paldies"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Turpināt"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Pierakstīšanās opcijas"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Lietotājam <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Paroļu pārvaldnieki, kuros nepieciešams autentificēties"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Pieskarieties, lai atbloķētu"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Pierakstīšanās informācijas pārvaldība"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"No citas ierīces"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Izmantot citu ierīci"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-mk/strings.xml b/packages/CredentialManager/res/values-mk/strings.xml
new file mode 100644
index 000000000000..e98bfc48a864
--- /dev/null
+++ b/packages/CredentialManager/res/values-mk/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Откажи"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Продолжи"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Создајте на друго место"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Зачувајте на друго место"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Употребете друг уред"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Зачувајте на друг уред"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Едноставен начин за безбедно најавување"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Користете го отпечатокот, заклучувањето со лик или заклучувањето екран за да се најавите со единствен криптографски клуч што не може да се заборави или украде. Дознајте повеќе"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Изберете каде да <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"се зачува лозинката"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"се зачуваат податоците за најавување"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Да се создаде криптографски клуч во <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Да се зачува вашата лозинка во <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Да се зачуваат вашите податоци за најавување во <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Вашиот <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> од типот <xliff:g id="TYPE">%2$s</xliff:g> може да го користите на секој уред. Зачуван е на <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> за <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"криптографски клуч"</string>
+ <string name="password" msgid="6738570945182936667">"лозинка"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"најавувања"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Да се користи <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> за сите ваши најавувања?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Постави како стандардна опција"</string>
+ <string name="use_once" msgid="9027366575315399714">"Употребете еднаш"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> лозинки, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> криптографски клучеви"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> лозинки"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> криптографски клучеви"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Друг уред"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Други управници со лозинки"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Затворете го листот"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Врати се на претходната страница"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Да се користи вашиот зачуван криптографски клуч за <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Да се користи вашето зачувано најавување за <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Изберете зачувано најавување за <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Најавете се на друг начин"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Не, фала"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Продолжи"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Опции за најавување"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"За <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Заклучени управници со лозинки"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Допрете за да отклучите"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Управувајте со најавувањата"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Од друг уред"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Употребете друг уред"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-ml/strings.xml b/packages/CredentialManager/res/values-ml/strings.xml
new file mode 100644
index 000000000000..34029cec907b
--- /dev/null
+++ b/packages/CredentialManager/res/values-ml/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"റദ്ദാക്കുക"</string>
+ <string name="string_continue" msgid="1346732695941131882">"തുടരുക"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"മറ്റൊരു സ്ഥലത്ത് സൃഷ്‌ടിക്കുക"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"മറ്റൊരു സ്ഥലത്തേക്ക് സംരക്ഷിക്കുക"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"മറ്റൊരു ഉപകരണം ഉപയോഗിക്കുക"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"മറ്റൊരു ഉപകരണത്തിലേക്ക് സംരക്ഷിക്കുക"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"സുരക്ഷിതമായി സൈൻ ഇൻ ചെയ്യാനുള്ള ലളിതമായ മാർഗ്ഗം"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"മറന്നുപോകാനും മോഷ്‌ടിക്കാനും സാധ്യതയില്ലാത്ത തനത് പാസ്‌കീ ഉപയോഗിച്ച് സൈൻ ഇൻ ചെയ്യാൻ നിങ്ങളുടെ ഫിംഗർപ്രിന്റോ മുഖമോ സ്‌ക്രീൻ ലോക്കോ ഉപയോഗിക്കുക. കൂടുതലറിയുക"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"എവിടെ <xliff:g id="CREATETYPES">%1$s</xliff:g> എന്ന് തിരഞ്ഞെടുക്കുക"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"നിങ്ങളുടെ പാസ്‌വേഡ് സംരക്ഷിക്കുക"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"നിങ്ങളുടെ സൈൻ ഇൻ വിവരങ്ങൾ സംരക്ഷിക്കുക"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> എന്നതിൽ ഒരു പാസ്‌കീ സൃഷ്‌ടിക്കണോ?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"പാസ്‌വേഡ് <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> എന്നതിലേക്ക് സംരക്ഷിക്കണോ?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"സൈൻ ഇൻ വിവരങ്ങൾ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> എന്നതിലേക്ക് സംരക്ഷിക്കണോ?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"നിങ്ങളുടെ <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> ഏത് ഉപകരണത്തിലും നിങ്ങൾക്ക് ഉപയോഗിക്കാം. ഇത് <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> എന്നതിനായി <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> എന്നതിലേക്ക് സംരക്ഷിച്ചു"</string>
+ <string name="passkey" msgid="632353688396759522">"പാസ്‌കീ"</string>
+ <string name="password" msgid="6738570945182936667">"പാസ്‌വേഡ്"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"സൈൻ ഇന്നുകൾ"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"നിങ്ങളുടെ എല്ലാ സൈൻ ഇന്നുകൾക്കും <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ഉപയോഗിക്കണോ?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"ഡിഫോൾട്ടായി സജ്ജീകരിക്കുക"</string>
+ <string name="use_once" msgid="9027366575315399714">"ഒരു തവണ ഉപയോഗിക്കുക"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> പാസ്‌വേഡുകൾ, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> പാസ്‌കീകൾ"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> പാസ്‌വേഡുകൾ"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> പാസ്‌കീകൾ"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"മറ്റൊരു ഉപകരണം"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"മറ്റ് പാസ്‌വേഡ് മാനേജർമാർ"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"ഷീറ്റ് അടയ്ക്കുക"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"മുമ്പത്തെ പേജിലേക്ക് മടങ്ങുക"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> എന്നതിനായി നിങ്ങൾ സംരക്ഷിച്ച പാസ്‌കീ ഉപയോഗിക്കണോ?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> എന്നതിനായി നിങ്ങൾ സംരക്ഷിച്ച സൈൻ ഇൻ ഉപയോഗിക്കണോ?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> എന്നതിനായി ഒരു സംരക്ഷിച്ച സൈൻ ഇൻ തിരഞ്ഞെടുക്കുക"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"മറ്റൊരു രീതിയിൽ സൈൻ ഇൻ ചെയ്യുക"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"വേണ്ട"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"തുടരുക"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"സൈൻ ഇൻ ഓപ്ഷനുകൾ"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> എന്നയാൾക്ക്"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"ലോക്ക് ചെയ്‌ത പാസ്‌വേഡ് സൈൻ ഇൻ മാനേജർമാർ"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"അൺലോക്ക് ചെയ്യാൻ ടാപ്പ് ചെയ്യുക"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"സൈൻ ഇന്നുകൾ മാനേജ് ചെയ്യുക"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"മറ്റൊരു ഉപകരണത്തിൽ നിന്ന്"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"മറ്റൊരു ഉപകരണം ഉപയോഗിക്കുക"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-mn/strings.xml b/packages/CredentialManager/res/values-mn/strings.xml
new file mode 100644
index 000000000000..f0909316755a
--- /dev/null
+++ b/packages/CredentialManager/res/values-mn/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Цуцлах"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Үргэлжлүүлэх"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Өөр газар үүсгэх"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Өөр газар хадгалах"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Өөр төхөөрөмж ашиглах"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Өөр төхөөрөмжид хадгалах"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Аюулгүй нэвтрэх энгийн арга"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Мартах эсвэл хулгайд алдах боломжгүй өвөрмөц passkey-н хамт нэвтрэх хурууны хээ, царай эсвэл дэлгэцийн түгжээгээ ашиглана уу. Нэмэлт мэдээлэл авах"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Хаана <xliff:g id="CREATETYPES">%1$s</xliff:g>-г сонгоно уу"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"нууц үгээ хадгалах"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"нэвтрэх мэдээллээ хадгалах"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>-д passkey үүсгэх үү?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Нууц үгээ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>-д хадгалах уу?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Нэвтрэх мэдээллээ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>-д хадгалах уу?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Та өөрийн <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g>-г дурын төхөөрөмж дээр ашиглах боломжтой. Үүнийг <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>-д <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>-д зориулж хадгалсан"</string>
+ <string name="passkey" msgid="632353688396759522">"passkey"</string>
+ <string name="password" msgid="6738570945182936667">"нууц үг"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"нэвтрэлт"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>-г бүх нэвтрэлтдээ ашиглах уу?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Өгөгдмөлөөр тохируулах"</string>
+ <string name="use_once" msgid="9027366575315399714">"Нэг удаа ашиглах"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> нууц үг, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> passkey"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> нууц үг"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> passkey"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Өөр төхөөрөмж"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Нууц үгний бусад менежер"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Хүснэгтийг хаах"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Өмнөх хуудас руу буцах"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g>-д өөрийн хадгалсан passkey-г ашиглах уу?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g>-д хадгалсан нэвтрэх мэдээллээ ашиглах уу?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g>-д зориулж хадгалсан нэвтрэх мэдээллийг сонгоно уу"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Өөр аргаар нэвтрэх"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Үгүй, баярлалаа"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Үргэлжлүүлэх"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Нэвтрэх сонголт"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g>-д"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Түгжээтэй нууц үгний менежерүүд"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Түгжээг тайлахын тулд товшино уу"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Нэвтрэлтийг удирдах"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Өөр төхөөрөмжөөс"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Өөр төхөөрөмж ашиглах"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-mr/strings.xml b/packages/CredentialManager/res/values-mr/strings.xml
new file mode 100644
index 000000000000..c4d12f54f0aa
--- /dev/null
+++ b/packages/CredentialManager/res/values-mr/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"रद्द करा"</string>
+ <string name="string_continue" msgid="1346732695941131882">"पुढे सुरू ठेवा"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"दुसऱ्या ठिकाणी तयार करा"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"दुसऱ्या ठिकाणी सेव्ह करा"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"दुसरे डिव्‍हाइस वापरा"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"दुसऱ्या डिव्हाइसवर सेव्ह करा"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"सुरक्षितपणे साइन इन करण्याचा सोपा मार्ग"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"युनिक पासकीसह साइन इन करण्यासाठी तुमचे फिंगरप्रिंट, फेस किंवा स्क्रीन लॉक वापरा, जे विसरता येणार नाही किंवा चोरीला जाणार नाही. अधिक जाणून घ्या"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> कुठे करायचे ते निवडा"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"तुमचा पासवर्ड सेव्ह करा"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"तुमची साइन-इन माहिती सेव्ह करा"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> मध्ये पासकी तयार करायची का?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"तुमचा पासवर्ड <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> वर सेव्ह करायचा का?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"तुमची साइन-इन माहिती <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> वर सेव्ह करायची का?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"तुम्ही कोणत्याही डिव्हाइसवर तुमचे <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> वापरू शकता. ते <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> साठी <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> वर सेव्ह केले जाते"</string>
+ <string name="passkey" msgid="632353688396759522">"पासकी"</string>
+ <string name="password" msgid="6738570945182936667">"पासवर्ड"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"साइन-इन"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"तुमच्या सर्व साइन-इन साठी <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>वापरायचे का?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"डिफॉल्ट म्हणून सेट करा"</string>
+ <string name="use_once" msgid="9027366575315399714">"एकदा वापरा"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> पासवर्ड, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> पासकी"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> पासवर्ड"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> पासकी"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"इतर डिव्हाइस"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"इतर पासवर्ड व्यवस्थापक"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"शीट बंद करा"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"मागील पेजवर परत जा"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> साठी तुमची सेव्ह केलेली पासकी वापरायची का?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> साठी तुमचे सेव्ह केलेले साइन-इन वापरायचे का?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> साठी सेव्ह केलेले साइन-इन निवडा"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"दुसऱ्या मार्गाने साइन इन करा"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"नाही, नको"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"पुढे सुरू ठेवा"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"साइन इन पर्याय"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> साठी"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"लॉक केलेले पासवर्ड व्यवस्थापक"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"अनलॉक करण्यासाठी टॅप करा"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"साइन-इन व्यवस्थापित करा"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"दुसऱ्या डिव्हाइस वरून"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"वेगळे डिव्हाइस वापरा"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-ms/strings.xml b/packages/CredentialManager/res/values-ms/strings.xml
new file mode 100644
index 000000000000..fb130feb39b8
--- /dev/null
+++ b/packages/CredentialManager/res/values-ms/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Batal"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Teruskan"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Buat di tempat lain"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Simpan di tempat lain"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Gunakan peranti lain"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Simpan kepada peranti lain"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Cara mudah untuk log masuk dengan selamat"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Gunakan cap jari, wajah atau kunci skrin anda untuk log masuk menggunakan kunci laluan unik yang tidak boleh dilupakan atau dicuri. Ketahui lebih lanjut"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Pilih tempat untuk <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"simpan kata laluan anda"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"simpan maklumat log masuk anda"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Buat kunci laluan dalam <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Simpan kata laluan anda pada <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Simpan maklumat log masuk anda pada <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Anda boleh menggunakan <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> anda pada mana-mana peranti. Ia disimpan pada <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> untuk <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"kunci laluan"</string>
+ <string name="password" msgid="6738570945182936667">"kata laluan"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"log masuk"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Gunakan <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> untuk semua log masuk anda?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Tetapkan sebagai lalai"</string>
+ <string name="use_once" msgid="9027366575315399714">"Gunakan sekali"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"Kata laluan <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>, kunci laluan <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"Kata laluan <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Kunci laluan <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Peranti lain"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Password Manager lain"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Tutup helaian"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Kembali ke halaman sebelumnya"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Gunakan kunci laluan anda yang telah disimpan untuk <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Gunakan maklumat log masuk anda yang telah disimpan untuk <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Pilih log masuk yang telah disimpan untuk <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Log masuk menggunakan cara lain"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Tidak perlu"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Teruskan"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Pilihan log masuk"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Untuk <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Password Manager dikunci"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Ketik untuk membuka kunci"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Urus log masuk"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Daripada peranti lain"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Gunakan peranti yang lain"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-my/strings.xml b/packages/CredentialManager/res/values-my/strings.xml
new file mode 100644
index 000000000000..b14960ab0858
--- /dev/null
+++ b/packages/CredentialManager/res/values-my/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"မလုပ်တော့"</string>
+ <string name="string_continue" msgid="1346732695941131882">"ရှေ့ဆက်ရန်"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"နောက်တစ်နေရာတွင် ပြုလုပ်ရန်"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"နောက်တစ်နေရာတွင် သိမ်းရန်"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"စက်နောက်တစ်ခု သုံးရန်"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"စက်နောက်တစ်ခုတွင် သိမ်းရန်"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"လုံခြုံစွာလက်မှတ်ထိုးဝင်ရန် ရိုးရှင်းသောနည်း"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"မေ့မသွား (သို့) ခိုးမသွားနိုင်သော သီးခြားလျှို့ဝှက်ကီးဖြင့် လက်မှတ်ထိုးဝင်ရန် သင့်လက်ဗွေ၊ မျက်နှာ (သို့) ဖန်သားပြင်လော့ခ် သုံးနိုင်သည်။ ပိုမိုလေ့လာရန်"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> ရန် နေရာရွေးပါ"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"သင့်စကားဝှက် သိမ်းရန်"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"သင်၏ လက်မှတ်ထိုးဝင်သည့်အချက်အလက်ကို သိမ်းရန်"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> တွင် လျှို့ဝှက်ကီး ပြုလုပ်မလား။"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"သင့်စကားဝှက်ကို <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> တွင် သိမ်းမလား။"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"သင်၏ လက်မှတ်ထိုးဝင်သည့်အချက်အလက်ကို <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> တွင် သိမ်းမလား။"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"မည်သည့်စက်တွင်မဆို သင်၏ <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> ကို သုံးနိုင်သည်။ ၎င်းကို <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> အတွက် <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> တွင် သိမ်းလိုက်သည်"</string>
+ <string name="passkey" msgid="632353688396759522">"လျှို့ဝှက်ကီး"</string>
+ <string name="password" msgid="6738570945182936667">"စကားဝှက်"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"လက်မှတ်ထိုးဝင်မှုများ"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"သင်၏လက်မှတ်ထိုးဝင်မှု အားလုံးအတွက် <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> သုံးမလား။"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"မူရင်းအဖြစ် သတ်မှတ်ရန်"</string>
+ <string name="use_once" msgid="9027366575315399714">"တစ်ကြိမ်သုံးရန်"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"စကားဝှက် <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> ခု၊ လျှို့ဝှက်ကီး <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> ခု"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"စကားဝှက် <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> ခု"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"လျှို့ဝှက်ကီး <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> ခု"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"စက်နောက်တစ်ခု"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"အခြားစကားဝှက်မန်နေဂျာများ"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"စာမျက်နှာ ပိတ်ရန်"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"ယခင်စာမျက်နှာကို ပြန်သွားပါ"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> အတွက် သိမ်းထားသောလျှို့ဝှက်ကီး သုံးမလား။"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> အတွက် သိမ်းထားသောလက်မှတ်ထိုးဝင်မှု သုံးမလား။"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> အတွက် သိမ်းထားသော လက်မှတ်ထိုးဝင်မှုကို ရွေးပါ"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"နောက်တစ်နည်းဖြင့် လက်မှတ်ထိုးဝင်ရန်"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"မလိုပါ"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"ရှေ့ဆက်ရန်"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"လက်မှတ်ထိုးဝင်ရန် နည်းလမ်းများ"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> အတွက်"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"လော့ခ်ချထားသည့် စကားဝှက်မန်နေဂျာများ"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"ဖွင့်ရန် တို့ပါ"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"လက်မှတ်ထိုးဝင်မှုများ စီမံခြင်း"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"စက်နောက်တစ်ခုမှ"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"အခြားစက်သုံးရန်"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-nb/strings.xml b/packages/CredentialManager/res/values-nb/strings.xml
new file mode 100644
index 000000000000..d53bc7e45cc6
--- /dev/null
+++ b/packages/CredentialManager/res/values-nb/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Avbryt"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Fortsett"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Opprett på et annet sted"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Lagre på et annet sted"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Bruk en annen enhet"</string>
+ <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+ <skip />
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"En enkel og trygg påloggingsmåte"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Bruk fingeravtrykk, ansiktet eller en skjermlås til å logge på med en unik tilgangsnøkkel du verken kan glemme eller miste. Finn ut mer"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Velg hvor <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"lagre passordet ditt"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"lagre påloggingsinformasjonen din"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Vil du opprette en tilgangsnøkkel i <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Vil du lagre passordet i <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Vil du lagre påloggingsinformasjonen i <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Du kan bruke <xliff:g id="TYPE">%2$s</xliff:g>-elementet for <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> på alle slags enheter. Den lagres i <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> for <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"tilgangsnøkkel"</string>
+ <string name="password" msgid="6738570945182936667">"passord"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"pålogginger"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Vil du bruke <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> for alle pålogginger?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Angi som standard"</string>
+ <string name="use_once" msgid="9027366575315399714">"Bruk én gang"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> passord, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> tilgangsnøkler"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> passord"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> tilgangsnøkler"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"En annen enhet"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Andre løsninger for passordlagring"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Lukk arket"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Gå tilbake til den forrige siden"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Vil du bruke den lagrede tilgangsnøkkelen for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Vil du bruke den lagrede påloggingen for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Velg en lagret pålogging for <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Bruk en annen påloggingsmetode"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Nei takk"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Fortsett"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Påloggingsalternativer"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"For <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Låste løsninger for passordlagring"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Trykk for å låse opp"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Administrer pålogginger"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Fra en annen enhet"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Bruk en annen enhet"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-ne/strings.xml b/packages/CredentialManager/res/values-ne/strings.xml
new file mode 100644
index 000000000000..77b095944f39
--- /dev/null
+++ b/packages/CredentialManager/res/values-ne/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"रद्द गर्नुहोस्"</string>
+ <string name="string_continue" msgid="1346732695941131882">"जारी राख्नुहोस्"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"अर्को ठाउँमा बनाउनुहोस्"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"अर्को ठाउँमा सेभ गर्नुहोस्"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"अर्को डिभाइस प्रयोग गर्नुहोस्"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"अर्को डिभाइसमा सेभ गर्नुहोस्"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"सुरक्षित तरिकाले साइन इन गर्ने सरल तरिका"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"नभुलिने वा चोरी नहुने खालको अद्वितीय पासकीका साथै आफ्ना फिंगरप्रिन्ट, अनुहार वा स्क्रिन लक प्रयोग गरी साइन इन गर्नुहोस्। थप जान्नुहोस्"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> सेभ गर्ने ठाउँ छनौट गर्नुहोस्"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"आफ्नो पासवर्ड सेभ गर्नुहोस्"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"आफ्नो साइन इनसम्बन्धी जानकारी सेभ गर्नुहोस्"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> मा पासकी बनाउने हो?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"तपाईंको पासवर्ड <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> मा सेभ गर्ने हो?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"तपाईंको साइन इनसम्बन्धी जानकारी <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> मा सेभ गर्ने हो?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"तपाईं जुनसुकै डिभाइसमा आफ्नो <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> प्रयोग गर्न सक्नुहुन्छ। यसलाई <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> का लागि <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> मा सेभ गरिएको छ"</string>
+ <string name="passkey" msgid="632353688396759522">"पासकी"</string>
+ <string name="password" msgid="6738570945182936667">"पासवर्ड"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"साइन इनसम्बन्धी जानकारी"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"तपाईंले साइन इन गर्ने सबै डिभाइसहरूमा <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> प्रयोग गर्ने हो?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"डिफल्ट जानकारीका रूपमा सेट गर्नुहोस्"</string>
+ <string name="use_once" msgid="9027366575315399714">"एक पटक प्रयोग गर्नुहोस्"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> वटा पासवर्ड, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> वटा पासकी"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> वटा पासवर्ड"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> वटा पासकी"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"अर्को डिभाइस"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"अन्य पासवर्ड म्यानेजरहरू"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"पाना बन्द गर्नुहोस्"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"अघिल्लो पेजमा फर्कनुहोस्"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"आफूले सेभ गरेको पासकी प्रयोग गरी <xliff:g id="APP_NAME">%1$s</xliff:g> मा साइन इन गर्ने हो?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"आफूले सेभ गरेको साइन इनसम्बन्धी जानकारी प्रयोग गरी <xliff:g id="APP_NAME">%1$s</xliff:g> मा साइन इन गर्ने हो?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> मा साइन इन गर्नका लागि सेभ गरिएका साइन इनसम्बन्धी जानकारी छनौट गर्नुहोस्"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"अर्कै विधि प्रयोग गरी साइन इन गर्नुहोस्"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"पर्दैन, धन्यवाद"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"जारी राख्नुहोस्"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"साइन‍ इनसम्बन्धी विकल्पहरू"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> का लागि"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"लक गरिएका पासवर्ड म्यानेजरहरू"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"अनलक गर्न ट्याप गर्नुहोस्"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"साइन इनसम्बन्धी विकल्पहरू व्यवस्थापन गर्नुहोस्"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"अर्को डिभाइसका लागि"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"अर्कै डिभाइस प्रयोग गरी हेर्नुहोस्"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-nl/strings.xml b/packages/CredentialManager/res/values-nl/strings.xml
new file mode 100644
index 000000000000..a80c2883f2fb
--- /dev/null
+++ b/packages/CredentialManager/res/values-nl/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Annuleren"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Doorgaan"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Op een andere locatie maken"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Op een andere locatie opslaan"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Een ander apparaat gebruiken"</string>
+ <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+ <skip />
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Een makkelijke manier om beveiligd in te loggen"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Gebruik je vingerafdruk, gezichtsvergrendeling of schermvergrendeling om in te loggen met een unieke toegangssleutel die je niet kunt vergeten en die anderen niet kunnen stelen. Meer informatie"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Een locatie kiezen voor <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"je wachtwoord opslaan"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"je inloggegevens opslaan"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Een toegangssleutel maken in <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Je wachtwoord opslaan in <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Je inloggegevens opslaan in <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Je kunt je <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> op elk apparaat gebruiken. Het wordt opgeslagen in <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> voor <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"toegangssleutel"</string>
+ <string name="password" msgid="6738570945182936667">"wachtwoord"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"inloggegevens"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> elke keer gebruiken als je inlogt?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Instellen als standaard"</string>
+ <string name="use_once" msgid="9027366575315399714">"Eén keer gebruiken"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> wachtwoorden, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> toegangssleutels"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> wachtwoorden"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> toegangssleutels"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Een ander apparaat"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Andere wachtwoordmanagers"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Blad sluiten"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Ga terug naar de vorige pagina"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Je opgeslagen toegangssleutel voor <xliff:g id="APP_NAME">%1$s</xliff:g> gebruiken?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Je opgeslagen inloggegevens voor <xliff:g id="APP_NAME">%1$s</xliff:g> gebruiken?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Opgeslagen inloggegevens kiezen voor <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Inloggen via een andere methode"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Nee, bedankt"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Doorgaan"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opties voor inloggen"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Voor <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Vergrendelde wachtwoordmanagers"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Tik om te ontgrendelen"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Inloggegevens beheren"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Via een ander apparaat"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Een ander apparaat gebruiken"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-or/strings.xml b/packages/CredentialManager/res/values-or/strings.xml
new file mode 100644
index 000000000000..5b401db5d90b
--- /dev/null
+++ b/packages/CredentialManager/res/values-or/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"ବାତିଲ କରନ୍ତୁ"</string>
+ <string name="string_continue" msgid="1346732695941131882">"ଜାରି ରଖନ୍ତୁ"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"ଅନ୍ୟ ଏକ ସ୍ଥାନରେ ତିଆରି କରନ୍ତୁ"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"ଅନ୍ୟ ଏକ ସ୍ଥାନରେ ସେଭ କରନ୍ତୁ"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"ଅନ୍ୟ ଏହି ଡିଭାଇସ ବ୍ୟବହାର କରନ୍ତୁ"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"ଅନ୍ୟ ଏକ ଡିଭାଇସରେ ସେଭ କରନ୍ତୁ"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"ସୁରକ୍ଷିତ ଭାବେ ସାଇନ ଇନ କରିବାର ଏକ ସରଳ ଉପାୟ"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"ଏକ ସ୍ୱତନ୍ତ୍ର ପାସକୀ ମାଧ୍ୟମରେ ସାଇନ ଇନ କରିବା ପାଇଁ ଆପଣଙ୍କ ଟିପଚିହ୍ନ, ଫେସ କିମ୍ବା ସ୍କ୍ରିନ ଲକ ବ୍ୟବହାର କରନ୍ତୁ ଯାହାକୁ ଭୁଲି ପାରିବେ ନାହିଁ କିମ୍ବା ଚୋରି ହୋଇପାରିବ ନାହିଁ। ଅଧିକ ଜାଣନ୍ତୁ"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"କେଉଁଠି <xliff:g id="CREATETYPES">%1$s</xliff:g> କରିବେ, ତାହା ବାଛନ୍ତୁ"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"ଆପଣଙ୍କ ପାସୱାର୍ଡ ସେଭ କରନ୍ତୁ"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"ଆପଣଙ୍କ ସାଇନ-ଇନ ସୂଚନା ସେଭ କରନ୍ତୁ"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>ରେ ଏକ ପାସକୀ ତିଆରି କରିବେ?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"ଆପଣଙ୍କ ପାସୱାର୍ଡକୁ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>ରେ ସେଭ କରିବେ?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"ଆପଣଙ୍କ ସାଇନ-ଇନ ସୂଚନାକୁ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>ରେ ସେଭ କରିବେ?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"ଆପଣ ଯେ କୌଣସି ଡିଭାଇସରେ <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g>କୁ ବ୍ୟବହାର କରିପାରିବେ। ଏହାକୁ <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> ପାଇଁ <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>ରେ ସେଭ କରାଯାଏ"</string>
+ <string name="passkey" msgid="632353688396759522">"ପାସକୀ"</string>
+ <string name="password" msgid="6738570945182936667">"ପାସୱାର୍ଡ"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"ସାଇନ-ଇନ"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"ଆପଣଙ୍କ ସମସ୍ତ ସାଇନ-ଇନ ପାଇଁ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ବ୍ୟବହାର କରିବେ?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"ଡିଫଲ୍ଟ ଭାବେ ସେଟ କରନ୍ତୁ"</string>
+ <string name="use_once" msgid="9027366575315399714">"ଥରେ ବ୍ୟବହାର କରନ୍ତୁ"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>ଟି ପାସୱାର୍ଡ, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>ଟି ପାସକୀ"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>ଟି ପାସୱାର୍ଡ"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>ଟି ପାସକୀ"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"ଅନ୍ୟ ଏକ ଡିଭାଇସ"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"ଅନ୍ୟ Password Manager"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"ସିଟ ବନ୍ଦ କରନ୍ତୁ"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"ପୂର୍ବବର୍ତ୍ତୀ ପୃଷ୍ଠାକୁ ଫେରନ୍ତୁ"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> ପାଇଁ ସେଭ କରାଯାଇଥିବା ଆପଣଙ୍କ ପାସକୀ ବ୍ୟବହାର କରିବେ?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> ପାଇଁ ସେଭ କରାଯାଇଥିବା ଆପଣଙ୍କ ସାଇନ-ଇନ ବ୍ୟବହାର କରିବେ?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> ପାଇଁ ସେଭ କରାଯାଇଥିବା ଏକ ସାଇନ-ଇନ ବାଛନ୍ତୁ"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"ଅନ୍ୟ ଏକ ଉପାୟରେ ସାଇନ ଇନ କରନ୍ତୁ"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"ନା, ଧନ୍ୟବାଦ"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"ଜାରି ରଖନ୍ତୁ"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"ସାଇନ ଇନ ବିକଳ୍ପଗୁଡ଼ିକ"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g>ରେ"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"ଲକ ଥିବା Password Manager"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"ଅନଲକ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"ସାଇନ-ଇନ ପରିଚାଳନା କରନ୍ତୁ"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"ଅନ୍ୟ ଏକ ଡିଭାଇସରୁ"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"ଏକ ଭିନ୍ନ ଡିଭାଇସ ବ୍ୟବହାର କରନ୍ତୁ"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-pa/strings.xml b/packages/CredentialManager/res/values-pa/strings.xml
new file mode 100644
index 000000000000..f8f5ba159099
--- /dev/null
+++ b/packages/CredentialManager/res/values-pa/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"ਰੱਦ ਕਰੋ"</string>
+ <string name="string_continue" msgid="1346732695941131882">"ਜਾਰੀ ਰੱਖੋ"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"ਕਿਸੇ ਹੋਰ ਥਾਂ \'ਤੇ ਬਣਾਓ"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"ਕਿਸੇ ਹੋਰ ਥਾਂ \'ਤੇ ਰੱਖਿਅਤ ਕਰੋ"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"ਕੋਈ ਹੋਰ ਡੀਵਾਈਸ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
+ <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+ <skip />
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"ਸੁਰੱਖਿਅਤ ਢੰਗ ਨਾਲ ਸਾਈਨ-ਇਨ ਕਰਨ ਦਾ ਆਸਾਨ ਤਰੀਕਾ"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"ਵਿਲੱਖਣ ਪਾਸਕੀ ਨਾਲ ਸਾਈਨ-ਇਨ ਕਰਨ ਵਾਸਤੇ ਆਪਣੇ ਫਿੰਗਰਪ੍ਰਿੰਟ, ਚਿਹਰੇ ਜਾਂ ਸਕ੍ਰੀਨ ਲਾਕ ਦੀ ਵਰਤੋਂ ਕਰੋ ਜਿਸਨੂੰ ਭੁੱਲਿਆ ਜਾਂ ਚੋਰੀ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ। ਹੋਰ ਜਾਣੋ"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> ਲਈ ਕੋਈ ਥਾਂ ਚੁਣੋ"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"ਆਪਣਾ ਪਾਸਵਰਡ ਰੱਖਿਅਤ ਕਰੋ"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"ਆਪਣੀ ਸਾਈਨ-ਇਨ ਜਾਣਕਾਰੀ ਰੱਖਿਅਤ ਕਰੋ"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"ਕੀ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ਵਿੱਚ ਪਾਸਕੀ ਨੂੰ ਬਣਾਉਣਾ ਹੈ?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"ਕੀ ਆਪਣੇ ਪਾਸਵਰਡ ਨੂੰ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ਵਿੱਚ ਰੱਖਿਅਤ ਕਰਨਾ ਹੈ?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"ਕੀ ਆਪਣੀ ਸਾਈਨ-ਇਨ ਜਾਣਕਾਰੀ ਨੂੰ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ਵਿੱਚ ਰੱਖਿਅਤ ਕਰਨਾ ਹੈ?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"ਤੁਸੀਂ ਕਿਸੇ ਵੀ ਡੀਵਾਈਸ \'ਤੇ ਆਪਣੀ <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰ ਸਕਦੇ ਹੋ। ਇਸਨੂੰ <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> ਲਈ <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> ਵਿੱਚ ਰੱਖਿਅਤ ਕੀਤਾ ਗਿਆ"</string>
+ <string name="passkey" msgid="632353688396759522">"ਪਾਸਕੀ"</string>
+ <string name="password" msgid="6738570945182936667">"ਪਾਸਵਰਡ"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"ਸਾਈਨ-ਇਨਾਂ ਦੀ ਜਾਣਕਾਰੀ"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"ਕੀ ਆਪਣੇ ਸਾਰੇ ਸਾਈਨ-ਇਨਾਂ ਲਈ<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰਨੀ ਹੈ?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਵਜੋਂ ਸੈੱਟ ਕਰੋ"</string>
+ <string name="use_once" msgid="9027366575315399714">"ਇੱਕ ਵਾਰ ਵਰਤੋ"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> ਪਾਸਵਰਡ, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> ਪਾਸਕੀਆਂ"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> ਪਾਸਵਰਡ"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> ਪਾਸਕੀਆਂ"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"ਹੋਰ ਡੀਵਾਈਸ"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"ਹੋਰ ਪਾਸਵਰਡ ਪ੍ਰਬੰਧਕ"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"ਸ਼ੀਟ ਬੰਦ ਕਰੋ"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"ਪਿਛਲੇ ਪੰਨੇ \'ਤੇ ਵਾਪਸ ਜਾਓ"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"ਕੀ <xliff:g id="APP_NAME">%1$s</xliff:g> ਲਈ ਆਪਣੀ ਰੱਖਿਅਤ ਕੀਤੀ ਪਾਸਕੀ ਦੀ ਵਰਤੋਂ ਕਰਨੀ ਹੈ?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"ਕੀ <xliff:g id="APP_NAME">%1$s</xliff:g> ਲਈ ਆਪਣੀ ਰੱਖਿਅਤ ਕੀਤੀ ਸਾਈਨ-ਇਨ ਜਾਣਕਾਰੀ ਦੀ ਵਰਤੋਂ ਕਰਨੀ ਹੈ?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਲਈ ਰੱਖਿਅਤ ਕੀਤੀ ਸਾਈਨ-ਇਨ ਜਾਣਕਾਰੀ ਚੁਣੋ"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"ਕਿਸੇ ਹੋਰ ਤਰੀਕੇ ਨਾਲ ਸਾਈਨ-ਇਨ ਕਰੋ"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"ਨਹੀਂ ਧੰਨਵਾਦ"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"ਜਾਰੀ ਰੱਖੋ"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"ਸਾਈਨ-ਇਨ ਕਰਨ ਦੇ ਵਿਕਲਪ"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> ਲਈ"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"ਲਾਕ ਕੀਤੇ ਪਾਸਵਰਡ ਪ੍ਰਬੰਧਕ"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"ਅਣਲਾਕ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"ਸਾਈਨ-ਇਨਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"ਹੋਰ ਡੀਵਾਈਸ ਤੋਂ"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"ਵੱਖਰੇ ਡੀਵਾਈਸ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-pl/strings.xml b/packages/CredentialManager/res/values-pl/strings.xml
new file mode 100644
index 000000000000..e684f232b00f
--- /dev/null
+++ b/packages/CredentialManager/res/values-pl/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Anuluj"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Dalej"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Utwórz w innym miejscu"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Zapisz w innym miejscu"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Użyj innego urządzenia"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Zapisz na innym urządzeniu"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Prosty sposób na bezpieczne logowanie"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Używaj odcisku palca, rozpoznawania twarzy lub blokady ekranu, aby logować się z wykorzystaniem unikalnego klucza, którego nie można zapomnieć ani utracić w wyniku kradzieży. Więcej informacji"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Wybierz, gdzie chcesz <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"zapisać hasło"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"zapisać dane logowania"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Utworzyć klucz w usłudze <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Zapisać hasło w usłudze <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Zapisać dane logowania w usłudze <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Elementu <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> typu <xliff:g id="TYPE">%2$s</xliff:g> możesz używać na każdym urządzeniu. Jest zapisany w usłudze <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> (<xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>)"</string>
+ <string name="passkey" msgid="632353688396759522">"klucz"</string>
+ <string name="password" msgid="6738570945182936667">"hasło"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"dane logowania"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Używać usługi <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> w przypadku wszystkich danych logowania?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Ustaw jako domyślną"</string>
+ <string name="use_once" msgid="9027366575315399714">"Użyj raz"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"Hasła: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>, klucze: <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"Hasła: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Klucze: <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Inne urządzenie"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Inne menedżery haseł"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Zamknij arkusz"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Wróć do poprzedniej strony"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Użyć zapisanego klucza dla aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Użyć zapisanych danych logowania dla aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Wybierz zapisane dane logowania dla aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Zaloguj się w inny sposób"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Nie, dziękuję"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Dalej"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opcje logowania"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Zablokowane menedżery haseł"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Kliknij, aby odblokować"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Zarządzanie danymi logowania"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Na innym urządzeniu"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Użyj innego urządzenia"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-pt-rBR/strings.xml b/packages/CredentialManager/res/values-pt-rBR/strings.xml
new file mode 100644
index 000000000000..570d0e6dbbfa
--- /dev/null
+++ b/packages/CredentialManager/res/values-pt-rBR/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Cancelar"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Continuar"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Criar em outro lugar"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Salvar em outro lugar"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Usar outro dispositivo"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Salvar em outro dispositivo"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Uma maneira simples de fazer login com segurança"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Use a impressão digital, o reconhecimento facial ou um bloqueio de tela para fazer login com uma única chave de acesso que não pode ser esquecida ou perdida. Saiba mais"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Escolha onde <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"salvar sua senha"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"salvar suas informações de login"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Criar uma chave de acesso em <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Salvar sua senha em <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Salvar suas informações de login em <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Você pode usar seu <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> em qualquer dispositivo. Ele está salvo em <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> para <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"chave de acesso"</string>
+ <string name="password" msgid="6738570945182936667">"senha"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"logins"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Usar <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> para todos os seus logins?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Definir como padrão"</string>
+ <string name="use_once" msgid="9027366575315399714">"Usar uma vez"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> senhas, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> chaves de acesso"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> senhas"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> chaves de acesso"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Outro dispositivo"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Outros gerenciadores de senha"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Fechar página"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Voltar à página anterior"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Usar sua chave de acesso salva para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Usar suas informações de login salvas para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Escolher um login salvo para <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Fazer login de outra forma"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Agora não"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Continuar"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opções de login"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Para <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Gerenciadores de senha bloqueados"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Toque para desbloquear"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Gerenciar logins"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"De outro dispositivo"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Usar um dispositivo diferente"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-pt-rPT/strings.xml b/packages/CredentialManager/res/values-pt-rPT/strings.xml
new file mode 100644
index 000000000000..8a105cd1d7b9
--- /dev/null
+++ b/packages/CredentialManager/res/values-pt-rPT/strings.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="4539824758261855508">"Credential Manager"</string>
+ <string name="string_cancel" msgid="6369133483981306063">"Cancelar"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Continuar"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Criar noutro lugar"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Guardar noutro lugar"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Usar outro dispositivo"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Guardar noutro dispositivo"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Uma forma simples de iniciar sessão em segurança"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Use a sua impressão digital, rosto ou bloqueio de ecrã para iniciar sessão com uma chave de acesso única que não pode ser esquecida nem perdida. Saiba mais"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Escolha onde quer guardar <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"guardar a sua palavra-passe"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"guardar as suas informações de início de sessão"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Criar uma chave de acesso em <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Guardar a sua palavra-passe em <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Guardar as suas informações de início de sessão em <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Pode usar <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> em qualquer dispositivo. É guardado em <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> para <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"chave de acesso"</string>
+ <string name="password" msgid="6738570945182936667">"palavra-passe"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"inícios de sessão"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Usar <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> para todos os seus inícios de sessão?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Definir como predefinição"</string>
+ <string name="use_once" msgid="9027366575315399714">"Usar uma vez"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> palavras-passe, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> chaves de acesso"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> palavras-passe"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> chaves de acesso"</string>
+ <string name="passkey_before_subtitle" msgid="2448119456208647444">"Chave de acesso"</string>
+ <string name="another_device" msgid="5147276802037801217">"Outro dispositivo"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Outros gestores de palavras-passe"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Fechar folha"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Volte à página anterior"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Usar a sua chave de acesso guardada na app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Usar o seu início de sessão guardado na app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Escolha um início de sessão guardado para a app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Iniciar sessão de outra forma"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Não"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Continuar"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opções de início de sessão"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Para <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Gestores de palavras-passe bloqueados"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Toque para desbloquear"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Faça a gestão dos inícios de sessão"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"De outro dispositivo"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Use um dispositivo diferente"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-pt/strings.xml b/packages/CredentialManager/res/values-pt/strings.xml
new file mode 100644
index 000000000000..570d0e6dbbfa
--- /dev/null
+++ b/packages/CredentialManager/res/values-pt/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Cancelar"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Continuar"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Criar em outro lugar"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Salvar em outro lugar"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Usar outro dispositivo"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Salvar em outro dispositivo"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Uma maneira simples de fazer login com segurança"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Use a impressão digital, o reconhecimento facial ou um bloqueio de tela para fazer login com uma única chave de acesso que não pode ser esquecida ou perdida. Saiba mais"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Escolha onde <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"salvar sua senha"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"salvar suas informações de login"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Criar uma chave de acesso em <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Salvar sua senha em <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Salvar suas informações de login em <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Você pode usar seu <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> em qualquer dispositivo. Ele está salvo em <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> para <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"chave de acesso"</string>
+ <string name="password" msgid="6738570945182936667">"senha"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"logins"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Usar <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> para todos os seus logins?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Definir como padrão"</string>
+ <string name="use_once" msgid="9027366575315399714">"Usar uma vez"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> senhas, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> chaves de acesso"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> senhas"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> chaves de acesso"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Outro dispositivo"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Outros gerenciadores de senha"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Fechar página"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Voltar à página anterior"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Usar sua chave de acesso salva para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Usar suas informações de login salvas para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Escolher um login salvo para <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Fazer login de outra forma"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Agora não"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Continuar"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opções de login"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Para <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Gerenciadores de senha bloqueados"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Toque para desbloquear"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Gerenciar logins"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"De outro dispositivo"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Usar um dispositivo diferente"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-ro/strings.xml b/packages/CredentialManager/res/values-ro/strings.xml
new file mode 100644
index 000000000000..51955d4b70f2
--- /dev/null
+++ b/packages/CredentialManager/res/values-ro/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Anulează"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Continuă"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Creează în alt loc"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Salvează în alt loc"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Folosește alt dispozitiv"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Salvează pe alt dispozitiv"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Un mod simplu de a te conecta în siguranță"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Folosește-ți amprenta, fața sau blocarea ecranului ca să te conectezi cu o cheie de acces unică, pe care nu o poți uita și care nu poate fi furată. Află mai multe"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Alege unde <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"salvează parola"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"salvează informațiile de conectare"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Creezi o cheie de acces în <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Salvezi parola în <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Salvezi informațiile de conectare în <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Poți folosi <xliff:g id="TYPE">%2$s</xliff:g> <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> pe orice dispozitiv. Se salvează în <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> pentru <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"cheie de acces"</string>
+ <string name="password" msgid="6738570945182936667">"parolă"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"date de conectare"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Folosești <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> pentru toate conectările?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Setează ca prestabilite"</string>
+ <string name="use_once" msgid="9027366575315399714">"Folosește o dată"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> parole, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> chei de acces"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> parole"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> chei de acces"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Alt dispozitiv"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Alți manageri de parole"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Închide foaia"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Revino la pagina precedentă"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Folosești cheia de acces salvată pentru <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Folosești datele de conectare salvate pentru <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Alege o conectare salvată pentru <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Conectează-te altfel"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Nu, mulțumesc"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Continuă"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opțiuni de conectare"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Pentru <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Manageri de parole blocate"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Atinge pentru a debloca"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Gestionează acreditările"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"De pe alt dispozitiv"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Folosește alt dispozitiv"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-ru/strings.xml b/packages/CredentialManager/res/values-ru/strings.xml
new file mode 100644
index 000000000000..2a459c1d98bb
--- /dev/null
+++ b/packages/CredentialManager/res/values-ru/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Отмена"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Продолжить"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Создать в другом месте"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Сохранить в другом месте"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Использовать другое устройство"</string>
+ <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+ <skip />
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Простой и безопасный способ входа"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"С уникальным ключом доступа, который невозможно украсть или забыть, вы можете подтверждать свою личность по отпечатку пальца, с помощью фейсконтроля или блокировки экрана. Подробнее…"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Выберите, где <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"сохранить пароль"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"сохранить данные для входа"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Создать ключ доступа в приложении \"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>\"?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Сохранить пароль в приложении \"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>\"?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Сохранить учетные данные в приложении \"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>\"?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Вы можете использовать <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> на любом устройстве. Данные <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> сохраняются в приложении \"<xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>\"."</string>
+ <string name="passkey" msgid="632353688396759522">"ключ доступа"</string>
+ <string name="password" msgid="6738570945182936667">"пароль"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"входы"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Всегда входить с помощью приложения \"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>\"?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Использовать по умолчанию"</string>
+ <string name="use_once" msgid="9027366575315399714">"Использовать один раз"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"Пароли (<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>) и ключи доступа (<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>)"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"Пароли (<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>)"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Ключи доступа (<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>)"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Другое устройство"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Другие менеджеры паролей"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Закрыть лист"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Вернуться на предыдущую страницу"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Использовать сохраненный ключ доступа для приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Использовать сохраненные учетные данные для приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Выберите сохраненные данные для приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Войти другим способом"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Нет, спасибо"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Продолжить"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Варианты входа"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Для пользователя <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Заблокированные менеджеры паролей"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Нажмите для разблокировки"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Управление входом"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"С другого устройства"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Использовать другое устройство"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-si/strings.xml b/packages/CredentialManager/res/values-si/strings.xml
new file mode 100644
index 000000000000..de5a5a2c95a4
--- /dev/null
+++ b/packages/CredentialManager/res/values-si/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"අවලංගු කරන්න"</string>
+ <string name="string_continue" msgid="1346732695941131882">"ඉදිරියට යන්න"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"වෙනත් ස්ථානයක තනන්න"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"වෙනත් ස්ථානයකට සුරකින්න"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"වෙනත් උපාංගයක් භාවිතා කරන්න"</string>
+ <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+ <skip />
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"සුරක්ෂිතව පුරනය වීමට සරල ක්‍රමයක්"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"අමතක කළ නොහැකි හෝ සොරකම් කළ නොහැකි අනන්‍ය මුරයතුරක් සමග පුරනය වීමට ඔබේ ඇඟිලි සලකුණ, මුහුණ හෝ තිර අගුල භාවිතා කරන්න. තව දැන ගන්න⁠"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> කොතැනක ද යන්න තෝරා ගන්න"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"ඔබේ මුරපදය සුරකින්න"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"ඔබේ පුරනය වීමේ තතු සුරකින්න"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> තුළ මුරයතුරක් තනන්න ද?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"ඔබේ මුරපදය <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> වෙත සුරකින්න ද?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"ඔබේ පුරනය වීමේ තතු <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> වෙත සුරකින්න ද?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"ඔබට ඕනෑම උපාංගයක ඔබේ <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> භාවිතා කළ හැක. එය <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> සඳහා <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> වෙත සුරැකෙයි"</string>
+ <string name="passkey" msgid="632353688396759522">"මුරයතුර"</string>
+ <string name="password" msgid="6738570945182936667">"මුරපදය"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"පුරනය වීම්"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"ඔබේ සියලු පුරනය වීම් සඳහා <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> භාවිතා කරන්න ද?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"පෙරනිමි ලෙස සකසන්න"</string>
+ <string name="use_once" msgid="9027366575315399714">"වරක් භාවිතා කරන්න"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"මුරපද <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>ක්, මුරයතුරු <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>ක්"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"මුරපද <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>ක්"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"මුරයතුරු <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>ක්"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"වෙනත් උපාංගයක්"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"වෙනත් මුරපද කළමනාකරුවන්"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"පත්‍රය වසන්න"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"පෙර පිටුවට ආපසු යන්න"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> සඳහා ඔබේ සුරැකි මුරයතුර භාවිතා කරන්න ද?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> සඳහා ඔබේ සුරැකි පුරනය භාවිතා කරන්න ද?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> සඳහා සුරැකි පුරනයක් තෝරා ගන්න"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"වෙනත් ආකාරයකින් පුරන්න"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"එපා ස්තුතියි"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"ඉදිරියට යන්න"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"පුරනය වීමේ විකල්ප"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> සඳහා"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"අගුළු දැමූ මුරපද කළමනාකරුවන්"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"අගුළු හැරීමට තට්ටු කරන්න"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"පුරනය වීම් කළමනාකරණය කරන්න"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"වෙනත් උපාංගයකින්"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"වෙනස් උපාංගයක් භාවිතා කරන්න"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-sk/strings.xml b/packages/CredentialManager/res/values-sk/strings.xml
new file mode 100644
index 000000000000..4545868ba76c
--- /dev/null
+++ b/packages/CredentialManager/res/values-sk/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Zrušiť"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Pokračovať"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Vytvoriť inde"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Uložiť inde"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Použiť iné zariadenie"</string>
+ <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+ <skip />
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Jednoduchý spôsob bezpečného prihlasovania"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Použite odtlačok prsta, tvár alebo zámku obrazovky a prihláste sa jedinečným prístupovým kľúčom, ktorý sa nedá zabudnúť ani ukradnúť. Ďalšie informácie"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Vyberte, kam <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"uložiť heslo"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"uložiť prihlasovacie údaje"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Chcete vytvoriť prístupový kľúč v službe <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Chcete uložiť heslo do služby <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Chcete uložiť svoje prihlasovacie údaje do služby <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"<xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> môžete používať v ľubovoľnom zariadení. Ukladá sa do služby <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> pre <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>."</string>
+ <string name="passkey" msgid="632353688396759522">"prístupový kľúč"</string>
+ <string name="password" msgid="6738570945182936667">"heslo"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"prihlasovacie údaje"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Chcete pre všetky svoje prihlasovacie údaje použiť <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Nastaviť ako predvolené"</string>
+ <string name="use_once" msgid="9027366575315399714">"Použiť raz"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"Počet hesiel: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>, počet prístupových kľúčov <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"Počet hesiel: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Počet prístupových kľúčov: <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Iné zariadenie"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Iní správcovia hesiel"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Zavrieť hárok"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Prejsť späť na predchádzajúcu stránku"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Chcete pre aplikáciu <xliff:g id="APP_NAME">%1$s</xliff:g> použiť uložený prístupový kľúč?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Chcete pre aplikáciu <xliff:g id="APP_NAME">%1$s</xliff:g> použiť uložené prihlasovacie údaje?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Vyberte uložené prihlasovacie údaje pre aplikáciu <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Prihláste sa inak"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Nie, vďaka"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Pokračovať"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Možnosti prihlásenia"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Pre používateľa <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Správcovia uzamknutých hesiel"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Odomknite klepnutím"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Spravovať prihlasovacie údaje"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Z iného zariadenia"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Použiť iné zariadenie"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-sl/strings.xml b/packages/CredentialManager/res/values-sl/strings.xml
new file mode 100644
index 000000000000..94edf669434a
--- /dev/null
+++ b/packages/CredentialManager/res/values-sl/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Prekliči"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Naprej"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Ustvarjanje na drugem mestu"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Shranjevanje na drugo mesto"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Uporabi drugo napravo"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Shrani v drugo napravo"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Preprost način za varno prijavo"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Če se želite prijaviti z enoličnim ključem za dostop, ki ga ni mogoče pozabiti ali ukrasti, uporabite prstni odtis, obraz ali nastavljeni način za odklepanje zaslona. Več o tem"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Izberite mesto za <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"shranjevanje gesla"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"shranjevanje podatkov za prijavo"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Želite ustvariti ključ za dostop pod »<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>«?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Želite shraniti geslo pod »<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>«?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Želite shraniti podatke za prijavo pod »<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>«?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"<xliff:g id="TYPE">%2$s</xliff:g> za <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> lahko uporabite v kateri koli napravi. Shranjeno je pod »<xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>« za <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>."</string>
+ <string name="passkey" msgid="632353688396759522">"ključ za dostop"</string>
+ <string name="password" msgid="6738570945182936667">"geslo"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"prijave"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Želite za vse prijave uporabiti »<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>«?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Nastavi kot privzeto"</string>
+ <string name="use_once" msgid="9027366575315399714">"Uporabi enkrat"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"Št. gesel: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>, št. ključev za dostop: <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"Št. gesel: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Št. ključev za dostop: <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Druga naprava"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Drugi upravitelji gesel"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Zapri list"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Nazaj na prejšnjo stran"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Želite uporabiti shranjeni ključ za dostop do aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Želite uporabiti shranjene podatke za prijavo v aplikacijo <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Izberite shranjene podatke za prijavo v aplikacijo <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Prijava na drug način"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Ne, hvala"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Naprej"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Možnosti prijave"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Za uporabnika <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Zaklenjeni upravitelji gesel"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Dotaknite se, da odklenete"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Upravljanje podatkov za prijavo"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Iz druge naprave"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Uporaba druge naprave"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-sq/strings.xml b/packages/CredentialManager/res/values-sq/strings.xml
new file mode 100644
index 000000000000..6b85a90a1e0c
--- /dev/null
+++ b/packages/CredentialManager/res/values-sq/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Anulo"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Vazhdo"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Krijo në një vend tjetër"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Ruaj në një vend tjetër"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Përdor një pajisje tjetër"</string>
+ <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+ <skip />
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Një mënyrë e thjeshtë për t\'u identifikuar në mënyrë të sigurt"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Përdor gjurmën e gishtit, fytyrën ose kyçjen e ekranit për t\'u identifikuar me një çelës unik kalimi i cili nuk mund të harrohet ose të vidhet. Mëso më shumë"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Zgjidh se ku të <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"ruaj fjalëkalimin"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"ruaj informacionet e tua të identifikimit"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Të krijohet një çelës kalimi në <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Të ruhet fjalëkalimi në <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Të ruhen informacionet e tua të identifikimit në <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Mund të përdorësh <xliff:g id="TYPE">%2$s</xliff:g> të <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> në çdo pajisje. Ai është i ruajtur te <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> për <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"çelësi i kalimit"</string>
+ <string name="password" msgid="6738570945182936667">"fjalëkalimi"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"identifikimet"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Të përdoret <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> për të gjitha identifikimet?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Cakto si parazgjedhje"</string>
+ <string name="use_once" msgid="9027366575315399714">"Përdor një herë"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> fjalëkalime, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> çelësa kalimi"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> fjalëkalime"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> çelësa kalimi"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Një pajisje tjetër"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Menaxherët e tjerë të fjalëkalimeve"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Mbyll fletën"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Kthehu te faqja e mëparshme"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Të përdoret fjalëkalimi yt i ruajtur për <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Të përdoret identifikimi yt i ruajtur për <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Zgjidh një identifikim të ruajtur për <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Identifikohu me një mënyrë tjetër"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Jo, faleminderit"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Vazhdo"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Opsionet e identifikimit"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Për <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Menaxherët e fjalëkalimeve të kyçura"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Trokit për të shkyçur"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Identifikimet e menaxhimit"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Nga një pajisje tjetër"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Përdor një pajisje tjetër"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-sr/strings.xml b/packages/CredentialManager/res/values-sr/strings.xml
new file mode 100644
index 000000000000..79c2eef82b6f
--- /dev/null
+++ b/packages/CredentialManager/res/values-sr/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Откажи"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Настави"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Направи на другом месту"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Сачувај на другом месту"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Користи други уређај"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Сачувај на други уређај"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Једноставан начин да се безбедно пријављујете"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Користите отисак прста, закључавање лицем или закључавање екрана да бисте се пријавили помоћу јединственог приступног кода који не може да се заборави или украде. Сазнајте више"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Одаберите локацију за: <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"сачувајте лозинку"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"сачувајте податке о пријављивању"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Желите да направите приступни кôд код корисника <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Желите да сачувате лозинку код корисника <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Желите да сачувате податке о пријављивању код корисника <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Можете да користите тип домена <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> на било ком уређају. Чува се код корисника <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> за: <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"приступни кôд"</string>
+ <string name="password" msgid="6738570945182936667">"лозинка"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"пријављивања"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Желите да за сва пријављивања користите: <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Подеси као подразумевано"</string>
+ <string name="use_once" msgid="9027366575315399714">"Користи једном"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"Лозинки: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>, приступних кодова:<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"Лозинки: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Приступних кодова: <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Други уређај"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Други менаџери лозинки"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Затворите табелу"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Вратите се на претходну страницу"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Желите да користите сачувани приступни кôд за: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Желите да користите сачуване податке за пријављивање за: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Одаберите сачувано пријављивање за: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Пријавите се на други начин"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Не, хвала"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Настави"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Опције за пријављивање"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"За: <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Менаџери закључаних лозинки"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Додирните да бисте откључали"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Управљајте пријављивањима"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Са другог уређаја"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Користи други уређај"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-sv/strings.xml b/packages/CredentialManager/res/values-sv/strings.xml
new file mode 100644
index 000000000000..7b250561e077
--- /dev/null
+++ b/packages/CredentialManager/res/values-sv/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Avbryt"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Fortsätt"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Skapa på en annan plats"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Spara på en annan plats"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Använd en annan enhet"</string>
+ <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+ <skip />
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Ett enkelt sätt att logga in säkert på"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Använd ditt fingeravtryck, ansikte eller skärmlås om du vill logga in med en unik nyckel som inte kan glömmas bort eller bli stulen. Läs mer"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Välj var du <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"spara lösenordet"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"spara inloggningsuppgifterna"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Vill du skapa en nyckel i <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Vill du spara ditt lösenord i <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Vill du spara dina inloggningsuppgifter i <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Du kan använda <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> på alla enheter. Du kan spara det i <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> för <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"nyckel"</string>
+ <string name="password" msgid="6738570945182936667">"lösenord"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"inloggningar"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Vill du använda <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> för alla dina inloggningar?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Ange som standard"</string>
+ <string name="use_once" msgid="9027366575315399714">"Använd en gång"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> lösenord, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> nycklar"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> lösenord"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> nycklar"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"En annan enhet"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Andra lösenordshanterare"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Stäng kalkylarket"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Gå tillbaka till föregående sida"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Vill du använda din sparade nyckel för <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Vill du använda dina sparade inloggningsuppgifter för <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Välj en sparad inloggning för <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Logga in på ett annat sätt"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Nej tack"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Fortsätt"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Inloggningsalternativ"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"För <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Låsta lösenordshanterare"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Tryck för att låsa upp"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Hantera inloggningar"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Via en annan enhet"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Använd en annan enhet"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-ta/strings.xml b/packages/CredentialManager/res/values-ta/strings.xml
new file mode 100644
index 000000000000..646c4699e215
--- /dev/null
+++ b/packages/CredentialManager/res/values-ta/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"ரத்துசெய்"</string>
+ <string name="string_continue" msgid="1346732695941131882">"தொடர்க"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"மற்றொரு இடத்தில் உருவாக்கவும்"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"மற்றொரு இடத்தில் சேமிக்கவும்"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"மற்றொரு சாதனத்தைப் பயன்படுத்தவும்"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"வேறொரு சாதனத்தில் சேமியுங்கள்"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"பாதுகாப்பாக உள்நுழைவதற்கான எளிய வழி"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"தனித்துவமான கடவுக்குறியீடு (மறக்காதவை அல்லது திருடமுடியாதவை) மூலம் உள்நுழைய, உங்கள் கைரேகை, முகம் அல்லது திரைப்பூட்டைப் பயன்படுத்தி உள்நுழையவும். மேலும் அறிக"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> எங்கே காட்டப்பட வேண்டும் என்பதைத் தேர்வுசெய்தல்"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"உங்கள் கடவுச்சொல்லைச் சேமிக்கவும்"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"உங்கள் உள்நுழைவு தகவலைச் சேமிக்கவும்"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>ல் கடவுக்குறியீட்டை உருவாக்க வேண்டுமா?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"உங்கள் கடவுச்சொல்லை <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>ல் சேமிக்க வேண்டுமா?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"உங்கள் உள்நுழைவுத் தகவலை <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>ல் சேமிக்க வேண்டுமா?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"உங்கள் <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> ஐ எந்தச் சாதனத்தில் வேண்டுமானாலும் பயன்படுத்தலாம். <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>ல் <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>க்காகச் சேமிக்கப்பட்டது"</string>
+ <string name="passkey" msgid="632353688396759522">"கடவுக்குறியீடு"</string>
+ <string name="password" msgid="6738570945182936667">"கடவுச்சொல்"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"உள்நுழைவுகள்"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"உங்கள் அனைத்து உள்நுழைவுகளுக்கும் <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>ஐப் பயன்படுத்தவா?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"இயல்பானதாக அமை"</string>
+ <string name="use_once" msgid="9027366575315399714">"ஒருமுறை பயன்படுத்தவும்"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> கடவுச்சொற்கள், <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> கடவுக்குறியீடுகள்"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> கடவுச்சொற்கள்"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> கடவுக்குறியீடுகள்"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"மற்றொரு சாதனம்"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"பிற கடவுச்சொல் நிர்வாகிகள்"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"ஷீட்டை மூடும்"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"முந்தைய பக்கத்திற்குச் செல்லும்"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸுக்கு ஏற்கெனவே சேமிக்கப்பட்ட கடவுக்குறியீட்டைப் பயன்படுத்தவா?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸுக்கு ஏற்கெனவே சேமிக்கப்பட்ட உள்நுழைவுத் தகவலைப் பயன்படுத்தவா?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸுக்கு ஏற்கெனவே சேமிக்கப்பட்ட உள்நுழைவுத் தகவலைத் தேர்வுசெய்யவும்"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"வேறு முறையில் உள்நுழைக"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"வேண்டாம்"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"தொடர்க"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"உள்நுழைவு விருப்பங்கள்"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g>க்கு"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"பூட்டப்பட்ட கடவுச்சொல் நிர்வாகிகள்"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"அன்லாக் செய்ய தட்டவும்"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"உள்நுழைவுகளை நிர்வகித்தல்"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"மற்றொரு சாதனத்திலிருந்து பயன்படுத்து"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"வேறு சாதனத்தைப் பயன்படுத்து"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-te/strings.xml b/packages/CredentialManager/res/values-te/strings.xml
new file mode 100644
index 000000000000..d94f3d35e87b
--- /dev/null
+++ b/packages/CredentialManager/res/values-te/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"రద్దు చేయండి"</string>
+ <string name="string_continue" msgid="1346732695941131882">"కొనసాగించండి"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"మరొక స్థలంలో క్రియేట్ చేయండి"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"మరొక స్థలంలో సేవ్ చేయండి"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"మరొక పరికరాన్ని ఉపయోగించండి"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"మరొక పరికరంలో సేవ్ చేయండి"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"సురక్షితంగా సైన్ ఇన్ చేయడానికి సులభమైన మార్గం"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"మర్చిపోలేని లేదా దొంగిలించలేని ప్రత్యేకమైన పాస్-కీతో సైన్ ఇన్ చేయడానికి మీ వేలిముద్ర, ముఖం లేదా స్క్రీన్ లాక్‌ను ఉపయోగించండి. మరింత తెలుసుకోండి"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"ఎక్కడ <xliff:g id="CREATETYPES">%1$s</xliff:g> చేయాలో ఎంచుకోండి"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"మీ పాస్‌వర్డ్‌ను సేవ్ చేయండి"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"మీ సైన్ ఇన్ సమాచారాన్ని సేవ్ చేయండి"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>లో పాస్-కీని క్రియేట్ చేయాలా?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"మీ పాస్‌వర్డ్‌ను <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>కు సేవ్ చేయాలా?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"మీ సైన్ ఇన్ సమాచారాన్ని <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>కు సేవ్ చేయాలా?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"మీరు మీ <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g>ని ఏ పరికరంలోనైనా ఉపయోగించవచ్చు. ఇది <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> కోసం <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>లో సేవ్ చేయబడింది"</string>
+ <string name="passkey" msgid="632353688396759522">"పాస్-కీ"</string>
+ <string name="password" msgid="6738570945182936667">"పాస్‌వర్డ్"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"సైన్‌ ఇన్‌లు"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"మీ అన్ని సైన్-ఇన్ వివరాల కోసం <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>ను ఉపయోగించాలా?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"ఆటోమేటిక్ సెట్టింగ్‌గా సెట్ చేయండి"</string>
+ <string name="use_once" msgid="9027366575315399714">"ఒకసారి ఉపయోగించండి"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> పాస్‌వర్డ్‌లు, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> పాస్-కీలు"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> పాస్‌వర్డ్‌లు"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> పాస్-కీలు"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"మరొక పరికరం"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"ఇతర పాస్‌వర్డ్ మేనేజర్‌లు"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"షీట్‌ను మూసివేయండి"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"మునుపటి పేజీకి తిరిగి వెళ్లండి"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> కోసం మీ సేవ్ చేసిన పాస్-కీ వివరాలను ఉపయోగించాలా?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> కోసం మీరు సేవ్ చేసిన సైన్ ఇన్ వివరాలను ఉపయోగించాలా?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> కోసం సేవ్ చేసిన సైన్ ఇన్ వివరాలను ఎంచుకోండి"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"మరొక పద్ధతిలో సైన్ ఇన్ చేయండి"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"వద్దు, థ్యాంక్స్"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"కొనసాగించండి"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"సైన్ ఇన్ ఆప్షన్‌లు"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> కోసం"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"లాక్ చేయబడిన పాస్‌వర్డ్ మేనేజర్‌లు"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"అన్‌లాక్ చేయడానికి ట్యాప్ చేయండి"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"సైన్‌ ఇన్‌లను మేనేజ్ చేయండి"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"మరొక పరికరం నుండి"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"వేరే పరికరాన్ని ఉపయోగించండి"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-th/strings.xml b/packages/CredentialManager/res/values-th/strings.xml
new file mode 100644
index 000000000000..43f3f0fcd2bc
--- /dev/null
+++ b/packages/CredentialManager/res/values-th/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"ยกเลิก"</string>
+ <string name="string_continue" msgid="1346732695941131882">"ต่อไป"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"สร้างในตำแหน่งอื่น"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"บันทึกลงในตำแหน่งอื่น"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"ใช้อุปกรณ์อื่น"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"ย้ายไปยังอุปกรณ์อื่น"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"วิธีง่ายๆ ในการลงชื่อเข้าใช้อย่างปลอดภัย"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"ใช้ลายนิ้วมือ ใบหน้า หรือล็อกหน้าจอในการลงชื่อเข้าใช้ด้วยพาสคีย์ที่ไม่ซ้ำกันเพื่อไม่ให้ลืมหรือถูกขโมยได้ ดูข้อมูลเพิ่มเติม"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"เลือกตำแหน่งที่จะ <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"บันทึกรหัสผ่าน"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"บันทึกข้อมูลการลงชื่อเข้าใช้"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"สร้างพาสคีย์ใน <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ใช่ไหม"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"บันทึกรหัสผ่านลงใน <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ใช่ไหม"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"บันทึกข้อมูลการลงชื่อเข้าใช้ลงใน <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ใช่ไหม"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"คุณใช้ <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> ในอุปกรณ์ใดก็ได้ โดยจะบันทึกลงใน <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> สำหรับ <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"พาสคีย์"</string>
+ <string name="password" msgid="6738570945182936667">"รหัสผ่าน"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"การลงชื่อเข้าใช้"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"ใช้ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> สำหรับการลงชื่อเข้าใช้ทั้งหมดใช่ไหม"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"ตั้งเป็นค่าเริ่มต้น"</string>
+ <string name="use_once" msgid="9027366575315399714">"ใช้ครั้งเดียว"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"รหัสผ่าน <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> รายการ พาสคีย์ <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> รายการ"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"รหัสผ่าน <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> รายการ"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"พาสคีย์ <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> รายการ"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"อุปกรณ์อื่น"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"เครื่องมือจัดการรหัสผ่านอื่นๆ"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"ปิดชีต"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"กลับไปยังหน้าก่อนหน้า"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"ใช้พาสคีย์ที่บันทึกไว้สำหรับ \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" ใช่ไหม"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"ใช้การลงชื่อเข้าใช้ที่บันทึกไว้สำหรับ \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" ใช่ไหม"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"เลือกการลงชื่อเข้าใช้ที่บันทึกไว้สำหรับ \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"ลงชื่อเข้าใช้ด้วยวิธีอื่น"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"ไม่เป็นไร"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"ต่อไป"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"ตัวเลือกการลงชื่อเข้าใช้"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"สำหรับ <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"เครื่องมือจัดการรหัสผ่านที่ล็อกไว้"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"แตะเพื่อปลดล็อก"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"จัดการการลงชื่อเข้าใช้"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"จากอุปกรณ์อื่น"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"ใช้อุปกรณ์อื่น"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-tl/strings.xml b/packages/CredentialManager/res/values-tl/strings.xml
new file mode 100644
index 000000000000..4dae03722dcc
--- /dev/null
+++ b/packages/CredentialManager/res/values-tl/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Kanselahin"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Magpatuloy"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Gumawa sa ibang lugar"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"I-save sa ibang lugar"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Gumamit ng ibang device"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"I-save sa ibang device"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Simpleng paraan para mag-sign in lang ligtas"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Gamitin ang iyong fingerprint, mukha, o lock ng screen para mag-sign in gamit ang natatanging passkey na hindi makakalimutan o mananakaw. Matuto pa"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Piliin kung saan <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"i-save ang iyong password"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"i-save ang iyong impormasyon sa pag-sign in"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Gumawa ng passkey sa <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"I-save ang iyong password sa <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"I-save ang iyong impormasyon sa pag-sign in sa <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Magagamit mo ang iyong <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> sa anumang device. Naka-save ito sa <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> para sa <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"passkey"</string>
+ <string name="password" msgid="6738570945182936667">"password"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"mga sign-in"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Gamitin ang <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> para sa lahat ng iyong pag-sign in?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Itakda bilang default"</string>
+ <string name="use_once" msgid="9027366575315399714">"Gamitin nang isang beses"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> (na) password, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> (na) passkey"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> (na) password"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> (na) passkey"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Ibang device"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Iba pang password manager"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Isara ang sheet"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Bumalik sa nakaraang page"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Gamitin ang iyong naka-save na passkey para sa <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Gamitin ang iyong naka-save na sign-in para sa <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Pumili ng naka-save na sign-in para sa <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Mag-sign in sa ibang paraan"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Hindi, salamat na lang"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Magpatuloy"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Mga opsyon sa pag-sign in"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Para kay <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Mga naka-lock na password manager"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"I-tap para i-unlock"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Pamahalaan ang mga sign-in"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Mula sa ibang device"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Gumamit ng ibang device"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-tr/strings.xml b/packages/CredentialManager/res/values-tr/strings.xml
new file mode 100644
index 000000000000..c1ccd984b375
--- /dev/null
+++ b/packages/CredentialManager/res/values-tr/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"İptal"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Devam"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Başka bir yerde oluşturun"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Başka bir yere kaydedin"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Başka bir cihaz kullan"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Başka bir cihaza kaydedin"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Güvenli bir şekilde oturum açmanın basit yolu"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Parmak iziniz, yüzünüz ya da ekran kilidinizi kullanarak unutması veya çalınması mümkün olmayan benzersiz bir şifre anahtarıyla oturum açın. Daha fazla bilgi"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> yerini seçin"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"şifrenizi kaydedin"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"oturum açma bilgilerinizi kaydedin"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> içinde şifre anahtarı oluşturulsun mu?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Şifreniz <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> içine kaydedilsin mi?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Oturum açma bilgileriniz <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> içine kaydedilsin mi?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"<xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> herhangi bir cihazda kullanılabilir. <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> için <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> içine kaydedilir"</string>
+ <string name="passkey" msgid="632353688396759522">"şifre anahtarı"</string>
+ <string name="password" msgid="6738570945182936667">"şifre"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"oturum aç"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Tüm oturum açma işlemlerinizde <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> kullanılsın mı?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Varsayılan olarak ayarla"</string>
+ <string name="use_once" msgid="9027366575315399714">"Bir kez kullanın"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> şifre, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> şifre anahtarı"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> şifre"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> şifre anahtarı"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Başka bir cihaz"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Diğer şifre yöneticileri"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Sayfayı kapat"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Önceki sayfaya geri dön"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> için kayıtlı şifre anahtarınız kullanılsın mı?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> için kayıtlı oturum açma bilgileriniz kullanılsın mı?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> için kayıtlı oturum açma bilgilerini kullanın"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Başka bir yöntemle oturum aç"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Hayır, teşekkürler"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Devam"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Oturum açma seçenekleri"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> için"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Kilitli şifre yöneticileri"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Kilidi açmak için dokunun"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Oturum açma bilgilerini yönetin"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Başka bir cihazdan"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Farklı bir cihaz kullan"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-uk/strings.xml b/packages/CredentialManager/res/values-uk/strings.xml
new file mode 100644
index 000000000000..396da4d1da87
--- /dev/null
+++ b/packages/CredentialManager/res/values-uk/strings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Скасувати"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Продовжити"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Створити в іншому місці"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Зберегти в іншому місці"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Скористатись іншим пристроєм"</string>
+ <!-- no translation found for string_save_to_another_device (1959562542075194458) -->
+ <skip />
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Зручний спосіб для безпечного входу"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Користуйтеся відбитком пальця, фейсконтролем або іншим способом розблокування екрана, щоб входити в обліковий запис за допомогою унікального ключа доступу, який неможливо забути чи викрасти. Докладніше"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Виберіть, де <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"зберегти пароль"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"зберегти дані для входу"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Створити ключ доступу в сервісі <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Зберегти ваш пароль у сервісі <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Зберегти ваші дані для входу в сервіс <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Ви можете використовувати <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> на будь-якому пристрої. Його збережено в постачальника послуг \"<xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>\" для <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"ключ доступу"</string>
+ <string name="password" msgid="6738570945182936667">"пароль"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"дані для входу"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Використовувати сервіс <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> в усіх випадках входу?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Вибрати за умовчанням"</string>
+ <string name="use_once" msgid="9027366575315399714">"Скористатися раз"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"Кількість паролів: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>; кількість ключів доступу: <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"Кількість паролів: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Кількість ключів доступу: <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Інший пристрій"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Інші менеджери паролів"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Закрити аркуш"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Повернутися на попередню сторінку"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Використати збережений ключ доступу для додатка <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Використати збережені дані для входу для додатка <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Виберіть збережені дані для входу в додаток <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Увійти іншим способом"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Ні, дякую"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Продовжити"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Опції входу"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Для користувача <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Заблоковані менеджери паролів"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Торкніться, щоб розблокувати"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Керування даними для входу"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"З іншого пристрою"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Використовувати інший пристрій"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-ur/strings.xml b/packages/CredentialManager/res/values-ur/strings.xml
new file mode 100644
index 000000000000..e67b94c7853b
--- /dev/null
+++ b/packages/CredentialManager/res/values-ur/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"منسوخ کریں"</string>
+ <string name="string_continue" msgid="1346732695941131882">"جاری رکھیں"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"دوسرے مقام میں تخلیق کریں"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"دوسرے مقام میں محفوظ کریں"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"کوئی دوسرا آلہ استعمال کریں"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"دوسرے آلے میں محفوظ کریں"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"محفوظ طریقے سے سائن ان کرنے کا آسان طریقہ"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"اپنے فنگر پرنٹ، چہرے یا اسکرین لاک کا استعمال کریں تاکہ ایک ایسی منفرد پاس کی سے سائن ان کیا جا سکے جسے بھولا یا چوری نہیں کیا جا سکتا۔ مزید جانیں"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> کی جگہ منتخب کریں"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"اپنا پاس ورڈ محفوظ کریں"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"اپنے سائن ان کی معلومات محفوظ کریں"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> میں پاس کی تخلیق کریں؟"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"اپنا پاس ورڈ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> میں محفوظ کریں؟"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"اپنے سائن ان کی معلومات کو <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> میں محفوظ کریں؟"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"آپ کسی بھی آلے پر اپنا <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> استعمال کر سکتے ہیں۔ یہ <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> کے <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> میں محفوظ ہو جاتا ہے"</string>
+ <string name="passkey" msgid="632353688396759522">"پاس کی"</string>
+ <string name="password" msgid="6738570945182936667">"پاس ورڈ"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"سائن انز"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"اپنے سبھی سائن انز کے لیے <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> کا استعمال کریں؟"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"بطور ڈیفالٹ سیٹ کریں"</string>
+ <string name="use_once" msgid="9027366575315399714">"ایک بار استعمال کریں"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> پاس ورڈز، <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> پاس کیز"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> پاس ورڈز"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> پاس کیز"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"دوسرا آلہ"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"دیگر پاس ورڈ مینیجرز"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"شیٹ بند کریں"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"گزشتہ صفحے پر واپس جائیں"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> کے لیے اپنی محفوظ کردہ پاس کی استعمال کریں؟"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> کے لیے اپنے محفوظ کردہ سائن ان کو استعمال کریں؟"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> کے لیے محفوظ کردہ سائن انز منتخب کریں"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"دوسرے طریقے سے سائن ان کریں"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"نہیں شکریہ"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"جاری رکھیں"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"سائن ان کے اختیارات"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> کے لیے"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"مقفل کردہ پاس ورڈ مینیجرز"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"غیر مقفل کرنے کے لیے تھپتھپائیں"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"سائن انز کا نظم کریں"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"دوسرے آلے سے"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"ایک مختلف آلہ استعمال کریں"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-uz/strings.xml b/packages/CredentialManager/res/values-uz/strings.xml
new file mode 100644
index 000000000000..6c3e211b9d6a
--- /dev/null
+++ b/packages/CredentialManager/res/values-uz/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Bekor qilish"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Davom etish"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Boshqa joyda yaratish"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Boshqa joyga saqlash"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Boshqa qurilmadan foydalaning"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Boshqa qurilmaga saqlash"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Xavfsiz kirishning oddiy usuli"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Esda qoladigan maxsus kalit bilan kirishda barmoq izi, yuz axboroti yoki ekran qulfidan foydalaning. Batafsil"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> joyini tanlang"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"Parolni saqlash"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"kirish axborotini saqlang"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Kalit <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> xizmatida yaratilsinmi?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Parol <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> xizmatida saqlansinmi?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Hisob maʼlumotlari <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> xizmatida saqlansinmi?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"<xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> istalgan qurilmada ishlatilishi mumkin. U <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> uchun <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> xizmatiga saqlandi"</string>
+ <string name="passkey" msgid="632353688396759522">"kalit"</string>
+ <string name="password" msgid="6738570945182936667">"parol"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"kirishlar"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Hamma kirishlarda <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ishlatilsinmi?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Birlamchi deb belgilash"</string>
+ <string name="use_once" msgid="9027366575315399714">"Bir marta ishlatish"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> ta parol, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> ta kalit"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> ta parol"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> ta kalit"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Boshqa qurilma"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Boshqa parol menejerlari"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Varaqni yopish"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Avvalgi sahifaga qaytish"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> uchun saqlangan kalit ishlatilsinmi?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> uchun saqlangan maʼlumotlar ishlatilsinmi?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> hisob maʼlumotlarini tanlang"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Boshqa usul orqali kirish"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Kerak emas"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Davom etish"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Kirish parametrlari"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> uchun"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Qulfli parol menejerlari"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Qulfni ochish uchun bosing"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Hisob maʼlumotlarini boshqarish"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Boshqa qurilmada"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Boshqa qurilmadan foydalanish"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-vi/strings.xml b/packages/CredentialManager/res/values-vi/strings.xml
new file mode 100644
index 000000000000..d4703f394c89
--- /dev/null
+++ b/packages/CredentialManager/res/values-vi/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Huỷ"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Tiếp tục"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Tạo ở vị trí khác"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Lưu vào vị trí khác"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Dùng thiết bị khác"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Lưu vào thiết bị khác"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Cách đơn giản để đăng nhập an toàn"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Dùng vân tay, khuôn mặt hoặc phương thức khoá màn hình để đăng nhập bằng một mã xác thực duy nhất mà bạn không lo sẽ quên hay bị đánh cắp. Tìm hiểu thêm"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Chọn vị trí <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"lưu mật khẩu của bạn"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"lưu thông tin đăng nhập của bạn"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Tạo một mã xác thực trong <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Lưu mật khẩu của bạn vào <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Lưu thông tin đăng nhập của bạn vào <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Bạn có thể sử dụng <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> trên mọi thiết bị. Thông tin này được lưu vào <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g> cho <xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"mã xác thực"</string>
+ <string name="password" msgid="6738570945182936667">"mật khẩu"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"thông tin đăng nhập"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Dùng <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> cho mọi thông tin đăng nhập của bạn?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Đặt làm mặc định"</string>
+ <string name="use_once" msgid="9027366575315399714">"Dùng một lần"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> mật khẩu, <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> mã xác thực"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> mật khẩu"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> mã xác thực"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Thiết bị khác"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Trình quản lý mật khẩu khác"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Đóng trang tính"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Quay lại trang trước"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Dùng mã xác thực bạn đã lưu cho <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Dùng thông tin đăng nhập bạn đã lưu cho <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Chọn thông tin đăng nhập đã lưu cho <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Đăng nhập bằng cách khác"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Không, cảm ơn"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Tiếp tục"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Tuỳ chọn đăng nhập"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Cho <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Trình quản lý mật khẩu đã khoá"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Nhấn để mở khoá"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Quản lý thông tin đăng nhập"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Từ một thiết bị khác"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Dùng thiết bị khác"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-zh-rCN/strings.xml b/packages/CredentialManager/res/values-zh-rCN/strings.xml
new file mode 100644
index 000000000000..145eac2701ba
--- /dev/null
+++ b/packages/CredentialManager/res/values-zh-rCN/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"取消"</string>
+ <string name="string_continue" msgid="1346732695941131882">"继续"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"在另一位置创建"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"保存到另一位置"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"使用另一台设备"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"保存到其他设备"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"简单又安全的登录方式"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"借助指纹、人脸识别或屏幕锁定功能,使用不会被忘记或被盗且具有唯一性的通行密钥登录。了解详情"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"选择<xliff:g id="CREATETYPES">%1$s</xliff:g>的位置"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"保存您的密码"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"保存您的登录信息"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"在“<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>”中创建通行密钥?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"将您的密码保存至“<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>”?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"将您的登录信息保存至“<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>”?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"您可以在任意设备上使用 <xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g>。它会保存到“<xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>”的<xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"通行密钥"</string>
+ <string name="password" msgid="6738570945182936667">"密码"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"登录"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"将“<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>”用于您的所有登录信息?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"设为默认项"</string>
+ <string name="use_once" msgid="9027366575315399714">"使用一次"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> 个密码,<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> 个通行密钥"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> 个密码"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> 个通行密钥"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"另一台设备"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"其他密码管理工具"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"关闭工作表"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"返回上一页"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"将您已保存的通行密钥用于<xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"将您已保存的登录信息用于<xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"为<xliff:g id="APP_NAME">%1$s</xliff:g>选择已保存的登录信息"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"以另一种方式登录"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"不用了"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"继续"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"登录选项"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"用户:<xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"已锁定的密码管理工具"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"点按即可解锁"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"管理登录信息"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"通过另一台设备"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"使用其他设备"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-zh-rHK/strings.xml b/packages/CredentialManager/res/values-zh-rHK/strings.xml
new file mode 100644
index 000000000000..f277c22e92c6
--- /dev/null
+++ b/packages/CredentialManager/res/values-zh-rHK/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"取消"</string>
+ <string name="string_continue" msgid="1346732695941131882">"繼續"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"在其他位置建立"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"儲存至其他位置"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"改用其他裝置"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"儲存至其他裝置"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"安全又簡便的登入方式"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"使用指紋、面孔或螢幕鎖定配合密鑰登入。密鑰獨一無二,您不用擔心忘記密鑰或密鑰被盜。瞭解詳情"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"選擇「<xliff:g id="CREATETYPES">%1$s</xliff:g>」的位置"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"儲存密碼"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"儲存登入資料"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"要在「<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>」建立密鑰嗎?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"要將密碼儲存至「<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>」嗎?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"要將登入資料儲存至「<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>」嗎?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"您可以在任何裝置上使用「<xliff:g id="APPDOMAINNAME">%1$s</xliff:g>」<xliff:g id="TYPE">%2$s</xliff:g>。系統會將此資料儲存至「<xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>」,供「<xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>」使用"</string>
+ <string name="passkey" msgid="632353688396759522">"密鑰"</string>
+ <string name="password" msgid="6738570945182936667">"密碼"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"登入資料"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"要將「<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>」用於所有的登入資料嗎?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"設定為預設"</string>
+ <string name="use_once" msgid="9027366575315399714">"單次使用"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> 個密碼,<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> 個密鑰"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> 個密碼"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> 個密鑰"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"其他裝置"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"其他密碼管理工具"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"閂工作表"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"返回上一頁"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"要使用已儲存的「<xliff:g id="APP_NAME">%1$s</xliff:g>」密鑰嗎?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"要使用已儲存的「<xliff:g id="APP_NAME">%1$s</xliff:g>」登入資料嗎?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"選擇已儲存的「<xliff:g id="APP_NAME">%1$s</xliff:g>」登入資料"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"使用其他方式登入"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"不用了,謝謝"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"繼續"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"登入選項"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> 專用"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"已鎖定的密碼管理工具"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"輕按即可解鎖"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"管理登入資料"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"透過其他裝置"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"使用其他裝置"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-zh-rTW/strings.xml b/packages/CredentialManager/res/values-zh-rTW/strings.xml
new file mode 100644
index 000000000000..fd1dfc8e29e1
--- /dev/null
+++ b/packages/CredentialManager/res/values-zh-rTW/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"取消"</string>
+ <string name="string_continue" msgid="1346732695941131882">"繼續"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"在其他位置建立"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"儲存至其他位置"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"改用其他裝置"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"儲存至其他裝置"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"安全又簡單的登入方式"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"登入帳戶時,你可以使用指紋、人臉或螢幕鎖定功能搭配不重複的密碼金鑰,不必擔心忘記密碼金鑰或遭人竊取。瞭解詳情"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"選擇「<xliff:g id="CREATETYPES">%1$s</xliff:g>」的位置"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"儲存密碼"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"儲存登入資訊"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"要在「<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>」建立密碼金鑰嗎?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"要將密碼儲存至「<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>」嗎?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"要將登入資訊儲存至「<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>」嗎?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"你可以在任何裝置上使用「<xliff:g id="APPDOMAINNAME">%1$s</xliff:g>」<xliff:g id="TYPE">%2$s</xliff:g>。系統會將這項資料儲存至「<xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>」,以供「<xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>」使用"</string>
+ <string name="passkey" msgid="632353688396759522">"密碼金鑰"</string>
+ <string name="password" msgid="6738570945182936667">"密碼"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"登入資訊"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"要將「<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>」用於所有的登入資訊嗎?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"設為預設"</string>
+ <string name="use_once" msgid="9027366575315399714">"單次使用"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> 個密碼,<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> 個密碼金鑰"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> 個密碼"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> 個密碼金鑰"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"其他裝置"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"其他密碼管理工具"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"關閉功能表"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"返回上一頁"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"要使用已儲存的「<xliff:g id="APP_NAME">%1$s</xliff:g>」密碼金鑰嗎?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"要使用已儲存的「<xliff:g id="APP_NAME">%1$s</xliff:g>」登入資訊嗎?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"選擇已儲存的「<xliff:g id="APP_NAME">%1$s</xliff:g>」登入資訊"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"使用其他方式登入"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"不用了,謝謝"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"繼續"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"登入選項"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> 專用"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"已鎖定的密碼管理工具"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"輕觸即可解鎖"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"管理登入資訊"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"透過其他裝置"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"使用其他裝置"</string>
+</resources>
diff --git a/packages/CredentialManager/res/values-zu/strings.xml b/packages/CredentialManager/res/values-zu/strings.xml
new file mode 100644
index 000000000000..fd2b83e7bfcc
--- /dev/null
+++ b/packages/CredentialManager/res/values-zu/strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for app_name (4539824758261855508) -->
+ <skip />
+ <string name="string_cancel" msgid="6369133483981306063">"Khansela"</string>
+ <string name="string_continue" msgid="1346732695941131882">"Qhubeka"</string>
+ <string name="string_create_in_another_place" msgid="1033635365843437603">"Sungula kwenye indawo"</string>
+ <string name="string_save_to_another_place" msgid="7590325934591079193">"Londoloza kwenye indawo"</string>
+ <string name="string_use_another_device" msgid="8754514926121520445">"Sebenzisa enye idivayisi"</string>
+ <string name="string_save_to_another_device" msgid="1959562542075194458">"Londoloza kwenye idivayisi"</string>
+ <string name="passkey_creation_intro_title" msgid="402553911484409884">"Indlela elula yokungena ngemvume ngokuphephile"</string>
+ <string name="passkey_creation_intro_body" msgid="7493320456005579290">"Sebenzisa isigxivizo somunwe, ubuso noma ukukhiya isikrini ukuze ungene ngemvume ngokhiye wokudlula oyingqayizivele ongenakulibaleka noma owebiwe. Funda kabanzi"</string>
+ <string name="choose_provider_title" msgid="7245243990139698508">"Khetha lapho onga-<xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
+ <!-- no translation found for create_your_passkeys (8901224153607590596) -->
+ <skip />
+ <string name="save_your_password" msgid="6597736507991704307">"Londoloza iphasiwedi yakho"</string>
+ <string name="save_your_sign_in_info" msgid="7213978049817076882">"londoloza ulwazi lwakho lokungena ngemvume"</string>
+ <!-- no translation found for choose_provider_body (8045759834416308059) -->
+ <skip />
+ <string name="choose_create_option_passkey_title" msgid="4146408187146573131">"Sungula ukhiye wokungena ku-<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_password_title" msgid="8812546498357380545">"Londoloza ulwazi lwakho lwephasiwedi ku-<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_sign_in_title" msgid="6318246378475961834">"Londoloza ulwazi lwakho lokungena ngemvume ku-<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>?"</string>
+ <string name="choose_create_option_description" msgid="4419171903963100257">"Ungasebenzisa i-<xliff:g id="APPDOMAINNAME">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> kunoma iyiphi idivayisi. Ilondolozelwe i-<xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> ku-<xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>"</string>
+ <string name="passkey" msgid="632353688396759522">"ukhiye wokudlula"</string>
+ <string name="password" msgid="6738570945182936667">"iphasiwedi"</string>
+ <string name="sign_ins" msgid="4710739369149469208">"ukungena ngemvume"</string>
+ <!-- no translation found for create_passkey_in_title (2714306562710897785) -->
+ <skip />
+ <!-- no translation found for save_password_to_title (3450480045270186421) -->
+ <skip />
+ <!-- no translation found for save_sign_in_to_title (8328143607671760232) -->
+ <skip />
+ <string name="use_provider_for_all_title" msgid="4201020195058980757">"Sebenzisa i-<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> kukho konke ukungena kwakho ngemvume?"</string>
+ <!-- no translation found for use_provider_for_all_description (6560593199974037820) -->
+ <skip />
+ <string name="set_as_default" msgid="4415328591568654603">"Setha njengokuzenzakalelayo"</string>
+ <string name="use_once" msgid="9027366575315399714">"Sebenzisa kanye"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="4794903978126339473">"Amaphasiwedi angu-<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>, okhiye bokudlula abangu-<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"Amaphasiwedi angu-<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Okhiye bokudlula abangu-<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
+ <!-- no translation found for passkey_before_subtitle (2448119456208647444) -->
+ <skip />
+ <string name="another_device" msgid="5147276802037801217">"Enye idivayisi"</string>
+ <string name="other_password_manager" msgid="565790221427004141">"Abanye abaphathi bephasiwedi"</string>
+ <string name="close_sheet" msgid="1393792015338908262">"Vala ishidi"</string>
+ <string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Buyela emuva ekhasini langaphambilini"</string>
+ <string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Sebenzisa ukhiye wakho wokungena olondoloziwe <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"Sebenzisa ukungena kwakho ngemvume okulondoloziwe <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"Khetha ukungena ngemvume okulondoloziwe kwakho <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Ngena ngemvume ngenye indlela"</string>
+ <string name="get_dialog_button_label_no_thanks" msgid="8114363019023838533">"Cha ngiyabonga"</string>
+ <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"Qhubeka"</string>
+ <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"Okungakhethwa kukho kokungena ngemvume"</string>
+ <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"Okuka-<xliff:g id="USERNAME">%1$s</xliff:g>"</string>
+ <string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Abaphathi bephasiwedi abakhiyiwe"</string>
+ <string name="locked_credential_entry_label_subtext" msgid="9213450912991988691">"Thepha ukuze uvule"</string>
+ <string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Phatha ukungena ngemvume"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Kusukela kwenye idivayisi"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Sebenzisa idivayisi ehlukile"</string>
+</resources>
diff --git a/packages/PackageInstaller/Android.bp b/packages/PackageInstaller/Android.bp
index 47575137558a..fe640ad5974e 100644
--- a/packages/PackageInstaller/Android.bp
+++ b/packages/PackageInstaller/Android.bp
@@ -58,6 +58,7 @@ android_app {
privileged: true,
platform_apis: true,
rename_resources_package: false,
+ overrides: ["PackageInstaller"],
static_libs: [
"xz-java",
@@ -76,6 +77,7 @@ android_app {
privileged: true,
platform_apis: true,
rename_resources_package: false,
+ overrides: ["PackageInstaller"],
static_libs: [
"xz-java",
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index caaa88d6aea8..b6939962676d 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1104,7 +1104,7 @@
<!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
<string name="power_charging_duration"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="time">%2$s</xliff:g> left until full</string>
<!-- [CHAR_LIMIT=80] Label for battery level chart when charge been limited -->
- <string name="power_charging_limited"><xliff:g id="level">%1$s</xliff:g> - Charging is paused</string>
+ <string name="power_charging_limited"><xliff:g id="level">%1$s</xliff:g> - Charging paused</string>
<!-- [CHAR_LIMIT=80] Label for battery charging future pause -->
<string name="power_charging_future_paused"><xliff:g id="level">%1$s</xliff:g> - Charging to <xliff:g id="dock_defender_threshold">%2$s</xliff:g></string>
diff --git a/packages/SystemUI/animation/Android.bp b/packages/SystemUI/animation/Android.bp
index 5df79e1bee94..e6ac48ff5af8 100644
--- a/packages/SystemUI/animation/Android.bp
+++ b/packages/SystemUI/animation/Android.bp
@@ -34,10 +34,7 @@ android_library {
"res",
],
- static_libs: [
- "PluginCoreLib",
- "androidx.core_core-animation-nodeps",
- ],
+ static_libs: ["androidx.core_core-animation-nodeps"],
manifest: "AndroidManifest.xml",
kotlincflags: ["-Xjvm-default=all"],
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index fdfad2bc2fa1..54aa3516d867 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -75,7 +75,7 @@ constructor(
*/
interface Controller {
/** The [ViewRootImpl] of this controller. */
- val viewRoot: ViewRootImpl
+ val viewRoot: ViewRootImpl?
/**
* The identity object of the source animated by this controller. This animator will ensure
@@ -807,15 +807,17 @@ private class AnimatedDialog(
* inversely, removed from the overlay when the source is moved back to its original position).
*/
private fun synchronizeNextDraw(then: () -> Unit) {
- if (forceDisableSynchronization) {
- // Don't synchronize when inside an automated test.
+ val controllerRootView = controller.viewRoot?.view
+ if (forceDisableSynchronization || controllerRootView == null) {
+ // Don't synchronize when inside an automated test or if the controller root view is
+ // detached.
then()
return
}
- ViewRootSync.synchronizeNextDraw(controller.viewRoot.view, decorView, then)
+ ViewRootSync.synchronizeNextDraw(controllerRootView, decorView, then)
decorView.invalidate()
- controller.viewRoot.view.invalidate()
+ controllerRootView.invalidate()
}
private fun findFirstViewGroupWithBackground(view: View): ViewGroup? {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt
index 43bfa74119b3..0e2d23b04a4f 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt
@@ -195,8 +195,16 @@ class RemoteTransitionAdapter {
val out = ArrayList<RemoteAnimationTarget>()
for (i in info.changes.indices) {
val change = info.changes[i]
- val changeIsWallpaper = change.flags and TransitionInfo.FLAG_IS_WALLPAPER != 0
- if (wallpapers != changeIsWallpaper) continue
+ if (change.hasFlags(TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) {
+ // For embedded container, when the parent Task is also in the transition, we
+ // should only animate the parent Task.
+ if (change.parent != null) continue
+ // For embedded container without parent, we should only animate if it fills
+ // the Task. Otherwise we may animate only partial of the Task.
+ if (!change.hasFlags(TransitionInfo.FLAG_FILLS_TASK)) continue
+ }
+ // Check if it is wallpaper
+ if (wallpapers != change.hasFlags(TransitionInfo.FLAG_IS_WALLPAPER)) continue
out.add(createTarget(change, info.changes.size - i, info, t))
if (leashMap != null) {
leashMap[change.leash] = out[out.size - 1].leash
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt
index ecee598afe4e..964ef8c88098 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewDialogLaunchAnimatorController.kt
@@ -28,7 +28,7 @@ internal constructor(
private val source: View,
override val cuj: DialogCuj?,
) : DialogLaunchAnimator.Controller {
- override val viewRoot: ViewRootImpl
+ override val viewRoot: ViewRootImpl?
get() = source.viewRootImpl
override val sourceIdentity: Any = source
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/ExpandableController.kt b/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/ExpandableController.kt
index 50c3d7e1e76b..d6db574a34ae 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/ExpandableController.kt
+++ b/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/ExpandableController.kt
@@ -262,7 +262,7 @@ internal class ExpandableControllerImpl(
private fun dialogController(cuj: DialogCuj?): DialogLaunchAnimator.Controller {
return object : DialogLaunchAnimator.Controller {
- override val viewRoot: ViewRootImpl = composeViewRoot.viewRootImpl
+ override val viewRoot: ViewRootImpl? = composeViewRoot.viewRootImpl
override val sourceIdentity: Any = this@ExpandableControllerImpl
override val cuj: DialogCuj? = cuj
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index 869884474ffe..e1f21742bf93 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -20,7 +20,6 @@ import android.graphics.Rect
import android.icu.text.NumberFormat
import android.util.TypedValue
import android.view.LayoutInflater
-import android.view.View
import android.widget.FrameLayout
import androidx.annotation.VisibleForTesting
import com.android.systemui.customization.R
@@ -152,15 +151,9 @@ class DefaultClockController(
view: AnimatableClockView,
) : DefaultClockFaceController(view) {
override fun recomputePadding(targetRegion: Rect?) {
- // We center the view within the targetRegion instead of within the parent
- // view by computing the difference and adding that to the padding.
- val parent = view.parent
- val yDiff =
- if (targetRegion != null && parent is View && parent.isLaidOut())
- targetRegion.centerY() - parent.height / 2f
- else 0f
+ // Ignore Target Region until top padding fixed in aod
val lp = view.getLayoutParams() as FrameLayout.LayoutParams
- lp.topMargin = (-0.5f * view.bottom + yDiff).toInt()
+ lp.topMargin = (-0.5f * view.bottom).toInt()
view.setLayoutParams(lp)
}
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index a156aab453e6..7290e7ed5394 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -753,7 +753,7 @@
-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerOldImplTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLoggerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 69767867ebd7..e598afe72f79 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -26,14 +26,11 @@
-keep class ** extends androidx.preference.PreferenceFragment
-keep class com.android.systemui.tuner.*
-# The plugins and animation subpackages both act as shared libraries that might be referenced in
+# The plugins subpackage acts as a shared library that might be referenced in
# dynamically-loaded plugin APKs.
-keep class com.android.systemui.plugins.** {
*;
}
--keep class !com.android.systemui.animation.R$**,com.android.systemui.animation.** {
- *;
-}
-keep class com.android.systemui.fragments.FragmentService$FragmentCreator {
*;
}
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index b49afeef09f3..218c5cc9b7fe 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -35,6 +35,7 @@
android:visibility="invisible" />
<FrameLayout
android:id="@+id/lockscreen_clock_view_large"
+ android:layout_marginTop="@dimen/keyguard_large_clock_top_margin"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
diff --git a/packages/SystemUI/res-keyguard/values-h650dp/dimens.xml b/packages/SystemUI/res-keyguard/values-h650dp/dimens.xml
index 669f8fb642de..e5e17b7d1554 100644
--- a/packages/SystemUI/res-keyguard/values-h650dp/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values-h650dp/dimens.xml
@@ -17,4 +17,7 @@
<resources>
<dimen name="widget_big_font_size">54dp</dimen>
+
+ <!-- Margin above the ambient indication container -->
+ <dimen name="ambient_indication_container_margin_top">10dp</dimen>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index e6593b16b731..6cc5b9d7b7e8 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -38,6 +38,9 @@
<!-- Minimum bottom margin under the security view -->
<dimen name="keyguard_security_view_bottom_margin">60dp</dimen>
+ <!-- Margin above the ambient indication container -->
+ <dimen name="ambient_indication_container_margin_top">0dp</dimen>
+
<dimen name="keyguard_eca_top_margin">18dp</dimen>
<dimen name="keyguard_eca_bottom_margin">12dp</dimen>
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index a129fb650ba6..da485a99c29b 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -53,7 +53,7 @@
<string name="keyguard_plugged_in_charging_slowly"><xliff:g id="percentage">%s</xliff:g> • Charging slowly</string>
<!-- When the lock screen is showing and the phone plugged in, and the defend mode is triggered, say that charging is temporarily limited. -->
- <string name="keyguard_plugged_in_charging_limited"><xliff:g id="percentage">%s</xliff:g> • Charging is paused to protect battery</string>
+ <string name="keyguard_plugged_in_charging_limited"><xliff:g id="percentage">%s</xliff:g> • Charging paused to protect battery</string>
<!-- On the keyguard screen, when pattern lock is disabled, only tell them to press menu to unlock. This is shown in small font at the bottom. -->
<string name="keyguard_instructions_when_pattern_disabled">Press Menu to unlock.</string>
diff --git a/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
index 2d67d95ab17e..efcb6f3435b9 100644
--- a/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
@@ -14,25 +14,32 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.shared.shadow.DoubleShadowTextClock
+<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
- android:id="@+id/time_view"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:fontFamily="@*android:string/config_clockFontFamily"
- android:textColor="@android:color/white"
- android:format12Hour="@string/dream_time_complication_12_hr_time_format"
- android:format24Hour="@string/dream_time_complication_24_hr_time_format"
- android:fontFeatureSettings="pnum, lnum"
- android:letterSpacing="0.02"
- android:textSize="@dimen/dream_overlay_complication_clock_time_text_size"
- app:keyShadowBlur="@dimen/dream_overlay_clock_key_text_shadow_radius"
- app:keyShadowOffsetX="@dimen/dream_overlay_clock_key_text_shadow_dx"
- app:keyShadowOffsetY="@dimen/dream_overlay_clock_key_text_shadow_dy"
- app:keyShadowAlpha="0.3"
- app:ambientShadowBlur="@dimen/dream_overlay_clock_ambient_text_shadow_radius"
- app:ambientShadowOffsetX="@dimen/dream_overlay_clock_ambient_text_shadow_dx"
- app:ambientShadowOffsetY="@dimen/dream_overlay_clock_ambient_text_shadow_dy"
- app:ambientShadowAlpha="0.3"
-/>
+ android:layout_height="wrap_content">
+
+ <com.android.systemui.shared.shadow.DoubleShadowTextClock
+ android:id="@+id/time_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:fontFamily="@*android:string/config_clockFontFamily"
+ android:textColor="@android:color/white"
+ android:format12Hour="@string/dream_time_complication_12_hr_time_format"
+ android:format24Hour="@string/dream_time_complication_24_hr_time_format"
+ android:fontFeatureSettings="pnum, lnum"
+ android:letterSpacing="0.02"
+ android:textSize="@dimen/dream_overlay_complication_clock_time_text_size"
+ android:translationY="@dimen/dream_overlay_complication_clock_time_translation_y"
+ app:keyShadowBlur="@dimen/dream_overlay_clock_key_text_shadow_radius"
+ app:keyShadowOffsetX="@dimen/dream_overlay_clock_key_text_shadow_dx"
+ app:keyShadowOffsetY="@dimen/dream_overlay_clock_key_text_shadow_dy"
+ app:keyShadowAlpha="0.3"
+ app:ambientShadowBlur="@dimen/dream_overlay_clock_ambient_text_shadow_radius"
+ app:ambientShadowOffsetX="@dimen/dream_overlay_clock_ambient_text_shadow_dx"
+ app:ambientShadowOffsetY="@dimen/dream_overlay_clock_ambient_text_shadow_dy"
+ app:ambientShadowAlpha="0.3"
+ />
+
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/dream_overlay_home_controls_chip.xml b/packages/SystemUI/res/layout/dream_overlay_home_controls_chip.xml
index 4f0a78e9c35d..de96e9765668 100644
--- a/packages/SystemUI/res/layout/dream_overlay_home_controls_chip.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_home_controls_chip.xml
@@ -14,16 +14,21 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<ImageView
+<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/home_controls_chip"
- android:layout_height="@dimen/keyguard_affordance_fixed_height"
- android:layout_width="@dimen/keyguard_affordance_fixed_width"
- android:layout_gravity="bottom|start"
- android:scaleType="center"
- android:tint="?android:attr/textColorPrimary"
- android:src="@drawable/controls_icon"
- android:background="@drawable/keyguard_bottom_affordance_bg"
- android:layout_marginStart="@dimen/keyguard_affordance_horizontal_offset"
- android:layout_marginBottom="@dimen/keyguard_affordance_vertical_offset"
- android:contentDescription="@string/quick_controls_title" />
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:paddingVertical="@dimen/dream_overlay_complication_home_controls_padding">
+
+ <ImageView
+ android:id="@+id/home_controls_chip"
+ android:layout_height="@dimen/keyguard_affordance_fixed_height"
+ android:layout_width="@dimen/keyguard_affordance_fixed_width"
+ android:layout_gravity="bottom|start"
+ android:scaleType="center"
+ android:tint="?android:attr/textColorPrimary"
+ android:src="@drawable/controls_icon"
+ android:background="@drawable/keyguard_bottom_affordance_bg"
+ android:contentDescription="@string/quick_controls_title" />
+
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/media_smartspace_recommendations.xml b/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
index 79ba7ead1ec3..aa655e6b3564 100644
--- a/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
+++ b/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
@@ -41,7 +41,7 @@
android:layout_width="@dimen/qs_media_app_icon_size"
android:layout_height="@dimen/qs_media_app_icon_size"
android:layout_marginStart="@dimen/qs_media_padding"
- android:layout_marginTop="@dimen/qs_media_padding"
+ android:layout_marginTop="@dimen/qs_media_rec_icon_top_margin"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
diff --git a/packages/SystemUI/res/values-h700dp/dimens.xml b/packages/SystemUI/res/values-h700dp/dimens.xml
new file mode 100644
index 000000000000..055308f17776
--- /dev/null
+++ b/packages/SystemUI/res/values-h700dp/dimens.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<resources>
+ <!-- Margin above the ambient indication container -->
+ <dimen name="ambient_indication_container_margin_top">15dp</dimen>
+</resources> \ No newline at end of file
diff --git a/packages/SystemUI/res/values-h800dp/dimens.xml b/packages/SystemUI/res/values-h800dp/dimens.xml
index 8efd6f0b7995..3a71994e07e2 100644
--- a/packages/SystemUI/res/values-h800dp/dimens.xml
+++ b/packages/SystemUI/res/values-h800dp/dimens.xml
@@ -17,4 +17,7 @@
<resources>
<!-- With the large clock, move up slightly from the center -->
<dimen name="keyguard_large_clock_top_margin">-112dp</dimen>
+
+ <!-- Margin above the ambient indication container -->
+ <dimen name="ambient_indication_container_margin_top">20dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw720dp-land/dimens.xml b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
index 868c003d99a5..3fc59e38ec6c 100644
--- a/packages/SystemUI/res/values-sw720dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
@@ -35,6 +35,11 @@
not appear immediately after user swipes to the side -->
<dimen name="qs_tiles_page_horizontal_margin">20dp</dimen>
+ <!-- Size of Smartspace media recommendations cards in the QSPanel carousel -->
+ <dimen name="qs_media_rec_icon_top_margin">27dp</dimen>
+ <dimen name="qs_media_rec_album_size">152dp</dimen>
+ <dimen name="qs_media_rec_album_side_margin">16dp</dimen>
+
<dimen name="lockscreen_shade_max_over_scroll_amount">42dp</dimen>
<!-- Roughly the same distance as media on LS to media on QS. We will translate by this value
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 8ee39dd5963f..70d53c73a4f7 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -133,6 +133,9 @@
<color name="biometric_dialog_accent">@color/material_dynamic_primary40</color>
<color name="biometric_dialog_error">#ffd93025</color> <!-- red 600 -->
+ <!-- SFPS colors -->
+ <color name="sfps_chevron_fill">@color/material_dynamic_primary90</color>
+
<!-- UDFPS colors -->
<color name="udfps_enroll_icon">#699FF3</color>
<color name="udfps_moving_target_fill">#C2D7F7</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 569b661cc522..46e05fe652e3 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1055,6 +1055,7 @@
<dimen name="qs_media_session_collapsed_guideline">144dp</dimen>
<!-- Size of Smartspace media recommendations cards in the QSPanel carousel -->
+ <dimen name="qs_media_rec_icon_top_margin">16dp</dimen>
<dimen name="qs_media_rec_album_size">88dp</dimen>
<dimen name="qs_media_rec_album_side_margin">16dp</dimen>
<dimen name="qs_media_rec_album_bottom_margin">8dp</dimen>
@@ -1523,13 +1524,15 @@
<dimen name="dream_overlay_status_bar_extra_margin">8dp</dimen>
<!-- Dream overlay complications related dimensions -->
- <dimen name="dream_overlay_complication_clock_time_text_size">86sp</dimen>
+ <dimen name="dream_overlay_complication_clock_time_text_size">86dp</dimen>
+ <dimen name="dream_overlay_complication_clock_time_translation_y">28dp</dimen>
<dimen name="dream_overlay_complication_home_controls_padding">28dp</dimen>
<dimen name="dream_overlay_complication_clock_subtitle_text_size">24sp</dimen>
<dimen name="dream_overlay_complication_preview_text_size">36sp</dimen>
<dimen name="dream_overlay_complication_preview_icon_padding">28dp</dimen>
<dimen name="dream_overlay_complication_shadow_padding">2dp</dimen>
<dimen name="dream_overlay_complication_smartspace_padding">24dp</dimen>
+ <dimen name="dream_overlay_complication_smartspace_max_width">408dp</dimen>
<!-- The position of the end guide, which dream overlay complications can align their start with
if their end is aligned with the parent end. Represented as the percentage over from the
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 2e5e11c03a65..7597c6219011 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -200,6 +200,8 @@
<!-- Informs the user that a screenshot is being saved. [CHAR LIMIT=50] -->
<string name="screenshot_saving_title">Saving screenshot\u2026</string>
+ <!-- Informs the user that a screenshot is being saved. [CHAR LIMIT=50] -->
+ <string name="screenshot_saving_work_profile_title">Saving screenshot to work profile\u2026</string>
<!-- Notification title displayed when a screenshot is saved to the Gallery. [CHAR LIMIT=50] -->
<string name="screenshot_saved_title">Screenshot saved</string>
<!-- Notification title displayed when we fail to take a screenshot. [CHAR LIMIT=50] -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
index f6c75a2d2752..c9ea79432360 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
@@ -39,6 +39,7 @@ public class PreviewPositionHelper {
private boolean mIsOrientationChanged;
private SplitBounds mSplitBounds;
private int mDesiredStagePosition;
+ private boolean mTaskbarInApp;
public Matrix getMatrix() {
return mMatrix;
@@ -57,6 +58,10 @@ public class PreviewPositionHelper {
mDesiredStagePosition = desiredStagePosition;
}
+ public void setTaskbarInApp(boolean taskbarInApp) {
+ mTaskbarInApp = taskbarInApp;
+ }
+
/**
* Updates the matrix based on the provided parameters
*/
@@ -83,8 +88,18 @@ public class PreviewPositionHelper {
? mSplitBounds.topTaskPercent
: (1 - (mSplitBounds.topTaskPercent + mSplitBounds.dividerHeightPercent));
// Scale portrait height to that of (actual screen - taskbar inset)
- fullscreenTaskHeight = (screenHeightPx - taskbarSize) * taskPercent;
- canvasScreenRatio = canvasHeight / fullscreenTaskHeight;
+ fullscreenTaskHeight = (screenHeightPx) * taskPercent;
+ if (mTaskbarInApp) {
+ canvasScreenRatio = canvasHeight / fullscreenTaskHeight;
+ } else {
+ if (mDesiredStagePosition == STAGE_POSITION_TOP_OR_LEFT) {
+ // Top app isn't cropped at all by taskbar
+ canvasScreenRatio = 0;
+ } else {
+ // Same as fullscreen ratio
+ canvasScreenRatio = (float) canvasWidth / screenWidthPx;
+ }
+ }
} else {
// For landscape, scale the width
taskPercent = mDesiredStagePosition == STAGE_POSITION_TOP_OR_LEFT
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowIconDrawable.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowIconDrawable.kt
index 3748eba47be5..19d0a3d6bf32 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowIconDrawable.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowIconDrawable.kt
@@ -71,7 +71,7 @@ class DoubleShadowIconDrawable(
mKeyShadowInfo.offsetY,
mKeyShadowInfo.alpha
)
- val blend = RenderEffect.createBlendModeEffect(ambientShadow, keyShadow, BlendMode.DARKEN)
+ val blend = RenderEffect.createBlendModeEffect(ambientShadow, keyShadow, BlendMode.DST_ATOP)
renderNode.setRenderEffect(blend)
return renderNode
}
diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
index 458d22efd206..a25b281f807c 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
@@ -25,7 +25,6 @@ import android.widget.Button;
import com.android.internal.util.EmergencyAffordanceManager;
import com.android.internal.widget.LockPatternUtils;
-import com.android.settingslib.Utils;
/**
* This class implements a smart emergency button that updates itself based
@@ -91,17 +90,6 @@ public class EmergencyButton extends Button {
return super.onTouchEvent(event);
}
- /**
- * Reload colors from resources.
- **/
- public void reloadColors() {
- int color = Utils.getColorAttrDefaultColor(getContext(),
- com.android.internal.R.attr.textColorOnAccent);
- setTextColor(color);
- setBackground(getContext()
- .getDrawable(com.android.systemui.R.drawable.kg_emergency_button_background));
- }
-
@Override
public boolean performLongClick() {
return super.performLongClick();
diff --git a/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt b/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt
index 4a41b3fe2589..5bb9367fa4a5 100644
--- a/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt
+++ b/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt
@@ -48,11 +48,13 @@ import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_INIT
import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_OCCLUSION_CHANGED
import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_RESET
import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_VISIBILITY_CHANGED
+import com.android.keyguard.InternalFaceAuthReasons.NON_STRONG_BIOMETRIC_ALLOWED_CHANGED
import com.android.keyguard.InternalFaceAuthReasons.OCCLUDING_APP_REQUESTED
import com.android.keyguard.InternalFaceAuthReasons.PRIMARY_BOUNCER_SHOWN
import com.android.keyguard.InternalFaceAuthReasons.PRIMARY_BOUNCER_SHOWN_OR_WILL_BE_SHOWN
import com.android.keyguard.InternalFaceAuthReasons.RETRY_AFTER_HW_UNAVAILABLE
import com.android.keyguard.InternalFaceAuthReasons.STARTED_WAKING_UP
+import com.android.keyguard.InternalFaceAuthReasons.STRONG_AUTH_ALLOWED_CHANGED
import com.android.keyguard.InternalFaceAuthReasons.TRUST_DISABLED
import com.android.keyguard.InternalFaceAuthReasons.TRUST_ENABLED
import com.android.keyguard.InternalFaceAuthReasons.USER_SWITCHING
@@ -121,6 +123,9 @@ private object InternalFaceAuthReasons {
const val FACE_AUTHENTICATED = "Face auth started/stopped because face is authenticated"
const val BIOMETRIC_ENABLED =
"Face auth started/stopped because biometric is enabled on keyguard"
+ const val STRONG_AUTH_ALLOWED_CHANGED = "Face auth stopped because strong auth allowed changed"
+ const val NON_STRONG_BIOMETRIC_ALLOWED_CHANGED =
+ "Face auth stopped because non strong biometric allowed changed"
}
/**
@@ -204,7 +209,11 @@ constructor(private val id: Int, val reason: String, var extraInfo: Int = 0) :
@UiEvent(doc = FACE_AUTHENTICATED)
FACE_AUTH_UPDATED_ON_FACE_AUTHENTICATED(1187, FACE_AUTHENTICATED),
@UiEvent(doc = BIOMETRIC_ENABLED)
- FACE_AUTH_UPDATED_BIOMETRIC_ENABLED_ON_KEYGUARD(1188, BIOMETRIC_ENABLED);
+ FACE_AUTH_UPDATED_BIOMETRIC_ENABLED_ON_KEYGUARD(1188, BIOMETRIC_ENABLED),
+ @UiEvent(doc = STRONG_AUTH_ALLOWED_CHANGED)
+ FACE_AUTH_UPDATED_STRONG_AUTH_CHANGED(1255, STRONG_AUTH_ALLOWED_CHANGED),
+ @UiEvent(doc = NON_STRONG_BIOMETRIC_ALLOWED_CHANGED)
+ FACE_AUTH_NON_STRONG_BIOMETRIC_ALLOWED_CHANGED(1256, NON_STRONG_BIOMETRIC_ALLOWED_CHANGED);
override fun getId(): Int = this.id
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index 3e32cf5521ff..860c8e3a9f77 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -20,7 +20,6 @@ import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
import static com.android.keyguard.KeyguardAbsKeyInputView.MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT;
-import android.annotation.CallSuper;
import android.content.res.ColorStateList;
import android.os.AsyncTask;
import android.os.CountDownTimer;
@@ -117,13 +116,6 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey
}
}
- @CallSuper
- @Override
- public void reloadColors() {
- super.reloadColors();
- mMessageAreaController.reloadColors();
- }
-
@Override
public boolean needsInput() {
return false;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index faaba63938bf..2e9ad5868eba 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -16,7 +16,6 @@
package com.android.keyguard;
-import android.annotation.CallSuper;
import android.annotation.Nullable;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -142,16 +141,6 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
public void showMessage(CharSequence message, ColorStateList colorState) {
}
- /**
- * Reload colors from resources.
- **/
- @CallSuper
- public void reloadColors() {
- if (mEmergencyButton != null) {
- mEmergencyButton.reloadColors();
- }
- }
-
public void startAppearAnimation() {
if (TextUtils.isEmpty(mMessageAreaController.getMessage())) {
mMessageAreaController.setMessage(getInitialMessageResId());
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
index e6283b86283b..52ca1668e4c9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
@@ -53,17 +53,17 @@ data class KeyguardFaceListenModel(
val biometricSettingEnabledForUser: Boolean,
val bouncerFullyShown: Boolean,
val faceAndFpNotAuthenticated: Boolean,
+ val faceAuthAllowed: Boolean,
val faceDisabled: Boolean,
val faceLockedOut: Boolean,
- val fpLockedOut: Boolean,
val goingToSleep: Boolean,
val keyguardAwake: Boolean,
val keyguardGoingAway: Boolean,
val listeningForFaceAssistant: Boolean,
val occludingAppRequestingFaceAuth: Boolean,
val primaryUser: Boolean,
- val scanningAllowedByStrongAuth: Boolean,
val secureCameraLaunched: Boolean,
+ val supportsDetect: Boolean,
val switchingUser: Boolean,
val udfpsBouncerShowing: Boolean,
val udfpsFingerDown: Boolean,
@@ -79,9 +79,8 @@ data class KeyguardActiveUnlockModel(
// keep sorted
val awakeKeyguard: Boolean,
val authInterruptActive: Boolean,
- val encryptedOrTimedOut: Boolean,
- val fpLockout: Boolean,
- val lockDown: Boolean,
+ val fpLockedOut: Boolean,
+ val primaryAuthRequired: Boolean,
val switchingUser: Boolean,
val triggerActiveUnlockForAssistant: Boolean,
val userCanDismissLockScreen: Boolean
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
index c29f632b88d3..6a9216218d07 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
@@ -116,13 +116,6 @@ public class KeyguardMessageAreaController<T extends KeyguardMessageArea>
return mView.getText();
}
- /**
- * Reload colors from resources.
- **/
- public void reloadColors() {
- mView.reloadColor();
- }
-
/** Factory for creating {@link com.android.keyguard.KeyguardMessageAreaController}. */
public static class Factory {
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
index 0025986c0e5c..195e8f92754d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
@@ -16,7 +16,6 @@
package com.android.keyguard;
-import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.os.UserHandle;
import android.text.Editable;
@@ -39,7 +38,6 @@ import android.widget.TextView.OnEditorActionListener;
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dagger.qualifiers.Main;
@@ -95,18 +93,6 @@ public class KeyguardPasswordViewController
}
};
- @Override
- public void reloadColors() {
- super.reloadColors();
- int textColor = Utils.getColorAttr(mView.getContext(),
- android.R.attr.textColorPrimary).getDefaultColor();
- mPasswordEntry.setTextColor(textColor);
- mPasswordEntry.setHighlightColor(textColor);
- mPasswordEntry.setBackgroundTintList(ColorStateList.valueOf(textColor));
- mPasswordEntry.setForegroundTintList(ColorStateList.valueOf(textColor));
- mSwitchImeButton.setImageTintList(ColorStateList.valueOf(textColor));
- }
-
protected KeyguardPasswordViewController(KeyguardPasswordView view,
KeyguardUpdateMonitor keyguardUpdateMonitor,
SecurityMode securityMode,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index cdbfb2492e27..571d2740773d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -35,7 +35,6 @@ import com.android.internal.widget.LockPatternView.Cell;
import com.android.internal.widget.LockscreenCredential;
import com.android.keyguard.EmergencyButtonController.EmergencyButtonCallback;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingClassifier;
import com.android.systemui.classifier.FalsingCollector;
@@ -272,16 +271,6 @@ public class KeyguardPatternViewController
}
@Override
- public void reloadColors() {
- super.reloadColors();
- mMessageAreaController.reloadColors();
- int textColor = Utils.getColorAttr(mLockPatternView.getContext(),
- android.R.attr.textColorSecondary).getDefaultColor();
- int errorColor = Utils.getColorError(mLockPatternView.getContext()).getDefaultColor();
- mLockPatternView.setColors(textColor, textColor, errorColor);
- }
-
- @Override
public void onPause() {
super.onPause();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index 7876f071fdf5..f51ac325c9c1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -70,12 +70,6 @@ public class KeyguardPinViewController
}
@Override
- public void reloadColors() {
- super.reloadColors();
- mView.reloadColors();
- }
-
- @Override
public boolean startDisappearAnimation(Runnable finishRunnable) {
return mView.startDisappearAnimation(
mKeyguardUpdateMonitor.needsSlowUnlockTransition(), finishRunnable);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 4d0a273a2189..a72a484fb6f1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -730,16 +730,20 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
}
private void reloadColors() {
- mSecurityViewFlipperController.reloadColors();
+ resetViewFlipper();
mView.reloadColors();
}
/** Handles density or font scale changes. */
private void onDensityOrFontScaleChanged() {
- mSecurityViewFlipperController.onDensityOrFontScaleChanged();
+ resetViewFlipper();
+ mView.onDensityOrFontScaleChanged();
+ }
+
+ private void resetViewFlipper() {
+ mSecurityViewFlipperController.clearViews();
mSecurityViewFlipperController.getSecurityView(mCurrentSecurityMode,
mKeyguardSecurityCallback);
- mView.onDensityOrFontScaleChanged();
}
static class Factory {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
index 25afe11ac536..a5c8c7881e3b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
@@ -74,17 +74,8 @@ public class KeyguardSecurityViewFlipperController
}
}
- /**
- * Reload colors of ui elements upon theme change.
- */
- public void reloadColors() {
- for (KeyguardInputViewController<KeyguardInputView> child : mChildren) {
- child.reloadColors();
- }
- }
-
/** Handles density or font scale changes. */
- public void onDensityOrFontScaleChanged() {
+ public void clearViews() {
mView.removeAllViews();
mChildren.clear();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index 91bf20f90690..a16f30475654 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -133,12 +133,6 @@ public class KeyguardSimPinViewController
}
@Override
- public void reloadColors() {
- super.reloadColors();
- mView.reloadColors();
- }
-
- @Override
protected void verifyPasswordAndUnlock() {
String entry = mPasswordEntry.getText();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index 5995e859c786..e9405eb79901 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -121,12 +121,6 @@ public class KeyguardSimPukViewController
}
@Override
- public void reloadColors() {
- super.reloadColors();
- mView.reloadColors();
- }
-
- @Override
protected void verifyPasswordAndUnlock() {
mStateMachine.next();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 993d80f49182..479d4549016c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -34,9 +34,9 @@ import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
import static com.android.keyguard.FaceAuthReasonKt.apiRequestReasonToUiEvent;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_NON_STRONG_BIOMETRIC_ALLOWED_CHANGED;
import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_STOPPED_DREAM_STARTED;
import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_STOPPED_FACE_CANCEL_NOT_RECEIVED;
import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_STOPPED_FINISHED_GOING_TO_SLEEP;
@@ -65,6 +65,7 @@ import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_ON_FACE_AUT
import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_ON_KEYGUARD_INIT;
import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN;
import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_STARTED_WAKING_UP;
+import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_STRONG_AUTH_CHANGED;
import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_USER_SWITCHING;
import static com.android.systemui.DejankUtils.whitelistIpcs;
@@ -170,7 +171,6 @@ import java.util.Map.Entry;
import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.Executor;
-import java.util.function.Consumer;
import java.util.stream.Collectors;
import javax.inject.Inject;
@@ -735,8 +735,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
*/
public void requestFaceAuthOnOccludingApp(boolean request) {
mOccludingAppRequestingFace = request;
- updateFaceListeningState(BIOMETRIC_ACTION_UPDATE,
- FACE_AUTH_TRIGGERED_OCCLUDING_APP_REQUESTED);
+ int action = mOccludingAppRequestingFace ? BIOMETRIC_ACTION_UPDATE : BIOMETRIC_ACTION_STOP;
+ updateFaceListeningState(action, FACE_AUTH_TRIGGERED_OCCLUDING_APP_REQUESTED);
}
/**
@@ -1378,16 +1378,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
&& !mFingerprintLockedOut;
}
- private boolean isUnlockingWithFaceAllowed() {
- return mStrongAuthTracker.isUnlockingWithBiometricAllowed(false);
- }
-
/**
* Whether fingerprint is allowed ot be used for unlocking based on the strongAuthTracker
* and temporary lockout state (tracked by FingerprintManager via error codes).
*/
public boolean isUnlockingWithFingerprintAllowed() {
- return isUnlockingWithBiometricAllowed(true);
+ return isUnlockingWithBiometricAllowed(FINGERPRINT);
}
/**
@@ -1397,14 +1393,17 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
@NonNull BiometricSourceType biometricSourceType) {
switch (biometricSourceType) {
case FINGERPRINT:
- return isUnlockingWithFingerprintAllowed();
+ return isUnlockingWithBiometricAllowed(true);
case FACE:
- return isUnlockingWithFaceAllowed();
+ return isUnlockingWithBiometricAllowed(false);
default:
return false;
}
}
+ /**
+ * Whether the user locked down the device. This doesn't include device policy manager lockdown.
+ */
public boolean isUserInLockdown(int userId) {
return containsFlag(mStrongAuthTracker.getStrongAuthForUser(userId),
STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
@@ -1436,7 +1435,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
return mStrongAuthTracker;
}
- private void notifyStrongAuthStateChanged(int userId) {
+ @VisibleForTesting
+ void notifyStrongAuthAllowedChanged(int userId) {
Assert.isMainThread();
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -1444,6 +1444,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
cb.onStrongAuthStateChanged(userId);
}
}
+ if (userId == getCurrentUser()) {
+ FACE_AUTH_UPDATED_STRONG_AUTH_CHANGED.setExtraInfo(
+ mStrongAuthTracker.getStrongAuthForUser(getCurrentUser()));
+
+ // Strong auth is only reset when primary auth is used to enter the device,
+ // so we only check whether to stop biometric listening states here
+ updateBiometricListeningState(
+ BIOMETRIC_ACTION_STOP, FACE_AUTH_UPDATED_STRONG_AUTH_CHANGED);
+ }
}
private void notifyLockedOutStateChanged(BiometricSourceType type) {
@@ -1455,8 +1464,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
}
}
-
- private void notifyNonStrongBiometricStateChanged(int userId) {
+ @VisibleForTesting
+ void notifyNonStrongBiometricAllowedChanged(int userId) {
Assert.isMainThread();
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -1464,6 +1473,16 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
cb.onNonStrongBiometricAllowedChanged(userId);
}
}
+ if (userId == getCurrentUser()) {
+ FACE_AUTH_NON_STRONG_BIOMETRIC_ALLOWED_CHANGED.setExtraInfo(
+ mStrongAuthTracker.isNonStrongBiometricAllowedAfterIdleTimeout(
+ getCurrentUser()) ? -1 : 1);
+
+ // This is only reset when primary auth is used to enter the device, so we only check
+ // whether to stop biometric listening states here
+ updateBiometricListeningState(BIOMETRIC_ACTION_STOP,
+ FACE_AUTH_NON_STRONG_BIOMETRIC_ALLOWED_CHANGED);
+ }
}
private void dispatchErrorMessage(CharSequence message) {
@@ -1809,16 +1828,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
}
- public static class StrongAuthTracker extends LockPatternUtils.StrongAuthTracker {
- private final Consumer<Integer> mStrongAuthRequiredChangedCallback;
- private final Consumer<Integer> mNonStrongBiometricAllowedChanged;
-
- public StrongAuthTracker(Context context,
- Consumer<Integer> strongAuthRequiredChangedCallback,
- Consumer<Integer> nonStrongBiometricAllowedChanged) {
+ /**
+ * Updates callbacks when strong auth requirements change.
+ */
+ public class StrongAuthTracker extends LockPatternUtils.StrongAuthTracker {
+ public StrongAuthTracker(Context context) {
super(context);
- mStrongAuthRequiredChangedCallback = strongAuthRequiredChangedCallback;
- mNonStrongBiometricAllowedChanged = nonStrongBiometricAllowedChanged;
}
public boolean isUnlockingWithBiometricAllowed(boolean isStrongBiometric) {
@@ -1834,7 +1849,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
@Override
public void onStrongAuthRequiredChanged(int userId) {
- mStrongAuthRequiredChangedCallback.accept(userId);
+ notifyStrongAuthAllowedChanged(userId);
}
// TODO(b/247091681): Renaming the inappropriate onIsNonStrongBiometricAllowedChanged
@@ -1842,7 +1857,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
// Strong-Auth
@Override
public void onIsNonStrongBiometricAllowedChanged(int userId) {
- mNonStrongBiometricAllowedChanged.accept(userId);
+ notifyNonStrongBiometricAllowedChanged(userId);
}
}
@@ -2003,8 +2018,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mUserTracker = userTracker;
mTelephonyListenerManager = telephonyListenerManager;
mDeviceProvisioned = isDeviceProvisionedInSettingsDb();
- mStrongAuthTracker = new StrongAuthTracker(context, this::notifyStrongAuthStateChanged,
- this::notifyNonStrongBiometricStateChanged);
+ mStrongAuthTracker = new StrongAuthTracker(context);
mBackgroundExecutor = backgroundExecutor;
mBroadcastDispatcher = broadcastDispatcher;
mInteractionJankMonitor = interactionJankMonitor;
@@ -2579,24 +2593,17 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
|| !mLockPatternUtils.isSecure(user);
// Don't trigger active unlock if fp is locked out
- final boolean fpLockedout = mFingerprintLockedOut || mFingerprintLockedOutPermanent;
+ final boolean fpLockedOut = mFingerprintLockedOut || mFingerprintLockedOutPermanent;
// Don't trigger active unlock if primary auth is required
- final int strongAuth = mStrongAuthTracker.getStrongAuthForUser(user);
- final boolean isLockDown =
- containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW)
- || containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
- final boolean isEncryptedOrTimedOut =
- containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT)
- || containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT);
+ final boolean primaryAuthRequired = !isUnlockingWithBiometricAllowed(true);
final boolean shouldTriggerActiveUnlock =
(mAuthInterruptActive || triggerActiveUnlockForAssistant || awakeKeyguard)
&& !mSwitchingUser
&& !userCanDismissLockScreen
- && !fpLockedout
- && !isLockDown
- && !isEncryptedOrTimedOut
+ && !fpLockedOut
+ && !primaryAuthRequired
&& !mKeyguardGoingAway
&& !mSecureCameraLaunched;
@@ -2608,9 +2615,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
shouldTriggerActiveUnlock,
awakeKeyguard,
mAuthInterruptActive,
- isEncryptedOrTimedOut,
- fpLockedout,
- isLockDown,
+ fpLockedOut,
+ primaryAuthRequired,
mSwitchingUser,
triggerActiveUnlockForAssistant,
userCanDismissLockScreen));
@@ -2662,7 +2668,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
&& !fingerprintDisabledForUser
&& (!mKeyguardGoingAway || !mDeviceInteractive)
&& mIsPrimaryUser
- && biometricEnabledForUser;
+ && biometricEnabledForUser
+ && !isUserInLockdown(user);
final boolean strongerAuthRequired = !isUnlockingWithFingerprintAllowed();
final boolean isSideFps = isSfpsSupported() && isSfpsEnrolled();
final boolean shouldListenBouncerState =
@@ -2724,14 +2731,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
final boolean awakeKeyguard = isKeyguardVisible() && mDeviceInteractive
&& !statusBarShadeLocked;
final int user = getCurrentUser();
- final int strongAuth = mStrongAuthTracker.getStrongAuthForUser(user);
- final boolean isLockDown =
- containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW)
- || containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
- final boolean isEncryptedOrTimedOut =
- containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT)
- || containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT);
- final boolean fpLockedOut = isFingerprintLockedOut();
+ final boolean faceAuthAllowed = isUnlockingWithBiometricAllowed(FACE);
final boolean canBypass = mKeyguardBypassController != null
&& mKeyguardBypassController.canBypass();
// There's no reason to ask the HAL for authentication when the user can dismiss the
@@ -2739,20 +2739,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
// the lock screen even when TrustAgents are keeping the device unlocked.
final boolean userNotTrustedOrDetectionIsNeeded = !getUserHasTrust(user) || canBypass;
- // Scan even when encrypted or timeout to show a preemptive bouncer when bypassing.
- // Lock-down mode shouldn't scan, since it is more explicit.
- boolean strongAuthAllowsScanning = (!isEncryptedOrTimedOut || canBypass
- && !mPrimaryBouncerFullyShown);
-
- // If the device supports face detection (without authentication) and bypass is enabled,
- // allow face scanning to happen if the device is in lockdown mode.
- // Otherwise, prevent scanning.
- final boolean supportsDetectOnly = !mFaceSensorProperties.isEmpty()
- && canBypass
- && mFaceSensorProperties.get(0).supportsFaceDetection;
- if (isLockDown && !supportsDetectOnly) {
- strongAuthAllowsScanning = false;
- }
+ // If the device supports face detection (without authentication), if bypass is enabled,
+ // allow face detection to happen even if stronger auth is required. When face is detected,
+ // we show the bouncer. However, if the user manually locked down the device themselves,
+ // never attempt to detect face.
+ final boolean supportsDetect = !mFaceSensorProperties.isEmpty()
+ && mFaceSensorProperties.get(0).supportsFaceDetection
+ && canBypass && !mPrimaryBouncerIsOrWillBeShowing
+ && !isUserInLockdown(user);
+ final boolean faceAuthAllowedOrDetectionIsNeeded = faceAuthAllowed || supportsDetect;
// If the face or fp has recently been authenticated do not attempt to authenticate again.
final boolean faceAndFpNotAuthenticated = !getUserUnlockedWithBiometric(user);
@@ -2773,14 +2768,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
|| mUdfpsBouncerShowing)
&& !mSwitchingUser && !faceDisabledForUser && userNotTrustedOrDetectionIsNeeded
&& !mKeyguardGoingAway && biometricEnabledForUser
- && strongAuthAllowsScanning && mIsPrimaryUser
+ && faceAuthAllowedOrDetectionIsNeeded && mIsPrimaryUser
&& (!mSecureCameraLaunched || mOccludingAppRequestingFace)
&& faceAndFpNotAuthenticated
- && !mGoingToSleep
- // We only care about fp locked out state and not face because we still trigger
- // face auth even when face is locked out to show the user a message that face
- // unlock was supposed to run but didn't
- && !fpLockedOut;
+ && !mGoingToSleep;
// Aggregate relevant fields for debug logging.
maybeLogListenerModelData(
@@ -2790,19 +2781,19 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
shouldListen,
mAuthInterruptActive,
biometricEnabledForUser,
- mPrimaryBouncerFullyShown,
+ mPrimaryBouncerFullyShown,
faceAndFpNotAuthenticated,
+ faceAuthAllowed,
faceDisabledForUser,
isFaceLockedOut(),
- fpLockedOut,
mGoingToSleep,
awakeKeyguard,
mKeyguardGoingAway,
shouldListenForFaceAssistant,
mOccludingAppRequestingFace,
mIsPrimaryUser,
- strongAuthAllowsScanning,
mSecureCameraLaunched,
+ supportsDetect,
mSwitchingUser,
mUdfpsBouncerShowing,
isUdfpsFingerDown,
@@ -2906,9 +2897,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
// This would need to be updated for multi-sensor devices
final boolean supportsFaceDetection = !mFaceSensorProperties.isEmpty()
&& mFaceSensorProperties.get(0).supportsFaceDetection;
- if (isEncryptedOrLockdown(userId) && supportsFaceDetection) {
+ if (!isUnlockingWithBiometricAllowed(FACE) && supportsFaceDetection) {
+ mLogger.v("startListeningForFace - detect");
mFaceManager.detectFace(mFaceCancelSignal, mFaceDetectionCallback, userId);
} else {
+ mLogger.v("startListeningForFace - authenticate");
final boolean isBypassEnabled = mKeyguardBypassController != null
&& mKeyguardBypassController.isBypassEnabled();
mFaceManager.authenticate(null /* crypto */, mFaceCancelSignal,
diff --git a/packages/SystemUI/src/com/android/systemui/CoreStartable.java b/packages/SystemUI/src/com/android/systemui/CoreStartable.java
index 929ebea37eef..becf5b39e9df 100644
--- a/packages/SystemUI/src/com/android/systemui/CoreStartable.java
+++ b/packages/SystemUI/src/com/android/systemui/CoreStartable.java
@@ -39,10 +39,13 @@ import java.io.PrintWriter;
*/
public interface CoreStartable extends Dumpable {
- /** Main entry point for implementations. Called shortly after app startup. */
+ /** Main entry point for implementations. Called shortly after SysUI startup. */
void start();
- /** */
+ /** Called when the device configuration changes. This will not be called before
+ * {@link #start()}, but it could be called before {@link #onBootCompleted()}.
+ *
+ * @see android.app.Application#onConfigurationChanged(Configuration) */
default void onConfigurationChanged(Configuration newConfig) {
}
@@ -50,7 +53,11 @@ public interface CoreStartable extends Dumpable {
default void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
}
- /** Called when the device reports BOOT_COMPLETED. */
+ /** Called immediately after the system broadcasts
+ * {@link android.content.Intent#ACTION_LOCKED_BOOT_COMPLETED} or during SysUI startup if the
+ * property {@code sys.boot_completed} is already set to 1. The latter typically occurs when
+ * starting a new SysUI instance, such as when starting SysUI for a secondary user.
+ * {@link #onBootCompleted()} will never be called before {@link #start()}. */
default void onBootCompleted() {
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
index 9a8d53228e3f..104b71f29219 100644
--- a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
@@ -17,24 +17,25 @@
package com.android.systemui;
import android.app.AlertDialog;
-import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.UserInfo;
import android.os.UserHandle;
-import android.util.Log;
+
+import androidx.annotation.NonNull;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.qs.QSUserSwitcherEvent;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.util.settings.SecureSettings;
+import java.util.concurrent.Executor;
+
import javax.inject.Inject;
import dagger.assisted.Assisted;
@@ -44,31 +45,66 @@ import dagger.assisted.AssistedInject;
/**
* Manages notification when a guest session is resumed.
*/
-public class GuestResumeSessionReceiver extends BroadcastReceiver {
-
- private static final String TAG = GuestResumeSessionReceiver.class.getSimpleName();
+public class GuestResumeSessionReceiver {
@VisibleForTesting
public static final String SETTING_GUEST_HAS_LOGGED_IN = "systemui.guest_has_logged_in";
@VisibleForTesting
public AlertDialog mNewSessionDialog;
+ private final Executor mMainExecutor;
private final UserTracker mUserTracker;
private final SecureSettings mSecureSettings;
- private final BroadcastDispatcher mBroadcastDispatcher;
private final ResetSessionDialog.Factory mResetSessionDialogFactory;
private final GuestSessionNotification mGuestSessionNotification;
+ @VisibleForTesting
+ public final UserTracker.Callback mUserChangedCallback =
+ new UserTracker.Callback() {
+ @Override
+ public void onUserChanged(int newUser, @NonNull Context userContext) {
+ cancelDialog();
+
+ UserInfo currentUser = mUserTracker.getUserInfo();
+ if (!currentUser.isGuest()) {
+ return;
+ }
+
+ int guestLoginState = mSecureSettings.getIntForUser(
+ SETTING_GUEST_HAS_LOGGED_IN, 0, newUser);
+
+ if (guestLoginState == 0) {
+ // set 1 to indicate, 1st login
+ guestLoginState = 1;
+ mSecureSettings.putIntForUser(SETTING_GUEST_HAS_LOGGED_IN, guestLoginState,
+ newUser);
+ } else if (guestLoginState == 1) {
+ // set 2 to indicate, 2nd or later login
+ guestLoginState = 2;
+ mSecureSettings.putIntForUser(SETTING_GUEST_HAS_LOGGED_IN, guestLoginState,
+ newUser);
+ }
+
+ mGuestSessionNotification.createPersistentNotification(currentUser,
+ (guestLoginState <= 1));
+
+ if (guestLoginState > 1) {
+ mNewSessionDialog = mResetSessionDialogFactory.create(newUser);
+ mNewSessionDialog.show();
+ }
+ }
+ };
+
@Inject
public GuestResumeSessionReceiver(
+ @Main Executor mainExecutor,
UserTracker userTracker,
SecureSettings secureSettings,
- BroadcastDispatcher broadcastDispatcher,
GuestSessionNotification guestSessionNotification,
ResetSessionDialog.Factory resetSessionDialogFactory) {
+ mMainExecutor = mainExecutor;
mUserTracker = userTracker;
mSecureSettings = secureSettings;
- mBroadcastDispatcher = broadcastDispatcher;
mGuestSessionNotification = guestSessionNotification;
mResetSessionDialogFactory = resetSessionDialogFactory;
}
@@ -77,49 +113,7 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver {
* Register this receiver with the {@link BroadcastDispatcher}
*/
public void register() {
- IntentFilter f = new IntentFilter(Intent.ACTION_USER_SWITCHED);
- mBroadcastDispatcher.registerReceiver(this, f, null /* handler */, UserHandle.SYSTEM);
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
-
- if (Intent.ACTION_USER_SWITCHED.equals(action)) {
- cancelDialog();
-
- int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
- if (userId == UserHandle.USER_NULL) {
- Log.e(TAG, intent + " sent to " + TAG + " without EXTRA_USER_HANDLE");
- return;
- }
-
- UserInfo currentUser = mUserTracker.getUserInfo();
- if (!currentUser.isGuest()) {
- return;
- }
-
- int guestLoginState = mSecureSettings.getIntForUser(
- SETTING_GUEST_HAS_LOGGED_IN, 0, userId);
-
- if (guestLoginState == 0) {
- // set 1 to indicate, 1st login
- guestLoginState = 1;
- mSecureSettings.putIntForUser(SETTING_GUEST_HAS_LOGGED_IN, guestLoginState, userId);
- } else if (guestLoginState == 1) {
- // set 2 to indicate, 2nd or later login
- guestLoginState = 2;
- mSecureSettings.putIntForUser(SETTING_GUEST_HAS_LOGGED_IN, guestLoginState, userId);
- }
-
- mGuestSessionNotification.createPersistentNotification(currentUser,
- (guestLoginState <= 1));
-
- if (guestLoginState > 1) {
- mNewSessionDialog = mResetSessionDialogFactory.create(userId);
- mNewSessionDialog.show();
- }
- }
+ mUserTracker.addCallback(mUserChangedCallback, mMainExecutor);
}
private void cancelDialog() {
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 7e3b1389792c..02a6d7be7143 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -26,10 +26,7 @@ import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_M
import android.annotation.IdRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -45,7 +42,6 @@ import android.hardware.graphics.common.DisplayDecorationSupport;
import android.os.Handler;
import android.os.SystemProperties;
import android.os.Trace;
-import android.os.UserHandle;
import android.provider.Settings.Secure;
import android.util.DisplayUtils;
import android.util.Log;
@@ -68,7 +64,6 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.util.Preconditions;
import com.android.settingslib.Utils;
-import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.decor.CutoutDecorProviderFactory;
@@ -128,7 +123,6 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable {
private DisplayManager mDisplayManager;
@VisibleForTesting
protected boolean mIsRegistered;
- private final BroadcastDispatcher mBroadcastDispatcher;
private final Context mContext;
private final Executor mMainExecutor;
private final TunerService mTunerService;
@@ -302,7 +296,6 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable {
public ScreenDecorations(Context context,
@Main Executor mainExecutor,
SecureSettings secureSettings,
- BroadcastDispatcher broadcastDispatcher,
TunerService tunerService,
UserTracker userTracker,
PrivacyDotViewController dotViewController,
@@ -312,7 +305,6 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable {
mContext = context;
mMainExecutor = mainExecutor;
mSecureSettings = secureSettings;
- mBroadcastDispatcher = broadcastDispatcher;
mTunerService = tunerService;
mUserTracker = userTracker;
mDotViewController = dotViewController;
@@ -598,10 +590,7 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable {
mColorInversionSetting.onChange(false);
updateColorInversion(mColorInversionSetting.getValue());
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_USER_SWITCHED);
- mBroadcastDispatcher.registerReceiver(mUserSwitchIntentReceiver, filter,
- mExecutor, UserHandle.ALL);
+ mUserTracker.addCallback(mUserChangedCallback, mExecutor);
mIsRegistered = true;
} else {
mMainExecutor.execute(() -> mTunerService.removeTunable(this));
@@ -610,7 +599,7 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable {
mColorInversionSetting.setListening(false);
}
- mBroadcastDispatcher.unregisterReceiver(mUserSwitchIntentReceiver);
+ mUserTracker.removeCallback(mUserChangedCallback);
mIsRegistered = false;
}
}
@@ -897,18 +886,18 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable {
}
}
- private final BroadcastReceiver mUserSwitchIntentReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- int newUserId = mUserTracker.getUserId();
- if (DEBUG) {
- Log.d(TAG, "UserSwitched newUserId=" + newUserId);
- }
- // update color inversion setting to the new user
- mColorInversionSetting.setUserId(newUserId);
- updateColorInversion(mColorInversionSetting.getValue());
- }
- };
+ private final UserTracker.Callback mUserChangedCallback =
+ new UserTracker.Callback() {
+ @Override
+ public void onUserChanged(int newUser, @NonNull Context userContext) {
+ if (DEBUG) {
+ Log.d(TAG, "UserSwitched newUserId=" + newUser);
+ }
+ // update color inversion setting to the new user
+ mColorInversionSetting.setUserId(newUser);
+ updateColorInversion(mColorInversionSetting.getValue());
+ }
+ };
private void updateColorInversion(int colorsInvertedValue) {
mTintColor = colorsInvertedValue != 0 ? Color.WHITE : Color.BLACK;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index a7519cf713d5..db2239b7e680 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -623,6 +623,10 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
getFingerprintSensorLocationInNaturalOrientation(),
mCachedDisplayInfo);
}
+
+ for (final Callback cb : mCallbacks) {
+ cb.onFingerprintLocationChanged();
+ }
}
/**
@@ -644,6 +648,10 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
mCachedDisplayInfo
);
}
+
+ for (final Callback cb : mCallbacks) {
+ cb.onFaceSensorLocationChanged();
+ }
}
/**
@@ -1325,8 +1333,24 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
default void onBiometricPromptDismissed() {}
/**
- * The location in pixels can change due to resolution changes.
+ * Called when the location of the fingerprint sensor changes. The location in pixels can
+ * change due to resolution changes.
+ */
+ default void onFingerprintLocationChanged() {}
+
+ /**
+ * Called when the location of the under display fingerprint sensor changes. The location in
+ * pixels can change due to resolution changes.
+ *
+ * On devices with UDFPS, this is always called alongside
+ * {@link #onFingerprintLocationChanged}.
*/
default void onUdfpsLocationChanged() {}
+
+ /**
+ * Called when the location of the face unlock sensor (typically the front facing camera)
+ * changes. The location in pixels can change due to resolution changes.
+ */
+ default void onFaceSensorLocationChanged() {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index 6ac54feeb935..d561cd7af7f0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -29,6 +29,8 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.settingslib.Utils
import com.android.systemui.R
import com.android.systemui.animation.Interpolators
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.CircleReveal
@@ -71,7 +73,8 @@ class AuthRippleController @Inject constructor(
private val biometricUnlockController: BiometricUnlockController,
private val udfpsControllerProvider: Provider<UdfpsController>,
private val statusBarStateController: StatusBarStateController,
- rippleView: AuthRippleView?
+ private val featureFlags: FeatureFlags,
+ rippleView: AuthRippleView?
) : ViewController<AuthRippleView>(rippleView), KeyguardStateController.Callback,
WakefulnessLifecycle.Observer {
@@ -159,12 +162,17 @@ class AuthRippleController @Inject constructor(
private fun showUnlockedRipple() {
notificationShadeWindowController.setForcePluginOpen(true, this)
- val lightRevealScrim = centralSurfaces.lightRevealScrim
- if (statusBarStateController.isDozing || biometricUnlockController.isWakeAndUnlock) {
- circleReveal?.let {
- lightRevealScrim?.revealAmount = 0f
- lightRevealScrim?.revealEffect = it
- startLightRevealScrimOnKeyguardFadingAway = true
+
+ // This code path is not used if the KeyguardTransitionRepository is managing the light
+ // reveal scrim.
+ if (!featureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+ val lightRevealScrim = centralSurfaces.lightRevealScrim
+ if (statusBarStateController.isDozing || biometricUnlockController.isWakeAndUnlock) {
+ circleReveal?.let {
+ lightRevealScrim?.revealAmount = 0f
+ lightRevealScrim?.revealEffect = it
+ startLightRevealScrimOnKeyguardFadingAway = true
+ }
}
}
@@ -177,6 +185,10 @@ class AuthRippleController @Inject constructor(
}
override fun onKeyguardFadingAwayChanged() {
+ if (featureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+ return
+ }
+
if (keyguardStateController.isKeyguardFadingAway) {
val lightRevealScrim = centralSurfaces.lightRevealScrim
if (startLightRevealScrimOnKeyguardFadingAway && lightRevealScrim != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
index 1c3dd451a1d3..17ebdadfa93f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
@@ -135,7 +135,7 @@ constructor(
WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
- WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS,
PixelFormat.TRANSLUCENT
)
@@ -370,11 +370,15 @@ private fun WindowInsets.hasBigNavigationBar(): Boolean =
private fun LottieAnimationView.addOverlayDynamicColor(context: Context) {
fun update() {
val c = context.getColor(R.color.biometric_dialog_accent)
+ val chevronFill = context.getColor(R.color.sfps_chevron_fill)
for (key in listOf(".blue600", ".blue400")) {
addValueCallback(KeyPath(key, "**"), LottieProperty.COLOR_FILTER) {
PorterDuffColorFilter(c, PorterDuff.Mode.SRC_ATOP)
}
}
+ addValueCallback(KeyPath(".black", "**"), LottieProperty.COLOR_FILTER) {
+ PorterDuffColorFilter(chevronFill, PorterDuff.Mode.SRC_ATOP)
+ }
}
if (composition != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 1d4281fbf451..19b054879d89 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -62,6 +62,11 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dumpable;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.biometrics.dagger.BiometricsBackground;
+import com.android.systemui.biometrics.udfps.InteractionEvent;
+import com.android.systemui.biometrics.udfps.NormalizedTouchData;
+import com.android.systemui.biometrics.udfps.SinglePointerTouchProcessor;
+import com.android.systemui.biometrics.udfps.TouchProcessor;
+import com.android.systemui.biometrics.udfps.TouchProcessorResult;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeReceiver;
@@ -143,6 +148,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
@VisibleForTesting @NonNull final BiometricDisplayListener mOrientationListener;
@NonNull private final ActivityLaunchAnimator mActivityLaunchAnimator;
@NonNull private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
+ @Nullable private final TouchProcessor mTouchProcessor;
// Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
// sensors, this, in addition to a lot of the code here, will be updated.
@@ -166,7 +172,6 @@ public class UdfpsController implements DozeReceiver, Dumpable {
// The current request from FingerprintService. Null if no current request.
@Nullable UdfpsControllerOverlay mOverlay;
- @Nullable private UdfpsEllipseDetection mUdfpsEllipseDetection;
// The fingerprint AOD trigger doesn't provide an ACTION_UP/ACTION_CANCEL event to tell us when
// to turn off high brightness mode. To get around this limitation, the state of the AOD
@@ -355,10 +360,6 @@ public class UdfpsController implements DozeReceiver, Dumpable {
if (!mOverlayParams.equals(overlayParams)) {
mOverlayParams = overlayParams;
- if (mFeatureFlags.isEnabled(Flags.UDFPS_ELLIPSE_DETECTION)) {
- mUdfpsEllipseDetection.updateOverlayParams(overlayParams);
- }
-
final boolean wasShowingAltAuth = mKeyguardViewManager.isShowingAlternateBouncer();
// When the bounds change it's always necessary to re-create the overlay's window with
@@ -467,8 +468,99 @@ public class UdfpsController implements DozeReceiver, Dumpable {
return portraitTouch;
}
+ private void tryDismissingKeyguard() {
+ if (!mOnFingerDown) {
+ playStartHaptic();
+ }
+ mKeyguardViewManager.notifyKeyguardAuthenticated(false /* strongAuth */);
+ mAttemptedToDismissKeyguard = true;
+ }
+
@VisibleForTesting
boolean onTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) {
+ if (mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) {
+ return newOnTouch(requestId, event, fromUdfpsView);
+ } else {
+ return oldOnTouch(requestId, event, fromUdfpsView);
+ }
+ }
+
+ private boolean newOnTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) {
+ if (!fromUdfpsView) {
+ Log.e(TAG, "ignoring the touch injected from outside of UdfpsView");
+ return false;
+ }
+ if (mOverlay == null) {
+ Log.w(TAG, "ignoring onTouch with null overlay");
+ return false;
+ }
+ if (!mOverlay.matchesRequestId(requestId)) {
+ Log.w(TAG, "ignoring stale touch event: " + requestId + " current: "
+ + mOverlay.getRequestId());
+ return false;
+ }
+
+ final TouchProcessorResult result = mTouchProcessor.processTouch(event, mActivePointerId,
+ mOverlayParams);
+ if (result instanceof TouchProcessorResult.Failure) {
+ Log.w(TAG, ((TouchProcessorResult.Failure) result).getReason());
+ return false;
+ }
+
+ final TouchProcessorResult.ProcessedTouch processedTouch =
+ (TouchProcessorResult.ProcessedTouch) result;
+ final NormalizedTouchData data = processedTouch.getTouchData();
+
+ mActivePointerId = processedTouch.getPointerOnSensorId();
+ switch (processedTouch.getEvent()) {
+ case DOWN:
+ if (shouldTryToDismissKeyguard()) {
+ tryDismissingKeyguard();
+ }
+ onFingerDown(requestId,
+ data.getPointerId(),
+ data.getX(),
+ data.getY(),
+ data.getMinor(),
+ data.getMajor(),
+ data.getOrientation(),
+ data.getTime(),
+ data.getGestureStart(),
+ mStatusBarStateController.isDozing());
+ break;
+
+ case UP:
+ case CANCEL:
+ if (InteractionEvent.CANCEL.equals(processedTouch.getEvent())) {
+ Log.w(TAG, "This is a CANCEL event that's reported as an UP event!");
+ }
+ mAttemptedToDismissKeyguard = false;
+ onFingerUp(requestId,
+ mOverlay.getOverlayView(),
+ data.getPointerId(),
+ data.getX(),
+ data.getY(),
+ data.getMinor(),
+ data.getMajor(),
+ data.getOrientation(),
+ data.getTime(),
+ data.getGestureStart(),
+ mStatusBarStateController.isDozing());
+ mFalsingManager.isFalseTouch(UDFPS_AUTHENTICATION);
+ break;
+
+
+ default:
+ break;
+ }
+
+ // We should only consume touches that are within the sensor. By returning "false" for
+ // touches outside of the sensor, we let other UI components consume these events and act on
+ // them appropriately.
+ return processedTouch.getTouchData().isWithinSensor(mOverlayParams.getNativeSensorBounds());
+ }
+
+ private boolean oldOnTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) {
if (mOverlay == null) {
Log.w(TAG, "ignoring onTouch with null overlay");
return false;
@@ -498,23 +590,8 @@ public class UdfpsController implements DozeReceiver, Dumpable {
mVelocityTracker.clear();
}
- boolean withinSensorArea;
- if (mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) {
- if (mFeatureFlags.isEnabled(Flags.UDFPS_ELLIPSE_DETECTION)) {
- // Ellipse detection
- withinSensorArea = mUdfpsEllipseDetection.isGoodEllipseOverlap(event);
- } else {
- // Centroid with expanded overlay
- withinSensorArea =
- isWithinSensorArea(udfpsView, event.getRawX(),
- event.getRawY(), fromUdfpsView);
- }
- } else {
- // Centroid with sensor sized view
- withinSensorArea =
+ final boolean withinSensorArea =
isWithinSensorArea(udfpsView, event.getX(), event.getY(), fromUdfpsView);
- }
-
if (withinSensorArea) {
Trace.beginAsyncSection("UdfpsController.e2e.onPointerDown", 0);
Log.v(TAG, "onTouch | action down");
@@ -528,11 +605,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
}
if ((withinSensorArea || fromUdfpsView) && shouldTryToDismissKeyguard()) {
Log.v(TAG, "onTouch | dismiss keyguard ACTION_DOWN");
- if (!mOnFingerDown) {
- playStartHaptic();
- }
- mKeyguardViewManager.notifyKeyguardAuthenticated(false /* strongAuth */);
- mAttemptedToDismissKeyguard = true;
+ tryDismissingKeyguard();
}
Trace.endSection();
@@ -545,33 +618,13 @@ public class UdfpsController implements DozeReceiver, Dumpable {
? event.getPointerId(0)
: event.findPointerIndex(mActivePointerId);
if (idx == event.getActionIndex()) {
- boolean actionMoveWithinSensorArea;
- if (mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) {
- if (mFeatureFlags.isEnabled(Flags.UDFPS_ELLIPSE_DETECTION)) {
- // Ellipse detection
- actionMoveWithinSensorArea =
- mUdfpsEllipseDetection.isGoodEllipseOverlap(event);
- } else {
- // Centroid with expanded overlay
- actionMoveWithinSensorArea =
- isWithinSensorArea(udfpsView, event.getRawX(idx),
- event.getRawY(idx), fromUdfpsView);
- }
- } else {
- // Centroid with sensor sized view
- actionMoveWithinSensorArea =
- isWithinSensorArea(udfpsView, event.getX(idx),
- event.getY(idx), fromUdfpsView);
- }
-
+ final boolean actionMoveWithinSensorArea =
+ isWithinSensorArea(udfpsView, event.getX(idx), event.getY(idx),
+ fromUdfpsView);
if ((fromUdfpsView || actionMoveWithinSensorArea)
&& shouldTryToDismissKeyguard()) {
Log.v(TAG, "onTouch | dismiss keyguard ACTION_MOVE");
- if (!mOnFingerDown) {
- playStartHaptic();
- }
- mKeyguardViewManager.notifyKeyguardAuthenticated(false /* strongAuth */);
- mAttemptedToDismissKeyguard = true;
+ tryDismissingKeyguard();
break;
}
// Map the touch to portrait mode if the device is in landscape mode.
@@ -696,7 +749,8 @@ public class UdfpsController implements DozeReceiver, Dumpable {
@NonNull ActivityLaunchAnimator activityLaunchAnimator,
@NonNull Optional<AlternateUdfpsTouchProvider> alternateTouchProvider,
@NonNull @BiometricsBackground Executor biometricsExecutor,
- @NonNull PrimaryBouncerInteractor primaryBouncerInteractor) {
+ @NonNull PrimaryBouncerInteractor primaryBouncerInteractor,
+ @NonNull SinglePointerTouchProcessor singlePointerTouchProcessor) {
mContext = context;
mExecution = execution;
mVibrator = vibrator;
@@ -737,6 +791,9 @@ public class UdfpsController implements DozeReceiver, Dumpable {
mBiometricExecutor = biometricsExecutor;
mPrimaryBouncerInteractor = primaryBouncerInteractor;
+ mTouchProcessor = mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)
+ ? singlePointerTouchProcessor : null;
+
mDumpManager.registerDumpable(TAG, this);
mOrientationListener = new BiometricDisplayListener(
@@ -761,10 +818,6 @@ public class UdfpsController implements DozeReceiver, Dumpable {
udfpsHapticsSimulator.setUdfpsController(this);
udfpsShell.setUdfpsOverlayController(mUdfpsOverlayController);
-
- if (featureFlags.isEnabled(Flags.UDFPS_ELLIPSE_DETECTION)) {
- mUdfpsEllipseDetection = new UdfpsEllipseDetection(mOverlayParams);
- }
}
/**
@@ -946,7 +999,36 @@ public class UdfpsController implements DozeReceiver, Dumpable {
return mOnFingerDown;
}
- private void onFingerDown(long requestId, int x, int y, float minor, float major) {
+ private void onFingerDown(
+ long requestId,
+ int x,
+ int y,
+ float minor,
+ float major) {
+ onFingerDown(
+ requestId,
+ MotionEvent.INVALID_POINTER_ID /* pointerId */,
+ x,
+ y,
+ minor,
+ major,
+ 0f /* orientation */,
+ 0L /* time */,
+ 0L /* gestureStart */,
+ false /* isAod */);
+ }
+
+ private void onFingerDown(
+ long requestId,
+ int pointerId,
+ float x,
+ float y,
+ float minor,
+ float major,
+ float orientation,
+ long time,
+ long gestureStart,
+ boolean isAod) {
mExecution.assertIsMainThread();
if (mOverlay == null) {
@@ -975,7 +1057,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
mOnFingerDown = true;
if (mAlternateTouchProvider != null) {
mBiometricExecutor.execute(() -> {
- mAlternateTouchProvider.onPointerDown(requestId, x, y, minor, major);
+ mAlternateTouchProvider.onPointerDown(requestId, (int) x, (int) y, minor, major);
});
mFgExecutor.execute(() -> {
if (mKeyguardUpdateMonitor.isFingerprintDetectionRunning()) {
@@ -983,7 +1065,13 @@ public class UdfpsController implements DozeReceiver, Dumpable {
}
});
} else {
- mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, x, y, minor, major);
+ if (mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) {
+ mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, pointerId, x, y,
+ minor, major, orientation, time, gestureStart, isAod);
+ } else {
+ mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, (int) x,
+ (int) y, minor, major);
+ }
}
Trace.endAsyncSection("UdfpsController.e2e.onPointerDown", 0);
final UdfpsView view = mOverlay.getOverlayView();
@@ -1007,6 +1095,32 @@ public class UdfpsController implements DozeReceiver, Dumpable {
}
private void onFingerUp(long requestId, @NonNull UdfpsView view) {
+ onFingerUp(
+ requestId,
+ view,
+ MotionEvent.INVALID_POINTER_ID /* pointerId */,
+ 0f /* x */,
+ 0f /* y */,
+ 0f /* minor */,
+ 0f /* major */,
+ 0f /* orientation */,
+ 0L /* time */,
+ 0L /* gestureStart */,
+ false /* isAod */);
+ }
+
+ private void onFingerUp(
+ long requestId,
+ @NonNull UdfpsView view,
+ int pointerId,
+ float x,
+ float y,
+ float minor,
+ float major,
+ float orientation,
+ long time,
+ long gestureStart,
+ boolean isAod) {
mExecution.assertIsMainThread();
mActivePointerId = -1;
mAcquiredReceived = false;
@@ -1021,7 +1135,12 @@ public class UdfpsController implements DozeReceiver, Dumpable {
}
});
} else {
- mFingerprintManager.onPointerUp(requestId, mSensorProps.sensorId);
+ if (mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) {
+ mFingerprintManager.onPointerUp(requestId, mSensorProps.sensorId, pointerId, x,
+ y, minor, major, orientation, time, gestureStart, isAod);
+ } else {
+ mFingerprintManager.onPointerUp(requestId, mSensorProps.sensorId);
+ }
}
for (Callback cb : mCallbacks) {
cb.onFingerUp();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEllipseDetection.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEllipseDetection.kt
deleted file mode 100644
index 8ae4775467df..000000000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEllipseDetection.kt
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics
-
-import android.graphics.Point
-import android.graphics.Rect
-import android.util.RotationUtils
-import android.view.MotionEvent
-import kotlin.math.cos
-import kotlin.math.pow
-import kotlin.math.sin
-
-private const val TAG = "UdfpsEllipseDetection"
-
-private const val NEEDED_POINTS = 2
-
-class UdfpsEllipseDetection(overlayParams: UdfpsOverlayParams) {
- var sensorRect = Rect()
- var points: Array<Point> = emptyArray()
-
- init {
- sensorRect = Rect(overlayParams.sensorBounds)
-
- points = calculateSensorPoints(sensorRect)
- }
-
- fun updateOverlayParams(params: UdfpsOverlayParams) {
- sensorRect = Rect(params.sensorBounds)
-
- val rot = params.rotation
- RotationUtils.rotateBounds(
- sensorRect,
- params.naturalDisplayWidth,
- params.naturalDisplayHeight,
- rot
- )
-
- points = calculateSensorPoints(sensorRect)
- }
-
- fun isGoodEllipseOverlap(event: MotionEvent): Boolean {
- return points.count { checkPoint(event, it) } >= NEEDED_POINTS
- }
-
- private fun checkPoint(event: MotionEvent, point: Point): Boolean {
- // Calculate if sensor point is within ellipse
- // Formula: ((cos(o)(xE - xS) + sin(o)(yE - yS))^2 / a^2) + ((sin(o)(xE - xS) + cos(o)(yE -
- // yS))^2 / b^2) <= 1
- val a: Float = cos(event.orientation) * (point.x - event.rawX)
- val b: Float = sin(event.orientation) * (point.y - event.rawY)
- val c: Float = sin(event.orientation) * (point.x - event.rawX)
- val d: Float = cos(event.orientation) * (point.y - event.rawY)
- val result =
- (a + b).pow(2) / (event.touchMinor / 2).pow(2) +
- (c - d).pow(2) / (event.touchMajor / 2).pow(2)
-
- return result <= 1
- }
-}
-
-fun calculateSensorPoints(sensorRect: Rect): Array<Point> {
- val sensorX = sensorRect.centerX()
- val sensorY = sensorRect.centerY()
- val cornerOffset: Int = sensorRect.width() / 4
- val sideOffset: Int = sensorRect.width() / 3
-
- return arrayOf(
- Point(sensorX - cornerOffset, sensorY - cornerOffset),
- Point(sensorX, sensorY - sideOffset),
- Point(sensorX + cornerOffset, sensorY - cornerOffset),
- Point(sensorX - sideOffset, sensorY),
- Point(sensorX, sensorY),
- Point(sensorX + sideOffset, sensorY),
- Point(sensorX - cornerOffset, sensorY + cornerOffset),
- Point(sensorX, sensorY + sideOffset),
- Point(sensorX + cornerOffset, sensorY + cornerOffset)
- )
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayParams.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayParams.kt
index c23b0f09f099..7f3846ca4e40 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayParams.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayParams.kt
@@ -7,17 +7,23 @@ import android.view.Surface.Rotation
/**
* Collection of parameters that define an under-display fingerprint sensor (UDFPS) overlay.
*
- * @property sensorBounds coordinates of the bounding box around the sensor, in natural orientation,
- * in pixels, for the current resolution.
- * @property naturalDisplayWidth width of the physical display, in natural orientation, in pixels,
- * for the current resolution.
- * @property naturalDisplayHeight height of the physical display, in natural orientation, in pixels,
- * for the current resolution.
- * @property scaleFactor ratio of a dimension in the current resolution to the corresponding
- * dimension in the native resolution.
- * @property rotation current rotation of the display.
+ * [sensorBounds] coordinates of the bounding box around the sensor in natural orientation, in
+ * pixels, for the current resolution.
+ *
+ * [overlayBounds] coordinates of the UI overlay in natural orientation, in pixels, for the current
+ * resolution.
+ *
+ * [naturalDisplayWidth] width of the physical display in natural orientation, in pixels, for the
+ * current resolution.
+ *
+ * [naturalDisplayHeight] height of the physical display in natural orientation, in pixels, for the
+ * current resolution.
+ *
+ * [scaleFactor] ratio of a dimension in the current resolution to the corresponding dimension in
+ * the native resolution.
+ *
+ * [rotation] current rotation of the display.
*/
-
data class UdfpsOverlayParams(
val sensorBounds: Rect = Rect(),
val overlayBounds: Rect = Rect(),
@@ -26,17 +32,21 @@ data class UdfpsOverlayParams(
val scaleFactor: Float = 1f,
@Rotation val rotation: Int = Surface.ROTATION_0
) {
+
+ /** Same as [sensorBounds], but in native resolution. */
+ val nativeSensorBounds = Rect(sensorBounds).apply { scale(1f / scaleFactor) }
+
/** See [android.view.DisplayInfo.logicalWidth] */
- val logicalDisplayWidth
- get() = if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
+ val logicalDisplayWidth =
+ if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
naturalDisplayHeight
} else {
naturalDisplayWidth
}
/** See [android.view.DisplayInfo.logicalHeight] */
- val logicalDisplayHeight
- get() = if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
+ val logicalDisplayHeight =
+ if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
naturalDisplayWidth
} else {
naturalDisplayHeight
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/UdfpsModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/UdfpsModule.kt
new file mode 100644
index 000000000000..001fed76c124
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/UdfpsModule.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.dagger
+
+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.dagger.SysUISingleton
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import dagger.Module
+import dagger.Provides
+
+/** Dagger module for all things UDFPS. TODO(b/260558624): Move to BiometricsModule. */
+@Module
+interface UdfpsModule {
+ companion object {
+
+ @Provides
+ @SysUISingleton
+ fun providesOverlapDetector(featureFlags: FeatureFlags): OverlapDetector {
+ return if (featureFlags.isEnabled(Flags.UDFPS_ELLIPSE_DETECTION)) {
+ EllipseOverlapDetector()
+ } else {
+ BoundingBoxOverlapDetector()
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetector.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetector.kt
new file mode 100644
index 000000000000..79a0acb8bbc1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetector.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.udfps
+
+import android.graphics.Rect
+import com.android.systemui.dagger.SysUISingleton
+
+/** Returns whether the touch coordinates are within the sensor's bounding box. */
+@SysUISingleton
+class BoundingBoxOverlapDetector : OverlapDetector {
+ override fun isGoodOverlap(touchData: NormalizedTouchData, nativeSensorBounds: Rect): Boolean =
+ touchData.isWithinSensor(nativeSensorBounds)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetector.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetector.kt
new file mode 100644
index 000000000000..857224290752
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetector.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.udfps
+
+import android.graphics.Point
+import android.graphics.Rect
+import com.android.systemui.dagger.SysUISingleton
+import kotlin.math.cos
+import kotlin.math.pow
+import kotlin.math.sin
+
+/**
+ * Approximates the touch as an ellipse and determines whether the ellipse has a sufficient overlap
+ * with the sensor.
+ */
+@SysUISingleton
+class EllipseOverlapDetector(private val neededPoints: Int = 2) : OverlapDetector {
+
+ override fun isGoodOverlap(touchData: NormalizedTouchData, nativeSensorBounds: Rect): Boolean {
+ val points = calculateSensorPoints(nativeSensorBounds)
+ return points.count { checkPoint(it, touchData) } >= neededPoints
+ }
+
+ private fun checkPoint(point: Point, touchData: NormalizedTouchData): Boolean {
+ // Calculate if sensor point is within ellipse
+ // Formula: ((cos(o)(xE - xS) + sin(o)(yE - yS))^2 / a^2) + ((sin(o)(xE - xS) + cos(o)(yE -
+ // yS))^2 / b^2) <= 1
+ val a: Float = cos(touchData.orientation) * (point.x - touchData.x)
+ val b: Float = sin(touchData.orientation) * (point.y - touchData.y)
+ val c: Float = sin(touchData.orientation) * (point.x - touchData.x)
+ val d: Float = cos(touchData.orientation) * (point.y - touchData.y)
+ val result =
+ (a + b).pow(2) / (touchData.minor / 2).pow(2) +
+ (c - d).pow(2) / (touchData.major / 2).pow(2)
+
+ return result <= 1
+ }
+
+ private fun calculateSensorPoints(sensorBounds: Rect): List<Point> {
+ val sensorX = sensorBounds.centerX()
+ val sensorY = sensorBounds.centerY()
+ val cornerOffset: Int = sensorBounds.width() / 4
+ val sideOffset: Int = sensorBounds.width() / 3
+
+ return listOf(
+ Point(sensorX - cornerOffset, sensorY - cornerOffset),
+ Point(sensorX, sensorY - sideOffset),
+ Point(sensorX + cornerOffset, sensorY - cornerOffset),
+ Point(sensorX - sideOffset, sensorY),
+ Point(sensorX, sensorY),
+ Point(sensorX + sideOffset, sensorY),
+ Point(sensorX - cornerOffset, sensorY + cornerOffset),
+ Point(sensorX, sensorY + sideOffset),
+ Point(sensorX + cornerOffset, sensorY + cornerOffset)
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/InteractionEvent.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/InteractionEvent.kt
new file mode 100644
index 000000000000..6e47dadc4545
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/InteractionEvent.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.udfps
+
+import android.view.MotionEvent
+
+/** Interaction event between a finger and the under-display fingerprint sensor (UDFPS). */
+enum class InteractionEvent {
+ /**
+ * A finger entered the sensor area. This can originate from either [MotionEvent.ACTION_DOWN] or
+ * [MotionEvent.ACTION_MOVE].
+ */
+ DOWN,
+
+ /**
+ * A finger left the sensor area. This can originate from either [MotionEvent.ACTION_UP] or
+ * [MotionEvent.ACTION_MOVE].
+ */
+ UP,
+
+ /**
+ * The touch reporting has stopped. This corresponds to [MotionEvent.ACTION_CANCEL]. This should
+ * not be confused with [UP]. If there was a finger on the sensor, it may or may not still be on
+ * the sensor.
+ */
+ CANCEL,
+
+ /**
+ * The interaction hasn't changed since the previous event. The can originate from any of
+ * [MotionEvent.ACTION_DOWN], [MotionEvent.ACTION_MOVE], or [MotionEvent.ACTION_UP] if one of
+ * these is true:
+ * - There was previously a finger on the sensor, and that finger is still on the sensor.
+ * - There was previously no finger on the sensor, and there still isn't.
+ */
+ UNCHANGED,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt
new file mode 100644
index 000000000000..62bedc627b07
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.udfps
+
+import android.graphics.Rect
+import android.view.MotionEvent
+
+/** Touch data in natural orientation and native resolution. */
+data class NormalizedTouchData(
+
+ /**
+ * Value obtained from [MotionEvent.getPointerId], or [MotionEvent.INVALID_POINTER_ID] if the ID
+ * is not available.
+ */
+ val pointerId: Int,
+
+ /** [MotionEvent.getRawX] mapped to natural orientation and native resolution. */
+ val x: Float,
+
+ /** [MotionEvent.getRawY] mapped to natural orientation and native resolution. */
+ val y: Float,
+
+ /** [MotionEvent.getTouchMinor] mapped to natural orientation and native resolution. */
+ val minor: Float,
+
+ /** [MotionEvent.getTouchMajor] mapped to natural orientation and native resolution. */
+ val major: Float,
+
+ /** [MotionEvent.getOrientation] mapped to natural orientation. */
+ val orientation: Float,
+
+ /** [MotionEvent.getEventTime]. */
+ val time: Long,
+
+ /** [MotionEvent.getDownTime]. */
+ val gestureStart: Long,
+) {
+
+ /**
+ * [nativeSensorBounds] contains the location and dimensions of the sensor area in native
+ * resolution and natural orientation.
+ *
+ * Returns whether the coordinates of the given pointer are within the sensor's bounding box.
+ */
+ fun isWithinSensor(nativeSensorBounds: Rect): Boolean {
+ return nativeSensorBounds.left <= x &&
+ nativeSensorBounds.right >= x &&
+ nativeSensorBounds.top <= y &&
+ nativeSensorBounds.bottom >= y
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/OverlapDetector.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/OverlapDetector.kt
new file mode 100644
index 000000000000..0fec8ffbaa0a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/OverlapDetector.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.udfps
+
+import android.graphics.Rect
+
+/** Determines whether the touch has a sufficient overlap with the sensor. */
+interface OverlapDetector {
+ fun isGoodOverlap(touchData: NormalizedTouchData, nativeSensorBounds: Rect): Boolean
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessor.kt
new file mode 100644
index 000000000000..338bf66d197e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessor.kt
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.udfps
+
+import android.graphics.PointF
+import android.util.RotationUtils
+import android.view.MotionEvent
+import android.view.MotionEvent.INVALID_POINTER_ID
+import android.view.Surface
+import com.android.systemui.biometrics.UdfpsOverlayParams
+import com.android.systemui.biometrics.udfps.TouchProcessorResult.Failure
+import com.android.systemui.biometrics.udfps.TouchProcessorResult.ProcessedTouch
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/**
+ * TODO(b/259140693): Consider using an object pool of TouchProcessorResult to avoid allocations.
+ */
+@SysUISingleton
+class SinglePointerTouchProcessor @Inject constructor(val overlapDetector: OverlapDetector) :
+ TouchProcessor {
+
+ override fun processTouch(
+ event: MotionEvent,
+ previousPointerOnSensorId: Int,
+ overlayParams: UdfpsOverlayParams,
+ ): TouchProcessorResult {
+
+ fun preprocess(): PreprocessedTouch {
+ // TODO(b/253085297): Add multitouch support. pointerIndex can be > 0 for ACTION_MOVE.
+ val pointerIndex = 0
+ val touchData = event.normalize(pointerIndex, overlayParams)
+ val isGoodOverlap =
+ overlapDetector.isGoodOverlap(touchData, overlayParams.nativeSensorBounds)
+ return PreprocessedTouch(touchData, previousPointerOnSensorId, isGoodOverlap)
+ }
+
+ return when (event.actionMasked) {
+ MotionEvent.ACTION_DOWN -> processActionDown(preprocess())
+ MotionEvent.ACTION_MOVE -> processActionMove(preprocess())
+ MotionEvent.ACTION_UP -> processActionUp(preprocess())
+ MotionEvent.ACTION_CANCEL ->
+ processActionCancel(event.normalize(pointerIndex = 0, overlayParams))
+ else ->
+ Failure("Unsupported MotionEvent." + MotionEvent.actionToString(event.actionMasked))
+ }
+ }
+}
+
+private data class PreprocessedTouch(
+ val data: NormalizedTouchData,
+ val previousPointerOnSensorId: Int,
+ val isGoodOverlap: Boolean,
+)
+
+private fun processActionDown(touch: PreprocessedTouch): TouchProcessorResult {
+ return if (touch.isGoodOverlap) {
+ ProcessedTouch(InteractionEvent.DOWN, pointerOnSensorId = touch.data.pointerId, touch.data)
+ } else {
+ val event =
+ if (touch.data.pointerId == touch.previousPointerOnSensorId) {
+ InteractionEvent.UP
+ } else {
+ InteractionEvent.UNCHANGED
+ }
+ ProcessedTouch(event, pointerOnSensorId = INVALID_POINTER_ID, touch.data)
+ }
+}
+
+private fun processActionMove(touch: PreprocessedTouch): TouchProcessorResult {
+ val hadPointerOnSensor = touch.previousPointerOnSensorId != INVALID_POINTER_ID
+ val interactionEvent =
+ when {
+ touch.isGoodOverlap && !hadPointerOnSensor -> InteractionEvent.DOWN
+ !touch.isGoodOverlap && hadPointerOnSensor -> InteractionEvent.UP
+ else -> InteractionEvent.UNCHANGED
+ }
+ val pointerOnSensorId =
+ when (interactionEvent) {
+ InteractionEvent.UNCHANGED -> touch.previousPointerOnSensorId
+ InteractionEvent.DOWN -> touch.data.pointerId
+ else -> INVALID_POINTER_ID
+ }
+ return ProcessedTouch(interactionEvent, pointerOnSensorId, touch.data)
+}
+
+private fun processActionUp(touch: PreprocessedTouch): TouchProcessorResult {
+ return if (touch.isGoodOverlap) {
+ ProcessedTouch(InteractionEvent.UP, pointerOnSensorId = INVALID_POINTER_ID, touch.data)
+ } else {
+ val event =
+ if (touch.previousPointerOnSensorId != INVALID_POINTER_ID) {
+ InteractionEvent.UP
+ } else {
+ InteractionEvent.UNCHANGED
+ }
+ ProcessedTouch(event, pointerOnSensorId = INVALID_POINTER_ID, touch.data)
+ }
+}
+
+private fun processActionCancel(data: NormalizedTouchData): TouchProcessorResult {
+ return ProcessedTouch(InteractionEvent.CANCEL, pointerOnSensorId = INVALID_POINTER_ID, data)
+}
+
+/**
+ * Returns the touch information from the given [MotionEvent] with the relevant fields mapped to
+ * natural orientation and native resolution.
+ */
+private fun MotionEvent.normalize(
+ pointerIndex: Int,
+ overlayParams: UdfpsOverlayParams
+): NormalizedTouchData {
+ val naturalTouch: PointF = rotateToNaturalOrientation(pointerIndex, overlayParams)
+ val nativeX = naturalTouch.x / overlayParams.scaleFactor
+ val nativeY = naturalTouch.y / overlayParams.scaleFactor
+ val nativeMinor: Float = getTouchMinor(pointerIndex) / overlayParams.scaleFactor
+ val nativeMajor: Float = getTouchMajor(pointerIndex) / overlayParams.scaleFactor
+ return NormalizedTouchData(
+ pointerId = getPointerId(pointerIndex),
+ x = nativeX,
+ y = nativeY,
+ minor = nativeMinor,
+ major = nativeMajor,
+ // TODO(b/259311354): touch orientation should be reported relative to Surface.ROTATION_O.
+ orientation = getOrientation(pointerIndex),
+ time = eventTime,
+ gestureStart = downTime,
+ )
+}
+
+/**
+ * Returns the [MotionEvent.getRawX] and [MotionEvent.getRawY] of the given pointer as if the device
+ * is in the [Surface.ROTATION_0] orientation.
+ */
+private fun MotionEvent.rotateToNaturalOrientation(
+ pointerIndex: Int,
+ overlayParams: UdfpsOverlayParams
+): PointF {
+ val touchPoint = PointF(getRawX(pointerIndex), getRawY(pointerIndex))
+ val rot = overlayParams.rotation
+ if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
+ RotationUtils.rotatePointF(
+ touchPoint,
+ RotationUtils.deltaRotation(rot, Surface.ROTATION_0),
+ overlayParams.logicalDisplayWidth.toFloat(),
+ overlayParams.logicalDisplayHeight.toFloat()
+ )
+ }
+ return touchPoint
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/TouchProcessor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/TouchProcessor.kt
new file mode 100644
index 000000000000..ffcebf9cff75
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/TouchProcessor.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.udfps
+
+import android.view.MotionEvent
+import com.android.systemui.biometrics.UdfpsOverlayParams
+
+/**
+ * Determines whether a finger entered or left the area of the under-display fingerprint sensor
+ * (UDFPS). Maps the touch information from a [MotionEvent] to the orientation and scale independent
+ * [NormalizedTouchData].
+ */
+interface TouchProcessor {
+
+ /**
+ * [event] touch event to be processed.
+ *
+ * [previousPointerOnSensorId] pointerId for the finger that was on the sensor prior to this
+ * event. See [MotionEvent.getPointerId]. If there was no finger on the sensor, this should be
+ * set to [MotionEvent.INVALID_POINTER_ID].
+ *
+ * [overlayParams] contains the location and dimensions of the sensor area, as well as the scale
+ * factor and orientation of the overlay. See [UdfpsOverlayParams].
+ *
+ * Returns [TouchProcessorResult.ProcessedTouch] on success, and [TouchProcessorResult.Failure]
+ * on failure.
+ */
+ fun processTouch(
+ event: MotionEvent,
+ previousPointerOnSensorId: Int,
+ overlayParams: UdfpsOverlayParams,
+ ): TouchProcessorResult
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/TouchProcessorResult.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/TouchProcessorResult.kt
new file mode 100644
index 000000000000..be75bb0d3821
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/TouchProcessorResult.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.udfps
+
+import android.view.MotionEvent
+
+/** Contains all the possible returns types for [TouchProcessor.processTouch] */
+sealed class TouchProcessorResult {
+
+ /**
+ * [event] whether a finger entered or left the sensor area. See [InteractionEvent].
+ *
+ * [pointerOnSensorId] pointerId fof the finger that's currently on the sensor. See
+ * [MotionEvent.getPointerId]. If there is no finger on the sensor, the value is set to
+ * [MotionEvent.INVALID_POINTER_ID].
+ *
+ * [touchData] relevant data from the MotionEvent, mapped to natural orientation and native
+ * resolution. See [NormalizedTouchData].
+ */
+ data class ProcessedTouch(
+ val event: InteractionEvent,
+ val pointerOnSensorId: Int,
+ val touchData: NormalizedTouchData
+ ) : TouchProcessorResult()
+
+ /** [reason] the reason for the failure. */
+ data class Failure(val reason: String = "") : TouchProcessorResult()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ControlsSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/controls/ControlsSettingsRepository.kt
new file mode 100644
index 000000000000..3d10ab906f2f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ControlsSettingsRepository.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.controls
+
+import kotlinx.coroutines.flow.StateFlow
+
+/** Repository for Device controls related settings. */
+interface ControlsSettingsRepository {
+ /** Whether device controls activity can be shown above lockscreen for this user. */
+ val canShowControlsInLockscreen: StateFlow<Boolean>
+
+ /** Whether trivial controls can be actioned from the lockscreen for this user. */
+ val allowActionOnTrivialControlsInLockscreen: StateFlow<Boolean>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ControlsSettingsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ControlsSettingsRepositoryImpl.kt
new file mode 100644
index 000000000000..9dc422a09674
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ControlsSettingsRepositoryImpl.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.controls
+
+import android.provider.Settings
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.SettingObserver
+import com.android.systemui.user.data.repository.UserRepository
+import com.android.systemui.util.settings.SecureSettings
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * This implementation uses an `@Application` [CoroutineScope] to provide hot flows for the values
+ * of the tracked settings.
+ */
+@SysUISingleton
+class ControlsSettingsRepositoryImpl
+@Inject
+constructor(
+ @Application private val scope: CoroutineScope,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+ private val userRepository: UserRepository,
+ private val secureSettings: SecureSettings
+) : ControlsSettingsRepository {
+
+ override val canShowControlsInLockscreen =
+ makeFlowForSetting(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS)
+
+ override val allowActionOnTrivialControlsInLockscreen =
+ makeFlowForSetting(Settings.Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS)
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ private fun makeFlowForSetting(setting: String): StateFlow<Boolean> {
+ return userRepository.selectedUserInfo
+ .distinctUntilChanged()
+ .flatMapLatest { userInfo ->
+ conflatedCallbackFlow {
+ val observer =
+ object : SettingObserver(secureSettings, null, setting, userInfo.id) {
+ override fun handleValueChanged(
+ value: Int,
+ observedChange: Boolean
+ ) {
+ trySend(value == 1)
+ }
+ }
+ observer.isListening = true
+ trySend(observer.value == 1)
+ awaitClose { observer.isListening = false }
+ }
+ .flowOn(backgroundDispatcher)
+ .distinctUntilChanged()
+ }
+ .stateIn(
+ scope,
+ started = SharingStarted.Eagerly,
+ // When the observer starts listening, the flow will emit the current value
+ // so the initialValue here is irrelevant.
+ initialValue = false,
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index 870649d41b4e..7b1c62326a68 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -34,7 +34,6 @@ import com.android.internal.annotations.VisibleForTesting
import com.android.internal.notification.NotificationAccessConfirmationActivityContract.EXTRA_USER_ID
import com.android.systemui.Dumpable
import com.android.systemui.backup.BackupHelper
-import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.controls.ControlStatus
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.management.ControlsListingController
@@ -62,11 +61,10 @@ class ControlsControllerImpl @Inject constructor (
private val uiController: ControlsUiController,
private val bindingController: ControlsBindingController,
private val listingController: ControlsListingController,
- private val broadcastDispatcher: BroadcastDispatcher,
private val userFileManager: UserFileManager,
+ private val userTracker: UserTracker,
optionalWrapper: Optional<ControlsFavoritePersistenceWrapper>,
dumpManager: DumpManager,
- userTracker: UserTracker
) : Dumpable, ControlsController {
companion object {
@@ -123,18 +121,15 @@ class ControlsControllerImpl @Inject constructor (
userChanging = false
}
- private val userSwitchReceiver = object : BroadcastReceiver() {
- override fun onReceive(context: Context, intent: Intent) {
- if (intent.action == Intent.ACTION_USER_SWITCHED) {
- userChanging = true
- val newUser =
- UserHandle.of(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, sendingUserId))
- if (currentUser == newUser) {
- userChanging = false
- return
- }
- setValuesForUser(newUser)
+ private val userTrackerCallback = object : UserTracker.Callback {
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ userChanging = true
+ val newUserHandle = UserHandle.of(newUser)
+ if (currentUser == newUserHandle) {
+ userChanging = false
+ return
}
+ setValuesForUser(newUserHandle)
}
}
@@ -236,12 +231,7 @@ class ControlsControllerImpl @Inject constructor (
dumpManager.registerDumpable(javaClass.name, this)
resetFavorites()
userChanging = false
- broadcastDispatcher.registerReceiver(
- userSwitchReceiver,
- IntentFilter(Intent.ACTION_USER_SWITCHED),
- executor,
- UserHandle.ALL
- )
+ userTracker.addCallback(userTrackerCallback, executor)
context.registerReceiver(
restoreFinishedReceiver,
IntentFilter(BackupHelper.ACTION_RESTORE_FINISHED),
@@ -253,7 +243,7 @@ class ControlsControllerImpl @Inject constructor (
}
fun destroy() {
- broadcastDispatcher.unregisterReceiver(userSwitchReceiver)
+ userTracker.removeCallback(userTrackerCallback)
context.unregisterReceiver(restoreFinishedReceiver)
listingController.removeCallback(listingCallback)
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt
index 9e4a364562e5..77d0496e43db 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt
@@ -16,13 +16,10 @@
package com.android.systemui.controls.dagger
-import android.content.ContentResolver
import android.content.Context
-import android.database.ContentObserver
-import android.os.UserHandle
-import android.provider.Settings
import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
+import com.android.systemui.controls.ControlsSettingsRepository
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.controls.controller.ControlsTileResourceConfiguration
import com.android.systemui.controls.controller.ControlsTileResourceConfigurationImpl
@@ -31,12 +28,10 @@ import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.util.settings.SecureSettings
import dagger.Lazy
+import kotlinx.coroutines.flow.StateFlow
import java.util.Optional
import javax.inject.Inject
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.asStateFlow
/**
* Pseudo-component to inject into classes outside `com.android.systemui.controls`.
@@ -46,47 +41,26 @@ import kotlinx.coroutines.flow.asStateFlow
*/
@SysUISingleton
class ControlsComponent @Inject constructor(
- @ControlsFeatureEnabled private val featureEnabled: Boolean,
- private val context: Context,
- private val lazyControlsController: Lazy<ControlsController>,
- private val lazyControlsUiController: Lazy<ControlsUiController>,
- private val lazyControlsListingController: Lazy<ControlsListingController>,
- private val lockPatternUtils: LockPatternUtils,
- private val keyguardStateController: KeyguardStateController,
- private val userTracker: UserTracker,
- private val secureSettings: SecureSettings,
- private val optionalControlsTileResourceConfiguration:
- Optional<ControlsTileResourceConfiguration>
+ @ControlsFeatureEnabled private val featureEnabled: Boolean,
+ private val context: Context,
+ private val lazyControlsController: Lazy<ControlsController>,
+ private val lazyControlsUiController: Lazy<ControlsUiController>,
+ private val lazyControlsListingController: Lazy<ControlsListingController>,
+ private val lockPatternUtils: LockPatternUtils,
+ private val keyguardStateController: KeyguardStateController,
+ private val userTracker: UserTracker,
+ controlsSettingsRepository: ControlsSettingsRepository,
+ optionalControlsTileResourceConfiguration: Optional<ControlsTileResourceConfiguration>
) {
- private val contentResolver: ContentResolver
- get() = context.contentResolver
- private val _canShowWhileLockedSetting = MutableStateFlow(false)
- val canShowWhileLockedSetting = _canShowWhileLockedSetting.asStateFlow()
+ val canShowWhileLockedSetting: StateFlow<Boolean> =
+ controlsSettingsRepository.canShowControlsInLockscreen
private val controlsTileResourceConfiguration: ControlsTileResourceConfiguration =
optionalControlsTileResourceConfiguration.orElse(
ControlsTileResourceConfigurationImpl()
)
- val showWhileLockedObserver = object : ContentObserver(null) {
- override fun onChange(selfChange: Boolean) {
- updateShowWhileLocked()
- }
- }
-
- init {
- if (featureEnabled) {
- secureSettings.registerContentObserverForUser(
- Settings.Secure.getUriFor(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS),
- false, /* notifyForDescendants */
- showWhileLockedObserver,
- UserHandle.USER_ALL
- )
- updateShowWhileLocked()
- }
- }
-
fun getControlsController(): Optional<ControlsController> {
return if (featureEnabled) Optional.of(lazyControlsController.get()) else Optional.empty()
}
@@ -127,11 +101,6 @@ class ControlsComponent @Inject constructor(
return Visibility.AVAILABLE
}
- private fun updateShowWhileLocked() {
- _canShowWhileLockedSetting.value = secureSettings.getIntForUser(
- Settings.Secure.LOCKSCREEN_SHOW_CONTROLS, 0, UserHandle.USER_CURRENT) != 0
- }
-
enum class Visibility {
AVAILABLE, AVAILABLE_AFTER_UNLOCK, UNAVAILABLE
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
index 6f58abdeed56..9ae605e30a83 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
@@ -20,6 +20,8 @@ import android.app.Activity
import android.content.pm.PackageManager
import com.android.systemui.controls.ControlsMetricsLogger
import com.android.systemui.controls.ControlsMetricsLoggerImpl
+import com.android.systemui.controls.ControlsSettingsRepository
+import com.android.systemui.controls.ControlsSettingsRepositoryImpl
import com.android.systemui.controls.controller.ControlsBindingController
import com.android.systemui.controls.controller.ControlsBindingControllerImpl
import com.android.systemui.controls.controller.ControlsController
@@ -83,6 +85,11 @@ abstract class ControlsModule {
abstract fun provideUiController(controller: ControlsUiControllerImpl): ControlsUiController
@Binds
+ abstract fun provideSettingsManager(
+ manager: ControlsSettingsRepositoryImpl
+ ): ControlsSettingsRepository
+
+ @Binds
abstract fun provideMetricsLogger(logger: ControlsMetricsLoggerImpl): ControlsMetricsLogger
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
index 1f7021e514e5..041ed1d557d7 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
@@ -25,9 +25,6 @@ import android.app.PendingIntent
import android.content.Context
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
-import android.database.ContentObserver
-import android.net.Uri
-import android.os.Handler
import android.os.UserHandle
import android.os.VibrationEffect
import android.provider.Settings.Secure
@@ -41,6 +38,7 @@ import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.R
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.controls.ControlsMetricsLogger
+import com.android.systemui.controls.ControlsSettingsRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
@@ -69,17 +67,17 @@ class ControlActionCoordinatorImpl @Inject constructor(
private val vibrator: VibratorHelper,
private val secureSettings: SecureSettings,
private val userContextProvider: UserContextProvider,
- @Main mainHandler: Handler
+ private val controlsSettingsRepository: ControlsSettingsRepository,
) : ControlActionCoordinator {
private var dialog: Dialog? = null
private var pendingAction: Action? = null
private var actionsInProgress = mutableSetOf<String>()
private val isLocked: Boolean
get() = !keyguardStateController.isUnlocked()
- private var mAllowTrivialControls: Boolean = secureSettings.getIntForUser(
- Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS, 0, UserHandle.USER_CURRENT) != 0
- private var mShowDeviceControlsInLockscreen: Boolean = secureSettings.getIntForUser(
- Secure.LOCKSCREEN_SHOW_CONTROLS, 0, UserHandle.USER_CURRENT) != 0
+ private val allowTrivialControls: Boolean
+ get() = controlsSettingsRepository.allowActionOnTrivialControlsInLockscreen.value
+ private val showDeviceControlsInLockscreen: Boolean
+ get() = controlsSettingsRepository.canShowControlsInLockscreen.value
override lateinit var activityContext: Context
companion object {
@@ -87,38 +85,6 @@ class ControlActionCoordinatorImpl @Inject constructor(
private const val MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG = 2
}
- init {
- val lockScreenShowControlsUri =
- secureSettings.getUriFor(Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS)
- val showControlsUri =
- secureSettings.getUriFor(Secure.LOCKSCREEN_SHOW_CONTROLS)
- val controlsContentObserver = object : ContentObserver(mainHandler) {
- override fun onChange(selfChange: Boolean, uri: Uri?) {
- super.onChange(selfChange, uri)
- when (uri) {
- lockScreenShowControlsUri -> {
- mAllowTrivialControls = secureSettings.getIntForUser(
- Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS,
- 0, UserHandle.USER_CURRENT) != 0
- }
- showControlsUri -> {
- mShowDeviceControlsInLockscreen = secureSettings
- .getIntForUser(Secure.LOCKSCREEN_SHOW_CONTROLS,
- 0, UserHandle.USER_CURRENT) != 0
- }
- }
- }
- }
- secureSettings.registerContentObserverForUser(
- lockScreenShowControlsUri,
- false /* notifyForDescendants */, controlsContentObserver, UserHandle.USER_ALL
- )
- secureSettings.registerContentObserverForUser(
- showControlsUri,
- false /* notifyForDescendants */, controlsContentObserver, UserHandle.USER_ALL
- )
- }
-
override fun closeDialogs() {
val isActivityFinishing =
(activityContext as? Activity)?.let { it.isFinishing || it.isDestroyed }
@@ -233,7 +199,7 @@ class ControlActionCoordinatorImpl @Inject constructor(
@AnyThread
@VisibleForTesting
fun bouncerOrRun(action: Action) {
- val authRequired = action.authIsRequired || !mAllowTrivialControls
+ val authRequired = action.authIsRequired || !allowTrivialControls
if (keyguardStateController.isShowing() && authRequired) {
if (isLocked) {
@@ -291,7 +257,7 @@ class ControlActionCoordinatorImpl @Inject constructor(
PREFS_CONTROLS_FILE, Context.MODE_PRIVATE)
val attempts = prefs.getInt(PREFS_SETTINGS_DIALOG_ATTEMPTS, 0)
if (attempts >= MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG ||
- (mShowDeviceControlsInLockscreen && mAllowTrivialControls)) {
+ (showDeviceControlsInLockscreen && allowTrivialControls)) {
return
}
val builder = AlertDialog
@@ -313,7 +279,7 @@ class ControlActionCoordinatorImpl @Inject constructor(
true
}
- if (mShowDeviceControlsInLockscreen) {
+ if (showDeviceControlsInLockscreen) {
dialog = builder
.setTitle(R.string.controls_settings_trivial_controls_dialog_title)
.setMessage(R.string.controls_settings_trivial_controls_dialog_message)
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
index d60a22204b3d..3d8e4cb71aca 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
@@ -19,7 +19,6 @@ package com.android.systemui.dagger;
import android.content.BroadcastReceiver;
import com.android.systemui.GuestResetOrExitSessionReceiver;
-import com.android.systemui.GuestResumeSessionReceiver;
import com.android.systemui.media.dialog.MediaOutputDialogReceiver;
import com.android.systemui.people.widget.PeopleSpaceWidgetPinnedReceiver;
import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
@@ -106,15 +105,6 @@ public abstract class DefaultBroadcastReceiverBinder {
*/
@Binds
@IntoMap
- @ClassKey(GuestResumeSessionReceiver.class)
- public abstract BroadcastReceiver bindGuestResumeSessionReceiver(
- GuestResumeSessionReceiver broadcastReceiver);
-
- /**
- *
- */
- @Binds
- @IntoMap
@ClassKey(GuestResetOrExitSessionReceiver.class)
public abstract BroadcastReceiver bindGuestResetOrExitSessionReceiver(
GuestResetOrExitSessionReceiver broadcastReceiver);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index bcf5e7ae3ed6..3a59f4b5436c 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -33,6 +33,7 @@ import com.android.systemui.assist.AssistModule;
import com.android.systemui.biometrics.AlternateUdfpsTouchProvider;
import com.android.systemui.biometrics.UdfpsDisplayModeProvider;
import com.android.systemui.biometrics.dagger.BiometricsModule;
+import com.android.systemui.biometrics.dagger.UdfpsModule;
import com.android.systemui.classifier.FalsingModule;
import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayModule;
import com.android.systemui.controls.dagger.ControlsModule;
@@ -154,6 +155,7 @@ import dagger.Provides;
TelephonyRepositoryModule.class,
TemporaryDisplayModule.class,
TunerModule.class,
+ UdfpsModule.class,
UserModule.class,
UtilModule.class,
NoteTaskModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 0b69b80689e0..5daf1ceaf592 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -29,12 +29,13 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.display.AmbientDisplayConfiguration;
import android.os.SystemClock;
-import android.os.UserHandle;
import android.text.format.Formatter;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.view.Display;
+import androidx.annotation.NonNull;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.UiEvent;
@@ -100,6 +101,7 @@ public class DozeTriggers implements DozeMachine.Part {
private final BroadcastDispatcher mBroadcastDispatcher;
private final AuthController mAuthController;
private final KeyguardStateController mKeyguardStateController;
+ private final UserTracker mUserTracker;
private final UiEventLogger mUiEventLogger;
private long mNotificationPulseTime;
@@ -110,6 +112,14 @@ public class DozeTriggers implements DozeMachine.Part {
private boolean mWantTouchScreenSensors;
private boolean mWantSensors;
+ private final UserTracker.Callback mUserChangedCallback =
+ new UserTracker.Callback() {
+ @Override
+ public void onUserChanged(int newUser, @NonNull Context userContext) {
+ mDozeSensors.onUserSwitched();
+ }
+ };
+
@VisibleForTesting
public enum DozingUpdateUiEvent implements UiEventLogger.UiEventEnum {
@UiEvent(doc = "Dozing updated due to notification.")
@@ -210,6 +220,7 @@ public class DozeTriggers implements DozeMachine.Part {
mAuthController = authController;
mUiEventLogger = uiEventLogger;
mKeyguardStateController = keyguardStateController;
+ mUserTracker = userTracker;
}
@Override
@@ -234,7 +245,7 @@ public class DozeTriggers implements DozeMachine.Part {
return;
}
mNotificationPulseTime = SystemClock.elapsedRealtime();
- if (!mConfig.pulseOnNotificationEnabled(UserHandle.USER_CURRENT)) {
+ if (!mConfig.pulseOnNotificationEnabled(mUserTracker.getUserId())) {
runIfNotNull(onPulseSuppressedListener);
mDozeLog.tracePulseDropped("pulseOnNotificationsDisabled");
return;
@@ -490,12 +501,14 @@ public class DozeTriggers implements DozeMachine.Part {
mBroadcastReceiver.register(mBroadcastDispatcher);
mDockManager.addListener(mDockEventListener);
mDozeHost.addCallback(mHostCallback);
+ mUserTracker.addCallback(mUserChangedCallback, mContext.getMainExecutor());
}
private void unregisterCallbacks() {
mBroadcastReceiver.unregister(mBroadcastDispatcher);
mDozeHost.removeCallback(mHostCallback);
mDockManager.removeListener(mDockEventListener);
+ mUserTracker.removeCallback(mUserChangedCallback);
}
private void stopListeningToAllTriggers() {
@@ -620,9 +633,6 @@ public class DozeTriggers implements DozeMachine.Part {
requestPulse(DozeLog.PULSE_REASON_INTENT, false, /* performedProxCheck */
null /* onPulseSuppressedListener */);
}
- if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
- mDozeSensors.onUserSwitched();
- }
}
public void register(BroadcastDispatcher broadcastDispatcher) {
@@ -630,7 +640,6 @@ public class DozeTriggers implements DozeMachine.Part {
return;
}
IntentFilter filter = new IntentFilter(PULSE_ACTION);
- filter.addAction(Intent.ACTION_USER_SWITCHED);
broadcastDispatcher.registerReceiver(this, filter);
mRegistered = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
index 48159aed524e..46ce7a90f5a3 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
@@ -192,9 +192,7 @@ public class ComplicationLayoutEngine implements Complication.VisibilityControll
break;
}
- // Add margin if specified by the complication. Otherwise add default margin
- // between complications.
- if (mLayoutParams.isMarginSpecified() || !isRoot) {
+ if (!isRoot) {
final int margin = mLayoutParams.getMargin(mDefaultMargin);
switch(direction) {
case ComplicationLayoutParams.DIRECTION_DOWN:
@@ -213,6 +211,19 @@ public class ComplicationLayoutEngine implements Complication.VisibilityControll
}
});
+ if (mLayoutParams.constraintSpecified()) {
+ switch (direction) {
+ case ComplicationLayoutParams.DIRECTION_START:
+ case ComplicationLayoutParams.DIRECTION_END:
+ params.matchConstraintMaxWidth = mLayoutParams.getConstraint();
+ break;
+ case ComplicationLayoutParams.DIRECTION_UP:
+ case ComplicationLayoutParams.DIRECTION_DOWN:
+ params.matchConstraintMaxHeight = mLayoutParams.getConstraint();
+ break;
+ }
+ }
+
mView.setLayoutParams(params);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java
index 4fae68d57ee8..1755cb92da70 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java
@@ -52,6 +52,7 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
private static final int LAST_POSITION = POSITION_END;
private static final int MARGIN_UNSPECIFIED = 0xFFFFFFFF;
+ private static final int CONSTRAINT_UNSPECIFIED = 0xFFFFFFFF;
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, prefix = { "DIRECTION_" }, value = {
@@ -81,6 +82,8 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
private final int mMargin;
+ private final int mConstraint;
+
private final boolean mSnapToGuide;
// Do not allow specifying opposite positions
@@ -110,7 +113,8 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
*/
public ComplicationLayoutParams(int width, int height, @Position int position,
@Direction int direction, int weight) {
- this(width, height, position, direction, weight, MARGIN_UNSPECIFIED, false);
+ this(width, height, position, direction, weight, MARGIN_UNSPECIFIED, CONSTRAINT_UNSPECIFIED,
+ false);
}
/**
@@ -127,7 +131,27 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
*/
public ComplicationLayoutParams(int width, int height, @Position int position,
@Direction int direction, int weight, int margin) {
- this(width, height, position, direction, weight, margin, false);
+ this(width, height, position, direction, weight, margin, CONSTRAINT_UNSPECIFIED, false);
+ }
+
+ /**
+ * Constructs a {@link ComplicationLayoutParams}.
+ * @param width The width {@link android.view.View.MeasureSpec} for the view.
+ * @param height The height {@link android.view.View.MeasureSpec} for the view.
+ * @param position The place within the parent container where the view should be positioned.
+ * @param direction The direction the view should be laid out from either the parent container
+ * or preceding view.
+ * @param weight The weight that should be considered for this view when compared to other
+ * views. This has an impact on the placement of the view but not the rendering of
+ * the view.
+ * @param margin The margin to apply between complications.
+ * @param constraint The max width or height the complication is allowed to spread, depending on
+ * its direction. For horizontal directions, this would be applied on width,
+ * and for vertical directions, height.
+ */
+ public ComplicationLayoutParams(int width, int height, @Position int position,
+ @Direction int direction, int weight, int margin, int constraint) {
+ this(width, height, position, direction, weight, margin, constraint, false);
}
/**
@@ -148,7 +172,8 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
*/
public ComplicationLayoutParams(int width, int height, @Position int position,
@Direction int direction, int weight, boolean snapToGuide) {
- this(width, height, position, direction, weight, MARGIN_UNSPECIFIED, snapToGuide);
+ this(width, height, position, direction, weight, MARGIN_UNSPECIFIED, CONSTRAINT_UNSPECIFIED,
+ snapToGuide);
}
/**
@@ -162,6 +187,9 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
* views. This has an impact on the placement of the view but not the rendering of
* the view.
* @param margin The margin to apply between complications.
+ * @param constraint The max width or height the complication is allowed to spread, depending on
+ * its direction. For horizontal directions, this would be applied on width,
+ * and for vertical directions, height.
* @param snapToGuide When set to {@code true}, the dimension perpendicular to the direction
* will be automatically set to align with a predetermined guide for that
* side. For example, if the complication is aligned to the top end and
@@ -169,7 +197,7 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
* from the end of the parent to the guide.
*/
public ComplicationLayoutParams(int width, int height, @Position int position,
- @Direction int direction, int weight, int margin, boolean snapToGuide) {
+ @Direction int direction, int weight, int margin, int constraint, boolean snapToGuide) {
super(width, height);
if (!validatePosition(position)) {
@@ -187,6 +215,8 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
mMargin = margin;
+ mConstraint = constraint;
+
mSnapToGuide = snapToGuide;
}
@@ -199,6 +229,7 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
mDirection = source.mDirection;
mWeight = source.mWeight;
mMargin = source.mMargin;
+ mConstraint = source.mConstraint;
mSnapToGuide = source.mSnapToGuide;
}
@@ -261,13 +292,6 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
}
/**
- * Returns whether margin has been specified by the complication.
- */
- public boolean isMarginSpecified() {
- return mMargin != MARGIN_UNSPECIFIED;
- }
-
- /**
* Returns the margin to apply between complications, or the given default if no margin is
* specified.
*/
@@ -276,6 +300,21 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams {
}
/**
+ * Returns whether the horizontal or vertical constraint has been specified.
+ */
+ public boolean constraintSpecified() {
+ return mConstraint != CONSTRAINT_UNSPECIFIED;
+ }
+
+ /**
+ * Returns the horizontal or vertical constraint of the complication, depending its direction.
+ * For horizontal directions, this is the max width, and for vertical directions, max height.
+ */
+ public int getConstraint() {
+ return mConstraint;
+ }
+
+ /**
* Returns whether the complication's dimension perpendicular to direction should be
* automatically set.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java
index c01cf43eae74..ee0051220787 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java
@@ -32,6 +32,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.CoreStartable;
+import com.android.systemui.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.controls.ControlsServiceInfo;
import com.android.systemui.controls.dagger.ControlsComponent;
@@ -151,7 +152,7 @@ public class DreamHomeControlsComplication implements Complication {
@Inject
DreamHomeControlsChipViewHolder(
DreamHomeControlsChipViewController dreamHomeControlsChipViewController,
- @Named(DREAM_HOME_CONTROLS_CHIP_VIEW) ImageView view,
+ @Named(DREAM_HOME_CONTROLS_CHIP_VIEW) View view,
@Named(DREAM_HOME_CONTROLS_CHIP_LAYOUT_PARAMS) ComplicationLayoutParams layoutParams
) {
mView = view;
@@ -174,7 +175,7 @@ public class DreamHomeControlsComplication implements Complication {
/**
* Controls behavior of the dream complication.
*/
- static class DreamHomeControlsChipViewController extends ViewController<ImageView> {
+ static class DreamHomeControlsChipViewController extends ViewController<View> {
private static final String TAG = "DreamHomeControlsCtrl";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -203,7 +204,7 @@ public class DreamHomeControlsComplication implements Complication {
@Inject
DreamHomeControlsChipViewController(
- @Named(DREAM_HOME_CONTROLS_CHIP_VIEW) ImageView view,
+ @Named(DREAM_HOME_CONTROLS_CHIP_VIEW) View view,
ActivityStarter activityStarter,
Context context,
ControlsComponent controlsComponent,
@@ -218,9 +219,10 @@ public class DreamHomeControlsComplication implements Complication {
@Override
protected void onViewAttached() {
- mView.setImageResource(mControlsComponent.getTileImageId());
- mView.setContentDescription(mContext.getString(mControlsComponent.getTileTitleId()));
- mView.setOnClickListener(this::onClickHomeControls);
+ final ImageView chip = mView.findViewById(R.id.home_controls_chip);
+ chip.setImageResource(mControlsComponent.getTileImageId());
+ chip.setContentDescription(mContext.getString(mControlsComponent.getTileTitleId()));
+ chip.setOnClickListener(this::onClickHomeControls);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationModule.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationModule.java
index 7d9f1059f3b8..5290e44aa7fb 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationModule.java
@@ -45,11 +45,12 @@ public interface DreamClockTimeComplicationModule {
@Provides
@Named(DREAM_CLOCK_TIME_COMPLICATION_VIEW)
static View provideComplicationView(LayoutInflater layoutInflater) {
- final TextClock view = Preconditions.checkNotNull((TextClock)
+ final View view = Preconditions.checkNotNull(
layoutInflater.inflate(R.layout.dream_overlay_complication_clock_time,
null, false),
"R.layout.dream_overlay_complication_clock_time did not properly inflated");
- view.setFontVariationSettings(TAG_WEIGHT + WEIGHT);
+ ((TextClock) view.findViewById(R.id.time_view)).setFontVariationSettings(
+ TAG_WEIGHT + WEIGHT);
return view;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamHomeControlsComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamHomeControlsComplicationComponent.java
index cf05d2d9cda0..a7aa97f74e31 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamHomeControlsComplicationComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamHomeControlsComplicationComponent.java
@@ -19,7 +19,7 @@ package com.android.systemui.dreams.complication.dagger;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import android.view.LayoutInflater;
-import android.widget.ImageView;
+import android.view.View;
import com.android.systemui.R;
import com.android.systemui.dreams.complication.DreamHomeControlsComplication;
@@ -74,8 +74,8 @@ public interface DreamHomeControlsComplicationComponent {
@Provides
@DreamHomeControlsComplicationScope
@Named(DREAM_HOME_CONTROLS_CHIP_VIEW)
- static ImageView provideHomeControlsChipView(LayoutInflater layoutInflater) {
- return (ImageView) layoutInflater.inflate(R.layout.dream_overlay_home_controls_chip,
+ static View provideHomeControlsChipView(LayoutInflater layoutInflater) {
+ return layoutInflater.inflate(R.layout.dream_overlay_home_controls_chip,
null, false);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
index a514c47f1fc1..9b954f5f0c4a 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
@@ -47,10 +47,10 @@ public interface RegisteredComplicationsModule {
String DREAM_MEDIA_ENTRY_LAYOUT_PARAMS = "media_entry_layout_params";
int DREAM_CLOCK_TIME_COMPLICATION_WEIGHT = 1;
- int DREAM_SMARTSPACE_COMPLICATION_WEIGHT = 0;
+ int DREAM_SMARTSPACE_COMPLICATION_WEIGHT = 2;
int DREAM_MEDIA_COMPLICATION_WEIGHT = 0;
- int DREAM_HOME_CONTROLS_CHIP_COMPLICATION_WEIGHT = 2;
- int DREAM_MEDIA_ENTRY_COMPLICATION_WEIGHT = 1;
+ int DREAM_HOME_CONTROLS_CHIP_COMPLICATION_WEIGHT = 4;
+ int DREAM_MEDIA_ENTRY_COMPLICATION_WEIGHT = 3;
/**
* Provides layout parameters for the clock time complication.
@@ -72,17 +72,14 @@ public interface RegisteredComplicationsModule {
*/
@Provides
@Named(DREAM_HOME_CONTROLS_CHIP_LAYOUT_PARAMS)
- static ComplicationLayoutParams provideHomeControlsChipLayoutParams(@Main Resources res) {
+ static ComplicationLayoutParams provideHomeControlsChipLayoutParams() {
return new ComplicationLayoutParams(
- res.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width),
- res.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height),
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
ComplicationLayoutParams.POSITION_BOTTOM
| ComplicationLayoutParams.POSITION_START,
- ComplicationLayoutParams.DIRECTION_UP,
- DREAM_HOME_CONTROLS_CHIP_COMPLICATION_WEIGHT,
- // Add margin to the bottom of home controls to horizontally align with smartspace.
- res.getDimensionPixelSize(
- R.dimen.dream_overlay_complication_home_controls_padding));
+ ComplicationLayoutParams.DIRECTION_END,
+ DREAM_HOME_CONTROLS_CHIP_COMPLICATION_WEIGHT);
}
/**
@@ -106,12 +103,14 @@ public interface RegisteredComplicationsModule {
@Provides
@Named(DREAM_SMARTSPACE_LAYOUT_PARAMS)
static ComplicationLayoutParams provideSmartspaceLayoutParams(@Main Resources res) {
- return new ComplicationLayoutParams(0,
+ return new ComplicationLayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
ComplicationLayoutParams.POSITION_BOTTOM
| ComplicationLayoutParams.POSITION_START,
ComplicationLayoutParams.DIRECTION_END,
DREAM_SMARTSPACE_COMPLICATION_WEIGHT,
- res.getDimensionPixelSize(R.dimen.dream_overlay_complication_smartspace_padding));
+ res.getDimensionPixelSize(R.dimen.dream_overlay_complication_smartspace_padding),
+ res.getDimensionPixelSize(R.dimen.dream_overlay_complication_smartspace_max_width));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 2d558ad49e2d..8019b569068a 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -164,6 +164,13 @@ object Flags {
// TODO(b/256513609): Tracking Bug
@JvmField val ACTIVE_UNLOCK_CHIPBAR = releasedFlag(217, "active_unlock_chipbar")
+ /**
+ * Migrates control of the LightRevealScrim's reveal effect and amount from legacy code to the
+ * new KeyguardTransitionRepository.
+ */
+ @JvmField
+ val LIGHT_REVEAL_MIGRATION = unreleasedFlag(218, "light_reveal_migration", teamfood = true)
+
// 300 - power menu
// TODO(b/254512600): Tracking Bug
@JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
@@ -205,15 +212,6 @@ object Flags {
@JvmField val QS_SECONDARY_DATA_SUB_INFO = releasedFlag(508, "qs_secondary_data_sub_info")
// 600- status bar
- // TODO(b/254512623): Tracking Bug
- @Deprecated("Replaced by mobile and wifi specific flags.")
- val NEW_STATUS_BAR_PIPELINE_BACKEND =
- unreleasedFlag(604, "new_status_bar_pipeline_backend", teamfood = false)
-
- // TODO(b/254512660): Tracking Bug
- @Deprecated("Replaced by mobile and wifi specific flags.")
- val NEW_STATUS_BAR_PIPELINE_FRONTEND =
- unreleasedFlag(605, "new_status_bar_pipeline_frontend", teamfood = false)
// TODO(b/256614753): Tracking Bug
val NEW_STATUS_BAR_MOBILE_ICONS = unreleasedFlag(606, "new_status_bar_mobile_icons")
@@ -231,6 +229,10 @@ object Flags {
// TODO(b/256623670): Tracking Bug
@JvmField val BATTERY_SHIELD_ICON = unreleasedFlag(610, "battery_shield_icon")
+ // TODO(b/260881289): Tracking Bug
+ val NEW_STATUS_BAR_ICONS_DEBUG_COLORING =
+ unreleasedFlag(611, "new_status_bar_icons_debug_coloring")
+
// 700 - dialer/calls
// TODO(b/254512734): Tracking Bug
val ONGOING_CALL_STATUS_BAR_CHIP = releasedFlag(700, "ongoing_call_status_bar_chip")
@@ -375,7 +377,7 @@ object Flags {
// TODO(b/255854141): Tracking Bug
@JvmField
val WM_ENABLE_PREDICTIVE_BACK_SYSUI =
- unreleasedFlag(1204, "persist.wm.debug.predictive_back_sysui_enable", teamfood = false)
+ unreleasedFlag(1204, "persist.wm.debug.predictive_back_sysui_enable", teamfood = true)
// 1300 - screenshots
// TODO(b/254512719): Tracking Bug
@@ -401,17 +403,15 @@ object Flags {
// 1700 - clipboard
@JvmField val CLIPBOARD_OVERLAY_REFACTOR = releasedFlag(1700, "clipboard_overlay_refactor")
- @JvmField
- val CLIPBOARD_REMOTE_BEHAVIOR =
- unreleasedFlag(1701, "clipboard_remote_behavior", teamfood = true)
+ @JvmField val CLIPBOARD_REMOTE_BEHAVIOR = releasedFlag(1701, "clipboard_remote_behavior")
// 1800 - shade container
@JvmField
val LEAVE_SHADE_OPEN_FOR_BUGREPORT =
unreleasedFlag(1800, "leave_shade_open_for_bugreport", teamfood = true)
- // 1900 - note task
- @JvmField val NOTE_TASKS = sysPropBooleanFlag(1900, "persist.sysui.debug.note_tasks")
+ // 1900
+ @JvmField val NOTE_TASKS = unreleasedFlag(1900, "keycode_flag")
// 2000 - device controls
@Keep @JvmField val USE_APP_PANELS = unreleasedFlag(2000, "use_app_panels", teamfood = true)
@@ -428,6 +428,12 @@ object Flags {
// 2300 - stylus
@JvmField val TRACK_STYLUS_EVER_USED = unreleasedFlag(2300, "track_stylus_ever_used")
+ // 2400 - performance tools and debugging info
+ // TODO(b/238923086): Tracking Bug
+ @JvmField
+ val WARN_ON_BLOCKING_BINDER_TRANSACTIONS =
+ unreleasedFlag(2400, "warn_on_blocking_binder_transactions")
+
// TODO(b259590361): Tracking bug
val EXPERIMENTAL_FLAG = unreleasedFlag(2, "exp_flag_release")
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt
index 49527d32d229..62fe80a82908 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt
@@ -21,50 +21,52 @@ import android.content.Context
import com.android.systemui.R
import com.android.systemui.animation.Expandable
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
import com.android.systemui.statusbar.policy.FlashlightController
+import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
-import javax.inject.Inject
@SysUISingleton
-class FlashlightQuickAffordanceConfig @Inject constructor(
- @Application private val context: Context,
- private val flashlightController: FlashlightController,
+class FlashlightQuickAffordanceConfig
+@Inject
+constructor(
+ @Application private val context: Context,
+ private val flashlightController: FlashlightController,
) : KeyguardQuickAffordanceConfig {
private sealed class FlashlightState {
abstract fun toLockScreenState(): KeyguardQuickAffordanceConfig.LockScreenState
- object On: FlashlightState() {
+ object On : FlashlightState() {
override fun toLockScreenState(): KeyguardQuickAffordanceConfig.LockScreenState =
KeyguardQuickAffordanceConfig.LockScreenState.Visible(
Icon.Resource(
- R.drawable.ic_flashlight_on,
+ R.drawable.qs_flashlight_icon_on,
ContentDescription.Resource(R.string.quick_settings_flashlight_label)
),
ActivationState.Active
)
}
- object OffAvailable: FlashlightState() {
+ object OffAvailable : FlashlightState() {
override fun toLockScreenState(): KeyguardQuickAffordanceConfig.LockScreenState =
KeyguardQuickAffordanceConfig.LockScreenState.Visible(
Icon.Resource(
- R.drawable.ic_flashlight_off,
+ R.drawable.qs_flashlight_icon_off,
ContentDescription.Resource(R.string.quick_settings_flashlight_label)
),
ActivationState.Inactive
)
}
- object Unavailable: FlashlightState() {
+ object Unavailable : FlashlightState() {
override fun toLockScreenState(): KeyguardQuickAffordanceConfig.LockScreenState =
KeyguardQuickAffordanceConfig.LockScreenState.Hidden
}
@@ -77,57 +79,57 @@ class FlashlightQuickAffordanceConfig @Inject constructor(
get() = context.getString(R.string.quick_settings_flashlight_label)
override val pickerIconResourceId: Int
- get() = if (flashlightController.isEnabled) {
- R.drawable.ic_flashlight_on
- } else {
- R.drawable.ic_flashlight_off
- }
+ get() = R.drawable.ic_flashlight_off
override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> =
- conflatedCallbackFlow {
- val flashlightCallback = object : FlashlightController.FlashlightListener {
- override fun onFlashlightChanged(enabled: Boolean) {
- trySendWithFailureLogging(
- if (enabled) {
- FlashlightState.On.toLockScreenState()
- } else {
- FlashlightState.OffAvailable.toLockScreenState()
- },
- TAG
- )
- }
-
- override fun onFlashlightError() {
- trySendWithFailureLogging(FlashlightState.OffAvailable.toLockScreenState(), TAG)
- }
-
- override fun onFlashlightAvailabilityChanged(available: Boolean) {
- trySendWithFailureLogging(
- if (!available) {
- FlashlightState.Unavailable.toLockScreenState()
- } else {
- if (flashlightController.isEnabled) {
- FlashlightState.On.toLockScreenState()
- } else {
- FlashlightState.OffAvailable.toLockScreenState()
- }
- },
- TAG
- )
- }
+ conflatedCallbackFlow {
+ val flashlightCallback =
+ object : FlashlightController.FlashlightListener {
+ override fun onFlashlightChanged(enabled: Boolean) {
+ trySendWithFailureLogging(
+ if (enabled) {
+ FlashlightState.On.toLockScreenState()
+ } else {
+ FlashlightState.OffAvailable.toLockScreenState()
+ },
+ TAG
+ )
+ }
+
+ override fun onFlashlightError() {
+ trySendWithFailureLogging(
+ FlashlightState.OffAvailable.toLockScreenState(),
+ TAG
+ )
+ }
+
+ override fun onFlashlightAvailabilityChanged(available: Boolean) {
+ trySendWithFailureLogging(
+ if (!available) {
+ FlashlightState.Unavailable.toLockScreenState()
+ } else {
+ if (flashlightController.isEnabled) {
+ FlashlightState.On.toLockScreenState()
+ } else {
+ FlashlightState.OffAvailable.toLockScreenState()
+ }
+ },
+ TAG
+ )
+ }
+ }
+
+ flashlightController.addCallback(flashlightCallback)
+
+ awaitClose { flashlightController.removeCallback(flashlightCallback) }
}
- flashlightController.addCallback(flashlightCallback)
-
- awaitClose {
- flashlightController.removeCallback(flashlightCallback)
- }
- }
-
- override fun onTriggered(expandable: Expandable?):
- KeyguardQuickAffordanceConfig.OnTriggeredResult {
- flashlightController
- .setFlashlight(flashlightController.isAvailable && !flashlightController.isEnabled)
+ override fun onTriggered(
+ expandable: Expandable?
+ ): KeyguardQuickAffordanceConfig.OnTriggeredResult {
+ flashlightController.setFlashlight(
+ flashlightController.isAvailable && !flashlightController.isEnabled
+ )
return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
}
@@ -141,4 +143,4 @@ class FlashlightQuickAffordanceConfig @Inject constructor(
companion object {
private const val TAG = "FlashlightQuickAffordanceConfig"
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
index 3013227c21c0..072cfb140e62 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
@@ -17,27 +17,35 @@
package com.android.systemui.keyguard.data.quickaffordance
+import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.multibindings.ElementsIntoSet
@Module
-object KeyguardDataQuickAffordanceModule {
- @Provides
- @ElementsIntoSet
- fun quickAffordanceConfigs(
- flashlight: FlashlightQuickAffordanceConfig,
- home: HomeControlsKeyguardQuickAffordanceConfig,
- quickAccessWallet: QuickAccessWalletKeyguardQuickAffordanceConfig,
- qrCodeScanner: QrCodeScannerKeyguardQuickAffordanceConfig,
- camera: CameraQuickAffordanceConfig,
- ): Set<KeyguardQuickAffordanceConfig> {
- return setOf(
- camera,
- flashlight,
- home,
- quickAccessWallet,
- qrCodeScanner,
- )
+interface KeyguardDataQuickAffordanceModule {
+ @Binds
+ fun providerClientFactory(
+ impl: KeyguardQuickAffordanceProviderClientFactoryImpl,
+ ): KeyguardQuickAffordanceProviderClientFactory
+
+ companion object {
+ @Provides
+ @ElementsIntoSet
+ fun quickAffordanceConfigs(
+ flashlight: FlashlightQuickAffordanceConfig,
+ home: HomeControlsKeyguardQuickAffordanceConfig,
+ quickAccessWallet: QuickAccessWalletKeyguardQuickAffordanceConfig,
+ qrCodeScanner: QrCodeScannerKeyguardQuickAffordanceConfig,
+ camera: CameraQuickAffordanceConfig,
+ ): Set<KeyguardQuickAffordanceConfig> {
+ return setOf(
+ camera,
+ flashlight,
+ home,
+ quickAccessWallet,
+ qrCodeScanner,
+ )
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncer.kt
index 766096f1fa2b..72747f68bbbd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncer.kt
@@ -67,7 +67,7 @@ constructor(
@Application private val scope: CoroutineScope,
@Background private val backgroundDispatcher: CoroutineDispatcher,
private val secureSettings: SecureSettings,
- private val selectionsManager: KeyguardQuickAffordanceSelectionManager,
+ private val selectionsManager: KeyguardQuickAffordanceLocalUserSelectionManager,
) {
companion object {
private val BINDINGS =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt
new file mode 100644
index 000000000000..006678546de8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.quickaffordance
+
+import android.content.Context
+import android.content.IntentFilter
+import android.content.SharedPreferences
+import com.android.systemui.R
+import com.android.systemui.backup.BackupHelper
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.settings.UserFileManager
+import com.android.systemui.settings.UserTracker
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.onStart
+
+/**
+ * Manages and provides access to the current "selections" of keyguard quick affordances, answering
+ * the question "which affordances should the keyguard show?" for the user associated with the
+ * System UI process.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class KeyguardQuickAffordanceLocalUserSelectionManager
+@Inject
+constructor(
+ @Application context: Context,
+ private val userFileManager: UserFileManager,
+ private val userTracker: UserTracker,
+ broadcastDispatcher: BroadcastDispatcher,
+) : KeyguardQuickAffordanceSelectionManager {
+
+ private var sharedPrefs: SharedPreferences = instantiateSharedPrefs()
+
+ private val userId: Flow<Int> = conflatedCallbackFlow {
+ val callback =
+ object : UserTracker.Callback {
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ trySendWithFailureLogging(newUser, TAG)
+ }
+ }
+
+ userTracker.addCallback(callback) { it.run() }
+ trySendWithFailureLogging(userTracker.userId, TAG)
+
+ awaitClose { userTracker.removeCallback(callback) }
+ }
+
+ private val defaults: Map<String, List<String>> by lazy {
+ context.resources
+ .getStringArray(R.array.config_keyguardQuickAffordanceDefaults)
+ .associate { item ->
+ val splitUp = item.split(SLOT_AFFORDANCES_DELIMITER)
+ check(splitUp.size == 2)
+ val slotId = splitUp[0]
+ val affordanceIds = splitUp[1].split(AFFORDANCE_DELIMITER)
+ slotId to affordanceIds
+ }
+ }
+
+ /**
+ * Emits an event each time a Backup & Restore restoration job is completed. Does not emit an
+ * initial value.
+ */
+ private val backupRestorationEvents: Flow<Unit> =
+ broadcastDispatcher.broadcastFlow(
+ filter = IntentFilter(BackupHelper.ACTION_RESTORE_FINISHED),
+ flags = Context.RECEIVER_NOT_EXPORTED,
+ permission = BackupHelper.PERMISSION_SELF,
+ )
+
+ override val selections: Flow<Map<String, List<String>>> =
+ combine(
+ userId,
+ backupRestorationEvents.onStart {
+ // We emit an initial event to make sure that the combine emits at least once,
+ // even if we never get a Backup & Restore restoration event (which is the most
+ // common case anyway as restoration really only happens on initial device
+ // setup).
+ emit(Unit)
+ }
+ ) { _, _ -> }
+ .flatMapLatest {
+ conflatedCallbackFlow {
+ // We want to instantiate a new SharedPreferences instance each time either the
+ // user ID changes or we have a backup & restore restoration event. The reason
+ // is that our sharedPrefs instance needs to be replaced with a new one as it
+ // depends on the user ID and when the B&R job completes, the backing file is
+ // replaced but the existing instance still has a stale in-memory cache.
+ sharedPrefs = instantiateSharedPrefs()
+
+ val listener =
+ SharedPreferences.OnSharedPreferenceChangeListener { _, _ ->
+ trySend(getSelections())
+ }
+
+ sharedPrefs.registerOnSharedPreferenceChangeListener(listener)
+ send(getSelections())
+
+ awaitClose { sharedPrefs.unregisterOnSharedPreferenceChangeListener(listener) }
+ }
+ }
+
+ override fun getSelections(): Map<String, List<String>> {
+ val slotKeys = sharedPrefs.all.keys.filter { it.startsWith(KEY_PREFIX_SLOT) }
+ val result =
+ slotKeys
+ .associate { key ->
+ val slotId = key.substring(KEY_PREFIX_SLOT.length)
+ val value = sharedPrefs.getString(key, null)
+ val affordanceIds =
+ if (!value.isNullOrEmpty()) {
+ value.split(AFFORDANCE_DELIMITER)
+ } else {
+ emptyList()
+ }
+ slotId to affordanceIds
+ }
+ .toMutableMap()
+
+ // If the result map is missing keys, it means that the system has never set anything for
+ // those slots. This is where we need examine our defaults and see if there should be a
+ // default value for the affordances in the slot IDs that are missing from the result.
+ //
+ // Once the user makes any selection for a slot, even when they select "None", this class
+ // will persist a key for that slot ID. In the case of "None", it will have a value of the
+ // empty string. This is why this system works.
+ defaults.forEach { (slotId, affordanceIds) ->
+ if (!result.containsKey(slotId)) {
+ result[slotId] = affordanceIds
+ }
+ }
+
+ return result
+ }
+
+ override fun setSelections(
+ slotId: String,
+ affordanceIds: List<String>,
+ ) {
+ val key = "$KEY_PREFIX_SLOT$slotId"
+ val value = affordanceIds.joinToString(AFFORDANCE_DELIMITER)
+ sharedPrefs.edit().putString(key, value).apply()
+ }
+
+ private fun instantiateSharedPrefs(): SharedPreferences {
+ return userFileManager.getSharedPreferences(
+ FILE_NAME,
+ Context.MODE_PRIVATE,
+ userTracker.userId,
+ )
+ }
+
+ companion object {
+ private const val TAG = "KeyguardQuickAffordancePrimaryUserSelectionManager"
+ const val FILE_NAME = "quick_affordance_selections"
+ private const val KEY_PREFIX_SLOT = "slot_"
+ private const val SLOT_AFFORDANCES_DELIMITER = ":"
+ private const val AFFORDANCE_DELIMITER = ","
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceProviderClientFactory.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceProviderClientFactory.kt
new file mode 100644
index 000000000000..727a81391dc2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceProviderClientFactory.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.quickaffordance
+
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClient
+import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClientImpl
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+
+interface KeyguardQuickAffordanceProviderClientFactory {
+ fun create(): KeyguardQuickAffordanceProviderClient
+}
+
+class KeyguardQuickAffordanceProviderClientFactoryImpl
+@Inject
+constructor(
+ private val userTracker: UserTracker,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+) : KeyguardQuickAffordanceProviderClientFactory {
+ override fun create(): KeyguardQuickAffordanceProviderClient {
+ return KeyguardQuickAffordanceProviderClientImpl(
+ context = userTracker.userContext,
+ backgroundDispatcher = backgroundDispatcher,
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt
new file mode 100644
index 000000000000..8ffef25d3aae
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.quickaffordance
+
+import android.content.Context
+import android.os.UserHandle
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClient
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+
+/**
+ * Manages and provides access to the current "selections" of keyguard quick affordances, answering
+ * the question "which affordances should the keyguard show?" for users associated with other System
+ * UI processes.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class KeyguardQuickAffordanceRemoteUserSelectionManager
+@Inject
+constructor(
+ @Application private val scope: CoroutineScope,
+ private val userTracker: UserTracker,
+ private val clientFactory: KeyguardQuickAffordanceProviderClientFactory,
+ private val userHandle: UserHandle,
+) : KeyguardQuickAffordanceSelectionManager {
+
+ private val userId: Flow<Int> = conflatedCallbackFlow {
+ val callback =
+ object : UserTracker.Callback {
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ trySendWithFailureLogging(newUser, TAG)
+ }
+ }
+
+ userTracker.addCallback(callback) { it.run() }
+ trySendWithFailureLogging(userTracker.userId, TAG)
+
+ awaitClose { userTracker.removeCallback(callback) }
+ }
+
+ private val clientOrNull: StateFlow<KeyguardQuickAffordanceProviderClient?> =
+ userId
+ .distinctUntilChanged()
+ .map { selectedUserId ->
+ if (userHandle.isSystem && userHandle.identifier != selectedUserId) {
+ clientFactory.create()
+ } else {
+ null
+ }
+ }
+ .stateIn(
+ scope = scope,
+ started = SharingStarted.Eagerly,
+ initialValue = null,
+ )
+
+ private val _selections: StateFlow<Map<String, List<String>>> =
+ clientOrNull
+ .flatMapLatest { client ->
+ client?.observeSelections()?.map { selections ->
+ buildMap<String, List<String>> {
+ selections.forEach { selection ->
+ val slotId = selection.slotId
+ val affordanceIds = (get(slotId) ?: emptyList()).toMutableList()
+ affordanceIds.add(selection.affordanceId)
+ put(slotId, affordanceIds)
+ }
+ }
+ }
+ ?: emptyFlow()
+ }
+ .stateIn(
+ scope = scope,
+ started = SharingStarted.Eagerly,
+ initialValue = emptyMap(),
+ )
+
+ override val selections: Flow<Map<String, List<String>>> = _selections
+
+ override fun getSelections(): Map<String, List<String>> {
+ return _selections.value
+ }
+
+ override fun setSelections(slotId: String, affordanceIds: List<String>) {
+ clientOrNull.value?.let { client ->
+ scope.launch {
+ client.deleteAllSelections(slotId = slotId)
+ affordanceIds.forEach { affordanceId ->
+ client.insertSelection(slotId = slotId, affordanceId = affordanceId)
+ }
+ }
+ }
+ }
+
+ companion object {
+ private const val TAG = "KeyguardQuickAffordanceMultiUserSelectionManager"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManager.kt
index 4f37e5f389ee..21fffede5f97 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManager.kt
@@ -17,153 +17,22 @@
package com.android.systemui.keyguard.data.quickaffordance
-import android.content.Context
-import android.content.IntentFilter
-import android.content.SharedPreferences
-import com.android.systemui.R
-import com.android.systemui.backup.BackupHelper
-import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.settings.UserFileManager
-import com.android.systemui.settings.UserTracker
-import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.onStart
/**
- * Manages and provides access to the current "selections" of keyguard quick affordances, answering
- * the question "which affordances should the keyguard show?".
+ * Defines interface for classes that manage and provide access to the current "selections" of
+ * keyguard quick affordances, answering the question "which affordances should the keyguard show?".
*/
-@OptIn(ExperimentalCoroutinesApi::class)
-@SysUISingleton
-class KeyguardQuickAffordanceSelectionManager
-@Inject
-constructor(
- @Application context: Context,
- private val userFileManager: UserFileManager,
- private val userTracker: UserTracker,
- broadcastDispatcher: BroadcastDispatcher,
-) {
-
- private var sharedPrefs: SharedPreferences = instantiateSharedPrefs()
-
- private val userId: Flow<Int> = conflatedCallbackFlow {
- val callback =
- object : UserTracker.Callback {
- override fun onUserChanged(newUser: Int, userContext: Context) {
- trySendWithFailureLogging(newUser, TAG)
- }
- }
-
- userTracker.addCallback(callback) { it.run() }
- trySendWithFailureLogging(userTracker.userId, TAG)
-
- awaitClose { userTracker.removeCallback(callback) }
- }
- private val defaults: Map<String, List<String>> by lazy {
- context.resources
- .getStringArray(R.array.config_keyguardQuickAffordanceDefaults)
- .associate { item ->
- val splitUp = item.split(SLOT_AFFORDANCES_DELIMITER)
- check(splitUp.size == 2)
- val slotId = splitUp[0]
- val affordanceIds = splitUp[1].split(AFFORDANCE_DELIMITER)
- slotId to affordanceIds
- }
- }
-
- /**
- * Emits an event each time a Backup & Restore restoration job is completed. Does not emit an
- * initial value.
- */
- private val backupRestorationEvents: Flow<Unit> =
- broadcastDispatcher.broadcastFlow(
- filter = IntentFilter(BackupHelper.ACTION_RESTORE_FINISHED),
- flags = Context.RECEIVER_NOT_EXPORTED,
- permission = BackupHelper.PERMISSION_SELF,
- )
+interface KeyguardQuickAffordanceSelectionManager {
/** IDs of affordances to show, indexed by slot ID, and sorted in descending priority order. */
- val selections: Flow<Map<String, List<String>>> =
- combine(
- userId,
- backupRestorationEvents.onStart {
- // We emit an initial event to make sure that the combine emits at least once,
- // even
- // if we never get a Backup & Restore restoration event (which is the most
- // common
- // case anyway as restoration really only happens on initial device setup).
- emit(Unit)
- }
- ) { _, _ ->
- }
- .flatMapLatest {
- conflatedCallbackFlow {
- // We want to instantiate a new SharedPreferences instance each time either the
- // user
- // ID changes or we have a backup & restore restoration event. The reason is
- // that
- // our sharedPrefs instance needs to be replaced with a new one as it depends on
- // the
- // user ID and when the B&R job completes, the backing file is replaced but the
- // existing instance still has a stale in-memory cache.
- sharedPrefs = instantiateSharedPrefs()
-
- val listener =
- SharedPreferences.OnSharedPreferenceChangeListener { _, _ ->
- trySend(getSelections())
- }
-
- sharedPrefs.registerOnSharedPreferenceChangeListener(listener)
- send(getSelections())
-
- awaitClose { sharedPrefs.unregisterOnSharedPreferenceChangeListener(listener) }
- }
- }
+ val selections: Flow<Map<String, List<String>>>
/**
* Returns a snapshot of the IDs of affordances to show, indexed by slot ID, and sorted in
* descending priority order.
*/
- fun getSelections(): Map<String, List<String>> {
- val slotKeys = sharedPrefs.all.keys.filter { it.startsWith(KEY_PREFIX_SLOT) }
- val result =
- slotKeys
- .associate { key ->
- val slotId = key.substring(KEY_PREFIX_SLOT.length)
- val value = sharedPrefs.getString(key, null)
- val affordanceIds =
- if (!value.isNullOrEmpty()) {
- value.split(AFFORDANCE_DELIMITER)
- } else {
- emptyList()
- }
- slotId to affordanceIds
- }
- .toMutableMap()
-
- // If the result map is missing keys, it means that the system has never set anything for
- // those slots. This is where we need examine our defaults and see if there should be a
- // default value for the affordances in the slot IDs that are missing from the result.
- //
- // Once the user makes any selection for a slot, even when they select "None", this class
- // will persist a key for that slot ID. In the case of "None", it will have a value of the
- // empty string. This is why this system works.
- defaults.forEach { (slotId, affordanceIds) ->
- if (!result.containsKey(slotId)) {
- result[slotId] = affordanceIds
- }
- }
-
- return result
- }
+ fun getSelections(): Map<String, List<String>>
/**
* Updates the IDs of affordances to show at the slot with the given ID. The order of affordance
@@ -172,25 +41,9 @@ constructor(
fun setSelections(
slotId: String,
affordanceIds: List<String>,
- ) {
- val key = "$KEY_PREFIX_SLOT$slotId"
- val value = affordanceIds.joinToString(AFFORDANCE_DELIMITER)
- sharedPrefs.edit().putString(key, value).apply()
- }
-
- private fun instantiateSharedPrefs(): SharedPreferences {
- return userFileManager.getSharedPreferences(
- FILE_NAME,
- Context.MODE_PRIVATE,
- userTracker.userId,
- )
- }
+ )
companion object {
- private const val TAG = "KeyguardQuickAffordanceSelectionManager"
const val FILE_NAME = "quick_affordance_selections"
- private const val KEY_PREFIX_SLOT = "slot_"
- private const val SLOT_AFFORDANCES_DELIMITER = ":"
- private const val AFFORDANCE_DELIMITER = ","
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
index d95a1a726bf5..e3f5e90b2300 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
@@ -18,45 +18,93 @@
package com.android.systemui.keyguard.data.repository
import android.content.Context
+import android.os.UserHandle
import com.android.systemui.Dumpable
import com.android.systemui.R
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation
import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentation
+import com.android.systemui.settings.UserTracker
import java.io.PrintWriter
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
/** Abstracts access to application state related to keyguard quick affordances. */
+@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class KeyguardQuickAffordanceRepository
@Inject
constructor(
@Application private val appContext: Context,
@Application private val scope: CoroutineScope,
- private val selectionManager: KeyguardQuickAffordanceSelectionManager,
+ private val localUserSelectionManager: KeyguardQuickAffordanceLocalUserSelectionManager,
+ private val remoteUserSelectionManager: KeyguardQuickAffordanceRemoteUserSelectionManager,
+ private val userTracker: UserTracker,
legacySettingSyncer: KeyguardQuickAffordanceLegacySettingSyncer,
private val configs: Set<@JvmSuppressWildcards KeyguardQuickAffordanceConfig>,
dumpManager: DumpManager,
+ userHandle: UserHandle,
) {
+ private val userId: Flow<Int> =
+ ConflatedCallbackFlow.conflatedCallbackFlow {
+ val callback =
+ object : UserTracker.Callback {
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ trySendWithFailureLogging(newUser, TAG)
+ }
+ }
+
+ userTracker.addCallback(callback) { it.run() }
+ trySendWithFailureLogging(userTracker.userId, TAG)
+
+ awaitClose { userTracker.removeCallback(callback) }
+ }
+
+ private val selectionManager: StateFlow<KeyguardQuickAffordanceSelectionManager> =
+ userId
+ .distinctUntilChanged()
+ .map { selectedUserId ->
+ if (userHandle.identifier == selectedUserId) {
+ localUserSelectionManager
+ } else {
+ remoteUserSelectionManager
+ }
+ }
+ .stateIn(
+ scope = scope,
+ started = SharingStarted.Eagerly,
+ initialValue = localUserSelectionManager,
+ )
+
/**
* List of [KeyguardQuickAffordanceConfig] instances of the affordances at the slot with the
* given ID. The configs are sorted in descending priority order.
*/
val selections: StateFlow<Map<String, List<KeyguardQuickAffordanceConfig>>> =
- selectionManager.selections
- .map { selectionsBySlotId ->
- selectionsBySlotId.mapValues { (_, selections) ->
- configs.filter { selections.contains(it.key) }
+ selectionManager
+ .flatMapLatest { selectionManager ->
+ selectionManager.selections.map { selectionsBySlotId ->
+ selectionsBySlotId.mapValues { (_, selections) ->
+ configs.filter { selections.contains(it.key) }
+ }
}
}
.stateIn(
@@ -99,7 +147,7 @@ constructor(
* slot with the given ID. The configs are sorted in descending priority order.
*/
fun getSelections(slotId: String): List<KeyguardQuickAffordanceConfig> {
- val selections = selectionManager.getSelections().getOrDefault(slotId, emptyList())
+ val selections = selectionManager.value.getSelections().getOrDefault(slotId, emptyList())
return configs.filter { selections.contains(it.key) }
}
@@ -108,7 +156,7 @@ constructor(
* are sorted in descending priority order.
*/
fun getSelections(): Map<String, List<String>> {
- return selectionManager.getSelections()
+ return selectionManager.value.getSelections()
}
/**
@@ -119,7 +167,7 @@ constructor(
slotId: String,
affordanceIds: List<String>,
) {
- selectionManager.setSelections(
+ selectionManager.value.setSelections(
slotId = slotId,
affordanceIds = affordanceIds,
)
@@ -188,6 +236,7 @@ constructor(
}
companion object {
+ private const val TAG = "KeyguardQuickAffordanceRepository"
private const val SLOT_CONFIG_DELIMITER = ":"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index 796f2b44effc..148792ba779b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -16,8 +16,11 @@
package com.android.systemui.keyguard.data.repository
+import android.graphics.Point
+import android.hardware.biometrics.BiometricSourceType
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.systemui.biometrics.AuthController
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.common.shared.model.Position
@@ -27,8 +30,8 @@ import com.android.systemui.doze.DozeMachine
import com.android.systemui.doze.DozeTransitionCallback
import com.android.systemui.doze.DozeTransitionListener
import com.android.systemui.keyguard.WakefulnessLifecycle
-import com.android.systemui.keyguard.WakefulnessLifecycle.Wakefulness
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.keyguard.shared.model.StatusBarState
@@ -88,8 +91,8 @@ interface KeyguardRepository {
* enter to conserve battery when the device is locked and inactive.
*
* Note that it is possible for the system to be transitioning into doze while this flow still
- * returns `false`. In order to account for that, observers should also use the [dozeAmount]
- * flow to check if it's greater than `0`
+ * returns `false`. In order to account for that, observers should also use the
+ * [linearDozeAmount] flow to check if it's greater than `0`
*/
val isDozing: Flow<Boolean>
@@ -111,7 +114,7 @@ interface KeyguardRepository {
* happens during an animation/transition into doze mode. An observer would be wise to account
* for both flows if needed.
*/
- val dozeAmount: Flow<Float>
+ val linearDozeAmount: Flow<Float>
/** Doze state information, as it transitions */
val dozeTransitionModel: Flow<DozeTransitionModel>
@@ -120,11 +123,20 @@ interface KeyguardRepository {
val statusBarState: Flow<StatusBarState>
/** Observable for device wake/sleep state */
- val wakefulnessState: Flow<WakefulnessModel>
+ val wakefulness: Flow<WakefulnessModel>
/** Observable for biometric unlock modes */
val biometricUnlockState: Flow<BiometricUnlockModel>
+ /** Approximate location on the screen of the fingerprint sensor. */
+ val fingerprintSensorLocation: Flow<Point?>
+
+ /** Approximate location on the screen of the face unlock sensor/front facing camera. */
+ val faceSensorLocation: Flow<Point?>
+
+ /** Source of the most recent biometric unlock, such as fingerprint or face. */
+ val biometricUnlockSource: Flow<BiometricUnlockSource?>
+
/**
* Returns `true` if the keyguard is showing; `false` otherwise.
*
@@ -163,6 +175,7 @@ constructor(
private val keyguardStateController: KeyguardStateController,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val dozeTransitionListener: DozeTransitionListener,
+ private val authController: AuthController,
) : KeyguardRepository {
private val _animateBottomAreaDozingTransitions = MutableStateFlow(false)
override val animateBottomAreaDozingTransitions =
@@ -281,11 +294,11 @@ constructor(
}
.distinctUntilChanged()
- override val dozeAmount: Flow<Float> = conflatedCallbackFlow {
+ override val linearDozeAmount: Flow<Float> = conflatedCallbackFlow {
val callback =
object : StatusBarStateController.StateListener {
override fun onDozeAmountChanged(linear: Float, eased: Float) {
- trySendWithFailureLogging(eased, TAG, "updated dozeAmount")
+ trySendWithFailureLogging(linear, TAG, "updated dozeAmount")
}
}
@@ -348,56 +361,137 @@ constructor(
awaitClose { statusBarStateController.removeCallback(callback) }
}
- override val wakefulnessState: Flow<WakefulnessModel> = conflatedCallbackFlow {
+ override val biometricUnlockState: Flow<BiometricUnlockModel> = conflatedCallbackFlow {
+ fun dispatchUpdate() {
+ trySendWithFailureLogging(
+ biometricModeIntToObject(biometricUnlockController.mode),
+ TAG,
+ "biometric mode"
+ )
+ }
+
val callback =
+ object : BiometricUnlockController.BiometricModeListener {
+ override fun onModeChanged(@WakeAndUnlockMode mode: Int) {
+ dispatchUpdate()
+ }
+
+ override fun onResetMode() {
+ dispatchUpdate()
+ }
+ }
+
+ biometricUnlockController.addBiometricModeListener(callback)
+ dispatchUpdate()
+
+ awaitClose { biometricUnlockController.removeBiometricModeListener(callback) }
+ }
+
+ override val wakefulness: Flow<WakefulnessModel> = conflatedCallbackFlow {
+ val observer =
object : WakefulnessLifecycle.Observer {
override fun onStartedWakingUp() {
- trySendWithFailureLogging(
- WakefulnessModel.STARTING_TO_WAKE,
- TAG,
- "Wakefulness: starting to wake"
- )
+ dispatchNewState()
}
+
override fun onFinishedWakingUp() {
- trySendWithFailureLogging(WakefulnessModel.AWAKE, TAG, "Wakefulness: awake")
+ dispatchNewState()
+ }
+
+ override fun onPostFinishedWakingUp() {
+ dispatchNewState()
}
+
override fun onStartedGoingToSleep() {
+ dispatchNewState()
+ }
+
+ override fun onFinishedGoingToSleep() {
+ dispatchNewState()
+ }
+
+ private fun dispatchNewState() {
trySendWithFailureLogging(
- WakefulnessModel.STARTING_TO_SLEEP,
+ WakefulnessModel.fromWakefulnessLifecycle(wakefulnessLifecycle),
TAG,
- "Wakefulness: starting to sleep"
+ "updated wakefulness state"
)
}
- override fun onFinishedGoingToSleep() {
- trySendWithFailureLogging(WakefulnessModel.ASLEEP, TAG, "Wakefulness: asleep")
- }
}
- wakefulnessLifecycle.addObserver(callback)
+
+ wakefulnessLifecycle.addObserver(observer)
trySendWithFailureLogging(
- wakefulnessIntToObject(wakefulnessLifecycle.getWakefulness()),
+ WakefulnessModel.fromWakefulnessLifecycle(wakefulnessLifecycle),
TAG,
"initial wakefulness state"
)
- awaitClose { wakefulnessLifecycle.removeObserver(callback) }
+ awaitClose { wakefulnessLifecycle.removeObserver(observer) }
}
- override val biometricUnlockState: Flow<BiometricUnlockModel> = conflatedCallbackFlow {
+ override val fingerprintSensorLocation: Flow<Point?> = conflatedCallbackFlow {
+ fun sendFpLocation() {
+ trySendWithFailureLogging(
+ authController.fingerprintSensorLocation,
+ TAG,
+ "AuthController.Callback#onFingerprintLocationChanged"
+ )
+ }
+
val callback =
- object : BiometricUnlockController.BiometricModeListener {
- override fun onModeChanged(@WakeAndUnlockMode mode: Int) {
- trySendWithFailureLogging(biometricModeIntToObject(mode), TAG, "biometric mode")
+ object : AuthController.Callback {
+ override fun onFingerprintLocationChanged() {
+ sendFpLocation()
}
}
- biometricUnlockController.addBiometricModeListener(callback)
- trySendWithFailureLogging(
- biometricModeIntToObject(biometricUnlockController.getMode()),
- TAG,
- "initial biometric mode"
- )
+ authController.addCallback(callback)
+ sendFpLocation()
- awaitClose { biometricUnlockController.removeBiometricModeListener(callback) }
+ awaitClose { authController.removeCallback(callback) }
+ }
+
+ override val faceSensorLocation: Flow<Point?> = conflatedCallbackFlow {
+ fun sendSensorLocation() {
+ trySendWithFailureLogging(
+ authController.faceSensorLocation,
+ TAG,
+ "AuthController.Callback#onFingerprintLocationChanged"
+ )
+ }
+
+ val callback =
+ object : AuthController.Callback {
+ override fun onFaceSensorLocationChanged() {
+ sendSensorLocation()
+ }
+ }
+
+ authController.addCallback(callback)
+ sendSensorLocation()
+
+ awaitClose { authController.removeCallback(callback) }
+ }
+
+ override val biometricUnlockSource: Flow<BiometricUnlockSource?> = conflatedCallbackFlow {
+ val callback =
+ object : KeyguardUpdateMonitorCallback() {
+ override fun onBiometricAuthenticated(
+ userId: Int,
+ biometricSourceType: BiometricSourceType?,
+ isStrongBiometric: Boolean
+ ) {
+ trySendWithFailureLogging(
+ BiometricUnlockSource.fromBiometricSourceType(biometricSourceType),
+ TAG,
+ "onBiometricAuthenticated"
+ )
+ }
+ }
+
+ keyguardUpdateMonitor.registerCallback(callback)
+ trySendWithFailureLogging(null, TAG, "initial value")
+ awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
}
override fun setAnimateDozingTransitions(animate: Boolean) {
@@ -423,16 +517,6 @@ constructor(
}
}
- private fun wakefulnessIntToObject(@Wakefulness value: Int): WakefulnessModel {
- return when (value) {
- 0 -> WakefulnessModel.ASLEEP
- 1 -> WakefulnessModel.STARTING_TO_WAKE
- 2 -> WakefulnessModel.AWAKE
- 3 -> WakefulnessModel.STARTING_TO_SLEEP
- else -> throw IllegalArgumentException("Invalid Wakefulness value: $value")
- }
- }
-
private fun biometricModeIntToObject(@WakeAndUnlockMode value: Int): BiometricUnlockModel {
return when (value) {
0 -> BiometricUnlockModel.NONE
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
index 0c725208e22d..26f853f3ad1c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
@@ -27,4 +27,7 @@ interface KeyguardRepositoryModule {
fun keyguardTransitionRepository(
impl: KeyguardTransitionRepositoryImpl
): KeyguardTransitionRepository
+
+ @Binds
+ fun lightRevealScrimRepository(impl: LightRevealScrimRepositoryImpl): LightRevealScrimRepository
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
new file mode 100644
index 000000000000..a17481a9978b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.data.repository
+
+import android.content.Context
+import android.graphics.Point
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
+import com.android.systemui.keyguard.shared.model.WakeSleepReason
+import com.android.systemui.statusbar.CircleReveal
+import com.android.systemui.statusbar.LiftReveal
+import com.android.systemui.statusbar.LightRevealEffect
+import com.android.systemui.statusbar.PowerButtonReveal
+import javax.inject.Inject
+import kotlin.math.max
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+
+val DEFAULT_REVEAL_EFFECT = LiftReveal
+
+/**
+ * Encapsulates state relevant to the light reveal scrim, the view used to reveal/hide screen
+ * contents during transitions between AOD and lockscreen/unlocked.
+ */
+interface LightRevealScrimRepository {
+
+ /**
+ * The reveal effect that should be used for the next lock/unlock. We switch between either the
+ * biometric unlock effect (if wake and unlocking) or the non-biometric effect, and position it
+ * at the current screen position of the appropriate sensor.
+ */
+ val revealEffect: Flow<LightRevealEffect>
+}
+
+@SysUISingleton
+class LightRevealScrimRepositoryImpl
+@Inject
+constructor(
+ keyguardRepository: KeyguardRepository,
+ val context: Context,
+) : LightRevealScrimRepository {
+
+ /** The reveal effect used if the device was locked/unlocked via the power button. */
+ private val powerButtonReveal =
+ PowerButtonReveal(
+ context.resources
+ .getDimensionPixelSize(R.dimen.physical_power_button_center_screen_location_y)
+ .toFloat()
+ )
+
+ /**
+ * Reveal effect to use for a fingerprint unlock. This is reconstructed if the fingerprint
+ * sensor location on the screen (in pixels) changes due to configuration changes.
+ */
+ private val fingerprintRevealEffect: Flow<LightRevealEffect?> =
+ keyguardRepository.fingerprintSensorLocation.map {
+ it?.let { constructCircleRevealFromPoint(it) }
+ }
+
+ /**
+ * Reveal effect to use for a face unlock. This is reconstructed if the face sensor/front camera
+ * location on the screen (in pixels) changes due to configuration changes.
+ */
+ private val faceRevealEffect: Flow<LightRevealEffect?> =
+ keyguardRepository.faceSensorLocation.map { it?.let { constructCircleRevealFromPoint(it) } }
+
+ /**
+ * The reveal effect we'll use for the next biometric unlock animation. We switch between the
+ * fingerprint/face unlock effect flows depending on the biometric unlock source.
+ */
+ private val biometricRevealEffect: Flow<LightRevealEffect?> =
+ keyguardRepository.biometricUnlockSource.flatMapLatest { source ->
+ when (source) {
+ BiometricUnlockSource.FINGERPRINT_SENSOR -> fingerprintRevealEffect
+ BiometricUnlockSource.FACE_SENSOR -> faceRevealEffect
+ else -> flowOf(null)
+ }
+ }
+
+ /** The reveal effect we'll use for the next non-biometric unlock (tap, power button, etc). */
+ private val nonBiometricRevealEffect: Flow<LightRevealEffect?> =
+ keyguardRepository.wakefulness.map { wakefulnessModel ->
+ val wakingUpFromPowerButton =
+ wakefulnessModel.isWakingUpOrAwake &&
+ wakefulnessModel.lastWakeReason == WakeSleepReason.POWER_BUTTON
+ val sleepingFromPowerButton =
+ !wakefulnessModel.isWakingUpOrAwake &&
+ wakefulnessModel.lastSleepReason == WakeSleepReason.POWER_BUTTON
+
+ if (wakingUpFromPowerButton || sleepingFromPowerButton) {
+ powerButtonReveal
+ } else {
+ LiftReveal
+ }
+ }
+
+ override val revealEffect =
+ combine(
+ keyguardRepository.biometricUnlockState,
+ biometricRevealEffect,
+ nonBiometricRevealEffect
+ ) { biometricUnlockState, biometricReveal, nonBiometricReveal ->
+
+ // Use the biometric reveal for any flavor of wake and unlocking.
+ when (biometricUnlockState) {
+ BiometricUnlockModel.WAKE_AND_UNLOCK,
+ BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING,
+ BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM -> biometricReveal
+ else -> nonBiometricReveal
+ }
+ ?: DEFAULT_REVEAL_EFFECT
+ }
+ .distinctUntilChanged()
+
+ private fun constructCircleRevealFromPoint(point: Point): LightRevealEffect {
+ return with(point) {
+ CircleReveal(
+ x,
+ y,
+ startRadius = 0,
+ endRadius =
+ max(max(x, context.display.width - x), max(y, context.display.height - y)),
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt
index 2dbacd5d3c69..9b193533805e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt
@@ -27,7 +27,6 @@ import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingToAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingToAodTransitionInteractor.kt
index 9e2b7241ade2..e34981eabcac 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingToAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingToAodTransitionInteractor.kt
@@ -42,7 +42,7 @@ constructor(
override fun start() {
scope.launch {
- keyguardInteractor.wakefulnessState
+ keyguardInteractor.wakefulnessModel
.sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
.collect { pair ->
val (wakefulnessState, keyguardState) = pair
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GoneAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GoneAodTransitionInteractor.kt
index 0e2a54c57bdb..483041a1b236 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GoneAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GoneAodTransitionInteractor.kt
@@ -23,11 +23,10 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionInfo
-import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
@SysUISingleton
@@ -42,13 +41,13 @@ constructor(
override fun start() {
scope.launch {
- keyguardInteractor.wakefulnessState
+ keyguardInteractor.wakefulnessModel
.sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
.collect { pair ->
val (wakefulnessState, keyguardState) = pair
if (
keyguardState == KeyguardState.GONE &&
- wakefulnessState == WakefulnessModel.STARTING_TO_SLEEP
+ wakefulnessState.state == WakefulnessState.STARTING_TO_SLEEP
) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 7cfd117d8eb4..6912e1d3558e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
+import android.graphics.Point
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
@@ -41,7 +42,7 @@ constructor(
* The amount of doze the system is in, where `1.0` is fully dozing and `0.0` is not dozing at
* all.
*/
- val dozeAmount: Flow<Float> = repository.dozeAmount
+ val dozeAmount: Flow<Float> = repository.linearDozeAmount
/** Whether the system is in doze mode. */
val isDozing: Flow<Boolean> = repository.isDozing
/** Doze transition information. */
@@ -58,7 +59,7 @@ constructor(
/** Whether the bouncer is showing or not. */
val isBouncerShowing: Flow<Boolean> = repository.isBouncerShowing
/** The device wake/sleep state */
- val wakefulnessState: Flow<WakefulnessModel> = repository.wakefulnessState
+ val wakefulnessModel: Flow<WakefulnessModel> = repository.wakefulness
/** Observable for the [StatusBarState] */
val statusBarState: Flow<StatusBarState> = repository.statusBarState
/**
@@ -67,10 +68,15 @@ constructor(
*/
val biometricUnlockState: Flow<BiometricUnlockModel> = repository.biometricUnlockState
+ /** The approximate location on the screen of the fingerprint sensor, if one is available. */
+ val fingerprintSensorLocation: Flow<Point?> = repository.fingerprintSensorLocation
+
+ /** The approximate location on the screen of the face unlock sensor, if one is available. */
+ val faceSensorLocation: Flow<Point?> = repository.faceSensorLocation
+
fun dozeTransitionTo(state: DozeStateModel): Flow<DozeTransitionModel> {
return dozeTransitionModel.filter { it.to == state }
}
-
fun isKeyguardShowing(): Boolean {
return repository.isKeyguardShowing()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index ee7154ff7219..748c6e8b75b9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -190,8 +190,6 @@ constructor(
/** Returns affordance IDs indexed by slot ID, for all known slots. */
suspend fun getSelections(): Map<String, List<KeyguardQuickAffordancePickerRepresentation>> {
- check(isUsingRepository)
-
val slots = repository.get().getSlotPickerRepresentations()
val selections = repository.get().getSelections()
val affordanceById =
@@ -312,8 +310,6 @@ constructor(
suspend fun getAffordancePickerRepresentations():
List<KeyguardQuickAffordancePickerRepresentation> {
- check(isUsingRepository)
-
return repository.get().getAffordancePickerRepresentations()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
index 58a8093d49d2..e30e7f6ece11 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
@@ -37,7 +37,7 @@ constructor(
fun start() {
scope.launch {
- keyguardInteractor.wakefulnessState.collect { logger.v("WakefulnessState", it) }
+ keyguardInteractor.wakefulnessModel.collect { logger.v("WakefulnessModel", it) }
}
scope.launch {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
new file mode 100644
index 000000000000..6e25200bc2f6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.LightRevealScrimRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.statusbar.LightRevealEffect
+import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
+
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class LightRevealScrimInteractor
+@Inject
+constructor(
+ transitionRepository: KeyguardTransitionRepository,
+ transitionInteractor: KeyguardTransitionInteractor,
+ lightRevealScrimRepository: LightRevealScrimRepository,
+) {
+
+ /**
+ * Whenever a keyguard transition starts, sample the latest reveal effect from the repository
+ * and use that for the starting transition.
+ *
+ * We can't simply use the nextRevealEffect since the effect may change midway through a
+ * transition, but we don't want to change effects part way through. For example, if we're using
+ * a CircleReveal to animate a biometric unlock, but the biometric unlock mode changes to NONE
+ * from WAKE_AND_UNLOCK before the unlock animation ends, we don't want to end up switching to a
+ * LiftReveal.
+ */
+ val lightRevealEffect: Flow<LightRevealEffect> =
+ transitionInteractor.startedKeyguardTransitionStep.sample(
+ lightRevealScrimRepository.revealEffect
+ )
+
+ /**
+ * The reveal amount to use for the light reveal scrim, which is derived from the keyguard
+ * transition steps.
+ */
+ val revealAmount: Flow<Float> =
+ transitionRepository.transitions
+ // Only listen to transitions that change the reveal amount.
+ .filter { willTransitionAffectRevealAmount(it) }
+ // Use the transition amount as the reveal amount, inverting it if we're transitioning
+ // to a non-revealed (hidden) state.
+ .map { step -> if (willBeRevealedInState(step.to)) step.value else 1f - step.value }
+
+ companion object {
+
+ /**
+ * Whether the transition requires a change in the reveal amount of the light reveal scrim.
+ * If not, we don't care about the transition and don't need to listen to it.
+ */
+ fun willTransitionAffectRevealAmount(transition: TransitionStep): Boolean {
+ return willBeRevealedInState(transition.from) != willBeRevealedInState(transition.to)
+ }
+
+ /**
+ * Whether the light reveal scrim will be fully revealed (revealAmount = 1.0f) in the given
+ * state after the transition is complete. If false, scrim will be fully hidden.
+ */
+ fun willBeRevealedInState(state: KeyguardState): Boolean {
+ return when (state) {
+ KeyguardState.OFF -> false
+ KeyguardState.DOZING -> false
+ KeyguardState.AOD -> false
+ KeyguardState.DREAMING -> true
+ KeyguardState.BOUNCER -> true
+ KeyguardState.LOCKSCREEN -> true
+ KeyguardState.GONE -> true
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
index 3bb8241257cc..3218f9699f35 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
@@ -25,7 +25,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.StatusBarState.SHADE_LOCKED
import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.TransitionState
-import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.util.kotlin.sample
import java.util.UUID
@@ -58,22 +58,26 @@ constructor(
keyguardInteractor.isBouncerShowing
.sample(
combine(
- keyguardInteractor.wakefulnessState,
+ keyguardInteractor.wakefulnessModel,
keyguardTransitionInteractor.startedKeyguardTransitionStep,
- ) { a, b ->
- Pair(a, b)
- },
- { a, bc -> Triple(a, bc.first, bc.second) }
- )
- .collect { triple ->
- val (isBouncerShowing, wakefulnessState, lastStartedTransitionStep) = triple
+ ) { wakefulnessModel, transitionStep ->
+ Pair(wakefulnessModel, transitionStep)
+ }
+ ) { bouncerShowing, wakefulnessAndTransition ->
+ Triple(
+ bouncerShowing,
+ wakefulnessAndTransition.first,
+ wakefulnessAndTransition.second
+ )
+ }
+ .collect { (isBouncerShowing, wakefulnessState, lastStartedTransitionStep) ->
if (
!isBouncerShowing && lastStartedTransitionStep.to == KeyguardState.BOUNCER
) {
val to =
if (
- wakefulnessState == WakefulnessModel.STARTING_TO_SLEEP ||
- wakefulnessState == WakefulnessModel.ASLEEP
+ wakefulnessState.state == WakefulnessState.STARTING_TO_SLEEP ||
+ wakefulnessState.state == WakefulnessState.ASLEEP
) {
KeyguardState.AOD
} else {
@@ -100,14 +104,17 @@ constructor(
combine(
keyguardTransitionInteractor.finishedKeyguardState,
keyguardInteractor.statusBarState,
- ) { a, b ->
- Pair(a, b)
- },
- { a, bc -> Triple(a, bc.first, bc.second) }
- )
- .collect { triple ->
- val (shadeModel, keyguardState, statusBarState) = triple
-
+ ) { finishedKeyguardState, statusBarState ->
+ Pair(finishedKeyguardState, statusBarState)
+ }
+ ) { shadeModel, keyguardStateAndStatusBarState ->
+ Triple(
+ shadeModel,
+ keyguardStateAndStatusBarState.first,
+ keyguardStateAndStatusBarState.second
+ )
+ }
+ .collect { (shadeModel, keyguardState, statusBarState) ->
val id = transitionId
if (id != null) {
// An existing `id` means a transition is started, and calls to
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockSource.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockSource.kt
new file mode 100644
index 000000000000..b403416c572c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockSource.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.shared.model
+
+import android.hardware.biometrics.BiometricSourceType
+
+/** Biometric unlock sensor sources, which we use to play sensor-specific animations. */
+enum class BiometricUnlockSource {
+ /** The unlock was initiated by a fingerprint sensor authentication. */
+ FINGERPRINT_SENSOR,
+
+ /** The unlock was initiated by the front-facing camera or a nearby sensor. */
+ FACE_SENSOR;
+
+ companion object {
+ fun fromBiometricSourceType(type: BiometricSourceType?): BiometricUnlockSource? {
+ return when (type) {
+ BiometricSourceType.FINGERPRINT -> FINGERPRINT_SENSOR
+ BiometricSourceType.FACE -> FACE_SENSOR
+ BiometricSourceType.IRIS -> FACE_SENSOR
+ else -> null
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakeSleepReason.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakeSleepReason.kt
new file mode 100644
index 000000000000..b32597d5cff0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakeSleepReason.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.shared.model
+
+import android.os.PowerManager
+
+/** The reason we're waking up or going to sleep, such as pressing the power button. */
+enum class WakeSleepReason {
+ /** The physical power button was pressed to wake up or sleep the device. */
+ POWER_BUTTON,
+
+ /** Something else happened to wake up or sleep the device. */
+ OTHER;
+
+ companion object {
+ fun fromPowerManagerWakeReason(reason: Int): WakeSleepReason {
+ return when (reason) {
+ PowerManager.WAKE_REASON_POWER_BUTTON -> POWER_BUTTON
+ else -> OTHER
+ }
+ }
+
+ fun fromPowerManagerSleepReason(reason: Int): WakeSleepReason {
+ return when (reason) {
+ PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON -> POWER_BUTTON
+ else -> OTHER
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt
index 92040f4f0348..03dee0045c10 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt
@@ -15,24 +15,34 @@
*/
package com.android.systemui.keyguard.shared.model
-/** Model device wakefulness states. */
-enum class WakefulnessModel {
- /** The device is asleep and not interactive. */
- ASLEEP,
- /** Received a signal that the device is beginning to wake up. */
- STARTING_TO_WAKE,
- /** Device is now fully awake and interactive. */
- AWAKE,
- /** Signal that the device is now going to sleep. */
- STARTING_TO_SLEEP;
+import com.android.systemui.keyguard.WakefulnessLifecycle
+/** Model device wakefulness states. */
+data class WakefulnessModel(
+ val state: WakefulnessState,
+ val isWakingUpOrAwake: Boolean,
+ val lastWakeReason: WakeSleepReason,
+ val lastSleepReason: WakeSleepReason,
+) {
companion object {
fun isSleepingOrStartingToSleep(model: WakefulnessModel): Boolean {
- return model == ASLEEP || model == STARTING_TO_SLEEP
+ return model.state == WakefulnessState.ASLEEP ||
+ model.state == WakefulnessState.STARTING_TO_SLEEP
}
fun isWakingOrStartingToWake(model: WakefulnessModel): Boolean {
- return model == AWAKE || model == STARTING_TO_WAKE
+ return model.state == WakefulnessState.AWAKE ||
+ model.state == WakefulnessState.STARTING_TO_WAKE
+ }
+
+ fun fromWakefulnessLifecycle(wakefulnessLifecycle: WakefulnessLifecycle): WakefulnessModel {
+ return WakefulnessModel(
+ WakefulnessState.fromWakefulnessLifecycleInt(wakefulnessLifecycle.wakefulness),
+ wakefulnessLifecycle.wakefulness == WakefulnessLifecycle.WAKEFULNESS_WAKING ||
+ wakefulnessLifecycle.wakefulness == WakefulnessLifecycle.WAKEFULNESS_AWAKE,
+ WakeSleepReason.fromPowerManagerWakeReason(wakefulnessLifecycle.lastWakeReason),
+ WakeSleepReason.fromPowerManagerSleepReason(wakefulnessLifecycle.lastSleepReason),
+ )
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessState.kt
new file mode 100644
index 000000000000..6791d88f16d1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessState.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.shared.model
+
+import com.android.systemui.keyguard.WakefulnessLifecycle
+
+enum class WakefulnessState {
+ /** The device is asleep and not interactive. */
+ ASLEEP,
+ /** Received a signal that the device is beginning to wake up. */
+ STARTING_TO_WAKE,
+ /** Device is now fully awake and interactive. */
+ AWAKE,
+ /** Signal that the device is now going to sleep. */
+ STARTING_TO_SLEEP;
+
+ companion object {
+ fun fromWakefulnessLifecycleInt(
+ @WakefulnessLifecycle.Wakefulness value: Int
+ ): WakefulnessState {
+ return when (value) {
+ WakefulnessLifecycle.WAKEFULNESS_ASLEEP -> ASLEEP
+ WakefulnessLifecycle.WAKEFULNESS_WAKING -> STARTING_TO_WAKE
+ WakefulnessLifecycle.WAKEFULNESS_AWAKE -> AWAKE
+ WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP -> STARTING_TO_SLEEP
+ else -> throw IllegalArgumentException("Invalid Wakefulness value: $value")
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index 3276b6dd9748..cbe512ff83ba 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.ui.binder
+import android.graphics.drawable.Animatable2
import android.util.Size
import android.util.TypedValue
import android.view.View
@@ -27,12 +28,11 @@ import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.keyguard.LockIconViewController
import com.android.settingslib.Utils
import com.android.systemui.R
import com.android.systemui.animation.Expandable
import com.android.systemui.animation.Interpolators
+import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
@@ -73,7 +73,8 @@ object KeyguardBottomAreaViewBinder {
fun onConfigurationChanged()
/**
- * Returns whether the keyguard bottom area should be constrained to the top of the lock icon
+ * Returns whether the keyguard bottom area should be constrained to the top of the lock
+ * icon
*/
fun shouldConstrainToTopOfLockIcon(): Boolean
}
@@ -217,7 +218,7 @@ object KeyguardBottomAreaViewBinder {
}
override fun shouldConstrainToTopOfLockIcon(): Boolean =
- viewModel.shouldConstrainToTopOfLockIcon()
+ viewModel.shouldConstrainToTopOfLockIcon()
}
}
@@ -248,6 +249,27 @@ object KeyguardBottomAreaViewBinder {
IconViewBinder.bind(viewModel.icon, view)
+ (view.drawable as? Animatable2)?.let { animatable ->
+ (viewModel.icon as? Icon.Resource)?.res?.let { iconResourceId ->
+ // Always start the animation (we do call stop() below, if we need to skip it).
+ animatable.start()
+
+ if (view.tag != iconResourceId) {
+ // Here when we haven't run the animation on a previous update.
+ //
+ // Save the resource ID for next time, so we know not to re-animate the same
+ // animation again.
+ view.tag = iconResourceId
+ } else {
+ // Here when we've already done this animation on a previous update and want to
+ // skip directly to the final frame of the animation to avoid running it.
+ //
+ // By calling stop after start, we go to the final frame of the animation.
+ animatable.stop()
+ }
+ }
+ }
+
view.isActivated = viewModel.isActivated
view.drawable.setTint(
Utils.getColorAttrDefaultColor(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt
new file mode 100644
index 000000000000..f1da8826a22c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.binder
+
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.statusbar.LightRevealScrim
+import kotlinx.coroutines.launch
+
+object LightRevealScrimViewBinder {
+ @JvmStatic
+ fun bind(revealScrim: LightRevealScrim, viewModel: LightRevealScrimViewModel) {
+ revealScrim.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ launch {
+ viewModel.revealAmount.collect { amount -> revealScrim.revealAmount = amount }
+ }
+
+ launch {
+ viewModel.lightRevealEffect.collect { effect ->
+ revealScrim.revealEffect = effect
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt
new file mode 100644
index 000000000000..a46d441613ac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.keyguard.domain.interactor.LightRevealScrimInteractor
+import com.android.systemui.statusbar.LightRevealEffect
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Models UI state for the light reveal scrim, which is used during screen on and off animations to
+ * draw a gradient that reveals/hides the contents of the screen.
+ */
+class LightRevealScrimViewModel @Inject constructor(interactor: LightRevealScrimInteractor) {
+ val lightRevealEffect: Flow<LightRevealEffect> = interactor.lightRevealEffect
+ val revealAmount: Flow<Float> = interactor.revealAmount
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt b/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt
index c27bfa338df3..bb04b6b41a33 100644
--- a/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt
@@ -30,10 +30,11 @@ import kotlinx.coroutines.flow.Flow
*/
interface Diffable<T> {
/**
- * Finds the differences between [prevVal] and [this] and logs those diffs to [row].
+ * Finds the differences between [prevVal] and this object and logs those diffs to [row].
*
* Each implementer should determine which individual fields have changed between [prevVal] and
- * [this], and only log the fields that have actually changed. This helps save buffer space.
+ * this object, and only log the fields that have actually changed. This helps save buffer
+ * space.
*
* For example, if:
* - prevVal = Object(val1=100, val2=200, val3=300)
@@ -42,6 +43,16 @@ interface Diffable<T> {
* Then only the val3 change should be logged.
*/
fun logDiffs(prevVal: T, row: TableRowLogger)
+
+ /**
+ * Logs all the relevant fields of this object to [row].
+ *
+ * As opposed to [logDiffs], this method should log *all* fields.
+ *
+ * Implementation is optional. This method will only be used with [logDiffsForTable] in order to
+ * fully log the initial value of the flow.
+ */
+ fun logFull(row: TableRowLogger) {}
}
/**
@@ -57,8 +68,35 @@ fun <T : Diffable<T>> Flow<T>.logDiffsForTable(
columnPrefix: String,
initialValue: T,
): Flow<T> {
- return this.pairwiseBy(initialValue) { prevVal, newVal ->
+ // Fully log the initial value to the table.
+ val getInitialValue = {
+ tableLogBuffer.logChange(columnPrefix) { row -> initialValue.logFull(row) }
+ initialValue
+ }
+ return this.pairwiseBy(getInitialValue) { prevVal: T, newVal: T ->
tableLogBuffer.logDiffs(columnPrefix, prevVal, newVal)
newVal
}
}
+
+/**
+ * Each time the boolean flow is updated with a new value that's different from the previous value,
+ * logs the new value to the given [tableLogBuffer].
+ */
+fun Flow<Boolean>.logDiffsForTable(
+ tableLogBuffer: TableLogBuffer,
+ columnPrefix: String,
+ columnName: String,
+ initialValue: Boolean,
+): Flow<Boolean> {
+ val initialValueFun = {
+ tableLogBuffer.logChange(columnPrefix, columnName, initialValue)
+ initialValue
+ }
+ return this.pairwiseBy(initialValueFun) { prevVal, newVal: Boolean ->
+ if (prevVal != newVal) {
+ tableLogBuffer.logChange(columnPrefix, columnName, newVal)
+ }
+ newVal
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
index 429637a0ee4d..9d0b833d26cc 100644
--- a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
@@ -16,10 +16,7 @@
package com.android.systemui.log.table
-import androidx.annotation.VisibleForTesting
import com.android.systemui.Dumpable
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.dump.DumpsysTableLogger
import com.android.systemui.plugins.util.RingBuffer
import com.android.systemui.util.time.SystemClock
import java.io.PrintWriter
@@ -83,7 +80,7 @@ class TableLogBuffer(
maxSize: Int,
private val name: String,
private val systemClock: SystemClock,
-) {
+) : Dumpable {
init {
if (maxSize <= 0) {
throw IllegalArgumentException("maxSize must be > 0")
@@ -104,6 +101,9 @@ class TableLogBuffer(
* @param columnPrefix a prefix that will be applied to every column name that gets logged. This
* ensures that all the columns related to the same state object will be grouped together in the
* table.
+ *
+ * @throws IllegalArgumentException if [columnPrefix] or column name contain "|". "|" is used as
+ * the separator token for parsing, so it can't be present in any part of the column name.
*/
@Synchronized
fun <T : Diffable<T>> logDiffs(columnPrefix: String, prevVal: T, newVal: T) {
@@ -113,6 +113,25 @@ class TableLogBuffer(
newVal.logDiffs(prevVal, row)
}
+ /**
+ * Logs change(s) to the buffer using [rowInitializer].
+ *
+ * @param rowInitializer a function that will be called immediately to store relevant data on
+ * the row.
+ */
+ @Synchronized
+ fun logChange(columnPrefix: String, rowInitializer: (TableRowLogger) -> Unit) {
+ val row = tempRow
+ row.timestamp = systemClock.currentTimeMillis()
+ row.columnPrefix = columnPrefix
+ rowInitializer(row)
+ }
+
+ /** Logs a boolean change. */
+ fun logChange(prefix: String, columnName: String, value: Boolean) {
+ logChange(systemClock.currentTimeMillis(), prefix, columnName, value)
+ }
+
// Keep these individual [logChange] methods private (don't let clients give us their own
// timestamps.)
@@ -135,32 +154,31 @@ class TableLogBuffer(
@Synchronized
private fun obtain(timestamp: Long, prefix: String, columnName: String): TableChange {
+ verifyValidName(prefix, columnName)
val tableChange = buffer.advance()
tableChange.reset(timestamp, prefix, columnName)
return tableChange
}
- /**
- * Registers this buffer as dumpables in [dumpManager]. Must be called for the table to be
- * dumped.
- *
- * This will be automatically called in [TableLogBufferFactory.create].
- */
- fun registerDumpables(dumpManager: DumpManager) {
- dumpManager.registerNormalDumpable("$name-changes", changeDumpable)
- dumpManager.registerNormalDumpable("$name-table", tableDumpable)
+ private fun verifyValidName(prefix: String, columnName: String) {
+ if (prefix.contains(SEPARATOR)) {
+ throw IllegalArgumentException("columnPrefix cannot contain $SEPARATOR but was $prefix")
+ }
+ if (columnName.contains(SEPARATOR)) {
+ throw IllegalArgumentException(
+ "columnName cannot contain $SEPARATOR but was $columnName"
+ )
+ }
}
- private val changeDumpable = Dumpable { pw, _ -> dumpChanges(pw) }
- private val tableDumpable = Dumpable { pw, _ -> dumpTable(pw) }
-
- /** Dumps the list of [TableChange] objects. */
@Synchronized
- @VisibleForTesting
- fun dumpChanges(pw: PrintWriter) {
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ pw.println(HEADER_PREFIX + name)
+ pw.println("version $VERSION")
for (i in 0 until buffer.size) {
buffer[i].dump(pw)
}
+ pw.println(FOOTER_PREFIX + name)
}
/** Dumps an individual [TableChange]. */
@@ -170,70 +188,14 @@ class TableLogBuffer(
}
val formattedTimestamp = TABLE_LOG_DATE_FORMAT.format(timestamp)
pw.print(formattedTimestamp)
- pw.print(" ")
+ pw.print(SEPARATOR)
pw.print(this.getName())
- pw.print("=")
+ pw.print(SEPARATOR)
pw.print(this.getVal())
pw.println()
}
/**
- * Coalesces all the [TableChange] objects into a table of values of time and dumps the table.
- */
- // TODO(b/259454430): Since this is an expensive process, it could cause the bug report dump to
- // fail and/or not dump anything else. We should move this processing to ABT (Android Bug
- // Tool), where we have unlimited time to process.
- @Synchronized
- @VisibleForTesting
- fun dumpTable(pw: PrintWriter) {
- val messages = buffer.iterator().asSequence().toList()
-
- if (messages.isEmpty()) {
- return
- }
-
- // Step 1: Create list of column headers
- val headerSet = mutableSetOf<String>()
- messages.forEach { headerSet.add(it.getName()) }
- val headers: MutableList<String> = headerSet.toList().sorted().toMutableList()
- headers.add(0, "timestamp")
-
- // Step 2: Create a list with the current values for each column. Will be updated with each
- // change.
- val currentRow: MutableList<String> = MutableList(headers.size) { DEFAULT_COLUMN_VALUE }
-
- // Step 3: For each message, make the correct update to [currentRow] and save it to [rows].
- val columnIndices: Map<String, Int> =
- headers.mapIndexed { index, headerName -> headerName to index }.toMap()
- val allRows = mutableListOf<List<String>>()
-
- messages.forEach {
- if (!it.hasData()) {
- return@forEach
- }
-
- val formattedTimestamp = TABLE_LOG_DATE_FORMAT.format(it.timestamp)
- if (formattedTimestamp != currentRow[0]) {
- // The timestamp has updated, so save the previous row and continue to the next row
- allRows.add(currentRow.toList())
- currentRow[0] = formattedTimestamp
- }
- val columnIndex = columnIndices[it.getName()]!!
- currentRow[columnIndex] = it.getVal()
- }
- // Add the last row
- allRows.add(currentRow.toList())
-
- // Step 4: Dump the rows
- DumpsysTableLogger(
- name,
- headers,
- allRows,
- )
- .printTableData(pw)
- }
-
- /**
* A private implementation of [TableRowLogger].
*
* Used so that external clients can't modify [timestamp].
@@ -261,4 +223,8 @@ class TableLogBuffer(
}
val TABLE_LOG_DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
-private const val DEFAULT_COLUMN_VALUE = "UNKNOWN"
+
+private const val HEADER_PREFIX = "SystemUI StateChangeTableSection START: "
+private const val FOOTER_PREFIX = "SystemUI StateChangeTableSection END: "
+private const val SEPARATOR = "|"
+private const val VERSION = "1"
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt
index f1f906f46d2d..7a90a7470cd2 100644
--- a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt
@@ -34,7 +34,7 @@ constructor(
maxSize: Int,
): TableLogBuffer {
val tableBuffer = TableLogBuffer(adjustMaxSize(maxSize), name, systemClock)
- tableBuffer.registerDumpables(dumpManager)
+ dumpManager.registerNormalDumpable(name, tableBuffer)
return tableBuffer
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt
index 4891297dbcf9..2d10b823f784 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt
@@ -32,10 +32,12 @@ import com.android.systemui.Dumpable
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.media.controls.models.player.MediaData
import com.android.systemui.media.controls.pipeline.MediaDataManager
import com.android.systemui.media.controls.pipeline.RESUME_MEDIA_TIMEOUT
+import com.android.systemui.settings.UserTracker
import com.android.systemui.tuner.TunerService
import com.android.systemui.util.Utils
import com.android.systemui.util.time.SystemClock
@@ -55,6 +57,8 @@ class MediaResumeListener
constructor(
private val context: Context,
private val broadcastDispatcher: BroadcastDispatcher,
+ private val userTracker: UserTracker,
+ @Main private val mainExecutor: Executor,
@Background private val backgroundExecutor: Executor,
private val tunerService: TunerService,
private val mediaBrowserFactory: ResumeMediaBrowserFactory,
@@ -77,18 +81,26 @@ constructor(
private var currentUserId: Int = context.userId
@VisibleForTesting
- val userChangeReceiver =
+ val userUnlockReceiver =
object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (Intent.ACTION_USER_UNLOCKED == intent.action) {
- loadMediaResumptionControls()
- } else if (Intent.ACTION_USER_SWITCHED == intent.action) {
- currentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1)
- loadSavedComponents()
+ val userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1)
+ if (userId == currentUserId) {
+ loadMediaResumptionControls()
+ }
}
}
}
+ private val userTrackerCallback =
+ object : UserTracker.Callback {
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ currentUserId = newUser
+ loadSavedComponents()
+ }
+ }
+
private val mediaBrowserCallback =
object : ResumeMediaBrowser.Callback() {
override fun addTrack(
@@ -126,13 +138,13 @@ constructor(
dumpManager.registerDumpable(TAG, this)
val unlockFilter = IntentFilter()
unlockFilter.addAction(Intent.ACTION_USER_UNLOCKED)
- unlockFilter.addAction(Intent.ACTION_USER_SWITCHED)
broadcastDispatcher.registerReceiver(
- userChangeReceiver,
+ userUnlockReceiver,
unlockFilter,
null,
UserHandle.ALL
)
+ userTracker.addCallback(userTrackerCallback, mainExecutor)
loadSavedComponents()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
index 8aaee81a57dd..1fdbc99333cb 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
@@ -184,6 +184,7 @@ constructor(
private val configListener =
object : ConfigurationController.ConfigurationListener {
+
override fun onDensityOrFontScaleChanged() {
// System font changes should only happen when UMO is offscreen or a flicker may
// occur
@@ -199,6 +200,7 @@ constructor(
override fun onConfigChanged(newConfig: Configuration?) {
if (newConfig == null) return
isRtl = newConfig.layoutDirection == View.LAYOUT_DIRECTION_RTL
+ updatePlayers(recreateMedia = true)
}
override fun onUiModeChanged() {
@@ -635,7 +637,7 @@ constructor(
val existingSmartspaceMediaKey = MediaPlayerData.smartspaceMediaKey()
existingSmartspaceMediaKey?.let {
val removedPlayer =
- MediaPlayerData.removeMediaPlayer(existingSmartspaceMediaKey, true)
+ removePlayer(existingSmartspaceMediaKey, dismissMediaData = false)
removedPlayer?.run {
debugLogger.logPotentialMemoryLeak(existingSmartspaceMediaKey)
}
@@ -685,7 +687,7 @@ constructor(
key: String,
dismissMediaData: Boolean = true,
dismissRecommendation: Boolean = true
- ) {
+ ): MediaControlPanel? {
if (key == MediaPlayerData.smartspaceMediaKey()) {
MediaPlayerData.smartspaceMediaData?.let {
logger.logRecommendationRemoved(it.packageName, it.instanceId)
@@ -693,7 +695,7 @@ constructor(
}
val removed =
MediaPlayerData.removeMediaPlayer(key, dismissMediaData || dismissRecommendation)
- removed?.apply {
+ return removed?.apply {
mediaCarouselScrollHandler.onPrePlayerRemoved(removed)
mediaContent.removeView(removed.mediaViewHolder?.player)
mediaContent.removeView(removed.recommendationViewHolder?.recommendations)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
index 21e64e28ff19..827ac789073a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
@@ -458,7 +458,9 @@ public class MediaControlPanel {
if (mMediaViewHolder == null) {
return;
}
- Trace.beginSection("MediaControlPanel#bindPlayer<" + key + ">");
+ if (Trace.isEnabled()) {
+ Trace.traceBegin(Trace.TRACE_TAG_APP, "MediaControlPanel#bindPlayer<" + key + ">");
+ }
mKey = key;
mMediaData = data;
MediaSession.Token token = data.getToken();
@@ -1179,8 +1181,10 @@ public class MediaControlPanel {
return;
}
- Trace.beginSection(
- "MediaControlPanel#bindRecommendation<" + data.getPackageName() + ">");
+ if (Trace.isEnabled()) {
+ Trace.traceBegin(Trace.TRACE_TAG_APP,
+ "MediaControlPanel#bindRecommendation<" + data.getPackageName() + ">");
+ }
mRecommendationData = data;
mSmartspaceId = SmallHash.hash(data.getTargetId());
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index d132a95dedcb..5202562861ea 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -57,13 +57,12 @@ import static com.android.systemui.statusbar.phone.CentralSurfaces.dumpBarTransi
import static com.android.systemui.util.Utils.isGesturalModeOnDefaultDisplay;
import android.annotation.IdRes;
+import android.annotation.NonNull;
import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
import android.app.StatusBarManager;
-import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.res.Configuration;
import android.graphics.Insets;
import android.graphics.PixelFormat;
@@ -120,7 +119,6 @@ import com.android.internal.view.AppearanceRegion;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
import com.android.systemui.assist.AssistManager;
-import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
@@ -138,6 +136,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
import com.android.systemui.settings.UserContextProvider;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
import com.android.systemui.shared.recents.utilities.Utilities;
@@ -208,7 +207,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
private final NotificationRemoteInputManager mNotificationRemoteInputManager;
private final OverviewProxyService mOverviewProxyService;
private final NavigationModeController mNavigationModeController;
- private final BroadcastDispatcher mBroadcastDispatcher;
+ private final UserTracker mUserTracker;
private final CommandQueue mCommandQueue;
private final Optional<Pip> mPipOptional;
private final Optional<Recents> mRecentsOptional;
@@ -516,7 +515,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
StatusBarStateController statusBarStateController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
SysUiState sysUiFlagsContainer,
- BroadcastDispatcher broadcastDispatcher,
+ UserTracker userTracker,
CommandQueue commandQueue,
Optional<Pip> pipOptional,
Optional<Recents> recentsOptional,
@@ -559,7 +558,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mNotificationRemoteInputManager = notificationRemoteInputManager;
mOverviewProxyService = overviewProxyService;
mNavigationModeController = navigationModeController;
- mBroadcastDispatcher = broadcastDispatcher;
+ mUserTracker = userTracker;
mCommandQueue = commandQueue;
mPipOptional = pipOptional;
mRecentsOptional = recentsOptional;
@@ -745,9 +744,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
prepareNavigationBarView();
checkNavBarModes();
- IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
- mBroadcastDispatcher.registerReceiverWithHandler(mBroadcastReceiver, filter,
- Handler.getMain(), UserHandle.ALL);
+ mUserTracker.addCallback(mUserChangedCallback, mContext.getMainExecutor());
mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
notifyNavigationBarScreenOn();
@@ -798,7 +795,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mView.setUpdateActiveTouchRegionsCallback(null);
getBarTransitions().destroy();
mOverviewProxyService.removeCallback(mOverviewProxyListener);
- mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver);
+ mUserTracker.removeCallback(mUserChangedCallback);
mWakefulnessLifecycle.removeObserver(mWakefulnessObserver);
if (mOrientationHandle != null) {
resetSecondaryHandle();
@@ -1743,21 +1740,14 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
}
};
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- // TODO(193941146): Currently unregistering a receiver through BroadcastDispatcher is
- // async, but we've already cleared the fields. Just return early in this case.
- if (mView == null) {
- return;
- }
- String action = intent.getAction();
- if (Intent.ACTION_USER_SWITCHED.equals(action)) {
- // The accessibility settings may be different for the new user
- updateAccessibilityStateFlags();
- }
- }
- };
+ private final UserTracker.Callback mUserChangedCallback =
+ new UserTracker.Callback() {
+ @Override
+ public void onUserChanged(int newUser, @NonNull Context userContext) {
+ // The accessibility settings may be different for the new user
+ updateAccessibilityStateFlags();
+ }
+ };
@VisibleForTesting
int getNavigationIconHints() {
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 1da866efc08d..5a1ad96da7a9 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -39,6 +39,8 @@ import android.text.format.DateUtils;
import android.util.Log;
import android.util.Slog;
+import androidx.annotation.NonNull;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.fuelgauge.Estimate;
import com.android.settingslib.utils.ThreadUtils;
@@ -47,6 +49,7 @@ import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.CentralSurfaces;
@@ -80,6 +83,7 @@ public class PowerUI implements CoreStartable, CommandQueue.Callbacks {
private final PowerManager mPowerManager;
private final WarningsUI mWarnings;
private final WakefulnessLifecycle mWakefulnessLifecycle;
+ private final UserTracker mUserTracker;
private InattentiveSleepWarningView mOverlayView;
private final Configuration mLastConfiguration = new Configuration();
private int mPlugType = 0;
@@ -122,12 +126,21 @@ public class PowerUI implements CoreStartable, CommandQueue.Callbacks {
}
};
+ private final UserTracker.Callback mUserChangedCallback =
+ new UserTracker.Callback() {
+ @Override
+ public void onUserChanged(int newUser, @NonNull Context userContext) {
+ mWarnings.userSwitched();
+ }
+ };
+
@Inject
public PowerUI(Context context, BroadcastDispatcher broadcastDispatcher,
CommandQueue commandQueue, Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
WarningsUI warningsUI, EnhancedEstimates enhancedEstimates,
WakefulnessLifecycle wakefulnessLifecycle,
- PowerManager powerManager) {
+ PowerManager powerManager,
+ UserTracker userTracker) {
mContext = context;
mBroadcastDispatcher = broadcastDispatcher;
mCommandQueue = commandQueue;
@@ -136,6 +149,7 @@ public class PowerUI implements CoreStartable, CommandQueue.Callbacks {
mEnhancedEstimates = enhancedEstimates;
mPowerManager = powerManager;
mWakefulnessLifecycle = wakefulnessLifecycle;
+ mUserTracker = userTracker;
}
public void start() {
@@ -154,6 +168,7 @@ public class PowerUI implements CoreStartable, CommandQueue.Callbacks {
false, obs, UserHandle.USER_ALL);
updateBatteryWarningLevels();
mReceiver.init();
+ mUserTracker.addCallback(mUserChangedCallback, mContext.getMainExecutor());
mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
// Check to see if we need to let the user know that the phone previously shut down due
@@ -250,7 +265,6 @@ public class PowerUI implements CoreStartable, CommandQueue.Callbacks {
IntentFilter filter = new IntentFilter();
filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
- filter.addAction(Intent.ACTION_USER_SWITCHED);
mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mHandler);
// Force get initial values. Relying on Sticky behavior until API for getting info.
if (!mHasReceivedBattery) {
@@ -332,8 +346,6 @@ public class PowerUI implements CoreStartable, CommandQueue.Callbacks {
plugged, bucket);
});
- } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
- mWarnings.userSwitched();
} else {
Slog.w(TAG, "unknown intent: " + intent);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
index 314252bf310b..4c9c99cc16f0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
@@ -36,6 +36,7 @@ import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.QSUserSwitcherEvent
import com.android.systemui.qs.tiles.UserDetailView
import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.user.ui.dialog.DialogShowerImpl
import javax.inject.Inject
import javax.inject.Provider
@@ -130,19 +131,6 @@ class UserSwitchDialogController @VisibleForTesting constructor(
}
}
- private class DialogShowerImpl(
- private val animateFrom: Dialog,
- private val dialogLaunchAnimator: DialogLaunchAnimator
- ) : DialogInterface by animateFrom, DialogShower {
- override fun showDialog(dialog: Dialog, cuj: DialogCuj) {
- dialogLaunchAnimator.showFromDialog(
- dialog,
- animateFrom = animateFrom,
- cuj
- )
- }
- }
-
interface DialogShower : DialogInterface {
fun showDialog(dialog: Dialog, cuj: DialogCuj)
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 2ee5f05549cf..645b1256e5f1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -51,10 +51,13 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.util.leak.RotationUtils;
@@ -76,6 +79,7 @@ public class ScreenPinningRequest implements View.OnClickListener,
private final AccessibilityManager mAccessibilityService;
private final WindowManager mWindowManager;
private final BroadcastDispatcher mBroadcastDispatcher;
+ private final UserTracker mUserTracker;
private RequestWindowView mRequestWindow;
private int mNavBarMode;
@@ -83,12 +87,21 @@ public class ScreenPinningRequest implements View.OnClickListener,
/** ID of task to be pinned or locked. */
private int taskId;
+ private final UserTracker.Callback mUserChangedCallback =
+ new UserTracker.Callback() {
+ @Override
+ public void onUserChanged(int newUser, @NonNull Context userContext) {
+ clearPrompt();
+ }
+ };
+
@Inject
public ScreenPinningRequest(
Context context,
Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
NavigationModeController navigationModeController,
- BroadcastDispatcher broadcastDispatcher) {
+ BroadcastDispatcher broadcastDispatcher,
+ UserTracker userTracker) {
mContext = context;
mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mAccessibilityService = (AccessibilityManager)
@@ -97,6 +110,7 @@ public class ScreenPinningRequest implements View.OnClickListener,
mContext.getSystemService(Context.WINDOW_SERVICE);
mNavBarMode = navigationModeController.addListener(this);
mBroadcastDispatcher = broadcastDispatcher;
+ mUserTracker = userTracker;
}
public void clearPrompt() {
@@ -228,9 +242,9 @@ public class ScreenPinningRequest implements View.OnClickListener,
}
IntentFilter filter = new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED);
- filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_SCREEN_OFF);
mBroadcastDispatcher.registerReceiver(mReceiver, filter);
+ mUserTracker.addCallback(mUserChangedCallback, mContext.getMainExecutor());
}
private void inflateView(int rotation) {
@@ -358,6 +372,7 @@ public class ScreenPinningRequest implements View.OnClickListener,
@Override
public void onDetachedFromWindow() {
mBroadcastDispatcher.unregisterReceiver(mReceiver);
+ mUserTracker.removeCallback(mUserChangedCallback);
}
protected void onConfigurationChanged() {
@@ -388,8 +403,7 @@ public class ScreenPinningRequest implements View.OnClickListener,
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
post(mUpdateLayoutRunnable);
- } else if (intent.getAction().equals(Intent.ACTION_USER_SWITCHED)
- || intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
+ } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
clearPrompt();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index ce4e0ecee914..b8684ee30b9a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -33,13 +33,16 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.settings.UserContextProvider;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.CallbackController;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -55,8 +58,10 @@ public class RecordingController
private boolean mIsRecording;
private PendingIntent mStopIntent;
private CountDownTimer mCountDownTimer = null;
- private BroadcastDispatcher mBroadcastDispatcher;
- private UserContextProvider mUserContextProvider;
+ private final Executor mMainExecutor;
+ private final BroadcastDispatcher mBroadcastDispatcher;
+ private final UserContextProvider mUserContextProvider;
+ private final UserTracker mUserTracker;
protected static final String INTENT_UPDATE_STATE =
"com.android.systemui.screenrecord.UPDATE_STATE";
@@ -66,12 +71,13 @@ public class RecordingController
new CopyOnWriteArrayList<>();
@VisibleForTesting
- protected final BroadcastReceiver mUserChangeReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- stopRecording();
- }
- };
+ final UserTracker.Callback mUserChangedCallback =
+ new UserTracker.Callback() {
+ @Override
+ public void onUserChanged(int newUser, @NonNull Context userContext) {
+ stopRecording();
+ }
+ };
@VisibleForTesting
protected final BroadcastReceiver mStateChangeReceiver = new BroadcastReceiver() {
@@ -92,10 +98,14 @@ public class RecordingController
* Create a new RecordingController
*/
@Inject
- public RecordingController(BroadcastDispatcher broadcastDispatcher,
- UserContextProvider userContextProvider) {
+ public RecordingController(@Main Executor mainExecutor,
+ BroadcastDispatcher broadcastDispatcher,
+ UserContextProvider userContextProvider,
+ UserTracker userTracker) {
+ mMainExecutor = mainExecutor;
mBroadcastDispatcher = broadcastDispatcher;
mUserContextProvider = userContextProvider;
+ mUserTracker = userTracker;
}
/** Create a dialog to show screen recording options to the user. */
@@ -139,9 +149,7 @@ public class RecordingController
}
try {
startIntent.send();
- IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
- mBroadcastDispatcher.registerReceiver(mUserChangeReceiver, userFilter, null,
- UserHandle.ALL);
+ mUserTracker.addCallback(mUserChangedCallback, mMainExecutor);
IntentFilter stateFilter = new IntentFilter(INTENT_UPDATE_STATE);
mBroadcastDispatcher.registerReceiver(mStateChangeReceiver, stateFilter, null,
@@ -211,7 +219,7 @@ public class RecordingController
public synchronized void updateState(boolean isRecording) {
if (!isRecording && mIsRecording) {
// Unregister receivers if we have stopped recording
- mBroadcastDispatcher.unregisterReceiver(mUserChangeReceiver);
+ mUserTracker.removeCallback(mUserChangedCallback);
mBroadcastDispatcher.unregisterReceiver(mStateChangeReceiver);
}
mIsRecording = isRecording;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 57b256e7b4a9..a6447a5bf500 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -579,9 +579,16 @@ public class ScreenshotController {
private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
Insets screenInsets, ComponentName topComponent, boolean showFlash, UserHandle owner) {
- withWindowAttached(() ->
+ withWindowAttached(() -> {
+ if (mFlags.isEnabled(SCREENSHOT_WORK_PROFILE_POLICY)
+ && mUserManager.isManagedProfile(owner.getIdentifier())) {
+ mScreenshotView.announceForAccessibility(mContext.getResources().getString(
+ R.string.screenshot_saving_work_profile_title));
+ } else {
mScreenshotView.announceForAccessibility(
- mContext.getResources().getString(R.string.screenshot_saving_title)));
+ mContext.getResources().getString(R.string.screenshot_saving_title));
+ }
+ });
mScreenshotView.reset();
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index 7641554ede3d..fae938d542f1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -825,12 +825,23 @@ public class ScreenshotView extends FrameLayout implements
}
});
if (mQuickShareChip != null) {
- mQuickShareChip.setPendingIntent(imageData.quickShareAction.actionIntent,
- () -> {
- mUiEventLogger.log(
- ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED, 0, mPackageName);
- animateDismissal();
- });
+ if (imageData.quickShareAction != null) {
+ mQuickShareChip.setPendingIntent(imageData.quickShareAction.actionIntent,
+ () -> {
+ mUiEventLogger.log(
+ ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED, 0,
+ mPackageName);
+ animateDismissal();
+ });
+ } else {
+ // hide chip and unset pending interaction if necessary, since we don't actually
+ // have a useable quick share intent
+ Log.wtf(TAG, "Showed quick share chip, but quick share intent was null");
+ if (mPendingInteraction == PendingInteraction.QUICK_SHARE) {
+ mPendingInteraction = null;
+ }
+ mQuickShareChip.setVisibility(GONE);
+ }
}
if (mPendingInteraction != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 7fbdeca3963a..60376f4deec8 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -393,6 +393,9 @@ public final class NotificationPanelViewController implements Dumpable {
private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
private int mQsTrackingPointer;
private VelocityTracker mQsVelocityTracker;
+ private TrackingStartedListener mTrackingStartedListener;
+ private OpenCloseListener mOpenCloseListener;
+ private GestureRecorder mGestureRecorder;
private boolean mQsTracking;
/** Whether the ongoing gesture might both trigger the expansion in both the view and QS. */
private boolean mConflictingQsExpansionGesture;
@@ -1362,6 +1365,14 @@ public final class NotificationPanelViewController implements Dumpable {
mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea);
}
+ void setOpenCloseListener(OpenCloseListener openCloseListener) {
+ mOpenCloseListener = openCloseListener;
+ }
+
+ void setTrackingStartedListener(TrackingStartedListener trackingStartedListener) {
+ mTrackingStartedListener = trackingStartedListener;
+ }
+
private void updateGestureExclusionRect() {
Rect exclusionRect = calculateGestureExclusionRect();
mView.setSystemGestureExclusionRects(exclusionRect.isEmpty() ? Collections.emptyList()
@@ -1936,9 +1947,9 @@ public final class NotificationPanelViewController implements Dumpable {
}
private void fling(float vel) {
- GestureRecorder gr = mCentralSurfaces.getGestureRecorder();
- if (gr != null) {
- gr.tag("fling " + ((vel > 0) ? "open" : "closed"), "notifications,v=" + vel);
+ if (mGestureRecorder != null) {
+ mGestureRecorder.tag("fling " + ((vel > 0) ? "open" : "closed"),
+ "notifications,v=" + vel);
}
fling(vel, true, 1.0f /* collapseSpeedUpFactor */, false);
}
@@ -2072,6 +2083,14 @@ public final class NotificationPanelViewController implements Dumpable {
mInitialTouchX = x;
initVelocityTracker();
trackMovement(event);
+ float qsExpansionFraction = computeQsExpansionFraction();
+ // Intercept the touch if QS is between fully collapsed and fully expanded state
+ if (!mSplitShadeEnabled
+ && qsExpansionFraction > 0.0 && qsExpansionFraction < 1.0) {
+ mShadeLog.logMotionEvent(event,
+ "onQsIntercept: down action, QS partially expanded/collapsed");
+ return true;
+ }
if (mKeyguardShowing
&& shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) {
// Dragging down on the lockscreen statusbar should prohibit other interactions
@@ -2324,6 +2343,14 @@ public final class NotificationPanelViewController implements Dumpable {
if (!isFullyCollapsed()) {
handleQsDown(event);
}
+ // defer touches on QQS to shade while shade is collapsing. Added margin for error
+ // as sometimes the qsExpansionFraction can be a tiny value instead of 0 when in QQS.
+ if (!mSplitShadeEnabled
+ && computeQsExpansionFraction() <= 0.01 && getExpandedFraction() < 1.0) {
+ mShadeLog.logMotionEvent(event,
+ "handleQsTouch: QQS touched while shade collapsing");
+ mQsTracking = false;
+ }
if (!mQsExpandImmediate && mQsTracking) {
onQsTouch(event);
if (!mConflictingQsExpansionGesture && !mSplitShadeEnabled) {
@@ -2564,7 +2591,6 @@ public final class NotificationPanelViewController implements Dumpable {
// Reset scroll position and apply that position to the expanded height.
float height = mQsExpansionHeight;
setQsExpansionHeight(height);
- updateExpandedHeightToMaxHeight();
mNotificationStackScrollLayoutController.checkSnoozeLeavebehind();
// When expanding QS, let's authenticate the user if possible,
@@ -3711,7 +3737,7 @@ public final class NotificationPanelViewController implements Dumpable {
mFalsingCollector.onTrackingStarted(!mKeyguardStateController.canDismissLockScreen());
endClosing();
mTracking = true;
- mCentralSurfaces.onTrackingStarted();
+ mTrackingStartedListener.onTrackingStarted();
notifyExpandingStarted();
updatePanelExpansionAndVisibility();
mScrimController.onTrackingStarted();
@@ -3945,7 +3971,7 @@ public final class NotificationPanelViewController implements Dumpable {
}
private void onClosingFinished() {
- mCentralSurfaces.onClosingFinished();
+ mOpenCloseListener.onClosingFinished();
setClosingWithAlphaFadeout(false);
mMediaHierarchyManager.closeGuts();
}
@@ -4504,11 +4530,13 @@ public final class NotificationPanelViewController implements Dumpable {
*/
public void initDependencies(
CentralSurfaces centralSurfaces,
+ GestureRecorder recorder,
Runnable hideExpandedRunnable,
NotificationShelfController notificationShelfController) {
// TODO(b/254859580): this can be injected.
mCentralSurfaces = centralSurfaces;
+ mGestureRecorder = recorder;
mHideExpandedRunnable = hideExpandedRunnable;
mNotificationStackScrollLayoutController.setShelfController(notificationShelfController);
mNotificationShelfController = notificationShelfController;
@@ -4590,20 +4618,20 @@ public final class NotificationPanelViewController implements Dumpable {
return false;
}
- // If the view that would receive the touch is disabled, just have status bar
- // eat the gesture.
- if (event.getAction() == MotionEvent.ACTION_DOWN && !mView.isEnabled()) {
- Log.v(TAG,
- String.format(
- "onTouchForwardedFromStatusBar: "
- + "panel view disabled, eating touch at (%d,%d)",
- (int) event.getX(),
- (int) event.getY()
- )
- );
- return true;
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ // If the view that would receive the touch is disabled, just have status
+ // bar eat the gesture.
+ if (!mView.isEnabled()) {
+ mShadeLog.logMotionEvent(event,
+ "onTouchForwardedFromStatusBar: panel view disabled");
+ return true;
+ }
+ if (isFullyCollapsed() && event.getY() < 1f) {
+ // b/235889526 Eat events on the top edge of the phone when collapsed
+ mShadeLog.logMotionEvent(event, "top edge touch ignored");
+ return true;
+ }
}
-
return mView.dispatchTouchEvent(event);
}
};
@@ -5757,7 +5785,7 @@ public final class NotificationPanelViewController implements Dumpable {
if (mSplitShadeEnabled && !isOnKeyguard()) {
setQsExpandImmediate(true);
}
- mCentralSurfaces.makeExpandedVisible(false);
+ mOpenCloseListener.onOpenStarted();
}
if (state == STATE_CLOSED) {
setQsExpandImmediate(false);
@@ -6240,5 +6268,18 @@ public final class NotificationPanelViewController implements Dumpable {
return super.performAccessibilityAction(host, action, args);
}
}
+
+ /** Listens for when touch tracking begins. */
+ interface TrackingStartedListener {
+ void onTrackingStarted();
+ }
+
+ /** Listens for when shade begins opening of finishes closing. */
+ interface OpenCloseListener {
+ /** Called when the shade finishes closing. */
+ void onClosingFinished();
+ /** Called when the shade starts opening. */
+ void onOpenStarted();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
index de9dcf99cde8..a41a15d4e2a2 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
@@ -111,6 +111,9 @@ public interface ShadeController {
/** Handle status bar touch event. */
void onStatusBarTouch(MotionEvent event);
+ /** Called when the shade finishes collapsing. */
+ void onClosingFinished();
+
/** Sets the listener for when the visibility of the shade changes. */
void setVisibilityListener(ShadeVisibilityListener listener);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
index 807e2e63156f..638e74883f23 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
@@ -227,6 +227,16 @@ public final class ShadeControllerImpl implements ShadeController {
}
@Override
+ public void onClosingFinished() {
+ runPostCollapseRunnables();
+ if (!mPresenter.isPresenterFullyCollapsed()) {
+ // if we set it not to be focusable when collapsing, we have to undo it when we aborted
+ // the closing
+ mNotificationShadeWindowController.setNotificationShadeFocusable(true);
+ }
+ }
+
+ @Override
public void instantCollapseShade() {
mNotificationPanelViewController.instantCollapse();
runPostCollapseRunnables();
@@ -329,5 +339,18 @@ public final class ShadeControllerImpl implements ShadeController {
public void setNotificationPanelViewController(
NotificationPanelViewController notificationPanelViewController) {
mNotificationPanelViewController = notificationPanelViewController;
+ mNotificationPanelViewController.setTrackingStartedListener(this::runPostCollapseRunnables);
+ mNotificationPanelViewController.setOpenCloseListener(
+ new NotificationPanelViewController.OpenCloseListener() {
+ @Override
+ public void onClosingFinished() {
+ ShadeControllerImpl.this.onClosingFinished();
+ }
+
+ @Override
+ public void onOpenStarted() {
+ makeExpandedVisible(false);
+ }
+ });
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
index bc456d5d4613..2334a4c27af8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
@@ -15,6 +15,7 @@ import android.os.Trace
import android.util.AttributeSet
import android.util.MathUtils.lerp
import android.view.View
+import android.view.animation.PathInterpolator
import com.android.systemui.animation.Interpolators
import com.android.systemui.statusbar.LightRevealEffect.Companion.getPercentPastThreshold
import com.android.systemui.util.getColorWithAlpha
@@ -88,10 +89,12 @@ object LiftReveal : LightRevealEffect {
class LinearLightRevealEffect(private val isVertical: Boolean) : LightRevealEffect {
- private val INTERPOLATOR = Interpolators.FAST_OUT_SLOW_IN_REVERSE
+ // Interpolator that reveals >80% of the content at 0.5 progress, makes revealing faster
+ private val interpolator = PathInterpolator(/* controlX1= */ 0.4f, /* controlY1= */ 0f,
+ /* controlX2= */ 0.2f, /* controlY2= */ 1f)
override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) {
- val interpolatedAmount = INTERPOLATOR.getInterpolation(amount)
+ val interpolatedAmount = interpolator.getInterpolation(amount)
scrim.interpolatedRevealAmount = interpolatedAmount
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index cdefae6b87f9..f4cd985adbdb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -30,6 +30,7 @@ import android.content.IntentSender;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -37,6 +38,7 @@ import android.util.Log;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import com.android.internal.statusbar.NotificationVisibility;
@@ -127,21 +129,6 @@ public class NotificationLockscreenUserManagerImpl implements
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
switch (action) {
- case Intent.ACTION_USER_SWITCHED:
- mCurrentUserId = intent.getIntExtra(
- Intent.EXTRA_USER_HANDLE, UserHandle.USER_ALL);
- updateCurrentProfilesCache();
-
- Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
-
- updateLockscreenNotificationSetting();
- updatePublicMode();
- mPresenter.onUserSwitched(mCurrentUserId);
-
- for (UserChangedListener listener : mListeners) {
- listener.onUserChanged(mCurrentUserId);
- }
- break;
case Intent.ACTION_USER_REMOVED:
int removedUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
if (removedUserId != -1) {
@@ -181,6 +168,25 @@ public class NotificationLockscreenUserManagerImpl implements
}
};
+ protected final UserTracker.Callback mUserChangedCallback =
+ new UserTracker.Callback() {
+ @Override
+ public void onUserChanged(int newUser, @NonNull Context userContext) {
+ mCurrentUserId = newUser;
+ updateCurrentProfilesCache();
+
+ Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
+
+ updateLockscreenNotificationSetting();
+ updatePublicMode();
+ mPresenter.onUserSwitched(mCurrentUserId);
+
+ for (UserChangedListener listener : mListeners) {
+ listener.onUserChanged(mCurrentUserId);
+ }
+ }
+ };
+
protected final Context mContext;
private final Handler mMainHandler;
protected final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<>();
@@ -284,7 +290,6 @@ public class NotificationLockscreenUserManagerImpl implements
null /* handler */, UserHandle.ALL);
IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_USER_ADDED);
filter.addAction(Intent.ACTION_USER_REMOVED);
filter.addAction(Intent.ACTION_USER_UNLOCKED);
@@ -298,6 +303,8 @@ public class NotificationLockscreenUserManagerImpl implements
mContext.registerReceiver(mBaseBroadcastReceiver, internalFilter, PERMISSION_SELF, null,
Context.RECEIVER_EXPORTED_UNAUDITED);
+ mUserTracker.addCallback(mUserChangedCallback, new HandlerExecutor(mMainHandler));
+
mCurrentUserId = mUserTracker.getUserId(); // in case we reg'd receiver too late
updateCurrentProfilesCache();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
index 7eb890677206..39daa13ae168 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
@@ -44,4 +44,8 @@ class NotifPipelineFlags @Inject constructor(
val shouldFilterUnseenNotifsOnKeyguard: Boolean by lazy {
featureFlags.isEnabled(Flags.FILTER_UNSEEN_NOTIFS_ON_KEYGUARD)
}
+
+ val isNoHunForOldWhenEnabled: Boolean by lazy {
+ featureFlags.isEnabled(Flags.NO_HUN_FOR_OLD_WHEN)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index d97b712df030..3e2dd053d938 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -38,6 +38,7 @@ import java.io.PrintWriter
import javax.inject.Inject
import kotlin.math.min
+
@SysUISingleton
class NotificationWakeUpCoordinator @Inject constructor(
dumpManager: DumpManager,
@@ -45,7 +46,8 @@ class NotificationWakeUpCoordinator @Inject constructor(
private val statusBarStateController: StatusBarStateController,
private val bypassController: KeyguardBypassController,
private val dozeParameters: DozeParameters,
- private val screenOffAnimationController: ScreenOffAnimationController
+ private val screenOffAnimationController: ScreenOffAnimationController,
+ private val logger: NotificationWakeUpCoordinatorLogger,
) : OnHeadsUpChangedListener, StatusBarStateController.StateListener, ShadeExpansionListener,
Dumpable {
@@ -242,6 +244,7 @@ class NotificationWakeUpCoordinator @Inject constructor(
}
override fun onDozeAmountChanged(linear: Float, eased: Float) {
+ logger.logOnDozeAmountChanged(linear, eased)
if (overrideDozeAmountIfAnimatingScreenOff(linear)) {
return
}
@@ -273,6 +276,7 @@ class NotificationWakeUpCoordinator @Inject constructor(
}
override fun onStateChanged(newState: Int) {
+ logger.logOnStateChanged(newState)
if (state == StatusBarState.SHADE && newState == StatusBarState.SHADE) {
// The SHADE -> SHADE transition is only possible as part of cancelling the screen-off
// animation (e.g. by fingerprint unlock). This is done because the system is in an
@@ -320,8 +324,12 @@ class NotificationWakeUpCoordinator @Inject constructor(
private fun overrideDozeAmountIfBypass(): Boolean {
if (bypassController.bypassEnabled) {
if (statusBarStateController.state == StatusBarState.KEYGUARD) {
+ logger.logSetDozeAmount("1.0", "1.0",
+ "Override: bypass (keyguard)", StatusBarState.KEYGUARD)
setDozeAmount(1f, 1f, source = "Override: bypass (keyguard)")
} else {
+ logger.logSetDozeAmount("0.0", "0.0",
+ "Override: bypass (shade)", statusBarStateController.state)
setDozeAmount(0f, 0f, source = "Override: bypass (shade)")
}
return true
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt
new file mode 100644
index 000000000000..b40ce25c58d2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification
+
+import com.android.systemui.log.dagger.NotificationLog
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogLevel.DEBUG
+import javax.inject.Inject
+
+class NotificationWakeUpCoordinatorLogger
+@Inject
+constructor(@NotificationLog private val buffer: LogBuffer) {
+ fun logSetDozeAmount(linear: String, eased: String, source: String, state: Int) {
+ buffer.log(
+ TAG,
+ DEBUG,
+ {
+ str1 = linear
+ str2 = eased
+ str3 = source
+ int1 = state
+ },
+ { "setDozeAmount: linear: $str1, eased: $str2, source: $str3, state: $int1" }
+ )
+ }
+
+ fun logOnDozeAmountChanged(linear: Float, eased: Float) {
+ buffer.log(
+ TAG,
+ DEBUG,
+ {
+ double1 = linear.toDouble()
+ str2 = eased.toString()
+ },
+ { "onDozeAmountChanged($double1, $str2)" }
+ )
+ }
+
+ fun logOnStateChanged(newState: Int) {
+ buffer.log(TAG, DEBUG, { int1 = newState }, { "onStateChanged($int1)" })
+ }
+}
+
+private const val TAG = "NotificationWakeUpCoordinator"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java
index 966ab4c61b50..afdadeb74e23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java
@@ -51,7 +51,9 @@ public abstract class Pluggable<This> {
*/
public final void invalidateList(@Nullable String reason) {
if (mListener != null) {
- Trace.beginSection("Pluggable<" + mName + ">.invalidateList");
+ if (Trace.isEnabled()) {
+ Trace.traceBegin(Trace.TRACE_TAG_APP, "Pluggable<" + mName + ">.invalidateList");
+ }
mListener.onPluggableInvalidated((This) this, reason);
Trace.endSection();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
index e6dbcee10f60..7513aa7fa2a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
@@ -2,22 +2,20 @@ package com.android.systemui.statusbar.notification.interruption
import android.app.Notification
import android.app.Notification.VISIBILITY_SECRET
-import android.content.BroadcastReceiver
import android.content.Context
-import android.content.Intent
-import android.content.IntentFilter
import android.database.ContentObserver
import android.net.Uri
import android.os.Handler
+import android.os.HandlerExecutor
import android.os.UserHandle
import android.provider.Settings
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.CoreStartable
-import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
@@ -78,7 +76,7 @@ private class KeyguardNotificationVisibilityProviderImpl @Inject constructor(
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val highPriorityProvider: HighPriorityProvider,
private val statusBarStateController: SysuiStatusBarStateController,
- private val broadcastDispatcher: BroadcastDispatcher,
+ private val userTracker: UserTracker,
private val secureSettings: SecureSettings,
private val globalSettings: GlobalSettings
) : CoreStartable, KeyguardNotificationVisibilityProvider {
@@ -87,6 +85,15 @@ private class KeyguardNotificationVisibilityProviderImpl @Inject constructor(
private val onStateChangedListeners = ListenerSet<Consumer<String>>()
private var hideSilentNotificationsOnLockscreen: Boolean = false
+ private val userTrackerCallback = object : UserTracker.Callback {
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ if (isLockedOrLocking) {
+ // maybe public mode changed
+ notifyStateChanged("onUserSwitched")
+ }
+ }
+ }
+
override fun start() {
readShowSilentNotificationSetting()
keyguardStateController.addCallback(object : KeyguardStateController.Callback {
@@ -143,14 +150,7 @@ private class KeyguardNotificationVisibilityProviderImpl @Inject constructor(
notifyStateChanged("onStatusBarUpcomingStateChanged")
}
})
- broadcastDispatcher.registerReceiver(object : BroadcastReceiver() {
- override fun onReceive(context: Context, intent: Intent) {
- if (isLockedOrLocking) {
- // maybe public mode changed
- notifyStateChanged(intent.action!!)
- }
- }
- }, IntentFilter(Intent.ACTION_USER_SWITCHED))
+ userTracker.addCallback(userTrackerCallback, HandlerExecutor(handler))
}
override fun addOnStateChangedListener(listener: Consumer<String>) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
index 073b6b041b81..13b3aca2a88f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
@@ -106,6 +106,36 @@ class NotificationInterruptLogger @Inject constructor(
})
}
+ fun logNoHeadsUpOldWhen(
+ entry: NotificationEntry,
+ notifWhen: Long,
+ notifAge: Long
+ ) {
+ buffer.log(TAG, DEBUG, {
+ str1 = entry.logKey
+ long1 = notifWhen
+ long2 = notifAge
+ }, {
+ "No heads up: old when $long1 (age=$long2 ms): $str1"
+ })
+ }
+
+ fun logMaybeHeadsUpDespiteOldWhen(
+ entry: NotificationEntry,
+ notifWhen: Long,
+ notifAge: Long,
+ reason: String
+ ) {
+ buffer.log(TAG, DEBUG, {
+ str1 = entry.logKey
+ str2 = reason
+ long1 = notifWhen
+ long2 = notifAge
+ }, {
+ "Maybe heads up: old when $long1 (age=$long2 ms) but $str2: $str1"
+ })
+ }
+
fun logNoHeadsUpSuppressedBy(
entry: NotificationEntry,
suppressor: NotificationInterruptSuppressor
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index c4f5a3a30608..ec5bd6894283 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -20,6 +20,7 @@ import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD;
import static com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR;
+import android.app.Notification;
import android.app.NotificationManager;
import android.content.ContentResolver;
import android.database.ContentObserver;
@@ -82,7 +83,10 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
FSI_SUPPRESSED_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR(1235),
@UiEvent(doc = "FSI suppressed for requiring neither HUN nor keyguard")
- FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD(1236);
+ FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD(1236),
+
+ @UiEvent(doc = "HUN suppressed for old when")
+ HUN_SUPPRESSED_OLD_WHEN(1237);
private final int mId;
@@ -346,6 +350,10 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
return false;
}
+ if (shouldSuppressHeadsUpWhenAwakeForOldWhen(entry, log)) {
+ return false;
+ }
+
for (int i = 0; i < mSuppressors.size(); i++) {
if (mSuppressors.get(i).suppressAwakeHeadsUp(entry)) {
if (log) mLogger.logNoHeadsUpSuppressedBy(entry, mSuppressors.get(i));
@@ -470,4 +478,51 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
private boolean isSnoozedPackage(StatusBarNotification sbn) {
return mHeadsUpManager.isSnoozed(sbn.getPackageName());
}
+
+ private boolean shouldSuppressHeadsUpWhenAwakeForOldWhen(NotificationEntry entry, boolean log) {
+ if (!mFlags.isNoHunForOldWhenEnabled()) {
+ return false;
+ }
+
+ final Notification notification = entry.getSbn().getNotification();
+ if (notification == null) {
+ return false;
+ }
+
+ final long when = notification.when;
+ final long now = System.currentTimeMillis();
+ final long age = now - when;
+
+ if (age < MAX_HUN_WHEN_AGE_MS) {
+ return false;
+ }
+
+ if (when <= 0) {
+ // Some notifications (including many system notifications) are posted with the "when"
+ // field set to 0. Nothing in the Javadocs for Notification mentions a special meaning
+ // for a "when" of 0, but Android didn't even exist at the dawn of the Unix epoch.
+ // Therefore, assume that these notifications effectively don't have a "when" value,
+ // and don't suppress HUNs.
+ if (log) mLogger.logMaybeHeadsUpDespiteOldWhen(entry, when, age, "when <= 0");
+ return false;
+ }
+
+ if (notification.fullScreenIntent != null) {
+ if (log) mLogger.logMaybeHeadsUpDespiteOldWhen(entry, when, age, "full-screen intent");
+ return false;
+ }
+
+ if (notification.isForegroundService()) {
+ if (log) mLogger.logMaybeHeadsUpDespiteOldWhen(entry, when, age, "foreground service");
+ return false;
+ }
+
+ if (log) mLogger.logNoHeadsUpOldWhen(entry, when, age);
+ final int uid = entry.getSbn().getUid();
+ final String packageName = entry.getSbn().getPackageName();
+ mUiEventLogger.log(NotificationInterruptEvent.HUN_SUPPRESSED_OLD_WHEN, uid, packageName);
+ return true;
+ }
+
+ public static final long MAX_HUN_WHEN_AGE_MS = 24 * 60 * 60 * 1000;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 1eccc9879a87..d7d5ac961249 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.row;
import static android.app.Notification.Action.SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
+import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.PARENT_DISMISSED;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
import android.animation.Animator;
@@ -1404,6 +1405,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mKeepInParentForDismissAnimation = keepInParent;
}
+ /** @return true if the User has dismissed this notif's parent */
+ public boolean isParentDismissed() {
+ return getEntry().getDismissState() == PARENT_DISMISSED;
+ }
+
@Override
public boolean isRemoved() {
return mRemoved;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index f9e9a2d38138..8a400d5fef99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -359,10 +359,15 @@ public class ExpandableNotificationRowController implements NotifViewController
@Override
public boolean offerToKeepInParentForAnimation() {
- if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_DISMISSAL_ANIMATION)) {
+ //If the User dismissed the notification's parent, we want to keep it attached until the
+ //dismiss animation is ongoing. Therefore we don't want to remove it in the ShadeViewDiffer.
+ if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_DISMISSAL_ANIMATION)
+ && mView.isParentDismissed()) {
mView.setKeepInParentForDismissAnimation(true);
return true;
}
+
+ //Otherwise the view system doesn't do the removal, so we rely on the ShadeViewDiffer
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 34e62ce321e0..03057a44ef90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -496,7 +496,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
&& mPendingAuthenticated.userId == KeyguardUpdateMonitor.getCurrentUser();
}
- public int getMode() {
+ public @WakeAndUnlockMode int getMode() {
return mMode;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 0ec7c622ba5f..e068f87f03b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -51,7 +51,6 @@ import com.android.systemui.qs.QSPanelController;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shade.NotificationShadeWindowViewController;
-import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.LightRevealScrim;
import com.android.systemui.statusbar.NotificationPresenter;
@@ -288,8 +287,6 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn
void onTouchEvent(MotionEvent event);
- GestureRecorder getGestureRecorder();
-
BiometricUnlockController getBiometricUnlockController();
void showWirelessChargingAnimation(int batteryLevel);
@@ -404,10 +401,6 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn
LightRevealScrim getLightRevealScrim();
- void onTrackingStarted();
-
- void onClosingFinished();
-
// TODO: Figure out way to remove these.
NavigationBarView getNavigationBarView();
@@ -491,13 +484,6 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn
void updateNotificationPanelTouchState();
- /**
- * TODO(b/257041702) delete this
- * @deprecated Use ShadeController#makeExpandedVisible
- */
- @Deprecated
- void makeExpandedVisible(boolean force);
-
int getDisplayId();
int getRotation();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index d98877237dae..d027ed055c7b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -158,6 +158,8 @@ import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.keyguard.ui.binder.LightRevealScrimViewBinder;
+import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.plugins.DarkIconDispatcher;
@@ -474,6 +476,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private final OngoingCallController mOngoingCallController;
private final StatusBarSignalPolicy mStatusBarSignalPolicy;
private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
+ private final Lazy<LightRevealScrimViewModel> mLightRevealScrimViewModelLazy;
/** Controller for the Shade. */
@VisibleForTesting
@@ -740,7 +743,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
DeviceStateManager deviceStateManager,
WiredChargingRippleController wiredChargingRippleController,
IDreamManager dreamManager,
- Lazy<CameraLauncher> cameraLauncherLazy) {
+ Lazy<CameraLauncher> cameraLauncherLazy,
+ Lazy<LightRevealScrimViewModel> lightRevealScrimViewModelLazy) {
mContext = context;
mNotificationsController = notificationsController;
mFragmentService = fragmentService;
@@ -854,6 +858,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
deviceStateManager.registerCallback(mMainExecutor,
new FoldStateListener(mContext, this::onFoldedStateChanged));
wiredChargingRippleController.registerCallbacks();
+
+ mLightRevealScrimViewModelLazy = lightRevealScrimViewModelLazy;
}
@Override
@@ -983,6 +989,12 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
@Override
public void onKeyguardGoingAwayChanged() {
+ if (mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+ // This code path is not used if the KeyguardTransitionRepository is managing
+ // the lightreveal scrim.
+ return;
+ }
+
// The light reveal scrim should always be fully revealed by the time the keyguard
// is done going away. Double check that this is true.
if (!mKeyguardStateController.isKeyguardGoingAway()) {
@@ -1219,6 +1231,12 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mScrimController.attachViews(scrimBehind, notificationsScrim, scrimInFront);
mLightRevealScrim = mNotificationShadeWindowView.findViewById(R.id.light_reveal_scrim);
+
+ if (mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+ LightRevealScrimViewBinder.bind(
+ mLightRevealScrim, mLightRevealScrimViewModelLazy.get());
+ }
+
mLightRevealScrim.setScrimOpaqueChangedListener((opaque) -> {
Runnable updateOpaqueness = () -> {
mNotificationShadeWindowController.setLightRevealScrimOpaque(
@@ -1240,6 +1258,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mNotificationPanelViewController.initDependencies(
this,
+ mGestureRec,
mShadeController::makeExpandedInvisible,
mNotificationShelfController);
@@ -1837,7 +1856,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
public void onLaunchAnimationCancelled(boolean isLaunchForActivity) {
if (mPresenter.isPresenterFullyCollapsed() && !mPresenter.isCollapsing()
&& isLaunchForActivity) {
- onClosingFinished();
+ mShadeController.onClosingFinished();
} else {
mShadeController.collapseShade(true /* animate */);
}
@@ -1847,7 +1866,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
@Override
public void onLaunchAnimationEnd(boolean launchIsFullScreen) {
if (!mPresenter.isCollapsing()) {
- onClosingFinished();
+ mShadeController.onClosingFinished();
}
if (launchIsFullScreen) {
mShadeController.instantCollapseShade();
@@ -2032,11 +2051,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
}
@Override
- public GestureRecorder getGestureRecorder() {
- return mGestureRec;
- }
-
- @Override
public BiometricUnlockController getBiometricUnlockController() {
return mBiometricUnlockController;
}
@@ -3293,6 +3307,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
return;
}
+ if (mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+ return;
+ }
+
final boolean wakingUpFromPowerButton = wakingUp
&& !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)
&& mWakefulnessLifecycle.getLastWakeReason()
@@ -3319,21 +3337,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
return mLightRevealScrim;
}
- @Override
- public void onTrackingStarted() {
- mShadeController.runPostCollapseRunnables();
- }
-
- @Override
- public void onClosingFinished() {
- mShadeController.runPostCollapseRunnables();
- if (!mPresenter.isPresenterFullyCollapsed()) {
- // if we set it not to be focusable when collapsing, we have to undo it when we aborted
- // the closing
- mNotificationShadeWindowController.setNotificationShadeFocusable(true);
- }
- }
-
// TODO: Figure out way to remove these.
@Override
public NavigationBarView getNavigationBarView() {
@@ -3565,12 +3568,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mNotificationIconAreaController.setAnimationsEnabled(!disabled);
}
- //TODO(b/257041702) delete
- @Override
- public void makeExpandedVisible(boolean force) {
- mShadeController.makeExpandedVisible(force);
- }
-
final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
@Override
public void onScreenTurningOn(Runnable onDrawn) {
@@ -4078,7 +4075,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
return;
}
- mLightRevealScrim.setAlpha(mScrimController.getState().getMaxLightRevealScrimAlpha());
+ if (!mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+ mLightRevealScrim.setAlpha(mScrimController.getState().getMaxLightRevealScrimAlpha());
+ }
}
@Override
@@ -4259,6 +4258,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
@Override
public void onDozeAmountChanged(float linear, float eased) {
if (mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ANIMATIONS)
+ && !mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)
&& !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
mLightRevealScrim.setRevealAmount(1f - linear);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
index 26e6db664e07..4beb87ddae2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
@@ -15,23 +15,21 @@
package com.android.systemui.statusbar.phone;
import android.app.StatusBarManager;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.UserInfo;
import android.os.UserHandle;
import android.os.UserManager;
import androidx.annotation.NonNull;
-import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.settings.UserTracker;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -43,9 +41,9 @@ public class ManagedProfileControllerImpl implements ManagedProfileController {
private final List<Callback> mCallbacks = new ArrayList<>();
private final Context mContext;
+ private final Executor mMainExecutor;
private final UserManager mUserManager;
private final UserTracker mUserTracker;
- private final BroadcastDispatcher mBroadcastDispatcher;
private final LinkedList<UserInfo> mProfiles;
private boolean mListening;
private int mCurrentUser;
@@ -53,12 +51,12 @@ public class ManagedProfileControllerImpl implements ManagedProfileController {
/**
*/
@Inject
- public ManagedProfileControllerImpl(Context context, UserTracker userTracker,
- BroadcastDispatcher broadcastDispatcher) {
+ public ManagedProfileControllerImpl(Context context, @Main Executor mainExecutor,
+ UserTracker userTracker) {
mContext = context;
+ mMainExecutor = mainExecutor;
mUserManager = UserManager.get(mContext);
mUserTracker = userTracker;
- mBroadcastDispatcher = broadcastDispatcher;
mProfiles = new LinkedList<UserInfo>();
}
@@ -130,30 +128,34 @@ public class ManagedProfileControllerImpl implements ManagedProfileController {
}
private void setListening(boolean listening) {
+ if (mListening == listening) {
+ return;
+ }
mListening = listening;
if (listening) {
reloadManagedProfiles();
-
- final IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_USER_SWITCHED);
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
- mBroadcastDispatcher.registerReceiver(
- mReceiver, filter, null /* handler */, UserHandle.ALL);
+ mUserTracker.addCallback(mUserChangedCallback, mMainExecutor);
} else {
- mBroadcastDispatcher.unregisterReceiver(mReceiver);
+ mUserTracker.removeCallback(mUserChangedCallback);
}
}
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- reloadManagedProfiles();
- for (Callback callback : mCallbacks) {
- callback.onManagedProfileChanged();
- }
- }
- };
+ private final UserTracker.Callback mUserChangedCallback =
+ new UserTracker.Callback() {
+ @Override
+ public void onUserChanged(int newUser, @NonNull Context userContext) {
+ reloadManagedProfiles();
+ for (Callback callback : mCallbacks) {
+ callback.onManagedProfileChanged();
+ }
+ }
+
+ @Override
+ public void onProfilesChanged(@NonNull List<UserInfo> profiles) {
+ reloadManagedProfiles();
+ for (Callback callback : mCallbacks) {
+ callback.onManagedProfileChanged();
+ }
+ }
+ };
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index fb0d3e4406bf..d500f999b5e2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -352,6 +352,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
.getBoolean(R.bool.notification_scrim_transparent);
updateScrims();
mKeyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback);
+
+ // prepare() sets proper initial values for most states
+ for (ScrimState state : ScrimState.values()) {
+ state.prepare(state);
+ }
}
/**
@@ -641,10 +646,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
private void setTransitionToFullShade(boolean transitioning) {
if (transitioning != mTransitioningToFullShade) {
mTransitioningToFullShade = transitioning;
- if (transitioning) {
- // Let's make sure the shade locked is ready
- ScrimState.SHADE_LOCKED.prepare(mState);
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 52430d33cbf0..0e9d3ce33d5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -146,18 +146,12 @@ public enum ScrimState {
mBehindAlpha = mClipQsScrim ? 1 : mDefaultScrimAlpha;
mNotifAlpha = 1f;
mFrontAlpha = 0f;
- mBehindTint = Color.BLACK;
+ mBehindTint = mClipQsScrim ? Color.TRANSPARENT : Color.BLACK;
if (mClipQsScrim) {
updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK);
}
}
-
- // to make sure correct color is returned before "prepare" is called
- @Override
- public int getBehindTint() {
- return Color.BLACK;
- }
},
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt
index 946d7e4a3e75..4d914fe0adef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt
@@ -52,6 +52,6 @@ class StatusBarPipelineFlags @Inject constructor(private val featureFlags: Featu
* Returns true if we should apply some coloring to the wifi icon that was rendered with the new
* pipeline to help with debugging.
*/
- // For now, just always apply the debug coloring if we've enabled the new icon.
- fun useWifiDebugColoring(): Boolean = useNewWifiIcon()
+ fun useWifiDebugColoring(): Boolean =
+ featureFlags.isEnabled(Flags.NEW_STATUS_BAR_ICONS_DEBUG_COLORING)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepository.kt
index 7aa5ee1389f3..8ff9198da119 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepository.kt
@@ -23,9 +23,10 @@ import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCall
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.qs.SettingObserver
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logInputChange
+import com.android.systemui.statusbar.pipeline.dagger.AirplaneTableLog
import com.android.systemui.util.settings.GlobalSettings
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -58,7 +59,7 @@ class AirplaneModeRepositoryImpl
constructor(
@Background private val bgHandler: Handler,
private val globalSettings: GlobalSettings,
- logger: ConnectivityPipelineLogger,
+ @AirplaneTableLog logger: TableLogBuffer,
@Application scope: CoroutineScope,
) : AirplaneModeRepository {
// TODO(b/254848912): Replace this with a generic SettingObserver coroutine once we have it.
@@ -82,7 +83,12 @@ constructor(
awaitClose { observer.isListening = false }
}
.distinctUntilChanged()
- .logInputChange(logger, "isAirplaneMode")
+ .logDiffsForTable(
+ logger,
+ columnPrefix = "",
+ columnName = "isAirplaneMode",
+ initialValue = false
+ )
.stateIn(
scope,
started = SharingStarted.WhileSubscribed(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModel.kt
index fe30c0169021..4a5342e0f765 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModel.kt
@@ -36,16 +36,20 @@ import kotlinx.coroutines.flow.stateIn
* [com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository] for
* more details.
*/
+interface AirplaneModeViewModel {
+ /** True if the airplane mode icon is currently visible in the status bar. */
+ val isAirplaneModeIconVisible: StateFlow<Boolean>
+}
+
@SysUISingleton
-class AirplaneModeViewModel
+class AirplaneModeViewModelImpl
@Inject
constructor(
interactor: AirplaneModeInteractor,
logger: ConnectivityPipelineLogger,
@Application private val scope: CoroutineScope,
-) {
- /** True if the airplane mode icon is currently visible in the status bar. */
- val isAirplaneModeIconVisible: StateFlow<Boolean> =
+) : AirplaneModeViewModel {
+ override val isAirplaneModeIconVisible: StateFlow<Boolean> =
combine(interactor.isAirplaneMode, interactor.isForceHidden) {
isAirplaneMode,
isAirplaneIconForceHidden ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/AirplaneTableLog.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/AirplaneTableLog.kt
new file mode 100644
index 000000000000..4f70f660187f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/AirplaneTableLog.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.dagger
+
+import javax.inject.Qualifier
+
+/** Airplane mode logs in table format. */
+@Qualifier
+@MustBeDocumented
+@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
+annotation class AirplaneTableLog
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index 0662fb3d52b9..fb67f1a1bf50 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -21,6 +21,8 @@ import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.TableLogBufferFactory
import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepositoryImpl
+import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
+import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepositoryImpl
import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository
@@ -33,6 +35,8 @@ import com.android.systemui.statusbar.pipeline.shared.data.repository.Connectivi
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryImpl
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
import dagger.Binds
import dagger.Module
import dagger.Provides
@@ -43,12 +47,18 @@ abstract class StatusBarPipelineModule {
abstract fun airplaneModeRepository(impl: AirplaneModeRepositoryImpl): AirplaneModeRepository
@Binds
+ abstract fun airplaneModeViewModel(impl: AirplaneModeViewModelImpl): AirplaneModeViewModel
+
+ @Binds
abstract fun connectivityRepository(impl: ConnectivityRepositoryImpl): ConnectivityRepository
@Binds
abstract fun wifiRepository(impl: WifiRepositoryImpl): WifiRepository
@Binds
+ abstract fun wifiInteractor(impl: WifiInteractorImpl): WifiInteractor
+
+ @Binds
abstract fun mobileConnectionsRepository(
impl: MobileConnectionsRepositoryImpl
): MobileConnectionsRepository
@@ -71,5 +81,13 @@ abstract class StatusBarPipelineModule {
fun provideWifiTableLogBuffer(factory: TableLogBufferFactory): TableLogBuffer {
return factory.create("WifiTableLog", 100)
}
+
+ @JvmStatic
+ @Provides
+ @SysUISingleton
+ @AirplaneTableLog
+ fun provideAirplaneTableLogBuffer(factory: TableLogBufferFactory): TableLogBuffer {
+ return factory.create("AirplaneTableLog", 30)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt
index 8436b13d7038..a682a5711a6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt
@@ -31,15 +31,19 @@ sealed class WifiNetworkModel : Diffable<WifiNetworkModel> {
if (prevVal is Inactive) {
return
}
- row.logChange(COL_NETWORK_TYPE, TYPE_INACTIVE)
if (prevVal is CarrierMerged) {
// The only difference between CarrierMerged and Inactive is the type
+ row.logChange(COL_NETWORK_TYPE, TYPE_INACTIVE)
return
}
// When changing from Active to Inactive, we need to log diffs to all the fields.
- logDiffsFromActiveToNotActive(prevVal as Active, row)
+ logFullNonActiveNetwork(TYPE_INACTIVE, row)
+ }
+
+ override fun logFull(row: TableRowLogger) {
+ logFullNonActiveNetwork(TYPE_INACTIVE, row)
}
}
@@ -56,15 +60,15 @@ sealed class WifiNetworkModel : Diffable<WifiNetworkModel> {
if (prevVal is CarrierMerged) {
return
}
- row.logChange(COL_NETWORK_TYPE, TYPE_CARRIER_MERGED)
if (prevVal is Inactive) {
// The only difference between CarrierMerged and Inactive is the type.
+ row.logChange(COL_NETWORK_TYPE, TYPE_CARRIER_MERGED)
return
}
// When changing from Active to CarrierMerged, we need to log diffs to all the fields.
- logDiffsFromActiveToNotActive(prevVal as Active, row)
+ logFullNonActiveNetwork(TYPE_CARRIER_MERGED, row)
}
}
@@ -121,7 +125,11 @@ sealed class WifiNetworkModel : Diffable<WifiNetworkModel> {
row.logChange(COL_VALIDATED, isValidated)
}
if (prevVal !is Active || prevVal.level != level) {
- row.logChange(COL_LEVEL, level ?: LEVEL_DEFAULT)
+ if (level != null) {
+ row.logChange(COL_LEVEL, level)
+ } else {
+ row.logChange(COL_LEVEL, LEVEL_DEFAULT)
+ }
}
if (prevVal !is Active || prevVal.ssid != ssid) {
row.logChange(COL_SSID, ssid)
@@ -143,7 +151,6 @@ sealed class WifiNetworkModel : Diffable<WifiNetworkModel> {
}
}
-
override fun toString(): String {
// Only include the passpoint-related values in the string if we have them. (Most
// networks won't have them so they'll be mostly clutter.)
@@ -170,21 +177,15 @@ sealed class WifiNetworkModel : Diffable<WifiNetworkModel> {
}
}
- internal fun logDiffsFromActiveToNotActive(prevActive: Active, row: TableRowLogger) {
+ internal fun logFullNonActiveNetwork(type: String, row: TableRowLogger) {
+ row.logChange(COL_NETWORK_TYPE, type)
row.logChange(COL_NETWORK_ID, NETWORK_ID_DEFAULT)
row.logChange(COL_VALIDATED, false)
row.logChange(COL_LEVEL, LEVEL_DEFAULT)
row.logChange(COL_SSID, null)
-
- if (prevActive.isPasspointAccessPoint) {
- row.logChange(COL_PASSPOINT_ACCESS_POINT, false)
- }
- if (prevActive.isOnlineSignUpForPasspointAccessPoint) {
- row.logChange(COL_ONLINE_SIGN_UP, false)
- }
- if (prevActive.passpointProviderFriendlyName != null) {
- row.logChange(COL_PASSPOINT_NAME, null)
- }
+ row.logChange(COL_PASSPOINT_ACCESS_POINT, false)
+ row.logChange(COL_ONLINE_SIGN_UP, false)
+ row.logChange(COL_PASSPOINT_NAME, null)
}
}
@@ -201,5 +202,5 @@ const val COL_PASSPOINT_ACCESS_POINT = "isPasspointAccessPoint"
const val COL_ONLINE_SIGN_UP = "isOnlineSignUpForPasspointAccessPoint"
const val COL_PASSPOINT_NAME = "passpointProviderFriendlyName"
-const val LEVEL_DEFAULT = -1
-const val NETWORK_ID_DEFAULT = -1
+val LEVEL_DEFAULT: String? = null
+val NETWORK_ID_DEFAULT: String? = null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
index 3a3e611de96a..ec935fe23d39 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
@@ -34,16 +34,36 @@ import kotlinx.coroutines.flow.map
* This interactor processes information from our data layer into information that the UI layer can
* use.
*/
-@SysUISingleton
-class WifiInteractor @Inject constructor(
- connectivityRepository: ConnectivityRepository,
- wifiRepository: WifiRepository,
-) {
+interface WifiInteractor {
/**
* The SSID (service set identifier) of the wifi network. Null if we don't have a network, or
* have a network but no valid SSID.
*/
- val ssid: Flow<String?> = wifiRepository.wifiNetwork.map { info ->
+ val ssid: Flow<String?>
+
+ /** Our current enabled status. */
+ val isEnabled: Flow<Boolean>
+
+ /** Our current default status. */
+ val isDefault: Flow<Boolean>
+
+ /** Our current wifi network. See [WifiNetworkModel]. */
+ val wifiNetwork: Flow<WifiNetworkModel>
+
+ /** Our current wifi activity. See [WifiActivityModel]. */
+ val activity: StateFlow<WifiActivityModel>
+
+ /** True if we're configured to force-hide the wifi icon and false otherwise. */
+ val isForceHidden: Flow<Boolean>
+}
+
+@SysUISingleton
+class WifiInteractorImpl @Inject constructor(
+ connectivityRepository: ConnectivityRepository,
+ wifiRepository: WifiRepository,
+) : WifiInteractor {
+
+ override val ssid: Flow<String?> = wifiRepository.wifiNetwork.map { info ->
when (info) {
is WifiNetworkModel.Inactive -> null
is WifiNetworkModel.CarrierMerged -> null
@@ -56,20 +76,15 @@ class WifiInteractor @Inject constructor(
}
}
- /** Our current enabled status. */
- val isEnabled: Flow<Boolean> = wifiRepository.isWifiEnabled
+ override val isEnabled: Flow<Boolean> = wifiRepository.isWifiEnabled
- /** Our current default status. */
- val isDefault: Flow<Boolean> = wifiRepository.isWifiDefault
+ override val isDefault: Flow<Boolean> = wifiRepository.isWifiDefault
- /** Our current wifi network. See [WifiNetworkModel]. */
- val wifiNetwork: Flow<WifiNetworkModel> = wifiRepository.wifiNetwork
+ override val wifiNetwork: Flow<WifiNetworkModel> = wifiRepository.wifiNetwork
- /** Our current wifi activity. See [WifiActivityModel]. */
- val activity: StateFlow<WifiActivityModel> = wifiRepository.wifiActivity
+ override val activity: StateFlow<WifiActivityModel> = wifiRepository.wifiActivity
- /** True if we're configured to force-hide the wifi icon and false otherwise. */
- val isForceHidden: Flow<Boolean> = connectivityRepository.forceHiddenSlots.map {
+ override val isForceHidden: Flow<Boolean> = connectivityRepository.forceHiddenSlots.map {
it.contains(ConnectivitySlot.WIFI)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index d84cbcc60853..6875b523a962 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -120,6 +120,7 @@ public class Clock extends TextView implements
@Override
public void onUserChanged(int newUser, @NonNull Context userContext) {
mCurrentUserId = newUser;
+ updateClock();
}
};
@@ -190,7 +191,6 @@ public class Clock extends TextView implements
filter.addAction(Intent.ACTION_TIME_CHANGED);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
- filter.addAction(Intent.ACTION_USER_SWITCHED);
// NOTE: This receiver could run before this method returns, as it's not dispatching
// on the main thread and BroadcastDispatcher may not need to register with Context.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java
index b234e9c4e746..63b9ff9717d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java
@@ -28,11 +28,14 @@ import androidx.annotation.NonNull;
import com.android.systemui.Dumpable;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.settings.UserTracker;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Date;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -45,22 +48,34 @@ public class NextAlarmControllerImpl extends BroadcastReceiver
private final ArrayList<NextAlarmChangeCallback> mChangeCallbacks = new ArrayList<>();
+ private final UserTracker mUserTracker;
private AlarmManager mAlarmManager;
private AlarmManager.AlarmClockInfo mNextAlarm;
+ private final UserTracker.Callback mUserChangedCallback =
+ new UserTracker.Callback() {
+ @Override
+ public void onUserChanged(int newUser, @NonNull Context userContext) {
+ updateNextAlarm();
+ }
+ };
+
/**
*/
@Inject
public NextAlarmControllerImpl(
+ @Main Executor mainExecutor,
AlarmManager alarmManager,
BroadcastDispatcher broadcastDispatcher,
- DumpManager dumpManager) {
+ DumpManager dumpManager,
+ UserTracker userTracker) {
dumpManager.registerDumpable("NextAlarmController", this);
mAlarmManager = alarmManager;
+ mUserTracker = userTracker;
IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED);
broadcastDispatcher.registerReceiver(this, filter, null, UserHandle.ALL);
+ mUserTracker.addCallback(mUserChangedCallback, mainExecutor);
updateNextAlarm();
}
@@ -98,14 +113,13 @@ public class NextAlarmControllerImpl extends BroadcastReceiver
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
- if (action.equals(Intent.ACTION_USER_SWITCHED)
- || action.equals(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED)) {
+ if (action.equals(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED)) {
updateNextAlarm();
}
}
private void updateNextAlarm() {
- mNextAlarm = mAlarmManager.getNextAlarmClock(UserHandle.USER_CURRENT);
+ mNextAlarm = mAlarmManager.getNextAlarmClock(mUserTracker.getUserId());
fireNextAlarmChanged();
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserModule.java b/packages/SystemUI/src/com/android/systemui/user/UserModule.java
index 0c72b78a3c46..2b29885db682 100644
--- a/packages/SystemUI/src/com/android/systemui/user/UserModule.java
+++ b/packages/SystemUI/src/com/android/systemui/user/UserModule.java
@@ -17,6 +17,7 @@
package com.android.systemui.user;
import android.app.Activity;
+import android.os.UserHandle;
import com.android.settingslib.users.EditUserInfoController;
import com.android.systemui.user.data.repository.UserRepositoryModule;
@@ -51,4 +52,22 @@ public abstract class UserModule {
@IntoMap
@ClassKey(UserSwitcherActivity.class)
public abstract Activity provideUserSwitcherActivity(UserSwitcherActivity activity);
+
+ /**
+ * Provides the {@link UserHandle} for the user associated with this System UI process.
+ *
+ * <p>Note that this is static and unchanging for the life-time of the process we are running
+ * in. It can be <i>different</i> from the user that is the currently-selected user, which may
+ * be associated with a different System UI process.
+ *
+ * <p>For example, the System UI process which creates all the windows and renders UI is always
+ * the one associated with the primary user on the device. However, if the user is switched to
+ * another, non-primary user (for example user "X"), then a secondary System UI process will be
+ * spawned. While the original primary user process continues to be the only one rendering UI,
+ * the new system UI process may be used for things like file or content access.
+ */
+ @Provides
+ public static UserHandle provideUserHandle() {
+ return new UserHandle(UserHandle.myUserId());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
index 4c9b8e4639ca..c0f03902202a 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
@@ -242,7 +242,15 @@ constructor(
val isUserSwitcherEnabled =
globalSettings.getIntForUser(
Settings.Global.USER_SWITCHER_ENABLED,
- 0,
+ if (
+ appContext.resources.getBoolean(
+ com.android.internal.R.bool.config_showUserSwitcherByDefault
+ )
+ ) {
+ 1
+ } else {
+ 0
+ },
UserHandle.USER_SYSTEM,
) != 0
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
index c5b697c90e0c..512fadf1384f 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
@@ -114,9 +114,9 @@ constructor(
private val callbackMutex = Mutex()
private val callbacks = mutableSetOf<UserCallback>()
- private val userInfos =
- combine(repository.userSwitcherSettings, repository.userInfos) { settings, userInfos ->
- userInfos.filter { !it.isGuest || canCreateGuestUser(settings) }.filter { it.isFull }
+ private val userInfos: Flow<List<UserInfo>> =
+ repository.userInfos.map { userInfos ->
+ userInfos.filter { it.isFull }
}
/** List of current on-device users to select from. */
@@ -493,7 +493,7 @@ constructor(
fun showUserSwitcher(context: Context, expandable: Expandable) {
if (!featureFlags.isEnabled(Flags.FULL_SCREEN_USER_SWITCHER)) {
- showDialog(ShowDialogRequestModel.ShowUserSwitcherDialog)
+ showDialog(ShowDialogRequestModel.ShowUserSwitcherDialog(expandable))
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/model/ShowDialogRequestModel.kt b/packages/SystemUI/src/com/android/systemui/user/domain/model/ShowDialogRequestModel.kt
index 85c29647719b..14cc3e783fed 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/model/ShowDialogRequestModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/model/ShowDialogRequestModel.kt
@@ -18,11 +18,13 @@
package com.android.systemui.user.domain.model
import android.os.UserHandle
+import com.android.systemui.animation.Expandable
import com.android.systemui.qs.user.UserSwitchDialogController
/** Encapsulates a request to show a dialog. */
sealed class ShowDialogRequestModel(
open val dialogShower: UserSwitchDialogController.DialogShower? = null,
+ open val expandable: Expandable? = null,
) {
data class ShowAddUserDialog(
val userHandle: UserHandle,
@@ -45,5 +47,7 @@ sealed class ShowDialogRequestModel(
) : ShowDialogRequestModel(dialogShower)
/** Show the user switcher dialog */
- object ShowUserSwitcherDialog : ShowDialogRequestModel()
+ data class ShowUserSwitcherDialog(
+ override val expandable: Expandable?,
+ ) : ShowDialogRequestModel()
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/DialogShowerImpl.kt b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/DialogShowerImpl.kt
new file mode 100644
index 000000000000..3fe2a7b19851
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/DialogShowerImpl.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.user.ui.dialog
+
+import android.app.Dialog
+import android.content.DialogInterface
+import com.android.systemui.animation.DialogCuj
+import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.qs.user.UserSwitchDialogController.DialogShower
+
+/** Extracted from [UserSwitchDialogController] */
+class DialogShowerImpl(
+ private val animateFrom: Dialog,
+ private val dialogLaunchAnimator: DialogLaunchAnimator,
+) : DialogInterface by animateFrom, DialogShower {
+ override fun showDialog(dialog: Dialog, cuj: DialogCuj) {
+ dialogLaunchAnimator.showFromDialog(dialog, animateFrom = animateFrom, cuj)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitchDialog.kt b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitchDialog.kt
index ed2589889435..b8ae257aaac5 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitchDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitchDialog.kt
@@ -60,6 +60,7 @@ class UserSwitchDialog(
setView(gridFrame)
adapter.linkToViewGroup(gridFrame.findViewById(R.id.grid))
+ adapter.injectDialogShower(DialogShowerImpl(this, dialogLaunchAnimator))
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt
index 41410542204c..d4512309f6c6 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt
@@ -133,7 +133,10 @@ constructor(
}
currentDialog = dialog
- if (request.dialogShower != null && dialogCuj != null) {
+ val controller = request.expandable?.dialogLaunchController(dialogCuj)
+ if (controller != null) {
+ dialogLaunchAnimator.get().show(dialog, controller)
+ } else if (request.dialogShower != null && dialogCuj != null) {
request.dialogShower?.showDialog(dialog, dialogCuj)
} else {
dialog.show()
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
index 0910ea36b7ff..37115ad53880 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
@@ -19,6 +19,8 @@ package com.android.systemui.user.ui.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
+import com.android.systemui.R
+import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.ui.drawable.CircularDrawable
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.user.domain.interactor.GuestUserInteractor
@@ -144,7 +146,12 @@ private constructor(
): UserViewModel {
return UserViewModel(
viewKey = model.id,
- name = model.name,
+ name =
+ if (model.isGuest && model.isSelected) {
+ Text.Resource(R.string.guest_exit_quick_settings_button)
+ } else {
+ model.name
+ },
image = CircularDrawable(model.image),
isSelectionMarkerVisible = model.isSelected,
alpha =
diff --git a/packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt b/packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt
index 5b16ae999aa3..b311318fb111 100644
--- a/packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt
@@ -22,11 +22,22 @@ import android.os.Trace
* Run a block within a [Trace] section.
* Calls [Trace.beginSection] before and [Trace.endSection] after the passed block.
*/
-inline fun <T> traceSection(tag: String, block: () -> T): T {
- Trace.beginSection(tag)
- try {
- return block()
- } finally {
- Trace.endSection()
+inline fun <T> traceSection(tag: String, block: () -> T): T =
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_APP)) {
+ Trace.traceBegin(Trace.TRACE_TAG_APP, tag)
+ try {
+ block()
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_APP)
+ }
+ } else {
+ block()
+ }
+
+class TraceUtils {
+ companion object {
+ inline fun traceRunnable(tag: String, crossinline block: () -> Unit): Runnable {
+ return Runnable { traceSection(tag) { block() } }
+ }
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
index f71d596ff835..b61b2e66fb22 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
@@ -20,7 +20,6 @@ import java.util.concurrent.atomic.AtomicReference
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onStart
@@ -58,6 +57,22 @@ fun <T, R> Flow<T>.pairwiseBy(
onStart { emit(initialValue) }.pairwiseBy(transform)
/**
+ * Returns a new [Flow] that combines the two most recent emissions from [this] using [transform].
+ *
+ *
+ * The output of [getInitialValue] will be used as the "old" value for the first emission. As
+ * opposed to the initial value in the above [pairwiseBy], [getInitialValue] can do some work before
+ * returning the initial value.
+ *
+ * Useful for code that needs to compare the current value to the previous value.
+ */
+fun <T, R> Flow<T>.pairwiseBy(
+ getInitialValue: suspend () -> T,
+ transform: suspend (previousValue: T, newValue: T) -> R,
+): Flow<R> =
+ onStart { emit(getInitialValue()) }.pairwiseBy(transform)
+
+/**
* Returns a new [Flow] that produces the two most recent emissions from [this]. Note that the new
* Flow will not start emitting until it has received two emissions from the upstream Flow.
*
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
index afd582a3b822..fa8c8982bccb 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
@@ -87,17 +87,17 @@ private fun faceModel(user: Int) = KeyguardFaceListenModel(
biometricSettingEnabledForUser = false,
bouncerFullyShown = false,
faceAndFpNotAuthenticated = false,
+ faceAuthAllowed = true,
faceDisabled = false,
faceLockedOut = false,
- fpLockedOut = false,
goingToSleep = false,
keyguardAwake = false,
keyguardGoingAway = false,
listeningForFaceAssistant = false,
occludingAppRequestingFaceAuth = false,
primaryUser = false,
- scanningAllowedByStrongAuth = false,
secureCameraLaunched = false,
+ supportsDetect = true,
switchingUser = false,
udfpsBouncerShowing = false,
udfpsFingerDown = false,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index e39b9b58158f..84f6d913b310 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -558,11 +558,40 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
configurationListenerArgumentCaptor.getValue().onDensityOrFontScaleChanged();
verify(mView).onDensityOrFontScaleChanged();
- verify(mKeyguardSecurityViewFlipperController).onDensityOrFontScaleChanged();
+ verify(mKeyguardSecurityViewFlipperController).clearViews();
verify(mKeyguardSecurityViewFlipperController).getSecurityView(any(SecurityMode.class),
any(KeyguardSecurityCallback.class));
}
+ @Test
+ public void onThemeChanged() {
+ ArgumentCaptor<ConfigurationController.ConfigurationListener>
+ configurationListenerArgumentCaptor = ArgumentCaptor.forClass(
+ ConfigurationController.ConfigurationListener.class);
+ mKeyguardSecurityContainerController.onViewAttached();
+ verify(mConfigurationController).addCallback(configurationListenerArgumentCaptor.capture());
+ configurationListenerArgumentCaptor.getValue().onThemeChanged();
+
+ verify(mView).reloadColors();
+ verify(mKeyguardSecurityViewFlipperController).clearViews();
+ verify(mKeyguardSecurityViewFlipperController).getSecurityView(any(SecurityMode.class),
+ any(KeyguardSecurityCallback.class));
+ }
+
+ @Test
+ public void onUiModeChanged() {
+ ArgumentCaptor<ConfigurationController.ConfigurationListener>
+ configurationListenerArgumentCaptor = ArgumentCaptor.forClass(
+ ConfigurationController.ConfigurationListener.class);
+ mKeyguardSecurityContainerController.onViewAttached();
+ verify(mConfigurationController).addCallback(configurationListenerArgumentCaptor.capture());
+ configurationListenerArgumentCaptor.getValue().onUiModeChanged();
+
+ verify(mView).reloadColors();
+ verify(mKeyguardSecurityViewFlipperController).clearViews();
+ verify(mKeyguardSecurityViewFlipperController).getSecurityView(any(SecurityMode.class),
+ any(KeyguardSecurityCallback.class));
+ }
private KeyguardSecurityContainer.SwipeListener getRegisteredSwipeListener() {
mKeyguardSecurityContainerController.onViewAttached();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
index fd02ac97cec2..1614b577a6cc 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
@@ -109,7 +109,7 @@ public class KeyguardSecurityViewFlipperControllerTest extends SysuiTestCase {
@Test
public void onDensityOrFontScaleChanged() {
- mKeyguardSecurityViewFlipperController.onDensityOrFontScaleChanged();
+ mKeyguardSecurityViewFlipperController.clearViews();
verify(mView).removeAllViews();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 63e160331e6c..f0d651a90cd0 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -27,6 +27,7 @@ import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
import static com.android.keyguard.FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED;
import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_STATE_CANCELLING_RESTARTING;
import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT;
@@ -101,6 +102,8 @@ import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.annotation.NonNull;
+
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.InstanceId;
@@ -267,21 +270,9 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
// IBiometricsFace@1.0 does not support detection, only authentication.
when(mFaceSensorProperties.isEmpty()).thenReturn(false);
+ when(mFaceSensorProperties.get(anyInt())).thenReturn(
+ createFaceSensorProperties(/* supportsFaceDetection = */ false));
- final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
- componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */,
- "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */,
- "00000001" /* serialNumber */, "" /* softwareVersion */));
- componentInfo.add(new ComponentInfoInternal("matchingAlgorithm" /* componentId */,
- "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
- "vendor/version/revision" /* softwareVersion */));
-
- when(mFaceSensorProperties.get(anyInt())).thenReturn(new FaceSensorPropertiesInternal(
- 0 /* id */,
- FaceSensorProperties.STRENGTH_STRONG, 1 /* maxTemplatesAllowed */,
- componentInfo, FaceSensorProperties.TYPE_UNKNOWN,
- false /* supportsFaceDetection */, true /* supportsSelfIllumination */,
- false /* resetLockoutRequiresChallenge */));
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(List.of(
@@ -354,6 +345,28 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
when(mAuthController.areAllFingerprintAuthenticatorsRegistered()).thenReturn(true);
}
+ @NonNull
+ private FaceSensorPropertiesInternal createFaceSensorProperties(boolean supportsFaceDetection) {
+ final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
+ componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */,
+ "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */,
+ "00000001" /* serialNumber */, "" /* softwareVersion */));
+ componentInfo.add(new ComponentInfoInternal("matchingAlgorithm" /* componentId */,
+ "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
+ "vendor/version/revision" /* softwareVersion */));
+
+
+ return new FaceSensorPropertiesInternal(
+ 0 /* id */,
+ FaceSensorProperties.STRENGTH_STRONG,
+ 1 /* maxTemplatesAllowed */,
+ componentInfo,
+ FaceSensorProperties.TYPE_UNKNOWN,
+ supportsFaceDetection /* supportsFaceDetection */,
+ true /* supportsSelfIllumination */,
+ false /* resetLockoutRequiresChallenge */);
+ }
+
@After
public void tearDown() {
if (mMockitoSession != null) {
@@ -609,6 +622,64 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
@Test
+ public void testUnlockingWithFaceAllowed_strongAuthTrackerUnlockingWithBiometricAllowed() {
+ // GIVEN unlocking with biometric is allowed
+ strongAuthNotRequired();
+
+ // THEN unlocking with face and fp is allowed
+ Assert.assertTrue(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+ BiometricSourceType.FACE));
+ Assert.assertTrue(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+ BiometricSourceType.FINGERPRINT));
+ }
+
+ @Test
+ public void testUnlockingWithFaceAllowed_strongAuthTrackerUnlockingWithBiometricNotAllowed() {
+ // GIVEN unlocking with biometric is not allowed
+ when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
+
+ // THEN unlocking with face is not allowed
+ Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+ BiometricSourceType.FACE));
+ }
+
+ @Test
+ public void testUnlockingWithFaceAllowed_fingerprintLockout() {
+ // GIVEN unlocking with biometric is allowed
+ strongAuthNotRequired();
+
+ // WHEN fingerprint is locked out
+ fingerprintErrorLockedOut();
+
+ // THEN unlocking with face is not allowed
+ Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+ BiometricSourceType.FACE));
+ }
+
+ @Test
+ public void testUnlockingWithFpAllowed_strongAuthTrackerUnlockingWithBiometricNotAllowed() {
+ // GIVEN unlocking with biometric is not allowed
+ when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
+
+ // THEN unlocking with fingerprint is not allowed
+ Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+ BiometricSourceType.FINGERPRINT));
+ }
+
+ @Test
+ public void testUnlockingWithFpAllowed_fingerprintLockout() {
+ // GIVEN unlocking with biometric is allowed
+ strongAuthNotRequired();
+
+ // WHEN fingerprint is locked out
+ fingerprintErrorLockedOut();
+
+ // THEN unlocking with fingeprint is not allowed
+ Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+ BiometricSourceType.FINGERPRINT));
+ }
+
+ @Test
public void testTriesToAuthenticate_whenBouncer() {
setKeyguardBouncerVisibility(true);
@@ -652,10 +723,9 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
@Test
- public void skipsAuthentication_whenEncryptedKeyguard() {
- when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(
- STRONG_AUTH_REQUIRED_AFTER_BOOT);
- mKeyguardUpdateMonitor.setKeyguardBypassController(mKeyguardBypassController);
+ public void skipsAuthentication_whenStrongAuthRequired_nonBypass() {
+ lockscreenBypassIsNotAllowed();
+ when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
mTestableLooper.processAllMessages();
@@ -665,15 +735,48 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
@Test
- public void requiresAuthentication_whenEncryptedKeyguard_andBypass() {
- testStrongAuthExceptOnBouncer(
- STRONG_AUTH_REQUIRED_AFTER_BOOT);
+ public void faceDetect_whenStrongAuthRequiredAndBypass() {
+ // GIVEN bypass is enabled, face detection is supported and strong auth is required
+ lockscreenBypassIsAllowed();
+ supportsFaceDetection();
+ strongAuthRequiredEncrypted();
+ keyguardIsVisible();
+
+ // WHEN the device wakes up
+ mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
+ mTestableLooper.processAllMessages();
+
+ // FACE detect is triggered, not authenticate
+ verify(mFaceManager).detectFace(any(), any(), anyInt());
+ verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
+ anyBoolean());
+
+ // WHEN bouncer becomes visible
+ setKeyguardBouncerVisibility(true);
+ clearInvocations(mFaceManager);
+
+ // THEN face scanning is not run
+ mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.UDFPS_POINTER_DOWN);
+ verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
+ anyBoolean());
+ verify(mFaceManager, never()).detectFace(any(), any(), anyInt());
}
@Test
- public void requiresAuthentication_whenTimeoutKeyguard_andBypass() {
- testStrongAuthExceptOnBouncer(
- KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT);
+ public void noFaceDetect_whenStrongAuthRequiredAndBypass_faceDetectionUnsupported() {
+ // GIVEN bypass is enabled, face detection is NOT supported and strong auth is required
+ lockscreenBypassIsAllowed();
+ strongAuthRequiredEncrypted();
+ keyguardIsVisible();
+
+ // WHEN the device wakes up
+ mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
+ mTestableLooper.processAllMessages();
+
+ // FACE detect and authenticate are NOT triggered
+ verify(mFaceManager, never()).detectFace(any(), any(), anyInt());
+ verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
+ anyBoolean());
}
@Test
@@ -706,24 +809,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
assertThat(didFaceAuthRun).isFalse();
}
- private void testStrongAuthExceptOnBouncer(int strongAuth) {
- when(mKeyguardBypassController.canBypass()).thenReturn(true);
- mKeyguardUpdateMonitor.setKeyguardBypassController(mKeyguardBypassController);
- when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(strongAuth);
-
- mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
- mTestableLooper.processAllMessages();
- keyguardIsVisible();
- verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
-
- // Stop scanning when bouncer becomes visible
- setKeyguardBouncerVisibility(true);
- clearInvocations(mFaceManager);
- mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.UDFPS_POINTER_DOWN);
- verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
- anyBoolean());
- }
-
@Test
public void testTriesToAuthenticate_whenAssistant() {
mKeyguardUpdateMonitor.setKeyguardShowing(true, true);
@@ -734,10 +819,9 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testTriesToAuthenticate_whenTrustOnAgentKeyguard_ifBypass() {
- mKeyguardUpdateMonitor.setKeyguardBypassController(mKeyguardBypassController);
mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
mTestableLooper.processAllMessages();
- when(mKeyguardBypassController.canBypass()).thenReturn(true);
+ lockscreenBypassIsAllowed();
mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */,
KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */,
new ArrayList<>());
@@ -757,26 +841,17 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
@Test
- public void testIgnoresAuth_whenLockdown() {
+ public void testNoFaceAuth_whenLockDown() {
+ when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
+ userDeviceLockDown();
+
mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
+ keyguardIsVisible();
mTestableLooper.processAllMessages();
- when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(
- KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
- keyguardIsVisible();
verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
anyBoolean());
- }
-
- @Test
- public void testTriesToAuthenticate_whenLockout() {
- mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
- mTestableLooper.processAllMessages();
- when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(
- KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT);
-
- keyguardIsVisible();
- verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
+ verify(mFaceManager, never()).detectFace(any(), any(), anyInt());
}
@Test
@@ -919,8 +994,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(),
anyInt());
-// resetFaceManager();
-// resetFingerprintManager();
when(mFingerprintManager.getLockoutModeForUser(eq(FINGERPRINT_SENSOR_ID), eq(newUser)))
.thenReturn(fingerprintLockoutMode);
@@ -1263,7 +1336,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mStatusBarStateListener.onStateChanged(StatusBarState.SHADE_LOCKED);
setKeyguardBouncerVisibility(false /* isVisible */);
mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
- when(mKeyguardBypassController.canBypass()).thenReturn(true);
+ lockscreenBypassIsAllowed();
keyguardIsVisible();
// WHEN status bar state reports a change to the keyguard that would normally indicate to
@@ -1284,6 +1357,24 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
@Test
+ public void testRequestFaceAuthFromOccludingApp_whenInvoked_startsFaceAuth() {
+ mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true);
+
+ assertThat(mKeyguardUpdateMonitor.isFaceDetectionRunning()).isTrue();
+ }
+
+ @Test
+ public void testRequestFaceAuthFromOccludingApp_whenInvoked_stopsFaceAuth() {
+ mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true);
+
+ assertThat(mKeyguardUpdateMonitor.isFaceDetectionRunning()).isTrue();
+
+ mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false);
+
+ assertThat(mKeyguardUpdateMonitor.isFaceDetectionRunning()).isFalse();
+ }
+
+ @Test
public void testRequireUnlockForNfc_Broadcast() {
KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
mKeyguardUpdateMonitor.registerCallback(callback);
@@ -1429,11 +1520,9 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
userNotCurrentlySwitching();
// This disables face auth
- when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
- .thenReturn(STRONG_AUTH_REQUIRED_AFTER_BOOT);
+ when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
mTestableLooper.processAllMessages();
-
assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
}
@@ -1897,6 +1986,109 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
);
}
+ @Test
+ public void testStrongAuthChange_lockDown_stopsFpAndFaceListeningState() {
+ // GIVEN device is listening for face and fingerprint
+ mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
+ mTestableLooper.processAllMessages();
+ keyguardIsVisible();
+
+ verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
+ verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(),
+ anyInt());
+
+ final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal);
+ final CancellationSignal fpCancel = spy(mKeyguardUpdateMonitor.mFingerprintCancelSignal);
+ mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel;
+ mKeyguardUpdateMonitor.mFingerprintCancelSignal = fpCancel;
+ KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
+ mKeyguardUpdateMonitor.registerCallback(callback);
+
+ // WHEN strong auth changes and device is in user lockdown
+ when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
+ userDeviceLockDown();
+ mKeyguardUpdateMonitor.notifyStrongAuthAllowedChanged(getCurrentUser());
+ mTestableLooper.processAllMessages();
+
+ // THEN face and fingerprint listening are cancelled
+ verify(faceCancel).cancel();
+ verify(callback).onBiometricRunningStateChanged(
+ eq(false), eq(BiometricSourceType.FACE));
+ verify(fpCancel).cancel();
+ verify(callback).onBiometricRunningStateChanged(
+ eq(false), eq(BiometricSourceType.FINGERPRINT));
+ }
+
+ @Test
+ public void testNonStrongBiometricAllowedChanged_stopsFaceListeningState() {
+ // GIVEN device is listening for face and fingerprint
+ mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
+ mTestableLooper.processAllMessages();
+ keyguardIsVisible();
+
+ verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
+
+ final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal);
+ mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel;
+ KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
+ mKeyguardUpdateMonitor.registerCallback(callback);
+
+ // WHEN non-strong biometric allowed changes
+ when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
+ mKeyguardUpdateMonitor.notifyNonStrongBiometricAllowedChanged(getCurrentUser());
+ mTestableLooper.processAllMessages();
+
+ // THEN face and fingerprint listening are cancelled
+ verify(faceCancel).cancel();
+ verify(callback).onBiometricRunningStateChanged(
+ eq(false), eq(BiometricSourceType.FACE));
+ }
+
+ @Test
+ public void testShouldListenForFace_withLockedDown_returnsFalse()
+ throws RemoteException {
+ keyguardNotGoingAway();
+ bouncerFullyVisibleAndNotGoingToSleep();
+ currentUserIsPrimary();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ biometricsEnabledForCurrentUser();
+ userNotCurrentlySwitching();
+ supportsFaceDetection();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+
+ userDeviceLockDown();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ }
+
+ private void userDeviceLockDown() {
+ when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
+ when(mStrongAuthTracker.getStrongAuthForUser(mCurrentUserId))
+ .thenReturn(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+ }
+
+ private void supportsFaceDetection() {
+ when(mFaceSensorProperties.get(anyInt()))
+ .thenReturn(createFaceSensorProperties(
+ /* supportsFaceDetection = */ true));
+ }
+
+ private void lockscreenBypassIsAllowed() {
+ mockCanBypassLockscreen(true);
+ }
+
+ private void mockCanBypassLockscreen(boolean canBypass) {
+ mKeyguardUpdateMonitor.setKeyguardBypassController(mKeyguardBypassController);
+ when(mKeyguardBypassController.canBypass()).thenReturn(canBypass);
+ }
+
+ private void lockscreenBypassIsNotAllowed() {
+ mockCanBypassLockscreen(false);
+ }
+
private void cleanupKeyguardUpdateMonitor() {
if (mKeyguardUpdateMonitor != null) {
mKeyguardUpdateMonitor.removeCallback(mTestCallback);
@@ -1990,9 +2182,16 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
);
}
+ private void strongAuthRequiredEncrypted() {
+ when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
+ .thenReturn(STRONG_AUTH_REQUIRED_AFTER_BOOT);
+ when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
+ }
+
private void strongAuthNotRequired() {
when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
.thenReturn(0);
+ when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
}
private void currentUserDoesNotHaveTrust() {
@@ -2033,6 +2232,10 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
setKeyguardBouncerVisibility(true);
}
+ private void bouncerNotVisible() {
+ setKeyguardBouncerVisibility(false);
+ }
+
private void setKeyguardBouncerVisibility(boolean isVisible) {
mKeyguardUpdateMonitor.sendPrimaryBouncerChanged(isVisible, isVisible);
mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 181839ab512f..0627fc6c542f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -77,7 +77,6 @@ import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.biometrics.AuthController;
-import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.decor.CornerDecorProvider;
import com.android.systemui.decor.CutoutDecorProviderFactory;
import com.android.systemui.decor.CutoutDecorProviderImpl;
@@ -132,8 +131,6 @@ public class ScreenDecorationsTest extends SysuiTestCase {
@Mock
private TunerService mTunerService;
@Mock
- private BroadcastDispatcher mBroadcastDispatcher;
- @Mock
private UserTracker mUserTracker;
@Mock
private PrivacyDotViewController mDotViewController;
@@ -223,8 +220,8 @@ public class ScreenDecorationsTest extends SysuiTestCase {
mExecutor));
mScreenDecorations = spy(new ScreenDecorations(mContext, mExecutor, mSecureSettings,
- mBroadcastDispatcher, mTunerService, mUserTracker, mDotViewController,
- mThreadFactory, mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory) {
+ mTunerService, mUserTracker, mDotViewController, mThreadFactory,
+ mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory) {
@Override
public void start() {
super.start();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 57ca9c064224..9d39a8ce7c52 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -34,7 +34,6 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -88,6 +87,8 @@ import com.android.systemui.model.SysUiState;
import com.android.systemui.util.leak.ReferenceTestUtils;
import com.android.systemui.utils.os.FakeHandler;
+import com.google.common.util.concurrent.AtomicDouble;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -710,7 +711,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
}
@Test
- public void onSingleTap_enabled_scaleIsChanged() {
+ public void onSingleTap_enabled_scaleAnimates() {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
Float.NaN);
@@ -721,14 +722,28 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
});
final View mirrorView = mWindowManager.getAttachedView();
+
final long timeout = SystemClock.uptimeMillis() + 1000;
- while (SystemClock.uptimeMillis() < timeout) {
- SystemClock.sleep(10);
- if (Float.compare(1.0f, mirrorView.getScaleX()) < 0) {
- return;
+ final AtomicDouble maxScaleX = new AtomicDouble();
+ final Runnable onAnimationFrame = new Runnable() {
+ @Override
+ public void run() {
+ // For some reason the fancy way doesn't compile...
+// maxScaleX.getAndAccumulate(mirrorView.getScaleX(), Math::max);
+ final double oldMax = maxScaleX.get();
+ final double newMax = Math.max(mirrorView.getScaleX(), oldMax);
+ assertTrue(maxScaleX.compareAndSet(oldMax, newMax));
+
+ if (SystemClock.uptimeMillis() < timeout) {
+ mirrorView.postOnAnimation(this);
+ }
}
- }
- fail("MirrorView scale is not changed");
+ };
+ mirrorView.postOnAnimation(onAnimationFrame);
+
+ waitForIdleSync();
+
+ ReferenceTestUtils.waitForCondition(() -> maxScaleX.get() > 1.0);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
index eb8c823ffe1c..b765ab3c5eac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -26,6 +26,7 @@ import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FeatureFlags
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.LightRevealScrim
@@ -77,6 +78,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
@Mock private lateinit var udfpsControllerProvider: Provider<UdfpsController>
@Mock private lateinit var udfpsController: UdfpsController
@Mock private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var featureFlags: FeatureFlags
@Mock private lateinit var lightRevealScrim: LightRevealScrim
@Mock private lateinit var fpSensorProp: FingerprintSensorPropertiesInternal
@@ -106,6 +108,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
biometricUnlockController,
udfpsControllerProvider,
statusBarStateController,
+ featureFlags,
rippleView
)
controller.init()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
index e7d5632c7087..3c40835fe59d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
@@ -47,6 +47,7 @@ import android.view.WindowInsets
import android.view.WindowManager
import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
+import android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG
import android.view.WindowMetrics
import androidx.test.filters.SmallTest
import com.airbnb.lottie.LottieAnimationView
@@ -423,6 +424,21 @@ class SideFpsControllerTest : SysuiTestCase() {
}
@Test
+ fun testLayoutParams_isKeyguardDialogType() =
+ testWithDisplay(deviceConfig = DeviceConfig.Y_ALIGNED_UNFOLDED) {
+ sideFpsController.overlayOffsets = sensorLocation
+ sideFpsController.updateOverlayParams(windowManager.defaultDisplay, indicatorBounds)
+ overlayController.show(SENSOR_ID, REASON_UNKNOWN)
+ executor.runAllReady()
+
+ verify(windowManager).updateViewLayout(any(), overlayViewParamsCaptor.capture())
+
+ val lpType = overlayViewParamsCaptor.value.type
+
+ assertThat((lpType and TYPE_KEYGUARD_DIALOG) != 0).isTrue()
+ }
+
+ @Test
fun testLayoutParams_hasNoMoveAnimationWindowFlag() =
testWithDisplay(deviceConfig = DeviceConfig.Y_ALIGNED_UNFOLDED) {
sideFpsController.overlayOffsets = sensorLocation
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index acdafe3e1c7d..b267a5c23a49 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -70,8 +70,13 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.biometrics.udfps.InteractionEvent;
+import com.android.systemui.biometrics.udfps.NormalizedTouchData;
+import com.android.systemui.biometrics.udfps.SinglePointerTouchProcessor;
+import com.android.systemui.biometrics.udfps.TouchProcessorResult;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.plugins.FalsingManager;
@@ -190,6 +195,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
private AlternateUdfpsTouchProvider mAlternateTouchProvider;
@Mock
private PrimaryBouncerInteractor mPrimaryBouncerInteractor;
+ @Mock
+ private SinglePointerTouchProcessor mSinglePointerTouchProcessor;
// Capture listeners so that they can be used to send events
@Captor
@@ -275,7 +282,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
mDisplayManager, mHandler, mConfigurationController, mSystemClock,
mUnlockedScreenOffAnimationController, mSystemUIDialogManager, mLatencyTracker,
mActivityLaunchAnimator, alternateTouchProvider, mBiometricsExecutor,
- mPrimaryBouncerInteractor);
+ mPrimaryBouncerInteractor, mSinglePointerTouchProcessor);
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
@@ -1086,4 +1093,100 @@ public class UdfpsControllerTest extends SysuiTestCase {
anyString(),
any());
}
+
+ @Test
+ public void onTouch_withoutNewTouchDetection_shouldCallOldFingerprintManagerPath()
+ throws RemoteException {
+ // Disable new touch detection.
+ when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(false);
+
+ // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
+ initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */);
+
+ // Configure UdfpsView to accept the ACTION_DOWN event
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
+ when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
+
+ // GIVEN that the overlay is showing and a11y touch exploration NOT enabled
+ when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+ mFgExecutor.runAllReady();
+
+ verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+
+ // WHEN ACTION_DOWN is received
+ MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
+ mBiometricsExecutor.runAllReady();
+ downEvent.recycle();
+
+ // AND ACTION_MOVE is received
+ MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
+ mBiometricsExecutor.runAllReady();
+ moveEvent.recycle();
+
+ // AND ACTION_UP is received
+ MotionEvent upEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent);
+ mBiometricsExecutor.runAllReady();
+ upEvent.recycle();
+
+ // THEN the old FingerprintManager path is invoked.
+ verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyInt(),
+ anyFloat(), anyFloat());
+ verify(mFingerprintManager).onPointerUp(anyLong(), anyInt());
+ }
+
+ @Test
+ public void onTouch_withNewTouchDetection_shouldCallOldFingerprintManagerPath()
+ throws RemoteException {
+ final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
+ 0L);
+ final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch(
+ InteractionEvent.DOWN, 1 /* pointerId */, touchData);
+ final TouchProcessorResult processorResultUp = new TouchProcessorResult.ProcessedTouch(
+ InteractionEvent.UP, 1 /* pointerId */, touchData);
+
+ // Enable new touch detection.
+ when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true);
+
+ // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
+ initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */);
+
+ // Configure UdfpsView to accept the ACTION_DOWN event
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
+ when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
+
+ // GIVEN that the overlay is showing and a11y touch exploration NOT enabled
+ when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+ mFgExecutor.runAllReady();
+
+ verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+
+ // WHEN ACTION_DOWN is received
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ processorResultDown);
+ MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
+ mBiometricsExecutor.runAllReady();
+ downEvent.recycle();
+
+ // AND ACTION_UP is received
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ processorResultUp);
+ MotionEvent upEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent);
+ mBiometricsExecutor.runAllReady();
+ upEvent.recycle();
+
+ // THEN the new FingerprintManager path is invoked.
+ verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(),
+ anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
+ verify(mFingerprintManager).onPointerUp(anyLong(), anyInt(), anyInt(), anyFloat(),
+ anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt
new file mode 100644
index 000000000000..4f89b69108f4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.udfps
+
+import android.graphics.Rect
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.junit.runners.Parameterized.Parameters
+
+@SmallTest
+@RunWith(Parameterized::class)
+class BoundingBoxOverlapDetectorTest(val testCase: TestCase) : SysuiTestCase() {
+ val underTest = BoundingBoxOverlapDetector()
+
+ @Test
+ fun isGoodOverlap() {
+ val touchData = TOUCH_DATA.copy(x = testCase.x.toFloat(), y = testCase.y.toFloat())
+ val actual = underTest.isGoodOverlap(touchData, SENSOR)
+
+ assertThat(actual).isEqualTo(testCase.expected)
+ }
+
+ data class TestCase(val x: Int, val y: Int, val expected: Boolean)
+
+ companion object {
+ @Parameters(name = "{0}")
+ @JvmStatic
+ fun data(): List<TestCase> =
+ listOf(
+ genPositiveTestCases(
+ validXs = listOf(SENSOR.left, SENSOR.right, SENSOR.centerX()),
+ validYs = listOf(SENSOR.top, SENSOR.bottom, SENSOR.centerY())
+ ),
+ genNegativeTestCases(
+ invalidXs = listOf(SENSOR.left - 1, SENSOR.right + 1),
+ invalidYs = listOf(SENSOR.top - 1, SENSOR.bottom + 1),
+ validXs = listOf(SENSOR.left, SENSOR.right, SENSOR.centerX()),
+ validYs = listOf(SENSOR.top, SENSOR.bottom, SENSOR.centerY())
+ )
+ )
+ .flatten()
+ }
+}
+
+/* Placeholder touch parameters. */
+private const val POINTER_ID = 42
+private const val NATIVE_MINOR = 2.71828f
+private const val NATIVE_MAJOR = 3.14f
+private const val ORIENTATION = 1.23f
+private const val TIME = 12345699L
+private const val GESTURE_START = 12345600L
+
+/* Template [NormalizedTouchData]. */
+private val TOUCH_DATA =
+ NormalizedTouchData(
+ POINTER_ID,
+ x = 0f,
+ y = 0f,
+ NATIVE_MINOR,
+ NATIVE_MAJOR,
+ ORIENTATION,
+ TIME,
+ GESTURE_START
+ )
+
+private val SENSOR = Rect(100 /* left */, 200 /* top */, 300 /* right */, 500 /* bottom */)
+
+private fun genTestCases(
+ xs: List<Int>,
+ ys: List<Int>,
+ expected: Boolean
+): List<BoundingBoxOverlapDetectorTest.TestCase> {
+ return xs.flatMap { x ->
+ ys.map { y -> BoundingBoxOverlapDetectorTest.TestCase(x, y, expected) }
+ }
+}
+
+private fun genPositiveTestCases(
+ validXs: List<Int>,
+ validYs: List<Int>,
+) = genTestCases(validXs, validYs, expected = true)
+
+private fun genNegativeTestCases(
+ invalidXs: List<Int>,
+ invalidYs: List<Int>,
+ validXs: List<Int>,
+ validYs: List<Int>,
+): List<BoundingBoxOverlapDetectorTest.TestCase> {
+ return genTestCases(invalidXs, validYs, expected = false) +
+ genTestCases(validXs, invalidYs, expected = false)
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt
new file mode 100644
index 000000000000..834d0a69e427
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt
@@ -0,0 +1,90 @@
+package com.android.systemui.biometrics.udfps
+
+import android.graphics.Rect
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.junit.runners.Parameterized.Parameters
+
+@SmallTest
+@RunWith(Parameterized::class)
+class NormalizedTouchDataTest(val testCase: TestCase) : SysuiTestCase() {
+
+ @Test
+ fun isWithinSensor() {
+ val touchData = TOUCH_DATA.copy(x = testCase.x.toFloat(), y = testCase.y.toFloat())
+ val actual = touchData.isWithinSensor(SENSOR)
+
+ assertThat(actual).isEqualTo(testCase.expected)
+ }
+
+ data class TestCase(val x: Int, val y: Int, val expected: Boolean)
+
+ companion object {
+ @Parameters(name = "{0}")
+ @JvmStatic
+ fun data(): List<TestCase> =
+ listOf(
+ genPositiveTestCases(
+ validXs = listOf(SENSOR.left, SENSOR.right, SENSOR.centerX()),
+ validYs = listOf(SENSOR.top, SENSOR.bottom, SENSOR.centerY())
+ ),
+ genNegativeTestCases(
+ invalidXs = listOf(SENSOR.left - 1, SENSOR.right + 1),
+ invalidYs = listOf(SENSOR.top - 1, SENSOR.bottom + 1),
+ validXs = listOf(SENSOR.left, SENSOR.right, SENSOR.centerX()),
+ validYs = listOf(SENSOR.top, SENSOR.bottom, SENSOR.centerY())
+ )
+ )
+ .flatten()
+ }
+}
+
+/* Placeholder touch parameters. */
+private const val POINTER_ID = 42
+private const val NATIVE_MINOR = 2.71828f
+private const val NATIVE_MAJOR = 3.14f
+private const val ORIENTATION = 1.23f
+private const val TIME = 12345699L
+private const val GESTURE_START = 12345600L
+
+/* Template [NormalizedTouchData]. */
+private val TOUCH_DATA =
+ NormalizedTouchData(
+ POINTER_ID,
+ x = 0f,
+ y = 0f,
+ NATIVE_MINOR,
+ NATIVE_MAJOR,
+ ORIENTATION,
+ TIME,
+ GESTURE_START
+ )
+
+private val SENSOR = Rect(100 /* left */, 200 /* top */, 300 /* right */, 500 /* bottom */)
+
+private fun genTestCases(
+ xs: List<Int>,
+ ys: List<Int>,
+ expected: Boolean
+): List<NormalizedTouchDataTest.TestCase> {
+ return xs.flatMap { x -> ys.map { y -> NormalizedTouchDataTest.TestCase(x, y, expected) } }
+}
+
+private fun genPositiveTestCases(
+ validXs: List<Int>,
+ validYs: List<Int>,
+) = genTestCases(validXs, validYs, expected = true)
+
+private fun genNegativeTestCases(
+ invalidXs: List<Int>,
+ invalidYs: List<Int>,
+ validXs: List<Int>,
+ validYs: List<Int>,
+): List<NormalizedTouchDataTest.TestCase> {
+ return genTestCases(invalidXs, validYs, expected = false) +
+ genTestCases(validXs, invalidYs, expected = false)
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt
new file mode 100644
index 000000000000..95c53b408056
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.udfps
+
+import android.graphics.Rect
+import android.view.MotionEvent
+import android.view.MotionEvent.INVALID_POINTER_ID
+import android.view.MotionEvent.PointerProperties
+import android.view.Surface
+import android.view.Surface.Rotation
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.UdfpsOverlayParams
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.junit.runners.Parameterized.Parameters
+
+@SmallTest
+@RunWith(Parameterized::class)
+class SinglePointerTouchProcessorTest(val testCase: TestCase) : SysuiTestCase() {
+ private val overlapDetector = FakeOverlapDetector()
+ private val underTest = SinglePointerTouchProcessor(overlapDetector)
+
+ @Test
+ fun processTouch() {
+ overlapDetector.shouldReturn = testCase.isGoodOverlap
+
+ val actual =
+ underTest.processTouch(
+ testCase.event,
+ testCase.previousPointerOnSensorId,
+ testCase.overlayParams,
+ )
+
+ assertThat(actual).isInstanceOf(testCase.expected.javaClass)
+ if (actual is TouchProcessorResult.ProcessedTouch) {
+ assertThat(actual).isEqualTo(testCase.expected)
+ }
+ }
+
+ data class TestCase(
+ val event: MotionEvent,
+ val isGoodOverlap: Boolean,
+ val previousPointerOnSensorId: Int,
+ val overlayParams: UdfpsOverlayParams,
+ val expected: TouchProcessorResult,
+ ) {
+ override fun toString(): String {
+ val expectedOutput =
+ if (expected is TouchProcessorResult.ProcessedTouch) {
+ expected.event.toString() +
+ ", (x: ${expected.touchData.x}, y: ${expected.touchData.y})" +
+ ", pointerOnSensorId: ${expected.pointerOnSensorId}" +
+ ", ..."
+ } else {
+ TouchProcessorResult.Failure().toString()
+ }
+ return "{" +
+ MotionEvent.actionToString(event.action) +
+ ", (x: ${event.x}, y: ${event.y})" +
+ ", scale: ${overlayParams.scaleFactor}" +
+ ", rotation: " +
+ Surface.rotationToString(overlayParams.rotation) +
+ ", previousPointerOnSensorId: $previousPointerOnSensorId" +
+ ", ...} expected: {$expectedOutput}"
+ }
+ }
+
+ companion object {
+ @Parameters(name = "{0}")
+ @JvmStatic
+ fun data(): List<TestCase> =
+ listOf(
+ // MotionEvent.ACTION_DOWN
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_DOWN,
+ previousPointerOnSensorId = INVALID_POINTER_ID,
+ isGoodOverlap = true,
+ expectedInteractionEvent = InteractionEvent.DOWN,
+ expectedPointerOnSensorId = POINTER_ID,
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_DOWN,
+ previousPointerOnSensorId = POINTER_ID,
+ isGoodOverlap = true,
+ expectedInteractionEvent = InteractionEvent.DOWN,
+ expectedPointerOnSensorId = POINTER_ID,
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_DOWN,
+ previousPointerOnSensorId = INVALID_POINTER_ID,
+ isGoodOverlap = false,
+ expectedInteractionEvent = InteractionEvent.UNCHANGED,
+ expectedPointerOnSensorId = INVALID_POINTER_ID,
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_DOWN,
+ previousPointerOnSensorId = POINTER_ID,
+ isGoodOverlap = false,
+ expectedInteractionEvent = InteractionEvent.UP,
+ expectedPointerOnSensorId = INVALID_POINTER_ID,
+ ),
+ // MotionEvent.ACTION_MOVE
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_MOVE,
+ previousPointerOnSensorId = INVALID_POINTER_ID,
+ isGoodOverlap = true,
+ expectedInteractionEvent = InteractionEvent.DOWN,
+ expectedPointerOnSensorId = POINTER_ID,
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_MOVE,
+ previousPointerOnSensorId = POINTER_ID,
+ isGoodOverlap = true,
+ expectedInteractionEvent = InteractionEvent.UNCHANGED,
+ expectedPointerOnSensorId = POINTER_ID,
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_MOVE,
+ previousPointerOnSensorId = INVALID_POINTER_ID,
+ isGoodOverlap = false,
+ expectedInteractionEvent = InteractionEvent.UNCHANGED,
+ expectedPointerOnSensorId = INVALID_POINTER_ID,
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_MOVE,
+ previousPointerOnSensorId = POINTER_ID,
+ isGoodOverlap = false,
+ expectedInteractionEvent = InteractionEvent.UP,
+ expectedPointerOnSensorId = INVALID_POINTER_ID,
+ ),
+ // MotionEvent.ACTION_UP
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_UP,
+ previousPointerOnSensorId = INVALID_POINTER_ID,
+ isGoodOverlap = true,
+ expectedInteractionEvent = InteractionEvent.UP,
+ expectedPointerOnSensorId = INVALID_POINTER_ID,
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_UP,
+ previousPointerOnSensorId = POINTER_ID,
+ isGoodOverlap = true,
+ expectedInteractionEvent = InteractionEvent.UP,
+ expectedPointerOnSensorId = INVALID_POINTER_ID,
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_UP,
+ previousPointerOnSensorId = INVALID_POINTER_ID,
+ isGoodOverlap = false,
+ expectedInteractionEvent = InteractionEvent.UNCHANGED,
+ expectedPointerOnSensorId = INVALID_POINTER_ID,
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_UP,
+ previousPointerOnSensorId = POINTER_ID,
+ isGoodOverlap = false,
+ expectedInteractionEvent = InteractionEvent.UP,
+ expectedPointerOnSensorId = INVALID_POINTER_ID,
+ ),
+ // MotionEvent.ACTION_CANCEL
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_CANCEL,
+ previousPointerOnSensorId = INVALID_POINTER_ID,
+ isGoodOverlap = true,
+ expectedInteractionEvent = InteractionEvent.CANCEL,
+ expectedPointerOnSensorId = INVALID_POINTER_ID,
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_CANCEL,
+ previousPointerOnSensorId = POINTER_ID,
+ isGoodOverlap = true,
+ expectedInteractionEvent = InteractionEvent.CANCEL,
+ expectedPointerOnSensorId = INVALID_POINTER_ID,
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_CANCEL,
+ previousPointerOnSensorId = INVALID_POINTER_ID,
+ isGoodOverlap = false,
+ expectedInteractionEvent = InteractionEvent.CANCEL,
+ expectedPointerOnSensorId = INVALID_POINTER_ID,
+ ),
+ genPositiveTestCases(
+ motionEventAction = MotionEvent.ACTION_CANCEL,
+ previousPointerOnSensorId = POINTER_ID,
+ isGoodOverlap = false,
+ expectedInteractionEvent = InteractionEvent.CANCEL,
+ expectedPointerOnSensorId = INVALID_POINTER_ID,
+ ),
+ )
+ .flatten() +
+ listOf(
+ // Unsupported MotionEvent actions.
+ genTestCasesForUnsupportedAction(MotionEvent.ACTION_POINTER_DOWN),
+ genTestCasesForUnsupportedAction(MotionEvent.ACTION_POINTER_UP),
+ genTestCasesForUnsupportedAction(MotionEvent.ACTION_HOVER_ENTER),
+ genTestCasesForUnsupportedAction(MotionEvent.ACTION_HOVER_MOVE),
+ genTestCasesForUnsupportedAction(MotionEvent.ACTION_HOVER_EXIT),
+ )
+ .flatten()
+ }
+}
+
+/* Display dimensions in native resolution and natural orientation. */
+private const val ROTATION_0_NATIVE_DISPLAY_WIDTH = 400
+private const val ROTATION_0_NATIVE_DISPLAY_HEIGHT = 600
+
+/*
+ * ROTATION_0 map:
+ * _ _ _ _
+ * _ _ O _
+ * _ _ _ _
+ * _ S _ _
+ * _ S _ _
+ * _ _ _ _
+ *
+ * (_) empty space
+ * (S) sensor
+ * (O) touch outside of the sensor
+ */
+private val ROTATION_0_NATIVE_SENSOR_BOUNDS =
+ Rect(
+ 100, /* left */
+ 300, /* top */
+ 200, /* right */
+ 500, /* bottom */
+ )
+private val ROTATION_0_INPUTS =
+ OrientationBasedInputs(
+ rotation = Surface.ROTATION_0,
+ nativeXWithinSensor = ROTATION_0_NATIVE_SENSOR_BOUNDS.exactCenterX(),
+ nativeYWithinSensor = ROTATION_0_NATIVE_SENSOR_BOUNDS.exactCenterY(),
+ nativeXOutsideSensor = 250f,
+ nativeYOutsideSensor = 150f,
+ )
+
+/*
+ * ROTATION_90 map:
+ * _ _ _ _ _ _
+ * _ O _ _ _ _
+ * _ _ _ S S _
+ * _ _ _ _ _ _
+ *
+ * (_) empty space
+ * (S) sensor
+ * (O) touch outside of the sensor
+ */
+private val ROTATION_90_NATIVE_SENSOR_BOUNDS =
+ Rect(
+ 300, /* left */
+ 200, /* top */
+ 500, /* right */
+ 300, /* bottom */
+ )
+private val ROTATION_90_INPUTS =
+ OrientationBasedInputs(
+ rotation = Surface.ROTATION_90,
+ nativeXWithinSensor = ROTATION_90_NATIVE_SENSOR_BOUNDS.exactCenterX(),
+ nativeYWithinSensor = ROTATION_90_NATIVE_SENSOR_BOUNDS.exactCenterY(),
+ nativeXOutsideSensor = 150f,
+ nativeYOutsideSensor = 150f,
+ )
+
+/* ROTATION_180 is not supported. It's treated the same as ROTATION_0. */
+private val ROTATION_180_INPUTS =
+ ROTATION_0_INPUTS.copy(
+ rotation = Surface.ROTATION_180,
+ )
+
+/*
+ * ROTATION_270 map:
+ * _ _ _ _ _ _
+ * _ S S _ _ _
+ * _ _ _ _ O _
+ * _ _ _ _ _ _
+ *
+ * (_) empty space
+ * (S) sensor
+ * (O) touch outside of the sensor
+ */
+private val ROTATION_270_NATIVE_SENSOR_BOUNDS =
+ Rect(
+ 100, /* left */
+ 100, /* top */
+ 300, /* right */
+ 200, /* bottom */
+ )
+private val ROTATION_270_INPUTS =
+ OrientationBasedInputs(
+ rotation = Surface.ROTATION_270,
+ nativeXWithinSensor = ROTATION_270_NATIVE_SENSOR_BOUNDS.exactCenterX(),
+ nativeYWithinSensor = ROTATION_270_NATIVE_SENSOR_BOUNDS.exactCenterY(),
+ nativeXOutsideSensor = 450f,
+ nativeYOutsideSensor = 250f,
+ )
+
+/* Placeholder touch parameters. */
+private const val POINTER_ID = 42
+private const val NATIVE_MINOR = 2.71828f
+private const val NATIVE_MAJOR = 3.14f
+private const val ORIENTATION = 1.23f
+private const val TIME = 12345699L
+private const val GESTURE_START = 12345600L
+
+/* Template [MotionEvent]. */
+private val MOTION_EVENT =
+ obtainMotionEvent(
+ action = 0,
+ pointerId = POINTER_ID,
+ x = 0f,
+ y = 0f,
+ minor = 0f,
+ major = 0f,
+ orientation = ORIENTATION,
+ time = TIME,
+ gestureStart = GESTURE_START,
+ )
+
+/* Template [NormalizedTouchData]. */
+private val NORMALIZED_TOUCH_DATA =
+ NormalizedTouchData(
+ POINTER_ID,
+ x = 0f,
+ y = 0f,
+ NATIVE_MINOR,
+ NATIVE_MAJOR,
+ ORIENTATION,
+ TIME,
+ GESTURE_START
+ )
+
+/*
+ * Contains test inputs that are tied to a particular device orientation.
+ *
+ * "native" means in native resolution (not scaled).
+ */
+private data class OrientationBasedInputs(
+ @Rotation val rotation: Int,
+ val nativeXWithinSensor: Float,
+ val nativeYWithinSensor: Float,
+ val nativeXOutsideSensor: Float,
+ val nativeYOutsideSensor: Float,
+) {
+
+ fun toOverlayParams(scaleFactor: Float): UdfpsOverlayParams =
+ UdfpsOverlayParams(
+ sensorBounds = ROTATION_0_NATIVE_SENSOR_BOUNDS.scaled(scaleFactor),
+ overlayBounds = ROTATION_0_NATIVE_SENSOR_BOUNDS.scaled(scaleFactor),
+ naturalDisplayHeight = (ROTATION_0_NATIVE_DISPLAY_HEIGHT * scaleFactor).toInt(),
+ naturalDisplayWidth = (ROTATION_0_NATIVE_DISPLAY_WIDTH * scaleFactor).toInt(),
+ scaleFactor = scaleFactor,
+ rotation = rotation
+ )
+
+ fun getNativeX(isWithinSensor: Boolean): Float {
+ return if (isWithinSensor) nativeXWithinSensor else nativeXOutsideSensor
+ }
+
+ fun getNativeY(isWithinSensor: Boolean): Float {
+ return if (isWithinSensor) nativeYWithinSensor else nativeYOutsideSensor
+ }
+}
+
+private fun genPositiveTestCases(
+ motionEventAction: Int,
+ previousPointerOnSensorId: Int,
+ isGoodOverlap: Boolean,
+ expectedInteractionEvent: InteractionEvent,
+ expectedPointerOnSensorId: Int
+): List<SinglePointerTouchProcessorTest.TestCase> {
+ val scaleFactors = listOf(0.75f, 1f, 1.5f)
+ val orientations =
+ listOf(
+ ROTATION_0_INPUTS,
+ ROTATION_90_INPUTS,
+ ROTATION_180_INPUTS,
+ ROTATION_270_INPUTS,
+ )
+ return scaleFactors.flatMap { scaleFactor ->
+ orientations.map { orientation ->
+ val overlayParams = orientation.toOverlayParams(scaleFactor)
+ val nativeX = orientation.getNativeX(isGoodOverlap)
+ val nativeY = orientation.getNativeY(isGoodOverlap)
+ val event =
+ MOTION_EVENT.copy(
+ action = motionEventAction,
+ x = nativeX * scaleFactor,
+ y = nativeY * scaleFactor,
+ minor = NATIVE_MINOR * scaleFactor,
+ major = NATIVE_MAJOR * scaleFactor,
+ )
+ val expectedTouchData =
+ NORMALIZED_TOUCH_DATA.copy(
+ x = ROTATION_0_INPUTS.getNativeX(isGoodOverlap),
+ y = ROTATION_0_INPUTS.getNativeY(isGoodOverlap),
+ )
+ val expected =
+ TouchProcessorResult.ProcessedTouch(
+ event = expectedInteractionEvent,
+ pointerOnSensorId = expectedPointerOnSensorId,
+ touchData = expectedTouchData,
+ )
+ SinglePointerTouchProcessorTest.TestCase(
+ event = event,
+ isGoodOverlap = isGoodOverlap,
+ previousPointerOnSensorId = previousPointerOnSensorId,
+ overlayParams = overlayParams,
+ expected = expected,
+ )
+ }
+ }
+}
+
+private fun genTestCasesForUnsupportedAction(
+ motionEventAction: Int
+): List<SinglePointerTouchProcessorTest.TestCase> {
+ val isGoodOverlap = true
+ val previousPointerOnSensorIds = listOf(INVALID_POINTER_ID, POINTER_ID)
+ return previousPointerOnSensorIds.map { previousPointerOnSensorId ->
+ val overlayParams = ROTATION_0_INPUTS.toOverlayParams(scaleFactor = 1f)
+ val nativeX = ROTATION_0_INPUTS.getNativeX(isGoodOverlap)
+ val nativeY = ROTATION_0_INPUTS.getNativeY(isGoodOverlap)
+ val event =
+ MOTION_EVENT.copy(
+ action = motionEventAction,
+ x = nativeX,
+ y = nativeY,
+ minor = NATIVE_MINOR,
+ major = NATIVE_MAJOR,
+ )
+ SinglePointerTouchProcessorTest.TestCase(
+ event = event,
+ isGoodOverlap = isGoodOverlap,
+ previousPointerOnSensorId = previousPointerOnSensorId,
+ overlayParams = overlayParams,
+ expected = TouchProcessorResult.Failure(),
+ )
+ }
+}
+
+private fun obtainMotionEvent(
+ action: Int,
+ pointerId: Int,
+ x: Float,
+ y: Float,
+ minor: Float,
+ major: Float,
+ orientation: Float,
+ time: Long,
+ gestureStart: Long,
+): MotionEvent {
+ val pp = PointerProperties()
+ pp.id = pointerId
+ val pc = MotionEvent.PointerCoords()
+ pc.x = x
+ pc.y = y
+ pc.touchMinor = minor
+ pc.touchMajor = major
+ pc.orientation = orientation
+ return MotionEvent.obtain(
+ gestureStart /* downTime */,
+ time /* eventTime */,
+ action /* action */,
+ 1 /* pointerCount */,
+ arrayOf(pp) /* pointerProperties */,
+ arrayOf(pc) /* pointerCoords */,
+ 0 /* metaState */,
+ 0 /* buttonState */,
+ 1f /* xPrecision */,
+ 1f /* yPrecision */,
+ 0 /* deviceId */,
+ 0 /* edgeFlags */,
+ 0 /* source */,
+ 0 /* flags */
+ )
+}
+
+private fun MotionEvent.copy(
+ action: Int = this.action,
+ pointerId: Int = this.getPointerId(0),
+ x: Float = this.rawX,
+ y: Float = this.rawY,
+ minor: Float = this.touchMinor,
+ major: Float = this.touchMajor,
+ orientation: Float = this.orientation,
+ time: Long = this.eventTime,
+ gestureStart: Long = this.downTime,
+) = obtainMotionEvent(action, pointerId, x, y, minor, major, orientation, time, gestureStart)
+
+private fun Rect.scaled(scaleFactor: Float) = Rect(this).apply { scale(scaleFactor) }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ControlsSettingsRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ControlsSettingsRepositoryImplTest.kt
new file mode 100644
index 000000000000..4b88b44c3f03
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ControlsSettingsRepositoryImplTest.kt
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.controls
+
+import android.content.pm.UserInfo
+import android.provider.Settings
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.util.settings.FakeSettings
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
+class ControlsSettingsRepositoryImplTest : SysuiTestCase() {
+
+ companion object {
+ private const val LOCKSCREEN_SHOW = Settings.Secure.LOCKSCREEN_SHOW_CONTROLS
+ private const val LOCKSCREEN_ACTION = Settings.Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS
+
+ private fun createUser(id: Int): UserInfo {
+ return UserInfo(id, "user_$id", 0)
+ }
+
+ private val ALL_USERS = (0..1).map { it to createUser(it) }.toMap()
+ }
+
+ private lateinit var underTest: ControlsSettingsRepository
+
+ private lateinit var testScope: TestScope
+ private lateinit var secureSettings: FakeSettings
+ private lateinit var userRepository: FakeUserRepository
+
+ @Before
+ fun setUp() {
+ secureSettings = FakeSettings()
+ userRepository = FakeUserRepository()
+ userRepository.setUserInfos(ALL_USERS.values.toList())
+
+ val coroutineDispatcher = UnconfinedTestDispatcher()
+ testScope = TestScope(coroutineDispatcher)
+
+ underTest =
+ ControlsSettingsRepositoryImpl(
+ scope = testScope.backgroundScope,
+ backgroundDispatcher = coroutineDispatcher,
+ userRepository = userRepository,
+ secureSettings = secureSettings,
+ )
+ }
+
+ @Test
+ fun showInLockScreen() =
+ testScope.runTest {
+ setUser(0)
+ val values = mutableListOf<Boolean>()
+ val job =
+ launch(UnconfinedTestDispatcher()) {
+ underTest.canShowControlsInLockscreen.toList(values)
+ }
+ assertThat(values.last()).isFalse()
+
+ secureSettings.putBool(LOCKSCREEN_SHOW, true)
+ assertThat(values.last()).isTrue()
+
+ secureSettings.putBool(LOCKSCREEN_SHOW, false)
+ assertThat(values.last()).isFalse()
+
+ secureSettings.putBoolForUser(LOCKSCREEN_SHOW, true, 1)
+ assertThat(values.last()).isFalse()
+
+ setUser(1)
+ assertThat(values.last()).isTrue()
+
+ job.cancel()
+ }
+
+ @Test
+ fun showInLockScreen_changesInOtherUsersAreNotQueued() =
+ testScope.runTest {
+ setUser(0)
+
+ val values = mutableListOf<Boolean>()
+ val job =
+ launch(UnconfinedTestDispatcher()) {
+ underTest.canShowControlsInLockscreen.toList(values)
+ }
+
+ secureSettings.putBoolForUser(LOCKSCREEN_SHOW, true, 1)
+ secureSettings.putBoolForUser(LOCKSCREEN_SHOW, false, 1)
+
+ setUser(1)
+ assertThat(values.last()).isFalse()
+ assertThat(values).containsNoneIn(listOf(true))
+
+ job.cancel()
+ }
+
+ @Test
+ fun actionInLockScreen() =
+ testScope.runTest {
+ setUser(0)
+ val values = mutableListOf<Boolean>()
+ val job =
+ launch(UnconfinedTestDispatcher()) {
+ underTest.allowActionOnTrivialControlsInLockscreen.toList(values)
+ }
+ assertThat(values.last()).isFalse()
+
+ secureSettings.putBool(LOCKSCREEN_ACTION, true)
+ assertThat(values.last()).isTrue()
+
+ secureSettings.putBool(LOCKSCREEN_ACTION, false)
+ assertThat(values.last()).isFalse()
+
+ secureSettings.putBoolForUser(LOCKSCREEN_ACTION, true, 1)
+ assertThat(values.last()).isFalse()
+
+ setUser(1)
+ assertThat(values.last()).isTrue()
+
+ job.cancel()
+ }
+
+ @Test
+ fun actionInLockScreen_changesInOtherUsersAreNotQueued() =
+ testScope.runTest {
+ setUser(0)
+
+ val values = mutableListOf<Boolean>()
+ val job =
+ launch(UnconfinedTestDispatcher()) {
+ underTest.allowActionOnTrivialControlsInLockscreen.toList(values)
+ }
+
+ secureSettings.putBoolForUser(LOCKSCREEN_ACTION, true, 1)
+ secureSettings.putBoolForUser(LOCKSCREEN_ACTION, false, 1)
+
+ setUser(1)
+ assertThat(values.last()).isFalse()
+ assertThat(values).containsNoneIn(listOf(true))
+
+ job.cancel()
+ }
+
+ @Test
+ fun valueIsUpdatedWhenNotSubscribed() =
+ testScope.runTest {
+ setUser(0)
+ assertThat(underTest.canShowControlsInLockscreen.value).isFalse()
+
+ secureSettings.putBool(LOCKSCREEN_SHOW, true)
+
+ assertThat(underTest.canShowControlsInLockscreen.value).isTrue()
+ }
+
+ private suspend fun setUser(id: Int) {
+ secureSettings.userId = id
+ userRepository.setSelectedUserInfo(ALL_USERS[id]!!)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/FakeControlsSettingsRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/FakeControlsSettingsRepository.kt
new file mode 100644
index 000000000000..8a1bed20e700
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/FakeControlsSettingsRepository.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.controls
+
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+class FakeControlsSettingsRepository : ControlsSettingsRepository {
+ private val _canShowControlsInLockscreen = MutableStateFlow(false)
+ override val canShowControlsInLockscreen = _canShowControlsInLockscreen.asStateFlow()
+ private val _allowActionOnTrivialControlsInLockscreen = MutableStateFlow(false)
+ override val allowActionOnTrivialControlsInLockscreen =
+ _allowActionOnTrivialControlsInLockscreen.asStateFlow()
+
+ fun setCanShowControlsInLockscreen(value: Boolean) {
+ _canShowControlsInLockscreen.value = value
+ }
+
+ fun setAllowActionOnTrivialControlsInLockscreen(value: Boolean) {
+ _allowActionOnTrivialControlsInLockscreen.value = value
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
index 4ed5649c9c50..1d00d6b05568 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
@@ -18,30 +18,24 @@ package com.android.systemui.controls.ui
import android.content.Context
import android.content.SharedPreferences
-import android.database.ContentObserver
-import android.net.Uri
-import android.os.Handler
-import android.os.UserHandle
-import android.provider.Settings
import android.test.suitebuilder.annotation.SmallTest
import android.testing.AndroidTestingRunner
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.controls.ControlsMetricsLogger
+import com.android.systemui.controls.FakeControlsSettingsRepository
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.UserContextProvider
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.concurrency.DelayableExecutor
-import com.android.systemui.util.mockito.any
import com.android.systemui.util.settings.SecureSettings
import com.android.wm.shell.TaskViewFactory
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Answers
-import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.`when`
@@ -79,8 +73,6 @@ class ControlActionCoordinatorImplTest : SysuiTestCase() {
@Mock
private lateinit var secureSettings: SecureSettings
@Mock
- private lateinit var mainHandler: Handler
- @Mock
private lateinit var userContextProvider: UserContextProvider
companion object {
@@ -91,17 +83,15 @@ class ControlActionCoordinatorImplTest : SysuiTestCase() {
private lateinit var coordinator: ControlActionCoordinatorImpl
private lateinit var action: ControlActionCoordinatorImpl.Action
+ private lateinit var controlsSettingsRepository: FakeControlsSettingsRepository
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- `when`(secureSettings.getUriFor(Settings.Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS))
- .thenReturn(Settings.Secure
- .getUriFor(Settings.Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS))
- `when`(secureSettings.getIntForUser(Settings.Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS,
- 0, UserHandle.USER_CURRENT))
- .thenReturn(1)
+ controlsSettingsRepository = FakeControlsSettingsRepository()
+ controlsSettingsRepository.setAllowActionOnTrivialControlsInLockscreen(true)
+ controlsSettingsRepository.setCanShowControlsInLockscreen(true)
coordinator = spy(ControlActionCoordinatorImpl(
mContext,
@@ -115,7 +105,7 @@ class ControlActionCoordinatorImplTest : SysuiTestCase() {
vibratorHelper,
secureSettings,
userContextProvider,
- mainHandler
+ controlsSettingsRepository
))
val userContext = mock(Context::class.java)
@@ -128,9 +118,6 @@ class ControlActionCoordinatorImplTest : SysuiTestCase() {
`when`(pref.getInt(DeviceControlsControllerImpl.PREFS_SETTINGS_DIALOG_ATTEMPTS, 0))
.thenReturn(2)
- verify(secureSettings).registerContentObserverForUser(any(Uri::class.java),
- anyBoolean(), any(ContentObserver::class.java), anyInt())
-
`when`(cvh.cws.ci.controlId).thenReturn(ID)
`when`(cvh.cws.control?.isAuthRequired()).thenReturn(true)
action = spy(coordinator.Action(ID, {}, false, true))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
index c31fd828c730..1b34706bd220 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
@@ -17,7 +17,6 @@
package com.android.systemui.controls.controller
import android.app.PendingIntent
-import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.ContextWrapper
@@ -31,7 +30,6 @@ import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.backup.BackupHelper
-import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.controls.ControlStatus
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.management.ControlsListingController
@@ -85,10 +83,8 @@ class ControlsControllerImplTest : SysuiTestCase() {
@Mock
private lateinit var auxiliaryPersistenceWrapper: AuxiliaryPersistenceWrapper
@Mock
- private lateinit var broadcastDispatcher: BroadcastDispatcher
- @Mock
private lateinit var listingController: ControlsListingController
- @Mock(stubOnly = true)
+ @Mock
private lateinit var userTracker: UserTracker
@Mock
private lateinit var userFileManager: UserFileManager
@@ -104,7 +100,7 @@ class ControlsControllerImplTest : SysuiTestCase() {
ArgumentCaptor<ControlsBindingController.LoadCallback>
@Captor
- private lateinit var broadcastReceiverCaptor: ArgumentCaptor<BroadcastReceiver>
+ private lateinit var userTrackerCallbackCaptor: ArgumentCaptor<UserTracker.Callback>
@Captor
private lateinit var listingCallbackCaptor:
ArgumentCaptor<ControlsListingController.ControlsListingCallback>
@@ -170,16 +166,15 @@ class ControlsControllerImplTest : SysuiTestCase() {
uiController,
bindingController,
listingController,
- broadcastDispatcher,
userFileManager,
+ userTracker,
Optional.of(persistenceWrapper),
- mock(DumpManager::class.java),
- userTracker
+ mock(DumpManager::class.java)
)
controller.auxiliaryPersistenceWrapper = auxiliaryPersistenceWrapper
- verify(broadcastDispatcher).registerReceiver(
- capture(broadcastReceiverCaptor), any(), any(), eq(UserHandle.ALL), anyInt(), any()
+ verify(userTracker).addCallback(
+ capture(userTrackerCallbackCaptor), any()
)
verify(listingController).addCallback(capture(listingCallbackCaptor))
@@ -227,11 +222,10 @@ class ControlsControllerImplTest : SysuiTestCase() {
uiController,
bindingController,
listingController,
- broadcastDispatcher,
userFileManager,
+ userTracker,
Optional.of(persistenceWrapper),
- mock(DumpManager::class.java),
- userTracker
+ mock(DumpManager::class.java)
)
assertEquals(listOf(TEST_STRUCTURE_INFO), controller_other.getFavorites())
}
@@ -518,14 +512,8 @@ class ControlsControllerImplTest : SysuiTestCase() {
delayableExecutor.runAllReady()
reset(persistenceWrapper)
- val intent = Intent(Intent.ACTION_USER_SWITCHED).apply {
- putExtra(Intent.EXTRA_USER_HANDLE, otherUser)
- }
- val pendingResult = mock(BroadcastReceiver.PendingResult::class.java)
- `when`(pendingResult.sendingUserId).thenReturn(otherUser)
- broadcastReceiverCaptor.value.pendingResult = pendingResult
- broadcastReceiverCaptor.value.onReceive(mContext, intent)
+ userTrackerCallbackCaptor.value.onUserChanged(otherUser, mContext)
verify(persistenceWrapper).changeFileAndBackupManager(any(), any())
verify(persistenceWrapper).readFavorites()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt
index 77f451f61c3c..48fc46b7e730 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt
@@ -17,19 +17,18 @@
package com.android.systemui.controls.dagger
import android.testing.AndroidTestingRunner
-import android.provider.Settings
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
import com.android.systemui.SysuiTestCase
+import com.android.systemui.controls.FakeControlsSettingsRepository
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.controls.controller.ControlsTileResourceConfiguration
import com.android.systemui.controls.management.ControlsListingController
import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.util.settings.SecureSettings
import dagger.Lazy
import java.util.Optional
import org.junit.Assert.assertEquals
@@ -63,13 +62,13 @@ class ControlsComponentTest : SysuiTestCase() {
@Mock
private lateinit var lockPatternUtils: LockPatternUtils
@Mock
- private lateinit var secureSettings: SecureSettings
- @Mock
private lateinit var optionalControlsTileResourceConfiguration:
Optional<ControlsTileResourceConfiguration>
@Mock
private lateinit var controlsTileResourceConfiguration: ControlsTileResourceConfiguration
+ private lateinit var controlsSettingsRepository: FakeControlsSettingsRepository
+
companion object {
fun <T> eq(value: T): T = Mockito.eq(value) ?: value
}
@@ -78,6 +77,8 @@ class ControlsComponentTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
+ controlsSettingsRepository = FakeControlsSettingsRepository()
+
`when`(userTracker.userHandle.identifier).thenReturn(0)
`when`(optionalControlsTileResourceConfiguration.orElse(any()))
.thenReturn(controlsTileResourceConfiguration)
@@ -125,8 +126,7 @@ class ControlsComponentTest : SysuiTestCase() {
`when`(lockPatternUtils.getStrongAuthForUser(anyInt()))
.thenReturn(STRONG_AUTH_NOT_REQUIRED)
`when`(keyguardStateController.isUnlocked()).thenReturn(false)
- `when`(secureSettings.getInt(eq(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS), anyInt()))
- .thenReturn(0)
+ controlsSettingsRepository.setCanShowControlsInLockscreen(false)
val component = setupComponent(true)
assertEquals(ControlsComponent.Visibility.AVAILABLE_AFTER_UNLOCK, component.getVisibility())
@@ -137,9 +137,7 @@ class ControlsComponentTest : SysuiTestCase() {
`when`(lockPatternUtils.getStrongAuthForUser(anyInt()))
.thenReturn(STRONG_AUTH_NOT_REQUIRED)
`when`(keyguardStateController.isUnlocked()).thenReturn(false)
- `when`(secureSettings.getIntForUser(eq(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS),
- anyInt(), anyInt()))
- .thenReturn(1)
+ controlsSettingsRepository.setCanShowControlsInLockscreen(true)
val component = setupComponent(true)
assertEquals(ControlsComponent.Visibility.AVAILABLE, component.getVisibility())
@@ -147,8 +145,7 @@ class ControlsComponentTest : SysuiTestCase() {
@Test
fun testFeatureEnabledAndCanShowWhileUnlockedVisibility() {
- `when`(secureSettings.getInt(eq(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS), anyInt()))
- .thenReturn(0)
+ controlsSettingsRepository.setCanShowControlsInLockscreen(false)
`when`(lockPatternUtils.getStrongAuthForUser(anyInt()))
.thenReturn(STRONG_AUTH_NOT_REQUIRED)
`when`(keyguardStateController.isUnlocked()).thenReturn(true)
@@ -187,7 +184,7 @@ class ControlsComponentTest : SysuiTestCase() {
lockPatternUtils,
keyguardStateController,
userTracker,
- secureSettings,
+ controlsSettingsRepository,
optionalControlsTileResourceConfiguration
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
index 7a2ba95f74a0..06a944e7a6d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
@@ -361,8 +361,7 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
assertThat(lp.getMarginEnd()).isEqualTo(margin);
});
- // The third view should be at the top end corner. No margin should be applied if not
- // specified.
+ // The third view should be at the top end corner. No margin should be applied.
verifyChange(thirdViewInfo, true, lp -> {
assertThat(lp.topToTop == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
assertThat(lp.endToEnd == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
@@ -442,65 +441,129 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase {
}
/**
- * Ensures the root complication applies margin if specified.
+ * Ensures layout sets correct max width constraint.
*/
@Test
- public void testRootComplicationSpecifiedMargin() {
- final int defaultMargin = 5;
- final int complicationMargin = 10;
+ public void testWidthConstraint() {
+ final int maxWidth = 20;
final ComplicationLayoutEngine engine =
- new ComplicationLayoutEngine(mLayout, defaultMargin, mTouchSession, 0, 0);
+ new ComplicationLayoutEngine(mLayout, 0, mTouchSession, 0, 0);
- final ViewInfo firstViewInfo = new ViewInfo(
+ final ViewInfo viewStartDirection = new ViewInfo(
new ComplicationLayoutParams(
100,
100,
ComplicationLayoutParams.POSITION_TOP
| ComplicationLayoutParams.POSITION_END,
- ComplicationLayoutParams.DIRECTION_DOWN,
- 0),
+ ComplicationLayoutParams.DIRECTION_START,
+ 0,
+ 5,
+ maxWidth),
+ Complication.CATEGORY_STANDARD,
+ mLayout);
+ final ViewInfo viewEndDirection = new ViewInfo(
+ new ComplicationLayoutParams(
+ 100,
+ 100,
+ ComplicationLayoutParams.POSITION_TOP
+ | ComplicationLayoutParams.POSITION_START,
+ ComplicationLayoutParams.DIRECTION_END,
+ 0,
+ 5,
+ maxWidth),
Complication.CATEGORY_STANDARD,
mLayout);
- addComplication(engine, firstViewInfo);
+ addComplication(engine, viewStartDirection);
+ addComplication(engine, viewEndDirection);
- final ViewInfo secondViewInfo = new ViewInfo(
+ // Verify both horizontal direction views have max width set correctly, and max height is
+ // not set.
+ verifyChange(viewStartDirection, false, lp -> {
+ assertThat(lp.matchConstraintMaxWidth).isEqualTo(maxWidth);
+ assertThat(lp.matchConstraintMaxHeight).isEqualTo(0);
+ });
+ verifyChange(viewEndDirection, false, lp -> {
+ assertThat(lp.matchConstraintMaxWidth).isEqualTo(maxWidth);
+ assertThat(lp.matchConstraintMaxHeight).isEqualTo(0);
+ });
+ }
+
+ /**
+ * Ensures layout sets correct max height constraint.
+ */
+ @Test
+ public void testHeightConstraint() {
+ final int maxHeight = 20;
+ final ComplicationLayoutEngine engine =
+ new ComplicationLayoutEngine(mLayout, 0, mTouchSession, 0, 0);
+
+ final ViewInfo viewUpDirection = new ViewInfo(
+ new ComplicationLayoutParams(
+ 100,
+ 100,
+ ComplicationLayoutParams.POSITION_BOTTOM
+ | ComplicationLayoutParams.POSITION_END,
+ ComplicationLayoutParams.DIRECTION_UP,
+ 0,
+ 5,
+ maxHeight),
+ Complication.CATEGORY_STANDARD,
+ mLayout);
+ final ViewInfo viewDownDirection = new ViewInfo(
new ComplicationLayoutParams(
100,
100,
ComplicationLayoutParams.POSITION_TOP
| ComplicationLayoutParams.POSITION_END,
- ComplicationLayoutParams.DIRECTION_START,
- 0),
- Complication.CATEGORY_SYSTEM,
+ ComplicationLayoutParams.DIRECTION_DOWN,
+ 0,
+ 5,
+ maxHeight),
+ Complication.CATEGORY_STANDARD,
mLayout);
- addComplication(engine, secondViewInfo);
+ addComplication(engine, viewUpDirection);
+ addComplication(engine, viewDownDirection);
- firstViewInfo.clearInvocations();
- secondViewInfo.clearInvocations();
+ // Verify both vertical direction views have max height set correctly, and max width is
+ // not set.
+ verifyChange(viewUpDirection, false, lp -> {
+ assertThat(lp.matchConstraintMaxHeight).isEqualTo(maxHeight);
+ assertThat(lp.matchConstraintMaxWidth).isEqualTo(0);
+ });
+ verifyChange(viewDownDirection, false, lp -> {
+ assertThat(lp.matchConstraintMaxHeight).isEqualTo(maxHeight);
+ assertThat(lp.matchConstraintMaxWidth).isEqualTo(0);
+ });
+ }
- final ViewInfo thirdViewInfo = new ViewInfo(
+ /**
+ * Ensures layout does not set any constraint if not specified.
+ */
+ @Test
+ public void testConstraintNotSetWhenNotSpecified() {
+ final ComplicationLayoutEngine engine =
+ new ComplicationLayoutEngine(mLayout, 0, mTouchSession, 0, 0);
+
+ final ViewInfo view = new ViewInfo(
new ComplicationLayoutParams(
100,
100,
ComplicationLayoutParams.POSITION_TOP
| ComplicationLayoutParams.POSITION_END,
- ComplicationLayoutParams.DIRECTION_START,
- 1,
- complicationMargin),
- Complication.CATEGORY_SYSTEM,
+ ComplicationLayoutParams.DIRECTION_DOWN,
+ 0,
+ 5),
+ Complication.CATEGORY_STANDARD,
mLayout);
- addComplication(engine, thirdViewInfo);
+ addComplication(engine, view);
- // The third view is the root view and has specified margin, which should be applied based
- // on its direction.
- verifyChange(thirdViewInfo, true, lp -> {
- assertThat(lp.getMarginStart()).isEqualTo(0);
- assertThat(lp.getMarginEnd()).isEqualTo(complicationMargin);
- assertThat(lp.topMargin).isEqualTo(0);
- assertThat(lp.bottomMargin).isEqualTo(0);
+ // Verify neither max height nor max width set.
+ verifyChange(view, false, lp -> {
+ assertThat(lp.matchConstraintMaxHeight).isEqualTo(0);
+ assertThat(lp.matchConstraintMaxWidth).isEqualTo(0);
});
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java
index ce7561e95f1e..fdb4cc4480da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutParamsTest.java
@@ -97,35 +97,10 @@ public class ComplicationLayoutParamsTest extends SysuiTestCase {
}
/**
- * Ensures ComplicationLayoutParams correctly returns whether the complication specified margin.
- */
- @Test
- public void testIsMarginSpecified() {
- final ComplicationLayoutParams paramsNoMargin = new ComplicationLayoutParams(
- 100,
- 100,
- ComplicationLayoutParams.POSITION_TOP
- | ComplicationLayoutParams.POSITION_START,
- ComplicationLayoutParams.DIRECTION_DOWN,
- 0);
- assertThat(paramsNoMargin.isMarginSpecified()).isFalse();
-
- final ComplicationLayoutParams paramsWithMargin = new ComplicationLayoutParams(
- 100,
- 100,
- ComplicationLayoutParams.POSITION_TOP
- | ComplicationLayoutParams.POSITION_START,
- ComplicationLayoutParams.DIRECTION_DOWN,
- 0,
- 20 /*margin*/);
- assertThat(paramsWithMargin.isMarginSpecified()).isTrue();
- }
-
- /**
* Ensures unspecified margin uses default.
*/
@Test
- public void testUnspecifiedMarginUsesDefault() {
+ public void testDefaultMargin() {
final ComplicationLayoutParams params = new ComplicationLayoutParams(
100,
100,
@@ -161,13 +136,15 @@ public class ComplicationLayoutParamsTest extends SysuiTestCase {
ComplicationLayoutParams.POSITION_TOP,
ComplicationLayoutParams.DIRECTION_DOWN,
3,
- 10);
+ 10,
+ 20);
final ComplicationLayoutParams copy = new ComplicationLayoutParams(params);
assertThat(copy.getDirection() == params.getDirection()).isTrue();
assertThat(copy.getPosition() == params.getPosition()).isTrue();
assertThat(copy.getWeight() == params.getWeight()).isTrue();
assertThat(copy.getMargin(0) == params.getMargin(1)).isTrue();
+ assertThat(copy.getConstraint() == params.getConstraint()).isTrue();
assertThat(copy.height == params.height).isTrue();
assertThat(copy.width == params.width).isTrue();
}
@@ -193,4 +170,31 @@ public class ComplicationLayoutParamsTest extends SysuiTestCase {
assertThat(copy.height == params.height).isTrue();
assertThat(copy.width == params.width).isTrue();
}
+
+ /**
+ * Ensures that constraint is set correctly.
+ */
+ @Test
+ public void testConstraint() {
+ final ComplicationLayoutParams paramsWithoutConstraint = new ComplicationLayoutParams(
+ 100,
+ 100,
+ ComplicationLayoutParams.POSITION_TOP,
+ ComplicationLayoutParams.DIRECTION_DOWN,
+ 3,
+ 10);
+ assertThat(paramsWithoutConstraint.constraintSpecified()).isFalse();
+
+ final int constraint = 10;
+ final ComplicationLayoutParams paramsWithConstraint = new ComplicationLayoutParams(
+ 100,
+ 100,
+ ComplicationLayoutParams.POSITION_TOP,
+ ComplicationLayoutParams.DIRECTION_DOWN,
+ 3,
+ 10,
+ constraint);
+ assertThat(paramsWithConstraint.constraintSpecified()).isTrue();
+ assertThat(paramsWithConstraint.getConstraint()).isEqualTo(constraint);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
index 30ad485d7ac3..e6d3a69593cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
@@ -35,6 +35,7 @@ import android.widget.ImageView;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.controls.ControlsServiceInfo;
import com.android.systemui.controls.controller.ControlsController;
@@ -84,7 +85,10 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase {
private ArgumentCaptor<ControlsListingController.ControlsListingCallback> mCallbackCaptor;
@Mock
- private ImageView mView;
+ private View mView;
+
+ @Mock
+ private ImageView mHomeControlsView;
@Mock
private ActivityStarter mActivityStarter;
@@ -105,6 +109,7 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase {
when(mControlsComponent.getControlsListingController()).thenReturn(
Optional.of(mControlsListingController));
when(mControlsComponent.getVisibility()).thenReturn(AVAILABLE);
+ when(mView.findViewById(R.id.home_controls_chip)).thenReturn(mHomeControlsView);
}
@Test
@@ -206,9 +211,9 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase {
final ArgumentCaptor<View.OnClickListener> clickListenerCaptor =
ArgumentCaptor.forClass(View.OnClickListener.class);
- verify(mView).setOnClickListener(clickListenerCaptor.capture());
+ verify(mHomeControlsView).setOnClickListener(clickListenerCaptor.capture());
- clickListenerCaptor.getValue().onClick(mView);
+ clickListenerCaptor.getValue().onClick(mHomeControlsView);
verify(mUiEventLogger).log(
DreamHomeControlsComplication.DreamHomeControlsChipViewController
.DreamOverlayEvent.DREAM_HOME_CONTROLS_TAPPED);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt
index 32c5b3f99d41..cef452b8ec22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt
@@ -20,6 +20,7 @@ package com.android.systemui.keyguard
import android.content.ContentValues
import android.content.pm.PackageManager
import android.content.pm.ProviderInfo
+import android.os.UserHandle
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.SystemUIAppComponentFactoryBase
@@ -27,8 +28,10 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -74,8 +77,8 @@ class KeyguardQuickAffordanceProviderTest : SysuiTestCase() {
underTest = KeyguardQuickAffordanceProvider()
val scope = CoroutineScope(IMMEDIATE)
- val selectionManager =
- KeyguardQuickAffordanceSelectionManager(
+ val localUserSelectionManager =
+ KeyguardQuickAffordanceLocalUserSelectionManager(
context = context,
userFileManager =
mock<UserFileManager>().apply {
@@ -91,11 +94,20 @@ class KeyguardQuickAffordanceProviderTest : SysuiTestCase() {
userTracker = userTracker,
broadcastDispatcher = fakeBroadcastDispatcher,
)
+ val remoteUserSelectionManager =
+ KeyguardQuickAffordanceRemoteUserSelectionManager(
+ scope = scope,
+ userTracker = userTracker,
+ clientFactory = FakeKeyguardQuickAffordanceProviderClientFactory(userTracker),
+ userHandle = UserHandle.SYSTEM,
+ )
val quickAffordanceRepository =
KeyguardQuickAffordanceRepository(
appContext = context,
scope = scope,
- selectionManager = selectionManager,
+ localUserSelectionManager = localUserSelectionManager,
+ remoteUserSelectionManager = remoteUserSelectionManager,
+ userTracker = userTracker,
configs =
setOf(
FakeKeyguardQuickAffordanceConfig(
@@ -114,9 +126,10 @@ class KeyguardQuickAffordanceProviderTest : SysuiTestCase() {
scope = scope,
backgroundDispatcher = IMMEDIATE,
secureSettings = FakeSettings(),
- selectionsManager = selectionManager,
+ selectionsManager = localUserSelectionManager,
),
dumpManager = mock(),
+ userHandle = UserHandle.SYSTEM,
)
underTest.interactor =
KeyguardQuickAffordanceInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
index cda701819d60..9fa7db127e1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
@@ -25,6 +25,7 @@ import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
import com.android.systemui.statusbar.policy.FlashlightController
import com.android.systemui.utils.leaks.FakeFlashlightController
import com.android.systemui.utils.leaks.LeakCheckedTest
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.UnconfinedTestDispatcher
@@ -38,156 +39,177 @@ import org.junit.runners.JUnit4
import org.mockito.Mock
import org.mockito.MockitoAnnotations
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(JUnit4::class)
class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() {
@Mock private lateinit var context: Context
private lateinit var flashlightController: FakeFlashlightController
- private lateinit var underTest : FlashlightQuickAffordanceConfig
+ private lateinit var underTest: FlashlightQuickAffordanceConfig
@Before
fun setUp() {
injectLeakCheckedDependency(FlashlightController::class.java)
MockitoAnnotations.initMocks(this)
- flashlightController = SysuiLeakCheck().getLeakChecker(FlashlightController::class.java) as FakeFlashlightController
+ flashlightController =
+ SysuiLeakCheck().getLeakChecker(FlashlightController::class.java)
+ as FakeFlashlightController
underTest = FlashlightQuickAffordanceConfig(context, flashlightController)
}
@Test
fun `flashlight is off -- triggered -- icon is on and active`() = runTest {
- //given
+ // given
flashlightController.isEnabled = false
flashlightController.isAvailable = true
val values = mutableListOf<KeyguardQuickAffordanceConfig.LockScreenState>()
- val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values)}
+ val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values) }
- //when
+ // when
underTest.onTriggered(null)
val lastValue = values.last()
- //then
+ // then
assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
- assertEquals(R.drawable.ic_flashlight_on,
- ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon as? Icon.Resource)?.res)
+ assertEquals(
+ R.drawable.qs_flashlight_icon_on,
+ ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon
+ as? Icon.Resource)
+ ?.res
+ )
job.cancel()
}
@Test
fun `flashlight is on -- triggered -- icon is off and inactive`() = runTest {
- //given
+ // given
flashlightController.isEnabled = true
flashlightController.isAvailable = true
val values = mutableListOf<KeyguardQuickAffordanceConfig.LockScreenState>()
- val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values)}
+ val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values) }
- //when
+ // when
underTest.onTriggered(null)
val lastValue = values.last()
- //then
+ // then
assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
- assertEquals(R.drawable.ic_flashlight_off,
- ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon as? Icon.Resource)?.res)
+ assertEquals(
+ R.drawable.qs_flashlight_icon_off,
+ ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon
+ as? Icon.Resource)
+ ?.res
+ )
job.cancel()
}
@Test
fun `flashlight is on -- receives error -- icon is off and inactive`() = runTest {
- //given
+ // given
flashlightController.isEnabled = true
flashlightController.isAvailable = false
val values = mutableListOf<KeyguardQuickAffordanceConfig.LockScreenState>()
- val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values)}
+ val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values) }
- //when
+ // when
flashlightController.onFlashlightError()
val lastValue = values.last()
- //then
+ // then
assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
- assertEquals(R.drawable.ic_flashlight_off,
- ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon as? Icon.Resource)?.res)
+ assertEquals(
+ R.drawable.qs_flashlight_icon_off,
+ ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon
+ as? Icon.Resource)
+ ?.res
+ )
job.cancel()
}
@Test
fun `flashlight availability now off -- hidden`() = runTest {
- //given
+ // given
flashlightController.isEnabled = true
flashlightController.isAvailable = false
val values = mutableListOf<KeyguardQuickAffordanceConfig.LockScreenState>()
- val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values)}
+ val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values) }
- //when
+ // when
flashlightController.onFlashlightAvailabilityChanged(false)
val lastValue = values.last()
- //then
+ // then
assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
job.cancel()
}
@Test
fun `flashlight availability now on -- flashlight on -- inactive and icon off`() = runTest {
- //given
+ // given
flashlightController.isEnabled = true
flashlightController.isAvailable = false
val values = mutableListOf<KeyguardQuickAffordanceConfig.LockScreenState>()
- val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values)}
+ val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values) }
- //when
+ // when
flashlightController.onFlashlightAvailabilityChanged(true)
val lastValue = values.last()
- //then
+ // then
assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
- assertTrue((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).activationState is ActivationState.Active)
- assertEquals(R.drawable.ic_flashlight_on, (lastValue.icon as? Icon.Resource)?.res)
+ assertTrue(
+ (lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).activationState
+ is ActivationState.Active
+ )
+ assertEquals(R.drawable.qs_flashlight_icon_on, (lastValue.icon as? Icon.Resource)?.res)
job.cancel()
}
@Test
fun `flashlight availability now on -- flashlight off -- inactive and icon off`() = runTest {
- //given
+ // given
flashlightController.isEnabled = false
flashlightController.isAvailable = false
val values = mutableListOf<KeyguardQuickAffordanceConfig.LockScreenState>()
- val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values)}
+ val job = launch(UnconfinedTestDispatcher()) { underTest.lockScreenState.toList(values) }
- //when
+ // when
flashlightController.onFlashlightAvailabilityChanged(true)
val lastValue = values.last()
- //then
+ // then
assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
- assertTrue((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).activationState is ActivationState.Inactive)
- assertEquals(R.drawable.ic_flashlight_off, (lastValue.icon as? Icon.Resource)?.res)
+ assertTrue(
+ (lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).activationState
+ is ActivationState.Inactive
+ )
+ assertEquals(R.drawable.qs_flashlight_icon_off, (lastValue.icon as? Icon.Resource)?.res)
job.cancel()
}
@Test
fun `flashlight available -- picker state default`() = runTest {
- //given
+ // given
flashlightController.isAvailable = true
- //when
+ // when
val result = underTest.getPickerScreenState()
- //then
+ // then
assertTrue(result is KeyguardQuickAffordanceConfig.PickerScreenState.Default)
}
@Test
fun `flashlight not available -- picker state unavailable`() = runTest {
- //given
+ // given
flashlightController.isAvailable = false
- //when
+ // when
val result = underTest.getPickerScreenState()
- //then
+ // then
assertTrue(result is KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
index 552b8cb96525..3b0169d77063 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
@@ -57,7 +57,7 @@ class KeyguardQuickAffordanceLegacySettingSyncerTest : SysuiTestCase() {
private lateinit var testScope: TestScope
private lateinit var testDispatcher: TestDispatcher
- private lateinit var selectionManager: KeyguardQuickAffordanceSelectionManager
+ private lateinit var selectionManager: KeyguardQuickAffordanceLocalUserSelectionManager
private lateinit var settings: FakeSettings
@Before
@@ -75,7 +75,7 @@ class KeyguardQuickAffordanceLegacySettingSyncerTest : SysuiTestCase() {
testDispatcher = UnconfinedTestDispatcher()
testScope = TestScope(testDispatcher)
selectionManager =
- KeyguardQuickAffordanceSelectionManager(
+ KeyguardQuickAffordanceLocalUserSelectionManager(
context = context,
userFileManager =
mock {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
index 6a2376b5bc4e..67091a9f40c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
@@ -52,11 +52,11 @@ import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(JUnit4::class)
-class KeyguardQuickAffordanceSelectionManagerTest : SysuiTestCase() {
+class KeyguardQuickAffordanceLocalUserSelectionManagerTest : SysuiTestCase() {
@Mock private lateinit var userFileManager: UserFileManager
- private lateinit var underTest: KeyguardQuickAffordanceSelectionManager
+ private lateinit var underTest: KeyguardQuickAffordanceLocalUserSelectionManager
private lateinit var userTracker: FakeUserTracker
private lateinit var sharedPrefs: MutableMap<Int, SharedPreferences>
@@ -74,7 +74,7 @@ class KeyguardQuickAffordanceSelectionManagerTest : SysuiTestCase() {
Dispatchers.setMain(dispatcher)
underTest =
- KeyguardQuickAffordanceSelectionManager(
+ KeyguardQuickAffordanceLocalUserSelectionManager(
context = context,
userFileManager = userFileManager,
userTracker = userTracker,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
new file mode 100644
index 000000000000..d7e9cf144f88
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.quickaffordance
+
+import android.content.pm.UserInfo
+import android.os.UserHandle
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
+import com.android.systemui.shared.quickaffordance.data.content.FakeKeyguardQuickAffordanceProviderClient
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestDispatcher
+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
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class KeyguardQuickAffordanceRemoteUserSelectionManagerTest : SysuiTestCase() {
+
+ @Mock private lateinit var userHandle: UserHandle
+
+ private lateinit var underTest: KeyguardQuickAffordanceRemoteUserSelectionManager
+
+ private lateinit var clientFactory: FakeKeyguardQuickAffordanceProviderClientFactory
+ private lateinit var testScope: TestScope
+ private lateinit var testDispatcher: TestDispatcher
+ private lateinit var userTracker: FakeUserTracker
+ private lateinit var client1: FakeKeyguardQuickAffordanceProviderClient
+ private lateinit var client2: FakeKeyguardQuickAffordanceProviderClient
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ whenever(userHandle.identifier).thenReturn(UserHandle.USER_SYSTEM)
+ whenever(userHandle.isSystem).thenReturn(true)
+ client1 = FakeKeyguardQuickAffordanceProviderClient()
+ client2 = FakeKeyguardQuickAffordanceProviderClient()
+
+ userTracker = FakeUserTracker()
+ userTracker.set(
+ userInfos =
+ listOf(
+ UserInfo(
+ UserHandle.USER_SYSTEM,
+ "Primary",
+ /* flags= */ 0,
+ ),
+ UserInfo(
+ OTHER_USER_ID_1,
+ "Secondary 1",
+ /* flags= */ 0,
+ ),
+ UserInfo(
+ OTHER_USER_ID_2,
+ "Secondary 2",
+ /* flags= */ 0,
+ ),
+ ),
+ selectedUserIndex = 0,
+ )
+
+ clientFactory =
+ FakeKeyguardQuickAffordanceProviderClientFactory(
+ userTracker,
+ ) { selectedUserId ->
+ when (selectedUserId) {
+ OTHER_USER_ID_1 -> client1
+ OTHER_USER_ID_2 -> client2
+ else -> error("No client set-up for user $selectedUserId!")
+ }
+ }
+
+ testDispatcher = StandardTestDispatcher()
+ testScope = TestScope(testDispatcher)
+
+ underTest =
+ KeyguardQuickAffordanceRemoteUserSelectionManager(
+ scope = testScope.backgroundScope,
+ userTracker = userTracker,
+ clientFactory = clientFactory,
+ userHandle = userHandle,
+ )
+ }
+
+ @Test
+ fun `selections - primary user process`() =
+ testScope.runTest {
+ val values = mutableListOf<Map<String, List<String>>>()
+ val job = launch { underTest.selections.toList(values) }
+
+ runCurrent()
+ assertThat(values.last()).isEmpty()
+
+ client1.insertSelection(
+ slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+ affordanceId = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1,
+ )
+ client2.insertSelection(
+ slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
+ affordanceId = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_2,
+ )
+
+ userTracker.set(
+ userInfos = userTracker.userProfiles,
+ selectedUserIndex = 1,
+ )
+ runCurrent()
+ assertThat(values.last())
+ .isEqualTo(
+ mapOf(
+ KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to
+ listOf(
+ FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1,
+ ),
+ )
+ )
+
+ userTracker.set(
+ userInfos = userTracker.userProfiles,
+ selectedUserIndex = 2,
+ )
+ runCurrent()
+ assertThat(values.last())
+ .isEqualTo(
+ mapOf(
+ KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to
+ listOf(
+ FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_2,
+ ),
+ )
+ )
+
+ job.cancel()
+ }
+
+ @Test
+ fun `selections - secondary user process - always empty`() =
+ testScope.runTest {
+ whenever(userHandle.isSystem).thenReturn(false)
+ val values = mutableListOf<Map<String, List<String>>>()
+ val job = launch { underTest.selections.toList(values) }
+
+ runCurrent()
+ assertThat(values.last()).isEmpty()
+
+ client1.insertSelection(
+ slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+ affordanceId = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1,
+ )
+ userTracker.set(
+ userInfos = userTracker.userProfiles,
+ selectedUserIndex = 1,
+ )
+ runCurrent()
+ assertThat(values.last()).isEmpty()
+
+ job.cancel()
+ }
+
+ @Test
+ fun setSelections() =
+ testScope.runTest {
+ userTracker.set(
+ userInfos = userTracker.userProfiles,
+ selectedUserIndex = 1,
+ )
+ runCurrent()
+
+ underTest.setSelections(
+ slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+ affordanceIds = listOf(FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1),
+ )
+ runCurrent()
+
+ assertThat(underTest.getSelections())
+ .isEqualTo(
+ mapOf(
+ KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to
+ listOf(
+ FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1,
+ ),
+ )
+ )
+ }
+
+ companion object {
+ private const val OTHER_USER_ID_1 = UserHandle.MIN_SECONDARY_USER_ID + 1
+ private const val OTHER_USER_ID_2 = UserHandle.MIN_SECONDARY_USER_ID + 2
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
index 652fae968744..c40488adf029 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
@@ -17,17 +17,23 @@
package com.android.systemui.keyguard.data.repository
+import android.content.pm.UserInfo
+import android.os.UserHandle
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation
import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentation
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.UserFileManager
+import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
+import com.android.systemui.shared.quickaffordance.data.content.FakeKeyguardQuickAffordanceProviderClient
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -39,6 +45,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.yield
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -55,14 +62,24 @@ class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() {
private lateinit var config1: FakeKeyguardQuickAffordanceConfig
private lateinit var config2: FakeKeyguardQuickAffordanceConfig
+ private lateinit var userTracker: FakeUserTracker
+ private lateinit var client1: FakeKeyguardQuickAffordanceProviderClient
+ private lateinit var client2: FakeKeyguardQuickAffordanceProviderClient
@Before
fun setUp() {
- config1 = FakeKeyguardQuickAffordanceConfig("built_in:1")
- config2 = FakeKeyguardQuickAffordanceConfig("built_in:2")
+ config1 =
+ FakeKeyguardQuickAffordanceConfig(
+ FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1
+ )
+ config2 =
+ FakeKeyguardQuickAffordanceConfig(
+ FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_2
+ )
val scope = CoroutineScope(IMMEDIATE)
- val selectionManager =
- KeyguardQuickAffordanceSelectionManager(
+ userTracker = FakeUserTracker()
+ val localUserSelectionManager =
+ KeyguardQuickAffordanceLocalUserSelectionManager(
context = context,
userFileManager =
mock<UserFileManager>().apply {
@@ -75,24 +92,45 @@ class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() {
)
.thenReturn(FakeSharedPreferences())
},
- userTracker = FakeUserTracker(),
+ userTracker = userTracker,
broadcastDispatcher = fakeBroadcastDispatcher,
)
+ client1 = FakeKeyguardQuickAffordanceProviderClient()
+ client2 = FakeKeyguardQuickAffordanceProviderClient()
+ val remoteUserSelectionManager =
+ KeyguardQuickAffordanceRemoteUserSelectionManager(
+ scope = scope,
+ userTracker = userTracker,
+ clientFactory =
+ FakeKeyguardQuickAffordanceProviderClientFactory(
+ userTracker,
+ ) { selectedUserId ->
+ when (selectedUserId) {
+ SECONDARY_USER_1 -> client1
+ SECONDARY_USER_2 -> client2
+ else -> error("No set-up client for user $selectedUserId!")
+ }
+ },
+ userHandle = UserHandle.SYSTEM,
+ )
underTest =
KeyguardQuickAffordanceRepository(
appContext = context,
scope = scope,
- selectionManager = selectionManager,
+ localUserSelectionManager = localUserSelectionManager,
+ remoteUserSelectionManager = remoteUserSelectionManager,
+ userTracker = userTracker,
legacySettingSyncer =
KeyguardQuickAffordanceLegacySettingSyncer(
scope = scope,
backgroundDispatcher = IMMEDIATE,
secureSettings = FakeSettings(),
- selectionsManager = selectionManager,
+ selectionsManager = localUserSelectionManager,
),
configs = setOf(config1, config2),
dumpManager = mock(),
+ userHandle = UserHandle.SYSTEM,
)
}
@@ -187,7 +225,53 @@ class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() {
)
}
- private suspend fun assertSelections(
+ @Test
+ fun `selections for secondary user`() =
+ runBlocking(IMMEDIATE) {
+ userTracker.set(
+ userInfos =
+ listOf(
+ UserInfo(
+ UserHandle.USER_SYSTEM,
+ "Primary",
+ /* flags= */ 0,
+ ),
+ UserInfo(
+ SECONDARY_USER_1,
+ "Secondary 1",
+ /* flags= */ 0,
+ ),
+ UserInfo(
+ SECONDARY_USER_2,
+ "Secondary 2",
+ /* flags= */ 0,
+ ),
+ ),
+ selectedUserIndex = 2,
+ )
+ client2.insertSelection(
+ slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+ affordanceId = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_2,
+ )
+ val observed = mutableListOf<Map<String, List<KeyguardQuickAffordanceConfig>>>()
+ val job = underTest.selections.onEach { observed.add(it) }.launchIn(this)
+ yield()
+
+ assertSelections(
+ observed = observed.last(),
+ expected =
+ mapOf(
+ KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to
+ listOf(
+ config2,
+ ),
+ )
+ )
+
+ job.cancel()
+ }
+
+ private fun assertSelections(
observed: Map<String, List<KeyguardQuickAffordanceConfig>>?,
expected: Map<String, List<KeyguardQuickAffordanceConfig>>,
) {
@@ -201,5 +285,7 @@ class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() {
companion object {
private val IMMEDIATE = Dispatchers.Main.immediate
+ private const val SECONDARY_USER_1 = UserHandle.MIN_SECONDARY_USER_ID + 1
+ private const val SECONDARY_USER_2 = UserHandle.MIN_SECONDARY_USER_ID + 2
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index 13fc9fcf834b..5deac1924ab7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -16,10 +16,13 @@
package com.android.systemui.keyguard.data.repository
+import android.graphics.Point
+import android.hardware.biometrics.BiometricSourceType
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.AuthController
import com.android.systemui.common.shared.model.Position
import com.android.systemui.doze.DozeHost
import com.android.systemui.doze.DozeMachine
@@ -27,9 +30,11 @@ import com.android.systemui.doze.DozeTransitionCallback
import com.android.systemui.doze.DozeTransitionListener
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -57,9 +62,10 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
@Mock private lateinit var dozeHost: DozeHost
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
- @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var biometricUnlockController: BiometricUnlockController
@Mock private lateinit var dozeTransitionListener: DozeTransitionListener
+ @Mock private lateinit var authController: AuthController
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
private lateinit var underTest: KeyguardRepositoryImpl
@@ -76,6 +82,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
keyguardStateController,
keyguardUpdateMonitor,
dozeTransitionListener,
+ authController,
)
}
@@ -198,7 +205,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
fun dozeAmount() =
runTest(UnconfinedTestDispatcher()) {
val values = mutableListOf<Float>()
- val job = underTest.dozeAmount.onEach(values::add).launchIn(this)
+ val job = underTest.linearDozeAmount.onEach(values::add).launchIn(this)
val captor = argumentCaptor<StatusBarStateController.StateListener>()
verify(statusBarStateController).addCallback(captor.capture())
@@ -207,7 +214,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
captor.value.onDozeAmountChanged(0.498f, 0.5f)
captor.value.onDozeAmountChanged(0.661f, 0.65f)
- assertThat(values).isEqualTo(listOf(0f, 0.4f, 0.5f, 0.65f))
+ assertThat(values).isEqualTo(listOf(0f, 0.433f, 0.498f, 0.661f))
job.cancel()
verify(statusBarStateController).removeCallback(captor.value)
@@ -217,25 +224,36 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
fun wakefulness() =
runTest(UnconfinedTestDispatcher()) {
val values = mutableListOf<WakefulnessModel>()
- val job = underTest.wakefulnessState.onEach(values::add).launchIn(this)
+ val job = underTest.wakefulness.onEach(values::add).launchIn(this)
val captor = argumentCaptor<WakefulnessLifecycle.Observer>()
verify(wakefulnessLifecycle).addObserver(captor.capture())
+ whenever(wakefulnessLifecycle.wakefulness)
+ .thenReturn(WakefulnessLifecycle.WAKEFULNESS_WAKING)
captor.value.onStartedWakingUp()
+
+ whenever(wakefulnessLifecycle.wakefulness)
+ .thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE)
captor.value.onFinishedWakingUp()
+
+ whenever(wakefulnessLifecycle.wakefulness)
+ .thenReturn(WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP)
captor.value.onStartedGoingToSleep()
+
+ whenever(wakefulnessLifecycle.wakefulness)
+ .thenReturn(WakefulnessLifecycle.WAKEFULNESS_ASLEEP)
captor.value.onFinishedGoingToSleep()
- assertThat(values)
+ assertThat(values.map { it.state })
.isEqualTo(
listOf(
// Initial value will be ASLEEP
- WakefulnessModel.ASLEEP,
- WakefulnessModel.STARTING_TO_WAKE,
- WakefulnessModel.AWAKE,
- WakefulnessModel.STARTING_TO_SLEEP,
- WakefulnessModel.ASLEEP,
+ WakefulnessState.ASLEEP,
+ WakefulnessState.STARTING_TO_WAKE,
+ WakefulnessState.AWAKE,
+ WakefulnessState.STARTING_TO_SLEEP,
+ WakefulnessState.ASLEEP,
)
)
@@ -329,14 +347,20 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
val captor = argumentCaptor<BiometricUnlockController.BiometricModeListener>()
verify(biometricUnlockController).addBiometricModeListener(captor.capture())
- captor.value.onModeChanged(BiometricUnlockController.MODE_NONE)
- captor.value.onModeChanged(BiometricUnlockController.MODE_WAKE_AND_UNLOCK)
- captor.value.onModeChanged(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING)
- captor.value.onModeChanged(BiometricUnlockController.MODE_SHOW_BOUNCER)
- captor.value.onModeChanged(BiometricUnlockController.MODE_ONLY_WAKE)
- captor.value.onModeChanged(BiometricUnlockController.MODE_UNLOCK_COLLAPSING)
- captor.value.onModeChanged(BiometricUnlockController.MODE_DISMISS_BOUNCER)
- captor.value.onModeChanged(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM)
+ listOf(
+ BiometricUnlockController.MODE_NONE,
+ BiometricUnlockController.MODE_WAKE_AND_UNLOCK,
+ BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING,
+ BiometricUnlockController.MODE_SHOW_BOUNCER,
+ BiometricUnlockController.MODE_ONLY_WAKE,
+ BiometricUnlockController.MODE_UNLOCK_COLLAPSING,
+ BiometricUnlockController.MODE_DISMISS_BOUNCER,
+ BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM,
+ )
+ .forEach {
+ whenever(biometricUnlockController.mode).thenReturn(it)
+ captor.value.onModeChanged(it)
+ }
assertThat(values)
.isEqualTo(
@@ -420,4 +444,104 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
job.cancel()
verify(dozeTransitionListener).removeCallback(listener)
}
+
+ @Test
+ fun fingerprintSensorLocation() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<Point?>()
+ val job = underTest.fingerprintSensorLocation.onEach(values::add).launchIn(this)
+
+ val captor = argumentCaptor<AuthController.Callback>()
+ verify(authController).addCallback(captor.capture())
+
+ // An initial, null value should be initially emitted so that flows combined with this
+ // one
+ // emit values immediately. The sensor location is expected to be nullable, so anyone
+ // consuming it should handle that properly.
+ assertThat(values).isEqualTo(listOf(null))
+
+ listOf(Point(500, 500), Point(0, 0), null, Point(250, 250))
+ .onEach {
+ whenever(authController.fingerprintSensorLocation).thenReturn(it)
+ captor.value.onFingerprintLocationChanged()
+ }
+ .also { dispatchedSensorLocations ->
+ assertThat(values).isEqualTo(listOf(null) + dispatchedSensorLocations)
+ }
+
+ job.cancel()
+ }
+
+ @Test
+ fun faceSensorLocation() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<Point?>()
+ val job = underTest.faceSensorLocation.onEach(values::add).launchIn(this)
+
+ val captor = argumentCaptor<AuthController.Callback>()
+ verify(authController).addCallback(captor.capture())
+
+ // An initial, null value should be initially emitted so that flows combined with this
+ // one
+ // emit values immediately. The sensor location is expected to be nullable, so anyone
+ // consuming it should handle that properly.
+ assertThat(values).isEqualTo(listOf(null))
+
+ listOf(
+ Point(500, 500),
+ Point(0, 0),
+ null,
+ Point(250, 250),
+ )
+ .onEach {
+ whenever(authController.faceSensorLocation).thenReturn(it)
+ captor.value.onFaceSensorLocationChanged()
+ }
+ .also { dispatchedSensorLocations ->
+ assertThat(values).isEqualTo(listOf(null) + dispatchedSensorLocations)
+ }
+
+ job.cancel()
+ }
+
+ @Test
+ fun biometricUnlockSource() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<BiometricUnlockSource?>()
+ val job = underTest.biometricUnlockSource.onEach(values::add).launchIn(this)
+
+ val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
+ verify(keyguardUpdateMonitor).registerCallback(captor.capture())
+
+ // An initial, null value should be initially emitted so that flows combined with this
+ // one
+ // emit values immediately. The biometric unlock source is expected to be nullable, so
+ // anyone consuming it should handle that properly.
+ assertThat(values).isEqualTo(listOf(null))
+
+ listOf(
+ BiometricSourceType.FINGERPRINT,
+ BiometricSourceType.IRIS,
+ null,
+ BiometricSourceType.FACE,
+ BiometricSourceType.FINGERPRINT,
+ )
+ .onEach { biometricSourceType ->
+ captor.value.onBiometricAuthenticated(0, biometricSourceType, false)
+ }
+
+ assertThat(values)
+ .isEqualTo(
+ listOf(
+ null,
+ BiometricUnlockSource.FINGERPRINT_SENSOR,
+ BiometricUnlockSource.FACE_SENSOR,
+ null,
+ BiometricUnlockSource.FACE_SENSOR,
+ BiometricUnlockSource.FINGERPRINT_SENSOR,
+ )
+ )
+
+ job.cancel()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
new file mode 100644
index 000000000000..d2db910ad443
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import android.graphics.Point
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
+import com.android.systemui.statusbar.CircleReveal
+import com.android.systemui.statusbar.LightRevealEffect
+import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertFalse
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(JUnit4::class)
+class LightRevealScrimRepositoryTest : SysuiTestCase() {
+ private lateinit var fakeKeyguardRepository: FakeKeyguardRepository
+ private lateinit var underTest: LightRevealScrimRepositoryImpl
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ fakeKeyguardRepository = FakeKeyguardRepository()
+ underTest = LightRevealScrimRepositoryImpl(fakeKeyguardRepository, context)
+ }
+
+ @Test
+ fun `nextRevealEffect - effect switches between default and biometric with no dupes`() =
+ runTest {
+ val values = mutableListOf<LightRevealEffect>()
+ val job = launch { underTest.revealEffect.collect { values.add(it) } }
+
+ // We should initially emit the default reveal effect.
+ runCurrent()
+ values.assertEffectsMatchPredicates({ it == DEFAULT_REVEAL_EFFECT })
+
+ // The source and sensor locations are still null, so we should still be using the
+ // default reveal despite a biometric unlock.
+ fakeKeyguardRepository.setBiometricUnlockState(BiometricUnlockModel.WAKE_AND_UNLOCK)
+
+ runCurrent()
+ values.assertEffectsMatchPredicates({ it == DEFAULT_REVEAL_EFFECT },)
+
+ // We got a source but still have no sensor locations, so should be sticking with
+ // the default effect.
+ fakeKeyguardRepository.setBiometricUnlockSource(
+ BiometricUnlockSource.FINGERPRINT_SENSOR
+ )
+
+ runCurrent()
+ values.assertEffectsMatchPredicates({ it == DEFAULT_REVEAL_EFFECT },)
+
+ // We got a location for the face sensor, but we unlocked with fingerprint.
+ val faceLocation = Point(250, 0)
+ fakeKeyguardRepository.setFaceSensorLocation(faceLocation)
+
+ runCurrent()
+ values.assertEffectsMatchPredicates({ it == DEFAULT_REVEAL_EFFECT },)
+
+ // Now we have fingerprint sensor locations, and wake and unlock via fingerprint.
+ val fingerprintLocation = Point(500, 500)
+ fakeKeyguardRepository.setFingerprintSensorLocation(fingerprintLocation)
+ fakeKeyguardRepository.setBiometricUnlockSource(
+ BiometricUnlockSource.FINGERPRINT_SENSOR
+ )
+ fakeKeyguardRepository.setBiometricUnlockState(
+ BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING
+ )
+
+ // We should now have switched to the circle reveal, at the fingerprint location.
+ runCurrent()
+ values.assertEffectsMatchPredicates(
+ { it == DEFAULT_REVEAL_EFFECT },
+ {
+ it is CircleReveal &&
+ it.centerX == fingerprintLocation.x &&
+ it.centerY == fingerprintLocation.y
+ },
+ )
+
+ // Subsequent wake and unlocks should not emit duplicate, identical CircleReveals.
+ val valuesPrevSize = values.size
+ fakeKeyguardRepository.setBiometricUnlockState(
+ BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING
+ )
+ fakeKeyguardRepository.setBiometricUnlockState(
+ BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM
+ )
+ assertEquals(valuesPrevSize, values.size)
+
+ // Non-biometric unlock, we should return to the default reveal.
+ fakeKeyguardRepository.setBiometricUnlockState(BiometricUnlockModel.NONE)
+
+ runCurrent()
+ values.assertEffectsMatchPredicates(
+ { it == DEFAULT_REVEAL_EFFECT },
+ {
+ it is CircleReveal &&
+ it.centerX == fingerprintLocation.x &&
+ it.centerY == fingerprintLocation.y
+ },
+ { it == DEFAULT_REVEAL_EFFECT },
+ )
+
+ // We already have a face location, so switching to face source should update the
+ // CircleReveal.
+ fakeKeyguardRepository.setBiometricUnlockSource(BiometricUnlockSource.FACE_SENSOR)
+ runCurrent()
+ fakeKeyguardRepository.setBiometricUnlockState(BiometricUnlockModel.WAKE_AND_UNLOCK)
+ runCurrent()
+
+ values.assertEffectsMatchPredicates(
+ { it == DEFAULT_REVEAL_EFFECT },
+ {
+ it is CircleReveal &&
+ it.centerX == fingerprintLocation.x &&
+ it.centerY == fingerprintLocation.y
+ },
+ { it == DEFAULT_REVEAL_EFFECT },
+ {
+ it is CircleReveal &&
+ it.centerX == faceLocation.x &&
+ it.centerY == faceLocation.y
+ },
+ )
+
+ job.cancel()
+ }
+
+ /**
+ * Asserts that the list of LightRevealEffects satisfies the list of predicates, in order, with
+ * no leftover elements.
+ */
+ private fun List<LightRevealEffect>.assertEffectsMatchPredicates(
+ vararg predicates: (LightRevealEffect) -> Boolean
+ ) {
+ println(this)
+ assertEquals(predicates.size, this.size)
+
+ assertFalse(
+ zip(predicates) { effect, predicate -> predicate(effect) }.any { matched -> !matched }
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index ba7c40b6b381..1c1f0399bb06 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.domain.interactor
import android.content.Intent
+import android.os.UserHandle
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.SysuiTestCase
@@ -29,9 +30,11 @@ import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
@@ -237,8 +240,8 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
val qrCodeScanner =
FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER)
val scope = CoroutineScope(IMMEDIATE)
- val selectionManager =
- KeyguardQuickAffordanceSelectionManager(
+ val localUserSelectionManager =
+ KeyguardQuickAffordanceLocalUserSelectionManager(
context = context,
userFileManager =
mock<UserFileManager>().apply {
@@ -254,20 +257,30 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
userTracker = userTracker,
broadcastDispatcher = fakeBroadcastDispatcher,
)
+ val remoteUserSelectionManager =
+ KeyguardQuickAffordanceRemoteUserSelectionManager(
+ scope = scope,
+ userTracker = userTracker,
+ clientFactory = FakeKeyguardQuickAffordanceProviderClientFactory(userTracker),
+ userHandle = UserHandle.SYSTEM,
+ )
val quickAffordanceRepository =
KeyguardQuickAffordanceRepository(
appContext = context,
scope = scope,
- selectionManager = selectionManager,
+ localUserSelectionManager = localUserSelectionManager,
+ remoteUserSelectionManager = remoteUserSelectionManager,
+ userTracker = userTracker,
legacySettingSyncer =
KeyguardQuickAffordanceLegacySettingSyncer(
scope = scope,
backgroundDispatcher = IMMEDIATE,
secureSettings = FakeSettings(),
- selectionsManager = selectionManager,
+ selectionsManager = localUserSelectionManager,
),
configs = setOf(homeControls, quickAccessWallet, qrCodeScanner),
dumpManager = mock(),
+ userHandle = UserHandle.SYSTEM,
)
underTest =
KeyguardQuickAffordanceInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 8d0c4ef4b3da..11fe905b1d1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
+import android.os.UserHandle
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.SysuiTestCase
@@ -26,9 +27,11 @@ import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
@@ -98,8 +101,8 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER)
val scope = CoroutineScope(IMMEDIATE)
- val selectionManager =
- KeyguardQuickAffordanceSelectionManager(
+ val localUserSelectionManager =
+ KeyguardQuickAffordanceLocalUserSelectionManager(
context = context,
userFileManager =
mock<UserFileManager>().apply {
@@ -115,20 +118,30 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
userTracker = userTracker,
broadcastDispatcher = fakeBroadcastDispatcher,
)
+ val remoteUserSelectionManager =
+ KeyguardQuickAffordanceRemoteUserSelectionManager(
+ scope = scope,
+ userTracker = userTracker,
+ clientFactory = FakeKeyguardQuickAffordanceProviderClientFactory(userTracker),
+ userHandle = UserHandle.SYSTEM,
+ )
val quickAffordanceRepository =
KeyguardQuickAffordanceRepository(
appContext = context,
scope = scope,
- selectionManager = selectionManager,
+ localUserSelectionManager = localUserSelectionManager,
+ remoteUserSelectionManager = remoteUserSelectionManager,
+ userTracker = userTracker,
legacySettingSyncer =
KeyguardQuickAffordanceLegacySettingSyncer(
scope = scope,
backgroundDispatcher = IMMEDIATE,
secureSettings = FakeSettings(),
- selectionsManager = selectionManager,
+ selectionsManager = localUserSelectionManager,
),
configs = setOf(homeControls, quickAccessWallet, qrCodeScanner),
dumpManager = mock(),
+ userHandle = UserHandle.SYSTEM,
)
featureFlags =
FakeFeatureFlags().apply {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
new file mode 100644
index 000000000000..31662145dfbe
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.FakeLightRevealScrimRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.statusbar.LightRevealEffect
+import com.android.systemui.statusbar.LightRevealScrim
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(JUnit4::class)
+class LightRevealScrimInteractorTest : SysuiTestCase() {
+ private val fakeKeyguardTransitionRepository = FakeKeyguardTransitionRepository()
+ private val fakeLightRevealScrimRepository = FakeLightRevealScrimRepository()
+
+ private val keyguardTransitionInteractor =
+ KeyguardTransitionInteractor(fakeKeyguardTransitionRepository)
+
+ private lateinit var underTest: LightRevealScrimInteractor
+
+ private val reveal1 =
+ object : LightRevealEffect {
+ override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) {}
+ }
+
+ private val reveal2 =
+ object : LightRevealEffect {
+ override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) {}
+ }
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ underTest =
+ LightRevealScrimInteractor(
+ fakeKeyguardTransitionRepository,
+ keyguardTransitionInteractor,
+ fakeLightRevealScrimRepository
+ )
+ }
+
+ @Test
+ fun `lightRevealEffect - does not change during keyguard transition`() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<LightRevealEffect>()
+ val job = underTest.lightRevealEffect.onEach(values::add).launchIn(this)
+
+ fakeLightRevealScrimRepository.setRevealEffect(reveal1)
+
+ // The reveal effect shouldn't emit anything until a keyguard transition starts.
+ assertEquals(values.size, 0)
+
+ // Once it starts, it should emit reveal1.
+ fakeKeyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(transitionState = TransitionState.STARTED)
+ )
+ assertEquals(values, listOf(reveal1))
+
+ // Until the next transition starts, reveal2 should not be emitted.
+ fakeLightRevealScrimRepository.setRevealEffect(reveal2)
+ fakeKeyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(transitionState = TransitionState.RUNNING)
+ )
+ fakeKeyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(transitionState = TransitionState.FINISHED)
+ )
+ assertEquals(values, listOf(reveal1))
+ fakeKeyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(transitionState = TransitionState.STARTED)
+ )
+ assertEquals(values, listOf(reveal1, reveal2))
+
+ job.cancel()
+ }
+
+ @Test
+ fun `revealAmount - inverted when appropriate`() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<Float>()
+ val job = underTest.revealAmount.onEach(values::add).launchIn(this)
+
+ fakeKeyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ value = 0.3f
+ )
+ )
+
+ assertEquals(values, listOf(0.3f))
+
+ fakeKeyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ value = 0.3f
+ )
+ )
+
+ assertEquals(values, listOf(0.3f, 0.7f))
+
+ job.cancel()
+ }
+
+ @Test
+ fun `revealAmount - ignores transitions that do not affect reveal amount`() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<Float>()
+ val job = underTest.revealAmount.onEach(values::add).launchIn(this)
+
+ fakeKeyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(from = KeyguardState.DOZING, to = KeyguardState.AOD, value = 0.3f)
+ )
+
+ assertEquals(values, emptyList<Float>())
+
+ fakeKeyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(from = KeyguardState.AOD, to = KeyguardState.DOZING, value = 0.3f)
+ )
+
+ assertEquals(values, emptyList<Float>())
+
+ job.cancel()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 32849cdce02e..83a5d0e90c84 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.content.Intent
+import android.os.UserHandle
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.SysuiTestCase
@@ -27,9 +28,11 @@ import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
@@ -121,8 +124,8 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
whenever(lockPatternUtils.getStrongAuthForUser(anyInt()))
.thenReturn(LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED)
val scope = CoroutineScope(IMMEDIATE)
- val selectionManager =
- KeyguardQuickAffordanceSelectionManager(
+ val localUserSelectionManager =
+ KeyguardQuickAffordanceLocalUserSelectionManager(
context = context,
userFileManager =
mock<UserFileManager>().apply {
@@ -138,17 +141,26 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
userTracker = userTracker,
broadcastDispatcher = fakeBroadcastDispatcher,
)
+ val remoteUserSelectionManager =
+ KeyguardQuickAffordanceRemoteUserSelectionManager(
+ scope = scope,
+ userTracker = userTracker,
+ clientFactory = FakeKeyguardQuickAffordanceProviderClientFactory(userTracker),
+ userHandle = UserHandle.SYSTEM,
+ )
val quickAffordanceRepository =
KeyguardQuickAffordanceRepository(
appContext = context,
scope = scope,
- selectionManager = selectionManager,
+ localUserSelectionManager = localUserSelectionManager,
+ remoteUserSelectionManager = remoteUserSelectionManager,
+ userTracker = userTracker,
legacySettingSyncer =
KeyguardQuickAffordanceLegacySettingSyncer(
scope = scope,
backgroundDispatcher = IMMEDIATE,
secureSettings = FakeSettings(),
- selectionsManager = selectionManager,
+ selectionsManager = localUserSelectionManager,
),
configs =
setOf(
@@ -157,6 +169,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
qrCodeScannerAffordanceConfig,
),
dumpManager = mock(),
+ userHandle = UserHandle.SYSTEM,
)
underTest =
KeyguardBottomAreaViewModel(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
index 688c66ac80c9..2c8d7abd4f4a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
@@ -46,6 +46,109 @@ class TableLogBufferTest : SysuiTestCase() {
}
@Test
+ fun dumpChanges_hasHeader() {
+ val dumpedString = dumpChanges()
+
+ assertThat(logLines(dumpedString)[0]).isEqualTo(HEADER_PREFIX + NAME)
+ }
+
+ @Test
+ fun dumpChanges_hasVersion() {
+ val dumpedString = dumpChanges()
+
+ assertThat(logLines(dumpedString)[1]).isEqualTo("version $VERSION")
+ }
+
+ @Test
+ fun dumpChanges_hasFooter() {
+ val dumpedString = dumpChanges()
+
+ assertThat(logLines(dumpedString).last()).isEqualTo(FOOTER_PREFIX + NAME)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun dumpChanges_str_separatorNotAllowedInPrefix() {
+ val next =
+ object : TestDiffable() {
+ override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {
+ row.logChange("columnName", "stringValue")
+ }
+ }
+ underTest.logDiffs("some${SEPARATOR}thing", TestDiffable(), next)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun dumpChanges_bool_separatorNotAllowedInPrefix() {
+ val next =
+ object : TestDiffable() {
+ override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {
+ row.logChange("columnName", true)
+ }
+ }
+ underTest.logDiffs("some${SEPARATOR}thing", TestDiffable(), next)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun dumpChanges_int_separatorNotAllowedInPrefix() {
+ val next =
+ object : TestDiffable() {
+ override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {
+ row.logChange("columnName", 567)
+ }
+ }
+ underTest.logDiffs("some${SEPARATOR}thing", TestDiffable(), next)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun dumpChanges_str_separatorNotAllowedInColumnName() {
+ val next =
+ object : TestDiffable() {
+ override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {
+ row.logChange("column${SEPARATOR}Name", "stringValue")
+ }
+ }
+ underTest.logDiffs("prefix", TestDiffable(), next)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun dumpChanges_bool_separatorNotAllowedInColumnName() {
+ val next =
+ object : TestDiffable() {
+ override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {
+ row.logChange("column${SEPARATOR}Name", true)
+ }
+ }
+ underTest.logDiffs("prefix", TestDiffable(), next)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun dumpChanges_int_separatorNotAllowedInColumnName() {
+ val next =
+ object : TestDiffable() {
+ override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {
+ row.logChange("column${SEPARATOR}Name", 456)
+ }
+ }
+ underTest.logDiffs("prefix", TestDiffable(), next)
+ }
+
+ @Test
+ fun logChange_bool_dumpsCorrectly() {
+ systemClock.setCurrentTimeMillis(4000L)
+
+ underTest.logChange("prefix", "columnName", true)
+
+ val dumpedString = dumpChanges()
+ val expected =
+ TABLE_LOG_DATE_FORMAT.format(4000L) +
+ SEPARATOR +
+ "prefix.columnName" +
+ SEPARATOR +
+ "true"
+ assertThat(dumpedString).contains(expected)
+ }
+
+ @Test
fun dumpChanges_strChange_logsFromNext() {
systemClock.setCurrentTimeMillis(100L)
@@ -66,11 +169,14 @@ class TableLogBufferTest : SysuiTestCase() {
val dumpedString = dumpChanges()
- assertThat(dumpedString).contains("prefix")
- assertThat(dumpedString).contains("stringValChange")
- assertThat(dumpedString).contains("newStringVal")
+ val expected =
+ TABLE_LOG_DATE_FORMAT.format(100L) +
+ SEPARATOR +
+ "prefix.stringValChange" +
+ SEPARATOR +
+ "newStringVal"
+ assertThat(dumpedString).contains(expected)
assertThat(dumpedString).doesNotContain("prevStringVal")
- assertThat(dumpedString).contains(TABLE_LOG_DATE_FORMAT.format(100L))
}
@Test
@@ -94,11 +200,14 @@ class TableLogBufferTest : SysuiTestCase() {
val dumpedString = dumpChanges()
- assertThat(dumpedString).contains("prefix")
- assertThat(dumpedString).contains("booleanValChange")
- assertThat(dumpedString).contains("true")
+ val expected =
+ TABLE_LOG_DATE_FORMAT.format(100L) +
+ SEPARATOR +
+ "prefix.booleanValChange" +
+ SEPARATOR +
+ "true"
+ assertThat(dumpedString).contains(expected)
assertThat(dumpedString).doesNotContain("false")
- assertThat(dumpedString).contains(TABLE_LOG_DATE_FORMAT.format(100L))
}
@Test
@@ -122,11 +231,14 @@ class TableLogBufferTest : SysuiTestCase() {
val dumpedString = dumpChanges()
- assertThat(dumpedString).contains("prefix")
- assertThat(dumpedString).contains("intValChange")
- assertThat(dumpedString).contains("67890")
+ val expected =
+ TABLE_LOG_DATE_FORMAT.format(100L) +
+ SEPARATOR +
+ "prefix.intValChange" +
+ SEPARATOR +
+ "67890"
+ assertThat(dumpedString).contains(expected)
assertThat(dumpedString).doesNotContain("12345")
- assertThat(dumpedString).contains(TABLE_LOG_DATE_FORMAT.format(100L))
}
@Test
@@ -152,9 +264,9 @@ class TableLogBufferTest : SysuiTestCase() {
val dumpedString = dumpChanges()
// THEN the dump still works
- assertThat(dumpedString).contains("booleanValChange")
- assertThat(dumpedString).contains("true")
- assertThat(dumpedString).contains(TABLE_LOG_DATE_FORMAT.format(100L))
+ val expected =
+ TABLE_LOG_DATE_FORMAT.format(100L) + SEPARATOR + "booleanValChange" + SEPARATOR + "true"
+ assertThat(dumpedString).contains(expected)
}
@Test
@@ -186,15 +298,34 @@ class TableLogBufferTest : SysuiTestCase() {
val dumpedString = dumpChanges()
- assertThat(dumpedString).contains("valChange")
- assertThat(dumpedString).contains("stateValue12")
- assertThat(dumpedString).contains("stateValue20")
- assertThat(dumpedString).contains("stateValue40")
- assertThat(dumpedString).contains("stateValue45")
- assertThat(dumpedString).contains(TABLE_LOG_DATE_FORMAT.format(12000L))
- assertThat(dumpedString).contains(TABLE_LOG_DATE_FORMAT.format(20000L))
- assertThat(dumpedString).contains(TABLE_LOG_DATE_FORMAT.format(40000L))
- assertThat(dumpedString).contains(TABLE_LOG_DATE_FORMAT.format(45000L))
+ val expected1 =
+ TABLE_LOG_DATE_FORMAT.format(12000L) +
+ SEPARATOR +
+ "valChange" +
+ SEPARATOR +
+ "stateValue12"
+ val expected2 =
+ TABLE_LOG_DATE_FORMAT.format(20000L) +
+ SEPARATOR +
+ "valChange" +
+ SEPARATOR +
+ "stateValue20"
+ val expected3 =
+ TABLE_LOG_DATE_FORMAT.format(40000L) +
+ SEPARATOR +
+ "valChange" +
+ SEPARATOR +
+ "stateValue40"
+ val expected4 =
+ TABLE_LOG_DATE_FORMAT.format(45000L) +
+ SEPARATOR +
+ "valChange" +
+ SEPARATOR +
+ "stateValue45"
+ assertThat(dumpedString).contains(expected1)
+ assertThat(dumpedString).contains(expected2)
+ assertThat(dumpedString).contains(expected3)
+ assertThat(dumpedString).contains(expected4)
}
@Test
@@ -214,10 +345,73 @@ class TableLogBufferTest : SysuiTestCase() {
val dumpedString = dumpChanges()
- assertThat(dumpedString).contains("status")
- assertThat(dumpedString).contains("in progress")
- assertThat(dumpedString).contains("connected")
- assertThat(dumpedString).contains("false")
+ val timestamp = TABLE_LOG_DATE_FORMAT.format(100L)
+ val expected1 = timestamp + SEPARATOR + "status" + SEPARATOR + "in progress"
+ val expected2 = timestamp + SEPARATOR + "connected" + SEPARATOR + "false"
+ assertThat(dumpedString).contains(expected1)
+ assertThat(dumpedString).contains(expected2)
+ }
+
+ @Test
+ fun logChange_rowInitializer_dumpsCorrectly() {
+ systemClock.setCurrentTimeMillis(100L)
+
+ underTest.logChange("") { row ->
+ row.logChange("column1", "val1")
+ row.logChange("column2", 2)
+ row.logChange("column3", true)
+ }
+
+ val dumpedString = dumpChanges()
+
+ val timestamp = TABLE_LOG_DATE_FORMAT.format(100L)
+ val expected1 = timestamp + SEPARATOR + "column1" + SEPARATOR + "val1"
+ val expected2 = timestamp + SEPARATOR + "column2" + SEPARATOR + "2"
+ val expected3 = timestamp + SEPARATOR + "column3" + SEPARATOR + "true"
+ assertThat(dumpedString).contains(expected1)
+ assertThat(dumpedString).contains(expected2)
+ assertThat(dumpedString).contains(expected3)
+ }
+
+ @Test
+ fun logChangeAndLogDiffs_bothLogged() {
+ systemClock.setCurrentTimeMillis(100L)
+
+ underTest.logChange("") { row ->
+ row.logChange("column1", "val1")
+ row.logChange("column2", 2)
+ row.logChange("column3", true)
+ }
+
+ systemClock.setCurrentTimeMillis(200L)
+ val prevDiffable = object : TestDiffable() {}
+ val nextDiffable =
+ object : TestDiffable() {
+ override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {
+ row.logChange("column1", "newVal1")
+ row.logChange("column2", 222)
+ row.logChange("column3", false)
+ }
+ }
+
+ underTest.logDiffs(columnPrefix = "", prevDiffable, nextDiffable)
+
+ val dumpedString = dumpChanges()
+
+ val timestamp1 = TABLE_LOG_DATE_FORMAT.format(100L)
+ val expected1 = timestamp1 + SEPARATOR + "column1" + SEPARATOR + "val1"
+ val expected2 = timestamp1 + SEPARATOR + "column2" + SEPARATOR + "2"
+ val expected3 = timestamp1 + SEPARATOR + "column3" + SEPARATOR + "true"
+ val timestamp2 = TABLE_LOG_DATE_FORMAT.format(200L)
+ val expected4 = timestamp2 + SEPARATOR + "column1" + SEPARATOR + "newVal1"
+ val expected5 = timestamp2 + SEPARATOR + "column2" + SEPARATOR + "222"
+ val expected6 = timestamp2 + SEPARATOR + "column3" + SEPARATOR + "false"
+ assertThat(dumpedString).contains(expected1)
+ assertThat(dumpedString).contains(expected2)
+ assertThat(dumpedString).contains(expected3)
+ assertThat(dumpedString).contains(expected4)
+ assertThat(dumpedString).contains(expected5)
+ assertThat(dumpedString).contains(expected6)
}
@Test
@@ -247,14 +441,24 @@ class TableLogBufferTest : SysuiTestCase() {
}
private fun dumpChanges(): String {
- underTest.dumpChanges(PrintWriter(outputWriter))
+ underTest.dump(PrintWriter(outputWriter), arrayOf())
return outputWriter.toString()
}
- private abstract class TestDiffable : Diffable<TestDiffable> {
+ private fun logLines(string: String): List<String> {
+ return string.split("\n").filter { it.isNotBlank() }
+ }
+
+ private open class TestDiffable : Diffable<TestDiffable> {
override fun logDiffs(prevVal: TestDiffable, row: TableRowLogger) {}
}
}
private const val NAME = "TestTableBuffer"
private const val MAX_SIZE = 10
+
+// Copying these here from [TableLogBuffer] so that we catch any accidental versioning change
+private const val HEADER_PREFIX = "SystemUI StateChangeTableSection START: "
+private const val FOOTER_PREFIX = "SystemUI StateChangeTableSection END: "
+private const val SEPARATOR = "|" // TBD
+private const val VERSION = "1"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt
index 84fdfd78e9fc..136ace173795 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt
@@ -38,6 +38,7 @@ import com.android.systemui.media.controls.models.player.MediaData
import com.android.systemui.media.controls.models.player.MediaDeviceData
import com.android.systemui.media.controls.pipeline.MediaDataManager
import com.android.systemui.media.controls.pipeline.RESUME_MEDIA_TIMEOUT
+import com.android.systemui.settings.UserTracker
import com.android.systemui.tuner.TunerService
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
@@ -79,6 +80,7 @@ private fun <T> any(): T = Mockito.any<T>()
class MediaResumeListenerTest : SysuiTestCase() {
@Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
+ @Mock private lateinit var userTracker: UserTracker
@Mock private lateinit var mediaDataManager: MediaDataManager
@Mock private lateinit var device: MediaDeviceData
@Mock private lateinit var token: MediaSession.Token
@@ -131,12 +133,15 @@ class MediaResumeListenerTest : SysuiTestCase() {
whenever(sharedPrefsEditor.putString(any(), any())).thenReturn(sharedPrefsEditor)
whenever(mockContext.packageManager).thenReturn(context.packageManager)
whenever(mockContext.contentResolver).thenReturn(context.contentResolver)
+ whenever(mockContext.userId).thenReturn(context.userId)
executor = FakeExecutor(clock)
resumeListener =
MediaResumeListener(
mockContext,
broadcastDispatcher,
+ userTracker,
+ executor,
executor,
tunerService,
resumeBrowserFactory,
@@ -177,6 +182,8 @@ class MediaResumeListenerTest : SysuiTestCase() {
MediaResumeListener(
context,
broadcastDispatcher,
+ userTracker,
+ executor,
executor,
tunerService,
resumeBrowserFactory,
@@ -185,7 +192,7 @@ class MediaResumeListenerTest : SysuiTestCase() {
)
listener.setManager(mediaDataManager)
verify(broadcastDispatcher, never())
- .registerReceiver(eq(listener.userChangeReceiver), any(), any(), any(), anyInt(), any())
+ .registerReceiver(eq(listener.userUnlockReceiver), any(), any(), any(), anyInt(), any())
// When data is loaded, we do NOT execute or update anything
listener.onMediaDataLoaded(KEY, OLD_KEY, data)
@@ -289,7 +296,7 @@ class MediaResumeListenerTest : SysuiTestCase() {
resumeListener.setManager(mediaDataManager)
verify(broadcastDispatcher)
.registerReceiver(
- eq(resumeListener.userChangeReceiver),
+ eq(resumeListener.userUnlockReceiver),
any(),
any(),
any(),
@@ -299,7 +306,8 @@ class MediaResumeListenerTest : SysuiTestCase() {
// When we get an unlock event
val intent = Intent(Intent.ACTION_USER_UNLOCKED)
- resumeListener.userChangeReceiver.onReceive(context, intent)
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, context.userId)
+ resumeListener.userUnlockReceiver.onReceive(context, intent)
// Then we should attempt to find recent media for each saved component
verify(resumeBrowser, times(3)).findRecentMedia()
@@ -375,6 +383,8 @@ class MediaResumeListenerTest : SysuiTestCase() {
MediaResumeListener(
mockContext,
broadcastDispatcher,
+ userTracker,
+ executor,
executor,
tunerService,
resumeBrowserFactory,
@@ -386,7 +396,8 @@ class MediaResumeListenerTest : SysuiTestCase() {
// When we load a component that was played recently
val intent = Intent(Intent.ACTION_USER_UNLOCKED)
- resumeListener.userChangeReceiver.onReceive(mockContext, intent)
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, context.userId)
+ resumeListener.userUnlockReceiver.onReceive(mockContext, intent)
// We add its resume controls
verify(resumeBrowser, times(1)).findRecentMedia()
@@ -404,6 +415,8 @@ class MediaResumeListenerTest : SysuiTestCase() {
MediaResumeListener(
mockContext,
broadcastDispatcher,
+ userTracker,
+ executor,
executor,
tunerService,
resumeBrowserFactory,
@@ -415,7 +428,8 @@ class MediaResumeListenerTest : SysuiTestCase() {
// When we load a component that is not recent
val intent = Intent(Intent.ACTION_USER_UNLOCKED)
- resumeListener.userChangeReceiver.onReceive(mockContext, intent)
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, context.userId)
+ resumeListener.userUnlockReceiver.onReceive(mockContext, intent)
// We do not try to add resume controls
verify(resumeBrowser, times(0)).findRecentMedia()
@@ -443,6 +457,8 @@ class MediaResumeListenerTest : SysuiTestCase() {
MediaResumeListener(
mockContext,
broadcastDispatcher,
+ userTracker,
+ executor,
executor,
tunerService,
resumeBrowserFactory,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index f43a34f6e89b..80adbf025e0b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -44,14 +44,11 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.IntentFilter;
import android.content.res.Resources;
import android.hardware.display.DisplayManagerGlobal;
import android.os.Handler;
import android.os.SystemClock;
-import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.telecom.TelecomManager;
import android.testing.AndroidTestingRunner;
@@ -79,7 +76,6 @@ import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
import com.android.systemui.accessibility.SystemActions;
import com.android.systemui.assist.AssistManager;
-import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.model.SysUiState;
@@ -119,6 +115,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Optional;
+import java.util.concurrent.Executor;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
@@ -166,7 +163,7 @@ public class NavigationBarTest extends SysuiTestCase {
@Mock
private Handler mHandler;
@Mock
- private BroadcastDispatcher mBroadcastDispatcher;
+ private UserTracker mUserTracker;
@Mock
private UiEventLogger mUiEventLogger;
@Mock
@@ -315,14 +312,10 @@ public class NavigationBarTest extends SysuiTestCase {
}
@Test
- public void testRegisteredWithDispatcher() {
+ public void testRegisteredWithUserTracker() {
mNavigationBar.init();
mNavigationBar.onViewAttached();
- verify(mBroadcastDispatcher).registerReceiverWithHandler(
- any(BroadcastReceiver.class),
- any(IntentFilter.class),
- any(Handler.class),
- any(UserHandle.class));
+ verify(mUserTracker).addCallback(any(UserTracker.Callback.class), any(Executor.class));
}
@Test
@@ -463,7 +456,7 @@ public class NavigationBarTest extends SysuiTestCase {
mStatusBarStateController,
mStatusBarKeyguardViewManager,
mMockSysUiState,
- mBroadcastDispatcher,
+ mUserTracker,
mCommandQueue,
Optional.of(mock(Pip.class)),
Optional.of(mock(Recents.class)),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index c377c374148f..338182a3e304 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -48,6 +48,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.power.PowerUI.WarningsUI;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.CentralSurfaces;
@@ -85,6 +86,7 @@ public class PowerUITest extends SysuiTestCase {
private PowerUI mPowerUI;
@Mock private EnhancedEstimates mEnhancedEstimates;
@Mock private PowerManager mPowerManager;
+ @Mock private UserTracker mUserTracker;
@Mock private WakefulnessLifecycle mWakefulnessLifecycle;
@Mock private IThermalService mThermalServiceMock;
private IThermalEventListener mUsbThermalEventListener;
@@ -682,7 +684,8 @@ public class PowerUITest extends SysuiTestCase {
private void createPowerUi() {
mPowerUI = new PowerUI(
mContext, mBroadcastDispatcher, mCommandQueue, mCentralSurfacesOptionalLazy,
- mMockWarnings, mEnhancedEstimates, mWakefulnessLifecycle, mPowerManager);
+ mMockWarnings, mEnhancedEstimates, mWakefulnessLifecycle, mPowerManager,
+ mUserTracker);
mPowerUI.mThermalService = mThermalServiceMock;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt
index 645b1cde632f..23466cc20f44 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt
@@ -41,6 +41,7 @@ import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.nullable
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.TestCoroutineScheduler
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -55,7 +56,7 @@ class FooterActionsInteractorTest : SysuiTestCase() {
@Before
fun setUp() {
- utils = FooterActionsTestUtils(context, TestableLooper.get(this))
+ utils = FooterActionsTestUtils(context, TestableLooper.get(this), TestCoroutineScheduler())
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
index 081a2181cfe5..47afa70fa84b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
@@ -29,6 +29,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
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.qs.FakeFgsManagerController
import com.android.systemui.qs.QSSecurityFooterUtils
import com.android.systemui.qs.footer.FooterActionsTestUtils
@@ -44,12 +45,9 @@ import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch
-import kotlinx.coroutines.test.TestCoroutineScheduler
import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -62,16 +60,20 @@ import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidTestingRunner::class)
@RunWithLooper
class FooterActionsViewModelTest : SysuiTestCase() {
+ private val testScope = TestScope()
private lateinit var utils: FooterActionsTestUtils
- private val testDispatcher = UnconfinedTestDispatcher(TestCoroutineScheduler())
@Before
fun setUp() {
- utils = FooterActionsTestUtils(context, TestableLooper.get(this))
+ utils = FooterActionsTestUtils(context, TestableLooper.get(this), testScope.testScheduler)
+ }
+
+ private fun runTest(block: suspend TestScope.() -> Unit) {
+ testScope.runTest(testBody = block)
}
@Test
- fun settingsButton() = runBlockingTest {
+ fun settingsButton() = runTest {
val underTest = utils.footerActionsViewModel(showPowerButton = false)
val settings = underTest.settings
@@ -87,7 +89,7 @@ class FooterActionsViewModelTest : SysuiTestCase() {
}
@Test
- fun powerButton() = runBlockingTest {
+ fun powerButton() = runTest {
// Without power button.
val underTestWithoutPower = utils.footerActionsViewModel(showPowerButton = false)
assertThat(underTestWithoutPower.power).isNull()
@@ -114,7 +116,7 @@ class FooterActionsViewModelTest : SysuiTestCase() {
}
@Test
- fun userSwitcher() = runBlockingTest {
+ fun userSwitcher() = runTest {
val picture: Drawable = mock()
val userInfoController = FakeUserInfoController(FakeInfo(picture = picture))
val settings = FakeSettings()
@@ -135,7 +137,6 @@ class FooterActionsViewModelTest : SysuiTestCase() {
showPowerButton = false,
footerActionsInteractor =
utils.footerActionsInteractor(
- bgDispatcher = testDispatcher,
userSwitcherRepository =
utils.userSwitcherRepository(
userTracker = userTracker,
@@ -143,22 +144,12 @@ class FooterActionsViewModelTest : SysuiTestCase() {
userManager = userManager,
userInfoController = userInfoController,
userSwitcherController = userSwitcherControllerWrapper.controller,
- bgDispatcher = testDispatcher,
),
)
)
// Collect the user switcher into currentUserSwitcher.
- var currentUserSwitcher: FooterActionsButtonViewModel? = null
- val job = launch { underTest.userSwitcher.collect { currentUserSwitcher = it } }
- fun currentUserSwitcher(): FooterActionsButtonViewModel? {
- // Make sure we finish collecting the current user switcher. This is necessary because
- // combined flows launch multiple coroutines in the current scope so we need to make
- // sure we process all coroutines triggered by our flow collection before we make
- // assertions on the current buttons.
- advanceUntilIdle()
- return currentUserSwitcher
- }
+ val currentUserSwitcher = collectLastValue(underTest.userSwitcher)
// The user switcher is disabled.
assertThat(currentUserSwitcher()).isNull()
@@ -203,12 +194,10 @@ class FooterActionsViewModelTest : SysuiTestCase() {
// in guest mode.
userInfoController.updateInfo { this.picture = mock<UserIconDrawable>() }
assertThat(iconTint()).isNull()
-
- job.cancel()
}
@Test
- fun security() = runBlockingTest {
+ fun security() = runTest {
val securityController = FakeSecurityController()
val qsSecurityFooterUtils = mock<QSSecurityFooterUtils>()
@@ -224,22 +213,15 @@ class FooterActionsViewModelTest : SysuiTestCase() {
footerActionsInteractor =
utils.footerActionsInteractor(
qsSecurityFooterUtils = qsSecurityFooterUtils,
- bgDispatcher = testDispatcher,
securityRepository =
utils.securityRepository(
securityController = securityController,
- bgDispatcher = testDispatcher,
),
),
)
// Collect the security model into currentSecurity.
- var currentSecurity: FooterActionsSecurityButtonViewModel? = null
- val job = launch { underTest.security.collect { currentSecurity = it } }
- fun currentSecurity(): FooterActionsSecurityButtonViewModel? {
- advanceUntilIdle()
- return currentSecurity
- }
+ val currentSecurity = collectLastValue(underTest.security)
// By default, we always return a null SecurityButtonConfig.
assertThat(currentSecurity()).isNull()
@@ -270,12 +252,10 @@ class FooterActionsViewModelTest : SysuiTestCase() {
security = currentSecurity()
assertThat(security).isNotNull()
assertThat(security!!.onClick).isNull()
-
- job.cancel()
}
@Test
- fun foregroundServices() = runBlockingTest {
+ fun foregroundServices() = runTest {
val securityController = FakeSecurityController()
val fgsManagerController =
FakeFgsManagerController(
@@ -300,21 +280,14 @@ class FooterActionsViewModelTest : SysuiTestCase() {
securityRepository =
utils.securityRepository(
securityController,
- bgDispatcher = testDispatcher,
),
foregroundServicesRepository =
utils.foregroundServicesRepository(fgsManagerController),
- bgDispatcher = testDispatcher,
),
)
// Collect the security model into currentSecurity.
- var currentForegroundServices: FooterActionsForegroundServicesButtonViewModel? = null
- val job = launch { underTest.foregroundServices.collect { currentForegroundServices = it } }
- fun currentForegroundServices(): FooterActionsForegroundServicesButtonViewModel? {
- advanceUntilIdle()
- return currentForegroundServices
- }
+ val currentForegroundServices = collectLastValue(underTest.foregroundServices)
// We don't show the foreground services button if the number of running packages is not
// > 1.
@@ -356,12 +329,10 @@ class FooterActionsViewModelTest : SysuiTestCase() {
}
securityController.updateState {}
assertThat(currentForegroundServices()?.displayText).isFalse()
-
- job.cancel()
}
@Test
- fun observeDeviceMonitoringDialogRequests() = runBlockingTest {
+ fun observeDeviceMonitoringDialogRequests() = runTest {
val qsSecurityFooterUtils = mock<QSSecurityFooterUtils>()
val broadcastDispatcher = mock<BroadcastDispatcher>()
@@ -390,7 +361,6 @@ class FooterActionsViewModelTest : SysuiTestCase() {
utils.footerActionsInteractor(
qsSecurityFooterUtils = qsSecurityFooterUtils,
broadcastDispatcher = broadcastDispatcher,
- bgDispatcher = testDispatcher,
),
)
@@ -415,7 +385,4 @@ class FooterActionsViewModelTest : SysuiTestCase() {
underTest.onVisibilityChangeRequested(visible = true)
assertThat(underTest.isVisible.value).isTrue()
}
-
- private fun runBlockingTest(block: suspend TestScope.() -> Unit) =
- runTest(testDispatcher) { block() }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
index 013e58ed99d7..69f3e987ec1d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
@@ -33,6 +33,9 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.settings.UserContextProvider;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -49,12 +52,16 @@ import org.mockito.MockitoAnnotations;
*/
public class RecordingControllerTest extends SysuiTestCase {
+ private FakeSystemClock mFakeSystemClock = new FakeSystemClock();
+ private FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
@Mock
private RecordingController.RecordingStateChangeCallback mCallback;
@Mock
private BroadcastDispatcher mBroadcastDispatcher;
@Mock
private UserContextProvider mUserContextProvider;
+ @Mock
+ private UserTracker mUserTracker;
private RecordingController mController;
@@ -63,7 +70,8 @@ public class RecordingControllerTest extends SysuiTestCase {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mController = new RecordingController(mBroadcastDispatcher, mUserContextProvider);
+ mController = new RecordingController(mMainExecutor, mBroadcastDispatcher,
+ mUserContextProvider, mUserTracker);
mController.addCallback(mCallback);
}
@@ -176,9 +184,7 @@ public class RecordingControllerTest extends SysuiTestCase {
mController.updateState(true);
// and user is changed
- Intent intent = new Intent(Intent.ACTION_USER_SWITCHED)
- .putExtra(Intent.EXTRA_USER_HANDLE, USER_ID);
- mController.mUserChangeReceiver.onReceive(mContext, intent);
+ mController.mUserChangedCallback.onUserChanged(USER_ID, mContext);
// Ensure that the recording was stopped
verify(mCallback).onRecordingEnd();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 69a45599668b..b6f74f0a13ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -131,6 +131,7 @@ import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.ConversationNotificationManager;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinatorLogger;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -383,7 +384,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mInteractionJankMonitor, mShadeExpansionStateManager),
mKeyguardBypassController,
mDozeParameters,
- mScreenOffAnimationController);
+ mScreenOffAnimationController,
+ mock(NotificationWakeUpCoordinatorLogger.class));
mConfigurationController = new ConfigurationControllerImpl(mContext);
PulseExpansionHandler expansionHandler = new PulseExpansionHandler(
mContext,
@@ -499,8 +501,18 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mDumpManager);
mNotificationPanelViewController.initDependencies(
mCentralSurfaces,
+ null,
() -> {},
mNotificationShelfController);
+ mNotificationPanelViewController.setTrackingStartedListener(() -> {});
+ mNotificationPanelViewController.setOpenCloseListener(
+ new NotificationPanelViewController.OpenCloseListener() {
+ @Override
+ public void onClosingFinished() {}
+
+ @Override
+ public void onOpenStarted() {}
+ });
mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
ArgumentCaptor<View.OnAttachStateChangeListener> onAttachStateChangeListenerArgumentCaptor =
ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
@@ -831,7 +843,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
public void handleTouchEventFromStatusBar_panelAndViewEnabled_viewReceivesEvent() {
when(mCommandQueue.panelsEnabled()).thenReturn(true);
when(mView.isEnabled()).thenReturn(true);
- MotionEvent event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0);
+ MotionEvent event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0);
mNotificationPanelViewController.getStatusBarTouchEventHandler().handleTouchEvent(event);
@@ -839,6 +851,17 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
}
@Test
+ public void handleTouchEventFromStatusBar_topEdgeTouch_viewNeverReceivesEvent() {
+ when(mCommandQueue.panelsEnabled()).thenReturn(true);
+ when(mView.isEnabled()).thenReturn(true);
+ MotionEvent event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0);
+
+ mNotificationPanelViewController.getStatusBarTouchEventHandler().handleTouchEvent(event);
+
+ verify(mView, never()).dispatchTouchEvent(event);
+ }
+
+ @Test
public void testA11y_initializeNode() {
AccessibilityNodeInfo nodeInfo = new AccessibilityNodeInfo();
mAccessibilityDelegate.onInitializeAccessibilityNodeInfo(mView, nodeInfo);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 15a687d2adc7..452606dfcca4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar;
-import static android.content.Intent.ACTION_USER_SWITCHED;
-
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -34,7 +32,6 @@ import android.app.Notification;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.os.Handler;
@@ -293,11 +290,9 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
}
@Test
- public void testActionUserSwitchedCallsOnUserSwitched() {
- Intent intent = new Intent()
- .setAction(ACTION_USER_SWITCHED)
- .putExtra(Intent.EXTRA_USER_HANDLE, mSecondaryUser.id);
- mLockscreenUserManager.getBaseBroadcastReceiverForTest().onReceive(mContext, intent);
+ public void testUserSwitchedCallsOnUserSwitched() {
+ mLockscreenUserManager.getUserTrackerCallbackForTest().onUserChanged(mSecondaryUser.id,
+ mContext);
verify(mPresenter, times(1)).onUserSwitched(mSecondaryUser.id);
}
@@ -366,6 +361,10 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
return mBaseBroadcastReceiver;
}
+ public UserTracker.Callback getUserTrackerCallbackForTest() {
+ return mUserChangedCallback;
+ }
+
public ContentObserver getLockscreenSettingsObserverForTest() {
return mLockscreenSettingsObserver;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
index 8b7b4dea155f..6bd3f7a27413 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
@@ -26,22 +26,17 @@ import static android.app.NotificationManager.IMPORTANCE_MIN;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.android.systemui.statusbar.notification.collection.EntryUtilKt.modifyEntry;
-import static com.android.systemui.util.mockito.KotlinMockitoHelpersKt.argThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;
@@ -54,10 +49,10 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.CoreStartable;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -97,7 +92,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock private HighPriorityProvider mHighPriorityProvider;
@Mock private SysuiStatusBarStateController mStatusBarStateController;
- @Mock private BroadcastDispatcher mBroadcastDispatcher;
+ @Mock private UserTracker mUserTracker;
private final FakeSettings mFakeSettings = new FakeSettings();
private KeyguardNotificationVisibilityProvider mKeyguardNotificationVisibilityProvider;
@@ -117,7 +112,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
mKeyguardUpdateMonitor,
mHighPriorityProvider,
mStatusBarStateController,
- mBroadcastDispatcher,
+ mUserTracker,
mFakeSettings,
mFakeSettings);
mKeyguardNotificationVisibilityProvider = component.getProvider();
@@ -205,23 +200,19 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
}
@Test
- public void notifyListeners_onReceiveUserSwitchBroadcast() {
- ArgumentCaptor<BroadcastReceiver> callbackCaptor =
- ArgumentCaptor.forClass(BroadcastReceiver.class);
- verify(mBroadcastDispatcher).registerReceiver(
+ public void notifyListeners_onReceiveUserSwitchCallback() {
+ ArgumentCaptor<UserTracker.Callback> callbackCaptor =
+ ArgumentCaptor.forClass(UserTracker.Callback.class);
+ verify(mUserTracker).addCallback(
callbackCaptor.capture(),
- argThat(intentFilter -> intentFilter.hasAction(Intent.ACTION_USER_SWITCHED)),
- isNull(),
- isNull(),
- eq(Context.RECEIVER_EXPORTED),
- isNull());
- BroadcastReceiver callback = callbackCaptor.getValue();
+ any());
+ UserTracker.Callback callback = callbackCaptor.getValue();
Consumer<String> listener = mock(Consumer.class);
mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener);
when(mStatusBarStateController.getCurrentOrUpcomingState()).thenReturn(KEYGUARD);
- callback.onReceive(mContext, new Intent(Intent.ACTION_USER_SWITCHED));
+ callback.onUserChanged(CURR_USER_ID, mContext);
verify(listener).accept(anyString());
}
@@ -619,7 +610,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
@BindsInstance KeyguardUpdateMonitor keyguardUpdateMonitor,
@BindsInstance HighPriorityProvider highPriorityProvider,
@BindsInstance SysuiStatusBarStateController statusBarStateController,
- @BindsInstance BroadcastDispatcher broadcastDispatcher,
+ @BindsInstance UserTracker userTracker,
@BindsInstance SecureSettings secureSettings,
@BindsInstance GlobalSettings globalSettings
);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
index ea311da3e20b..21aae00f12ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
@@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.interruption;
import static android.app.Notification.FLAG_BUBBLE;
+import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static android.app.Notification.GROUP_ALERT_SUMMARY;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
@@ -33,6 +34,8 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -390,6 +393,127 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
}
+ private long makeWhenHoursAgo(long hoursAgo) {
+ return System.currentTimeMillis() - (1000 * 60 * 60 * hoursAgo);
+ }
+
+ @Test
+ public void testShouldHeadsUp_oldWhen_flagDisabled() throws Exception {
+ ensureStateForHeadsUpWhenAwake();
+ when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(false);
+
+ NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
+ entry.getSbn().getNotification().when = makeWhenHoursAgo(25);
+
+ assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
+
+ verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong());
+ verify(mLogger, never()).logMaybeHeadsUpDespiteOldWhen(any(), anyLong(), anyLong(), any());
+ }
+
+ @Test
+ public void testShouldHeadsUp_oldWhen_whenNow() throws Exception {
+ ensureStateForHeadsUpWhenAwake();
+ when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true);
+
+ NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
+
+ assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
+
+ verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong());
+ verify(mLogger, never()).logMaybeHeadsUpDespiteOldWhen(any(), anyLong(), anyLong(), any());
+ }
+
+ @Test
+ public void testShouldHeadsUp_oldWhen_whenRecent() throws Exception {
+ ensureStateForHeadsUpWhenAwake();
+ when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true);
+
+ NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
+ entry.getSbn().getNotification().when = makeWhenHoursAgo(13);
+
+ assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
+
+ verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong());
+ verify(mLogger, never()).logMaybeHeadsUpDespiteOldWhen(any(), anyLong(), anyLong(), any());
+ }
+
+ @Test
+ public void testShouldHeadsUp_oldWhen_whenZero() throws Exception {
+ ensureStateForHeadsUpWhenAwake();
+ when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true);
+
+ NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
+ entry.getSbn().getNotification().when = 0L;
+
+ assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
+
+ verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong());
+ verify(mLogger).logMaybeHeadsUpDespiteOldWhen(eq(entry), eq(0L), anyLong(),
+ eq("when <= 0"));
+ }
+
+ @Test
+ public void testShouldHeadsUp_oldWhen_whenNegative() throws Exception {
+ ensureStateForHeadsUpWhenAwake();
+ when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true);
+
+ NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
+ entry.getSbn().getNotification().when = -1L;
+
+ assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
+ verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong());
+ verify(mLogger).logMaybeHeadsUpDespiteOldWhen(eq(entry), eq(-1L), anyLong(),
+ eq("when <= 0"));
+ }
+
+ @Test
+ public void testShouldHeadsUp_oldWhen_hasFullScreenIntent() throws Exception {
+ ensureStateForHeadsUpWhenAwake();
+ when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true);
+ long when = makeWhenHoursAgo(25);
+
+ NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silent= */ false);
+ entry.getSbn().getNotification().when = when;
+
+ assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
+
+ verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong());
+ verify(mLogger).logMaybeHeadsUpDespiteOldWhen(eq(entry), eq(when), anyLong(),
+ eq("full-screen intent"));
+ }
+
+ @Test
+ public void testShouldHeadsUp_oldWhen_isForegroundService() throws Exception {
+ ensureStateForHeadsUpWhenAwake();
+ when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true);
+ long when = makeWhenHoursAgo(25);
+
+ NotificationEntry entry = createFgsNotification(IMPORTANCE_HIGH);
+ entry.getSbn().getNotification().when = when;
+
+ assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
+
+ verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong());
+ verify(mLogger).logMaybeHeadsUpDespiteOldWhen(eq(entry), eq(when), anyLong(),
+ eq("foreground service"));
+ }
+
+ @Test
+ public void testShouldNotHeadsUp_oldWhen() throws Exception {
+ ensureStateForHeadsUpWhenAwake();
+ when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true);
+ long when = makeWhenHoursAgo(25);
+
+ NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
+ entry.getSbn().getNotification().when = when;
+
+ assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
+
+ verify(mLogger).logNoHeadsUpOldWhen(eq(entry), eq(when), anyLong());
+ verify(mLogger, never()).logMaybeHeadsUpDespiteOldWhen(any(), anyLong(), anyLong(), any());
+ }
+
@Test
public void testShouldNotFullScreen_notPendingIntent_withStrictFlag() throws Exception {
when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
@@ -763,6 +887,16 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
return createNotification(importance, n);
}
+ private NotificationEntry createFgsNotification(int importance) {
+ Notification n = new Notification.Builder(getContext(), "a")
+ .setContentTitle("title")
+ .setContentText("content text")
+ .setFlag(FLAG_FOREGROUND_SERVICE, true)
+ .build();
+
+ return createNotification(importance, n);
+ }
+
private final NotificationInterruptSuppressor
mSuppressAwakeHeadsUp =
new NotificationInterruptSuppressor() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
new file mode 100644
index 000000000000..2d23f3c4aea8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.statusbar.notification.row
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.PluginManager
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.NotificationMediaManager
+import com.android.systemui.statusbar.SmartReplyController
+import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager
+import com.android.systemui.statusbar.notification.logging.NotificationLogger
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.policy.HeadsUpManager
+import com.android.systemui.statusbar.policy.SmartReplyConstants
+import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.time.SystemClock
+import com.android.systemui.wmshell.BubblesManager
+import java.util.Optional
+import junit.framework.Assert
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito
+import org.mockito.Mockito.anyBoolean
+import org.mockito.Mockito.never
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class ExpandableNotificationRowControllerTest : SysuiTestCase() {
+
+ private val appName = "MyApp"
+ private val notifKey = "MyNotifKey"
+
+ private val view: ExpandableNotificationRow = mock()
+ private val activableNotificationViewController: ActivatableNotificationViewController = mock()
+ private val rivSubComponentFactory: RemoteInputViewSubcomponent.Factory = mock()
+ private val metricsLogger: MetricsLogger = mock()
+ private val logBufferLogger: NotificationRowLogger = mock()
+ private val listContainer: NotificationListContainer = mock()
+ private val mediaManager: NotificationMediaManager = mock()
+ private val smartReplyConstants: SmartReplyConstants = mock()
+ private val smartReplyController: SmartReplyController = mock()
+ private val pluginManager: PluginManager = mock()
+ private val systemClock: SystemClock = mock()
+ private val keyguardBypassController: KeyguardBypassController = mock()
+ private val groupMembershipManager: GroupMembershipManager = mock()
+ private val groupExpansionManager: GroupExpansionManager = mock()
+ private val rowContentBindStage: RowContentBindStage = mock()
+ private val notifLogger: NotificationLogger = mock()
+ private val headsUpManager: HeadsUpManager = mock()
+ private val onExpandClickListener: ExpandableNotificationRow.OnExpandClickListener = mock()
+ private val statusBarStateController: StatusBarStateController = mock()
+ private val gutsManager: NotificationGutsManager = mock()
+ private val onUserInteractionCallback: OnUserInteractionCallback = mock()
+ private val falsingManager: FalsingManager = mock()
+ private val falsingCollector: FalsingCollector = mock()
+ private val featureFlags: FeatureFlags = mock()
+ private val peopleNotificationIdentifier: PeopleNotificationIdentifier = mock()
+ private val bubblesManager: BubblesManager = mock()
+ private val dragController: ExpandableNotificationRowDragController = mock()
+ private lateinit var controller: ExpandableNotificationRowController
+
+ @Before
+ fun setUp() {
+ allowTestableLooperAsMainThread()
+ controller =
+ ExpandableNotificationRowController(
+ view,
+ activableNotificationViewController,
+ rivSubComponentFactory,
+ metricsLogger,
+ logBufferLogger,
+ listContainer,
+ mediaManager,
+ smartReplyConstants,
+ smartReplyController,
+ pluginManager,
+ systemClock,
+ appName,
+ notifKey,
+ keyguardBypassController,
+ groupMembershipManager,
+ groupExpansionManager,
+ rowContentBindStage,
+ notifLogger,
+ headsUpManager,
+ onExpandClickListener,
+ statusBarStateController,
+ gutsManager,
+ /*allowLongPress=*/ false,
+ onUserInteractionCallback,
+ falsingManager,
+ falsingCollector,
+ featureFlags,
+ peopleNotificationIdentifier,
+ Optional.of(bubblesManager),
+ dragController
+ )
+ }
+
+ @After
+ fun tearDown() {
+ disallowTestableLooperAsMainThread()
+ }
+
+ @Test
+ fun offerKeepInParent_parentDismissed() {
+ whenever(featureFlags.isEnabled(Flags.NOTIFICATION_GROUP_DISMISSAL_ANIMATION))
+ .thenReturn(true)
+ whenever(view.isParentDismissed).thenReturn(true)
+
+ Assert.assertTrue(controller.offerToKeepInParentForAnimation())
+ Mockito.verify(view).setKeepInParentForDismissAnimation(true)
+ }
+
+ @Test
+ fun offerKeepInParent_parentNotDismissed() {
+ whenever(featureFlags.isEnabled(Flags.NOTIFICATION_GROUP_DISMISSAL_ANIMATION))
+ .thenReturn(true)
+
+ Assert.assertFalse(controller.offerToKeepInParentForAnimation())
+ Mockito.verify(view, never()).setKeepInParentForDismissAnimation(anyBoolean())
+ }
+
+ @Test
+ fun removeFromParent_keptForAnimation() {
+ val parentView: ExpandableNotificationRow = mock()
+ whenever(view.notificationParent).thenReturn(parentView)
+ whenever(view.keepInParentForDismissAnimation()).thenReturn(true)
+
+ Assert.assertTrue(controller.removeFromParentIfKeptForAnimation())
+ Mockito.verify(parentView).removeChildNotification(view)
+ }
+
+ @Test
+ fun removeFromParent_notKeptForAnimation() {
+ val parentView: ExpandableNotificationRow = mock()
+ whenever(view.notificationParent).thenReturn(parentView)
+
+ Assert.assertFalse(controller.removeFromParentIfKeptForAnimation())
+ Mockito.verifyNoMoreInteractions(parentView)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 496bf37ffaeb..088d1654d1f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -299,7 +299,8 @@ public class NotificationTestHelper {
public ExpandableNotificationRow createBubble()
throws Exception {
Notification n = createNotification(false /* isGroupSummary */,
- null /* groupKey */, makeBubbleMetadata(null));
+ null /* groupKey */,
+ makeBubbleMetadata(null /* deleteIntent */, false /* autoExpand */));
n.flags |= FLAG_BUBBLE;
ExpandableNotificationRow row = generateRow(n, PKG, UID, USER_HANDLE,
mDefaultInflationFlags, IMPORTANCE_HIGH);
@@ -332,7 +333,8 @@ public class NotificationTestHelper {
public ExpandableNotificationRow createBubbleInGroup()
throws Exception {
Notification n = createNotification(false /* isGroupSummary */,
- GROUP_KEY /* groupKey */, makeBubbleMetadata(null));
+ GROUP_KEY /* groupKey */,
+ makeBubbleMetadata(null /* deleteIntent */, false /* autoExpand */));
n.flags |= FLAG_BUBBLE;
ExpandableNotificationRow row = generateRow(n, PKG, UID, USER_HANDLE,
mDefaultInflationFlags, IMPORTANCE_HIGH);
@@ -348,7 +350,7 @@ public class NotificationTestHelper {
* @param deleteIntent the intent to assign to {@link BubbleMetadata#deleteIntent}
*/
public NotificationEntry createBubble(@Nullable PendingIntent deleteIntent) {
- return createBubble(makeBubbleMetadata(deleteIntent), USER_HANDLE);
+ return createBubble(makeBubbleMetadata(deleteIntent, false /* autoExpand */), USER_HANDLE);
}
/**
@@ -357,7 +359,16 @@ public class NotificationTestHelper {
* @param handle the user to associate with this bubble.
*/
public NotificationEntry createBubble(UserHandle handle) {
- return createBubble(makeBubbleMetadata(null), handle);
+ return createBubble(makeBubbleMetadata(null /* deleteIntent */, false /* autoExpand */),
+ handle);
+ }
+
+ /**
+ * Returns an {@link NotificationEntry} that should be shown as a auto-expanded bubble.
+ */
+ public NotificationEntry createAutoExpandedBubble() {
+ return createBubble(makeBubbleMetadata(null /* deleteIntent */, true /* autoExpand */),
+ USER_HANDLE);
}
/**
@@ -565,7 +576,7 @@ public class NotificationTestHelper {
assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS));
}
- private BubbleMetadata makeBubbleMetadata(PendingIntent deleteIntent) {
+ private BubbleMetadata makeBubbleMetadata(PendingIntent deleteIntent, boolean autoExpand) {
Intent target = new Intent(mContext, BubblesTestActivity.class);
PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, target,
PendingIntent.FLAG_MUTABLE);
@@ -574,6 +585,7 @@ public class NotificationTestHelper {
Icon.createWithResource(mContext, R.drawable.android))
.setDeleteIntent(deleteIntent)
.setDesiredHeight(314)
+ .setAutoExpandBubble(autoExpand)
.build();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index ed84e4268c90..521e51846834 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -106,6 +106,7 @@ import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.plugins.PluginDependencyProvider;
@@ -212,6 +213,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Mock private NotificationPanelView mNotificationPanelView;
@Mock private IStatusBarService mBarService;
@Mock private IDreamManager mDreamManager;
+ @Mock private LightRevealScrimViewModel mLightRevealScrimViewModel;
@Mock private ScrimController mScrimController;
@Mock private DozeScrimController mDozeScrimController;
@Mock private Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
@@ -497,7 +499,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mDeviceStateManager,
mWiredChargingRippleController,
mDreamManager,
- mCameraLauncherLazy) {
+ mCameraLauncherLazy,
+ () -> mLightRevealScrimViewModel) {
@Override
protected ViewRootImpl getViewRootImpl() {
return mViewRootImpl;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index de71e2c250c4..e4759057a59c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -1442,16 +1442,30 @@ public class ScrimControllerTest extends SysuiTestCase {
@Test
public void testNotificationTransparency_followsTransitionToFullShade() {
+ mScrimController.setClipsQsScrim(true);
+
mScrimController.transitionTo(SHADE_LOCKED);
mScrimController.setRawPanelExpansionFraction(1.0f);
finishAnimationsImmediately();
+
+ assertScrimTinted(Map.of(
+ mScrimInFront, false,
+ mScrimBehind, true,
+ mNotificationsScrim, false
+ ));
+
float shadeLockedAlpha = mNotificationsScrim.getViewAlpha();
mScrimController.transitionTo(ScrimState.KEYGUARD);
mScrimController.setRawPanelExpansionFraction(1.0f);
finishAnimationsImmediately();
float keyguardAlpha = mNotificationsScrim.getViewAlpha();
- mScrimController.setClipsQsScrim(true);
+ assertScrimTinted(Map.of(
+ mScrimInFront, true,
+ mScrimBehind, true,
+ mNotificationsScrim, true
+ ));
+
float progress = 0.5f;
float lsNotifProgress = 0.3f;
mScrimController.setTransitionToFullShadeProgress(progress, lsNotifProgress);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryImplTest.kt
index b7a6c0125cfa..d35ce76d7a9a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryImplTest.kt
@@ -22,7 +22,7 @@ import android.os.UserHandle
import android.provider.Settings.Global
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
+import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
@@ -45,7 +45,7 @@ class AirplaneModeRepositoryImplTest : SysuiTestCase() {
private lateinit var underTest: AirplaneModeRepositoryImpl
- @Mock private lateinit var logger: ConnectivityPipelineLogger
+ @Mock private lateinit var logger: TableLogBuffer
private lateinit var bgHandler: Handler
private lateinit var scope: CoroutineScope
private lateinit var settings: FakeSettings
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt
index 76016a121e68..5a6bb301743a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt
@@ -38,9 +38,9 @@ import org.mockito.MockitoAnnotations
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-class AirplaneModeViewModelTest : SysuiTestCase() {
+class AirplaneModeViewModelImplTest : SysuiTestCase() {
- private lateinit var underTest: AirplaneModeViewModel
+ private lateinit var underTest: AirplaneModeViewModelImpl
@Mock private lateinit var logger: ConnectivityPipelineLogger
private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
@@ -57,7 +57,7 @@ class AirplaneModeViewModelTest : SysuiTestCase() {
scope = CoroutineScope(IMMEDIATE)
underTest =
- AirplaneModeViewModel(
+ AirplaneModeViewModelImpl(
interactor,
logger,
scope,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
index 71b8bab87d19..b38497a7bbdd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
@@ -35,8 +35,9 @@ import org.junit.Before
import org.junit.Test
@OptIn(ExperimentalCoroutinesApi::class)
+@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@SmallTest
-class WifiInteractorTest : SysuiTestCase() {
+class WifiInteractorImplTest : SysuiTestCase() {
private lateinit var underTest: WifiInteractor
@@ -47,7 +48,7 @@ class WifiInteractorTest : SysuiTestCase() {
fun setUp() {
connectivityRepository = FakeConnectivityRepository()
wifiRepository = FakeWifiRepository()
- underTest = WifiInteractor(connectivityRepository, wifiRepository)
+ underTest = WifiInteractorImpl(connectivityRepository, wifiRepository)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
index 37457b308597..5c16e1295b65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
@@ -32,12 +32,14 @@ import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
+import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel
@@ -86,9 +88,9 @@ class ModernStatusBarWifiViewTest : SysuiTestCase() {
connectivityRepository = FakeConnectivityRepository()
wifiRepository = FakeWifiRepository()
wifiRepository.setIsWifiEnabled(true)
- interactor = WifiInteractor(connectivityRepository, wifiRepository)
+ interactor = WifiInteractorImpl(connectivityRepository, wifiRepository)
scope = CoroutineScope(Dispatchers.Unconfined)
- airplaneModeViewModel = AirplaneModeViewModel(
+ airplaneModeViewModel = AirplaneModeViewModelImpl(
AirplaneModeInteractor(
airplaneModeRepository,
connectivityRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
index a1afcd71e3c3..3001b8162185 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
@@ -30,6 +30,7 @@ import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
+import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
@@ -37,6 +38,7 @@ import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnec
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel.Companion.NO_INTERNET
import com.google.common.truth.Truth.assertThat
@@ -81,10 +83,10 @@ internal class WifiViewModelIconParameterizedTest(private val testCase: TestCase
connectivityRepository = FakeConnectivityRepository()
wifiRepository = FakeWifiRepository()
wifiRepository.setIsWifiEnabled(true)
- interactor = WifiInteractor(connectivityRepository, wifiRepository)
+ interactor = WifiInteractorImpl(connectivityRepository, wifiRepository)
scope = CoroutineScope(IMMEDIATE)
airplaneModeViewModel =
- AirplaneModeViewModel(
+ AirplaneModeViewModelImpl(
AirplaneModeInteractor(
airplaneModeRepository,
connectivityRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
index 7d2c56098584..6a6b2a801ab0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
@@ -23,6 +23,7 @@ import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
+import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
@@ -30,6 +31,7 @@ import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnec
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiActivityModel
import com.google.common.truth.Truth.assertThat
@@ -73,9 +75,9 @@ class WifiViewModelTest : SysuiTestCase() {
connectivityRepository = FakeConnectivityRepository()
wifiRepository = FakeWifiRepository()
wifiRepository.setIsWifiEnabled(true)
- interactor = WifiInteractor(connectivityRepository, wifiRepository)
+ interactor = WifiInteractorImpl(connectivityRepository, wifiRepository)
scope = CoroutineScope(IMMEDIATE)
- airplaneModeViewModel = AirplaneModeViewModel(
+ airplaneModeViewModel = AirplaneModeViewModelImpl(
AirplaneModeInteractor(
airplaneModeRepository,
connectivityRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
index 2e527be1af89..034c618e55d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
@@ -95,6 +95,24 @@ class UserRepositoryImplTest : SysuiTestCase() {
}
@Test
+ fun userSwitcherSettings_isUserSwitcherEnabled_notInitialized() = runSelfCancelingTest {
+ underTest = create(this)
+
+ var value: UserSwitcherSettingsModel? = null
+ underTest.userSwitcherSettings.onEach { value = it }.launchIn(this)
+
+ assertUserSwitcherSettings(
+ model = value,
+ expectedSimpleUserSwitcher = false,
+ expectedAddUsersFromLockscreen = false,
+ expectedUserSwitcherEnabled =
+ context.resources.getBoolean(
+ com.android.internal.R.bool.config_showUserSwitcherByDefault
+ ),
+ )
+ }
+
+ @Test
fun refreshUsers() = runSelfCancelingTest {
underTest = create(this)
val initialExpectedValue =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
index 50d239d25607..5beb2b389349 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
@@ -761,7 +761,7 @@ class UserInteractorTest : SysuiTestCase() {
}
@Test
- fun `users - secondary user - no guest user`() =
+ fun `users - secondary user - guest user can be switched to`() =
runBlocking(IMMEDIATE) {
val userInfos = createUserInfos(count = 3, includeGuest = true)
userRepository.setUserInfos(userInfos)
@@ -770,8 +770,8 @@ class UserInteractorTest : SysuiTestCase() {
var res: List<UserModel>? = null
val job = underTest.users.onEach { res = it }.launchIn(this)
- assertThat(res?.size == 2).isTrue()
- assertThat(res?.find { it.isGuest }).isNull()
+ assertThat(res?.size == 3).isTrue()
+ assertThat(res?.find { it.isGuest }).isNotNull()
job.cancel()
}
@@ -813,7 +813,8 @@ class UserInteractorTest : SysuiTestCase() {
val job = underTest.dialogShowRequests.onEach { dialogRequest = it }.launchIn(this)
// Dialog is shown.
- assertThat(dialogRequest).isEqualTo(ShowDialogRequestModel.ShowUserSwitcherDialog)
+ assertThat(dialogRequest)
+ .isEqualTo(ShowDialogRequestModel.ShowUserSwitcherDialog(expandable))
underTest.onDialogShown()
assertThat(dialogRequest).isNull()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
index 4b6bdac46a3d..784a26bb371b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
@@ -47,18 +47,14 @@ import com.android.systemui.user.shared.model.UserActionModel
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.SupervisorJob
-import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.TestDispatcher
-import kotlinx.coroutines.test.TestResult
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -89,7 +85,6 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
private lateinit var testDispatcher: TestDispatcher
private lateinit var testScope: TestScope
- private lateinit var injectedScope: CoroutineScope
@Before
fun setUp() {
@@ -104,7 +99,6 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
testDispatcher = UnconfinedTestDispatcher()
testScope = TestScope(testDispatcher)
- injectedScope = CoroutineScope(testScope.coroutineContext + SupervisorJob())
userRepository = FakeUserRepository()
runBlocking {
userRepository.setSettings(
@@ -118,14 +112,14 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
powerRepository = FakePowerRepository()
val refreshUsersScheduler =
RefreshUsersScheduler(
- applicationScope = injectedScope,
+ applicationScope = testScope.backgroundScope,
mainDispatcher = testDispatcher,
repository = userRepository,
)
val guestUserInteractor =
GuestUserInteractor(
applicationContext = context,
- applicationScope = injectedScope,
+ applicationScope = testScope.backgroundScope,
mainDispatcher = testDispatcher,
backgroundDispatcher = testDispatcher,
manager = manager,
@@ -154,7 +148,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
set(Flags.FULL_SCREEN_USER_SWITCHER, false)
},
manager = manager,
- applicationScope = injectedScope,
+ applicationScope = testScope.backgroundScope,
telephonyInteractor =
TelephonyInteractor(
repository = FakeTelephonyRepository(),
@@ -175,7 +169,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
}
@Test
- fun users() = selfCancelingTest {
+ fun users() = testScope.runTest {
val userInfos =
listOf(
UserInfo(
@@ -210,26 +204,26 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
assertUserViewModel(
viewModel = userViewModels.last()[0],
viewKey = 0,
- name = "zero",
+ name = Text.Loaded("zero"),
isSelectionMarkerVisible = true,
)
assertUserViewModel(
viewModel = userViewModels.last()[1],
viewKey = 1,
- name = "one",
+ name = Text.Loaded("one"),
isSelectionMarkerVisible = false,
)
assertUserViewModel(
viewModel = userViewModels.last()[2],
viewKey = 2,
- name = "two",
+ name = Text.Loaded("two"),
isSelectionMarkerVisible = false,
)
job.cancel()
}
@Test
- fun `maximumUserColumns - few users`() = selfCancelingTest {
+ fun `maximumUserColumns - few users`() = testScope.runTest {
setUsers(count = 2)
val values = mutableListOf<Int>()
val job = launch(testDispatcher) { underTest.maximumUserColumns.toList(values) }
@@ -240,7 +234,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
}
@Test
- fun `maximumUserColumns - many users`() = selfCancelingTest {
+ fun `maximumUserColumns - many users`() = testScope.runTest {
setUsers(count = 5)
val values = mutableListOf<Int>()
val job = launch(testDispatcher) { underTest.maximumUserColumns.toList(values) }
@@ -250,7 +244,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
}
@Test
- fun `isOpenMenuButtonVisible - has actions - true`() = selfCancelingTest {
+ fun `isOpenMenuButtonVisible - has actions - true`() = testScope.runTest {
setUsers(2)
val isVisible = mutableListOf<Boolean>()
@@ -261,7 +255,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
}
@Test
- fun `isOpenMenuButtonVisible - no actions - false`() = selfCancelingTest {
+ fun `isOpenMenuButtonVisible - no actions - false`() = testScope.runTest {
val userInfos = setUsers(2)
userRepository.setSelectedUserInfo(userInfos[1])
keyguardRepository.setKeyguardShowing(true)
@@ -275,7 +269,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
}
@Test
- fun menu() = selfCancelingTest {
+ fun menu() = testScope.runTest {
val isMenuVisible = mutableListOf<Boolean>()
val job = launch(testDispatcher) { underTest.isMenuVisible.toList(isMenuVisible) }
assertThat(isMenuVisible.last()).isFalse()
@@ -290,7 +284,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
}
@Test
- fun `menu actions`() = selfCancelingTest {
+ fun `menu actions`() = testScope.runTest {
setUsers(2)
val actions = mutableListOf<List<UserActionViewModel>>()
val job = launch(testDispatcher) { underTest.menu.toList(actions) }
@@ -309,7 +303,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
}
@Test
- fun `isFinishRequested - finishes when user is switched`() = selfCancelingTest {
+ fun `isFinishRequested - finishes when user is switched`() = testScope.runTest {
val userInfos = setUsers(count = 2)
val isFinishRequested = mutableListOf<Boolean>()
val job = launch(testDispatcher) { underTest.isFinishRequested.toList(isFinishRequested) }
@@ -323,7 +317,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
}
@Test
- fun `isFinishRequested - finishes when the screen turns off`() = selfCancelingTest {
+ fun `isFinishRequested - finishes when the screen turns off`() = testScope.runTest {
setUsers(count = 2)
powerRepository.setInteractive(true)
val isFinishRequested = mutableListOf<Boolean>()
@@ -338,7 +332,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
}
@Test
- fun `isFinishRequested - finishes when cancel button is clicked`() = selfCancelingTest {
+ fun `isFinishRequested - finishes when cancel button is clicked`() = testScope.runTest {
setUsers(count = 2)
powerRepository.setInteractive(true)
val isFinishRequested = mutableListOf<Boolean>()
@@ -356,6 +350,93 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
job.cancel()
}
+ @Test
+ fun `guest selected -- name is exit guest`() = testScope.runTest {
+ val userInfos =
+ listOf(
+ UserInfo(
+ /* id= */ 0,
+ /* name= */ "zero",
+ /* iconPath= */ "",
+ /* flags= */ UserInfo.FLAG_PRIMARY or UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL,
+ UserManager.USER_TYPE_FULL_SYSTEM,
+ ),
+ UserInfo(
+ /* id= */ 1,
+ /* name= */ "one",
+ /* iconPath= */ "",
+ /* flags= */ UserInfo.FLAG_FULL,
+ UserManager.USER_TYPE_FULL_GUEST,
+ ),
+ )
+
+ userRepository.setUserInfos(userInfos)
+ userRepository.setSelectedUserInfo(userInfos[1])
+
+ val userViewModels = mutableListOf<List<UserViewModel>>()
+ val job = launch(testDispatcher) { underTest.users.toList(userViewModels) }
+
+ assertThat(userViewModels.last()).hasSize(2)
+ assertUserViewModel(
+ viewModel = userViewModels.last()[0],
+ viewKey = 0,
+ name = Text.Loaded("zero"),
+ isSelectionMarkerVisible = false,
+ )
+ assertUserViewModel(
+ viewModel = userViewModels.last()[1],
+ viewKey = 1,
+ name = Text.Resource(
+ com.android.settingslib.R.string.guest_exit_quick_settings_button
+ ),
+ isSelectionMarkerVisible = true,
+ )
+ job.cancel()
+ }
+
+ @Test
+ fun `guest not selected -- name is guest`() = testScope.runTest {
+ val userInfos =
+ listOf(
+ UserInfo(
+ /* id= */ 0,
+ /* name= */ "zero",
+ /* iconPath= */ "",
+ /* flags= */ UserInfo.FLAG_PRIMARY or UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL,
+ UserManager.USER_TYPE_FULL_SYSTEM,
+ ),
+ UserInfo(
+ /* id= */ 1,
+ /* name= */ "one",
+ /* iconPath= */ "",
+ /* flags= */ UserInfo.FLAG_FULL,
+ UserManager.USER_TYPE_FULL_GUEST,
+ ),
+ )
+
+ userRepository.setUserInfos(userInfos)
+ userRepository.setSelectedUserInfo(userInfos[0])
+ runCurrent()
+
+ val userViewModels = mutableListOf<List<UserViewModel>>()
+ val job = launch(testDispatcher) { underTest.users.toList(userViewModels) }
+
+ assertThat(userViewModels.last()).hasSize(2)
+ assertUserViewModel(
+ viewModel = userViewModels.last()[0],
+ viewKey = 0,
+ name = Text.Loaded("zero"),
+ isSelectionMarkerVisible = true,
+ )
+ assertUserViewModel(
+ viewModel = userViewModels.last()[1],
+ viewKey = 1,
+ name = Text.Loaded("one"),
+ isSelectionMarkerVisible = false,
+ )
+ job.cancel()
+ }
+
private suspend fun setUsers(count: Int): List<UserInfo> {
val userInfos =
(0 until count).map { index ->
@@ -384,26 +465,18 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
private fun assertUserViewModel(
viewModel: UserViewModel?,
viewKey: Int,
- name: String,
+ name: Text,
isSelectionMarkerVisible: Boolean,
) {
checkNotNull(viewModel)
assertThat(viewModel.viewKey).isEqualTo(viewKey)
- assertThat(viewModel.name).isEqualTo(Text.Loaded(name))
+ assertThat(viewModel.name).isEqualTo(name)
assertThat(viewModel.isSelectionMarkerVisible).isEqualTo(isSelectionMarkerVisible)
assertThat(viewModel.alpha)
.isEqualTo(LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_SELECTABLE_ALPHA)
assertThat(viewModel.onClicked).isNotNull()
}
- private fun selfCancelingTest(
- block: suspend TestScope.() -> Unit,
- ): TestResult =
- testScope.runTest {
- block()
- injectedScope.coroutineContext[Job.Key]?.cancelAndJoin()
- }
-
companion object {
private const val SUPERVISED_USER_CREATION_PACKAGE = "com.some.package"
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
index 7df707789290..6bfc2f1a8b8b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
@@ -51,15 +51,11 @@ class PairwiseFlowTest : SysuiTestCase() {
)
}
- @Test
- fun notEnough() = runBlocking {
- assertThatFlow(flowOf(1).pairwise()).emitsNothing()
- }
+ @Test fun notEnough() = runBlocking { assertThatFlow(flowOf(1).pairwise()).emitsNothing() }
@Test
fun withInit() = runBlocking {
- assertThatFlow(flowOf(2).pairwise(initialValue = 1))
- .emitsExactly(WithPrev(1, 2))
+ assertThatFlow(flowOf(2).pairwise(initialValue = 1)).emitsExactly(WithPrev(1, 2))
}
@Test
@@ -68,25 +64,78 @@ class PairwiseFlowTest : SysuiTestCase() {
}
@Test
- fun withStateFlow() = runBlocking(Dispatchers.Main.immediate) {
- val state = MutableStateFlow(1)
- val stop = MutableSharedFlow<Unit>()
-
- val stoppable = merge(state, stop)
- .takeWhile { it is Int }
- .filterIsInstance<Int>()
+ fun withTransform() = runBlocking {
+ assertThatFlow(
+ flowOf("val1", "val2", "val3").pairwiseBy { prev: String, next: String ->
+ "$prev|$next"
+ }
+ )
+ .emitsExactly("val1|val2", "val2|val3")
+ }
- val job1 = launch {
- assertThatFlow(stoppable.pairwise()).emitsExactly(WithPrev(1, 2))
- }
- state.value = 2
- val job2 = launch { assertThatFlow(stoppable.pairwise()).emitsNothing() }
+ @Test
+ fun withGetInit() = runBlocking {
+ var initRun = false
+ assertThatFlow(
+ flowOf("val1", "val2").pairwiseBy(
+ getInitialValue = {
+ initRun = true
+ "initial"
+ }
+ ) { prev: String, next: String -> "$prev|$next" }
+ )
+ .emitsExactly("initial|val1", "val1|val2")
+ assertThat(initRun).isTrue()
+ }
- stop.emit(Unit)
+ @Test
+ fun notEnoughWithGetInit() = runBlocking {
+ var initRun = false
+ assertThatFlow(
+ emptyFlow<String>().pairwiseBy(
+ getInitialValue = {
+ initRun = true
+ "initial"
+ }
+ ) { prev: String, next: String -> "$prev|$next" }
+ )
+ .emitsNothing()
+ // Even though the flow will not emit anything, the initial value function should still get
+ // run.
+ assertThat(initRun).isTrue()
+ }
- assertThatJob(job1).isCompleted()
- assertThatJob(job2).isCompleted()
+ @Test
+ fun getInitNotRunWhenFlowNotCollected() = runBlocking {
+ var initRun = false
+ flowOf("val1", "val2").pairwiseBy(
+ getInitialValue = {
+ initRun = true
+ "initial"
+ }
+ ) { prev: String, next: String -> "$prev|$next" }
+
+ // Since the flow isn't collected, ensure [initialValueFun] isn't run.
+ assertThat(initRun).isFalse()
}
+
+ @Test
+ fun withStateFlow() =
+ runBlocking(Dispatchers.Main.immediate) {
+ val state = MutableStateFlow(1)
+ val stop = MutableSharedFlow<Unit>()
+
+ val stoppable = merge(state, stop).takeWhile { it is Int }.filterIsInstance<Int>()
+
+ val job1 = launch { assertThatFlow(stoppable.pairwise()).emitsExactly(WithPrev(1, 2)) }
+ state.value = 2
+ val job2 = launch { assertThatFlow(stoppable.pairwise()).emitsNothing() }
+
+ stop.emit(Unit)
+
+ assertThatJob(job1).isCompleted()
+ assertThatJob(job2).isCompleted()
+ }
}
@SmallTest
@@ -94,18 +143,17 @@ class PairwiseFlowTest : SysuiTestCase() {
class SetChangesFlowTest : SysuiTestCase() {
@Test
fun simple() = runBlocking {
- assertThatFlow(
- flowOf(setOf(1, 2, 3), setOf(2, 3, 4)).setChanges()
- ).emitsExactly(
- SetChanges(
- added = setOf(1, 2, 3),
- removed = emptySet(),
- ),
- SetChanges(
- added = setOf(4),
- removed = setOf(1),
- ),
- )
+ assertThatFlow(flowOf(setOf(1, 2, 3), setOf(2, 3, 4)).setChanges())
+ .emitsExactly(
+ SetChanges(
+ added = setOf(1, 2, 3),
+ removed = emptySet(),
+ ),
+ SetChanges(
+ added = setOf(4),
+ removed = setOf(1),
+ ),
+ )
}
@Test
@@ -147,14 +195,19 @@ class SetChangesFlowTest : SysuiTestCase() {
class SampleFlowTest : SysuiTestCase() {
@Test
fun simple() = runBlocking {
- assertThatFlow(flow { yield(); emit(1) }.sample(flowOf(2)) { a, b -> a to b })
+ assertThatFlow(
+ flow {
+ yield()
+ emit(1)
+ }
+ .sample(flowOf(2)) { a, b -> a to b }
+ )
.emitsExactly(1 to 2)
}
@Test
fun otherFlowNoValueYet() = runBlocking {
- assertThatFlow(flowOf(1).sample(emptyFlow<Unit>()))
- .emitsNothing()
+ assertThatFlow(flowOf(1).sample(emptyFlow<Unit>())).emitsNothing()
}
@Test
@@ -178,13 +231,14 @@ class SampleFlowTest : SysuiTestCase() {
}
}
-private fun <T> assertThatFlow(flow: Flow<T>) = object {
- suspend fun emitsExactly(vararg emissions: T) =
- assertThat(flow.toList()).containsExactly(*emissions).inOrder()
- suspend fun emitsNothing() =
- assertThat(flow.toList()).isEmpty()
-}
+private fun <T> assertThatFlow(flow: Flow<T>) =
+ object {
+ suspend fun emitsExactly(vararg emissions: T) =
+ assertThat(flow.toList()).containsExactly(*emissions).inOrder()
+ suspend fun emitsNothing() = assertThat(flow.toList()).isEmpty()
+ }
-private fun assertThatJob(job: Job) = object {
- fun isCompleted() = assertThat(job.isCompleted).isTrue()
-}
+private fun assertThatJob(job: Job) =
+ object {
+ fun isCompleted() = assertThat(job.isCompleted).isTrue()
+ }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index db3b9b5c8f4e..d31f0bbf49af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -1423,13 +1423,43 @@ public class BubblesTest extends SysuiTestCase {
assertStackCollapsed();
// Post status bar state change update with the same value
mBubbleController.onStatusBarStateChanged(false);
- // Stack should remain collapsedb
+ // Stack should remain collapsed
assertStackCollapsed();
// Post status bar state change which should trigger bubble to expand
mBubbleController.onStatusBarStateChanged(true);
assertStackExpanded();
}
+ /**
+ * Test to verify behavior for the following scenario:
+ * <ol>
+ * <li>device is locked with keyguard on, status bar shade state updates to
+ * <code>false</code></li>
+ * <li>notification entry is marked to be a bubble and it is set to auto-expand</li>
+ * <li>device unlock starts, status bar shade state receives another update to
+ * <code>false</code></li>
+ * <li>device is unlocked and status bar shade state is set to <code>true</code></li>
+ * <li>bubble should be expanded</li>
+ * </ol>
+ */
+ @Test
+ public void testOnStatusBarStateChanged_newAutoExpandedBubbleRemainsExpanded() {
+ // Set device as locked
+ mBubbleController.onStatusBarStateChanged(false);
+
+ // Create a auto-expanded bubble
+ NotificationEntry entry = mNotificationTestHelper.createAutoExpandedBubble();
+ mEntryListener.onEntryAdded(entry);
+
+ // When unlocking, we may receive duplicate updates with shade=false, ensure they don't
+ // clear the expanded state
+ mBubbleController.onStatusBarStateChanged(false);
+ mBubbleController.onStatusBarStateChanged(true);
+
+ // After unlocking, stack should be expanded
+ assertStackExpanded();
+ }
+
@Test
public void testSetShouldAutoExpand_notifiesFlagChanged() {
mBubbleController.updateBubble(mBubbleEntry);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
index fa3cc9905c3f..bf2235aa98a9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
@@ -136,6 +136,8 @@ public abstract class SysuiTestCase {
InstrumentationRegistry.getArguments());
if (TestableLooper.get(this) != null) {
TestableLooper.get(this).processAllMessages();
+ // Must remove static reference to this test object to prevent leak (b/261039202)
+ TestableLooper.remove(this);
}
disallowTestableLooperAsMainThread();
mContext.cleanUpReceivers(this.getClass().getSimpleName());
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/udfps/FakeOverlapDetector.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/udfps/FakeOverlapDetector.kt
new file mode 100644
index 000000000000..8176dd07b84a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/udfps/FakeOverlapDetector.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.udfps
+
+import android.graphics.Rect
+
+class FakeOverlapDetector : OverlapDetector {
+ var shouldReturn: Boolean = false
+
+ override fun isGoodOverlap(touchData: NormalizedTouchData, nativeSensorBounds: Rect): Boolean {
+ return shouldReturn
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
index 52e0c982cae0..ad086ff9c664 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
@@ -22,12 +22,12 @@ import android.content.IntentFilter
import android.os.Handler
import android.os.Looper
import android.os.UserHandle
-import android.util.ArraySet
import android.util.Log
import com.android.systemui.SysuiTestableContext
import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
import com.android.systemui.dump.DumpManager
import com.android.systemui.settings.UserTracker
+import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Executor
class FakeBroadcastDispatcher(
@@ -50,7 +50,7 @@ class FakeBroadcastDispatcher(
PendingRemovalStore(logger)
) {
- val registeredReceivers = ArraySet<BroadcastReceiver>()
+ val registeredReceivers: MutableSet<BroadcastReceiver> = ConcurrentHashMap.newKeySet()
override fun registerReceiverWithHandler(
receiver: BroadcastReceiver,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/Flow.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/Flow.kt
new file mode 100644
index 000000000000..b7a8d2e9f684
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/Flow.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.coroutines
+
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.EmptyCoroutineContext
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+
+/** Collect [flow] in a new [Job] and return a getter for the last collected value. */
+fun <T> TestScope.collectLastValue(
+ flow: Flow<T>,
+ context: CoroutineContext = EmptyCoroutineContext,
+ start: CoroutineStart = CoroutineStart.DEFAULT,
+): () -> T? {
+ var lastValue: T? = null
+ backgroundScope.launch(context, start) { flow.collect { lastValue = it } }
+ return {
+ runCurrent()
+ lastValue
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceProviderClientFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceProviderClientFactory.kt
new file mode 100644
index 000000000000..d85dd2e7bc8a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceProviderClientFactory.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.quickaffordance
+
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.shared.quickaffordance.data.content.FakeKeyguardQuickAffordanceProviderClient
+import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClient
+
+class FakeKeyguardQuickAffordanceProviderClientFactory(
+ private val userTracker: UserTracker,
+ private val callback: (Int) -> KeyguardQuickAffordanceProviderClient = {
+ FakeKeyguardQuickAffordanceProviderClient()
+ },
+) : KeyguardQuickAffordanceProviderClientFactory {
+
+ override fun create(): KeyguardQuickAffordanceProviderClient {
+ return callback(userTracker.userId)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 36016678fb91..5c2a915e81b6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -17,11 +17,15 @@
package com.android.systemui.keyguard.data.repository
+import android.graphics.Point
import com.android.systemui.common.shared.model.Position
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.keyguard.shared.model.WakeSleepReason
import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.keyguard.shared.model.WakefulnessState
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -49,7 +53,7 @@ class FakeKeyguardRepository : KeyguardRepository {
override val isDreaming: Flow<Boolean> = _isDreaming
private val _dozeAmount = MutableStateFlow(0f)
- override val dozeAmount: Flow<Float> = _dozeAmount
+ override val linearDozeAmount: Flow<Float> = _dozeAmount
private val _statusBarState = MutableStateFlow(StatusBarState.SHADE)
override val statusBarState: Flow<StatusBarState> = _statusBarState
@@ -57,8 +61,16 @@ class FakeKeyguardRepository : KeyguardRepository {
private val _dozeTransitionModel = MutableStateFlow(DozeTransitionModel())
override val dozeTransitionModel: Flow<DozeTransitionModel> = _dozeTransitionModel
- private val _wakefulnessState = MutableStateFlow(WakefulnessModel.ASLEEP)
- override val wakefulnessState: Flow<WakefulnessModel> = _wakefulnessState
+ private val _wakefulnessModel =
+ MutableStateFlow(
+ WakefulnessModel(
+ WakefulnessState.ASLEEP,
+ false,
+ WakeSleepReason.OTHER,
+ WakeSleepReason.OTHER
+ )
+ )
+ override val wakefulness: Flow<WakefulnessModel> = _wakefulnessModel
private val _isUdfpsSupported = MutableStateFlow(false)
@@ -71,6 +83,15 @@ class FakeKeyguardRepository : KeyguardRepository {
private val _biometricUnlockState = MutableStateFlow(BiometricUnlockModel.NONE)
override val biometricUnlockState: Flow<BiometricUnlockModel> = _biometricUnlockState
+ private val _fingerprintSensorLocation = MutableStateFlow<Point?>(null)
+ override val fingerprintSensorLocation: Flow<Point?> = _fingerprintSensorLocation
+
+ private val _faceSensorLocation = MutableStateFlow<Point?>(null)
+ override val faceSensorLocation: Flow<Point?> = _faceSensorLocation
+
+ private val _biometricUnlockSource = MutableStateFlow<BiometricUnlockSource?>(null)
+ override val biometricUnlockSource: Flow<BiometricUnlockSource?> = _biometricUnlockSource
+
override fun isKeyguardShowing(): Boolean {
return _isKeyguardShowing.value
}
@@ -99,6 +120,22 @@ class FakeKeyguardRepository : KeyguardRepository {
_dozeAmount.value = dozeAmount
}
+ fun setBiometricUnlockState(state: BiometricUnlockModel) {
+ _biometricUnlockState.tryEmit(state)
+ }
+
+ fun setBiometricUnlockSource(source: BiometricUnlockSource?) {
+ _biometricUnlockSource.tryEmit(source)
+ }
+
+ fun setFaceSensorLocation(location: Point?) {
+ _faceSensorLocation.tryEmit(location)
+ }
+
+ fun setFingerprintSensorLocation(location: Point?) {
+ _fingerprintSensorLocation.tryEmit(location)
+ }
+
override fun isUdfpsSupported(): Boolean {
return _isUdfpsSupported.value
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeLightRevealScrimRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeLightRevealScrimRepository.kt
new file mode 100644
index 000000000000..7c22604dc546
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeLightRevealScrimRepository.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import com.android.systemui.statusbar.LightRevealEffect
+import kotlinx.coroutines.flow.MutableStateFlow
+
+/** Fake implementation of [LightRevealScrimRepository] */
+class FakeLightRevealScrimRepository : LightRevealScrimRepository {
+
+ private val _revealEffect: MutableStateFlow<LightRevealEffect> =
+ MutableStateFlow(DEFAULT_REVEAL_EFFECT)
+ override val revealEffect = _revealEffect
+
+ fun setRevealEffect(effect: LightRevealEffect) {
+ _revealEffect.tryEmit(effect)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt
index 63448e236867..1a893f8c523c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt
@@ -56,7 +56,8 @@ import com.android.systemui.util.mockito.mock
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.util.settings.GlobalSettings
import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.test.TestCoroutineDispatcher
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestCoroutineScheduler
/**
* Util class to create real implementations of the FooterActions repositories, viewModel and
@@ -65,6 +66,7 @@ import kotlinx.coroutines.test.TestCoroutineDispatcher
class FooterActionsTestUtils(
private val context: Context,
private val testableLooper: TestableLooper,
+ private val scheduler: TestCoroutineScheduler,
) {
/** Enable or disable the user switcher in the settings. */
fun setUserSwitcherEnabled(settings: GlobalSettings, enabled: Boolean, userId: Int) {
@@ -105,7 +107,7 @@ class FooterActionsTestUtils(
foregroundServicesRepository: ForegroundServicesRepository = foregroundServicesRepository(),
userSwitcherRepository: UserSwitcherRepository = userSwitcherRepository(),
broadcastDispatcher: BroadcastDispatcher = mock(),
- bgDispatcher: CoroutineDispatcher = TestCoroutineDispatcher(),
+ bgDispatcher: CoroutineDispatcher = StandardTestDispatcher(scheduler),
): FooterActionsInteractor {
return FooterActionsInteractorImpl(
activityStarter,
@@ -126,7 +128,7 @@ class FooterActionsTestUtils(
/** Create a [SecurityRepository] to be used in tests. */
fun securityRepository(
securityController: SecurityController = FakeSecurityController(),
- bgDispatcher: CoroutineDispatcher = TestCoroutineDispatcher(),
+ bgDispatcher: CoroutineDispatcher = StandardTestDispatcher(scheduler),
): SecurityRepository {
return SecurityRepositoryImpl(
securityController,
@@ -145,7 +147,7 @@ class FooterActionsTestUtils(
fun userSwitcherRepository(
@Application context: Context = this.context.applicationContext,
bgHandler: Handler = Handler(testableLooper.looper),
- bgDispatcher: CoroutineDispatcher = TestCoroutineDispatcher(),
+ bgDispatcher: CoroutineDispatcher = StandardTestDispatcher(scheduler),
userManager: UserManager = mock(),
userTracker: UserTracker = FakeUserTracker(),
userSwitcherController: UserSwitcherController = mock(),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
index a7eadba60ddc..0dd1fc758a27 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
@@ -66,7 +66,8 @@ class FakeUserTracker(
_userId = _userInfo.id
_userHandle = UserHandle.of(_userId)
- callbacks.forEach { it.onUserChanged(_userId, userContext) }
+ val copy = callbacks.toList()
+ copy.forEach { it.onUserChanged(_userId, userContext) }
}
fun onProfileChanged() {
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index 28141960f119..f1ba5ffbf1b4 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -355,10 +355,10 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
private boolean activityMatchesDisplayCategory(ActivityInfo activityInfo) {
if (mDisplayCategories.isEmpty()) {
- return activityInfo.targetDisplayCategory == null;
+ return activityInfo.requiredDisplayCategory == null;
}
- return activityInfo.targetDisplayCategory != null
- && mDisplayCategories.contains(activityInfo.targetDisplayCategory);
+ return activityInfo.requiredDisplayCategory != null
+ && mDisplayCategories.contains(activityInfo.requiredDisplayCategory);
}
@@ -375,9 +375,9 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
}
if (!activityMatchesDisplayCategory(activityInfo)) {
Slog.d(TAG, String.format(
- "The activity's target display category: %s is not found on virtual display"
- + " with the following allowed display categories: %s",
- activityInfo.targetDisplayCategory, mDisplayCategories.toString()));
+ "The activity's required display category: %s is not found on virtual display"
+ + " with the following categories: %s",
+ activityInfo.requiredDisplayCategory, mDisplayCategories.toString()));
return false;
}
final UserHandle activityUser =
diff --git a/services/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java
index 551ffff2921b..84c033cbdf0e 100644
--- a/services/core/java/com/android/server/BootReceiver.java
+++ b/services/core/java/com/android/server/BootReceiver.java
@@ -341,7 +341,8 @@ public class BootReceiver extends BroadcastReceiver {
// non-proto tombstones, even though proto tombstones do not support including the counter
// of events dropped since rate limiting activated yet.
DropboxRateLimiter.RateLimitResult rateLimitResult =
- sDropboxRateLimiter.shouldRateLimit(TAG_TOMBSTONE, processName);
+ sDropboxRateLimiter.shouldRateLimit(
+ proto ? TAG_TOMBSTONE_PROTO : TAG_TOMBSTONE, processName);
if (rateLimitResult.shouldRateLimit()) return;
HashMap<String, Long> timestamps = readTimestamps();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 50be45804e4f..c51e14fdc8a2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5149,8 +5149,10 @@ public class ActivityManagerService extends IActivityManager.Stub
ApplicationExitInfo.REASON_INITIALIZATION_FAILURE,
ApplicationExitInfo.SUBREASON_UNKNOWN,
"wrong startSeq");
- app.killLocked("unexpected process record",
- ApplicationExitInfo.REASON_OTHER, true);
+ synchronized (this) {
+ app.killLocked("unexpected process record",
+ ApplicationExitInfo.REASON_OTHER, true);
+ }
return;
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 2ea49b338b38..af0bd633c471 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1769,7 +1769,7 @@ class UserController implements Handler.Callback {
if (foreground) {
t.traceBegin("moveUserToForeground");
- moveUserToForeground(uss, oldUserId, userId);
+ moveUserToForeground(uss, userId);
t.traceEnd();
} else {
t.traceBegin("finishUserBoot");
@@ -1983,21 +1983,25 @@ class UserController implements Handler.Callback {
/** Called on handler thread */
@VisibleForTesting
- void dispatchUserSwitchComplete(@UserIdInt int userId) {
+ void dispatchUserSwitchComplete(@UserIdInt int oldUserId, @UserIdInt int newUserId) {
final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
- t.traceBegin("dispatchUserSwitchComplete-" + userId);
+ t.traceBegin("dispatchUserSwitchComplete-" + newUserId);
mInjector.getWindowManager().setSwitchingUser(false);
final int observerCount = mUserSwitchObservers.beginBroadcast();
for (int i = 0; i < observerCount; i++) {
try {
- t.traceBegin("onUserSwitchComplete-" + userId + " #" + i + " "
+ t.traceBegin("onUserSwitchComplete-" + newUserId + " #" + i + " "
+ mUserSwitchObservers.getBroadcastCookie(i));
- mUserSwitchObservers.getBroadcastItem(i).onUserSwitchComplete(userId);
+ mUserSwitchObservers.getBroadcastItem(i).onUserSwitchComplete(newUserId);
t.traceEnd();
} catch (RemoteException e) {
+ // Ignore
}
}
mUserSwitchObservers.finishBroadcast();
+ t.traceBegin("sendUserSwitchBroadcasts-" + oldUserId + "-" + newUserId);
+ sendUserSwitchBroadcasts(oldUserId, newUserId);
+ t.traceEnd();
t.traceEnd();
}
@@ -2159,7 +2163,8 @@ class UserController implements Handler.Callback {
// Do the keyguard dismiss and unfreeze later
mHandler.removeMessages(COMPLETE_USER_SWITCH_MSG);
- mHandler.sendMessage(mHandler.obtainMessage(COMPLETE_USER_SWITCH_MSG, newUserId, 0));
+ mHandler.sendMessage(mHandler.obtainMessage(
+ COMPLETE_USER_SWITCH_MSG, oldUserId, newUserId));
uss.switching = false;
stopGuestOrEphemeralUserIfBackground(oldUserId);
@@ -2169,7 +2174,7 @@ class UserController implements Handler.Callback {
}
@VisibleForTesting
- void completeUserSwitch(int newUserId) {
+ void completeUserSwitch(int oldUserId, int newUserId) {
final boolean isUserSwitchUiEnabled = isUserSwitchUiEnabled();
final Runnable runnable = () -> {
if (isUserSwitchUiEnabled) {
@@ -2177,7 +2182,7 @@ class UserController implements Handler.Callback {
}
mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG);
mHandler.sendMessage(mHandler.obtainMessage(
- REPORT_USER_SWITCH_COMPLETE_MSG, newUserId, 0));
+ REPORT_USER_SWITCH_COMPLETE_MSG, oldUserId, newUserId));
};
// If there is no challenge set, dismiss the keyguard right away
@@ -2202,7 +2207,7 @@ class UserController implements Handler.Callback {
t.traceEnd();
}
- private void moveUserToForeground(UserState uss, int oldUserId, int newUserId) {
+ private void moveUserToForeground(UserState uss, int newUserId) {
boolean homeInFront = mInjector.taskSupervisorSwitchUser(newUserId, uss);
if (homeInFront) {
mInjector.startHomeActivity(newUserId, "moveUserToForeground");
@@ -2210,7 +2215,6 @@ class UserController implements Handler.Callback {
mInjector.taskSupervisorResumeFocusedStackTopActivity();
}
EventLogTags.writeAmSwitchUser(newUserId);
- sendUserSwitchBroadcasts(oldUserId, newUserId);
}
void sendUserSwitchBroadcasts(int oldUserId, int newUserId) {
@@ -3080,9 +3084,8 @@ class UserController implements Handler.Callback {
dispatchForegroundProfileChanged(msg.arg1);
break;
case REPORT_USER_SWITCH_COMPLETE_MSG:
- dispatchUserSwitchComplete(msg.arg1);
-
- logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_SWITCH_USER,
+ dispatchUserSwitchComplete(msg.arg1, msg.arg2);
+ logUserLifecycleEvent(msg.arg2, USER_LIFECYCLE_EVENT_SWITCH_USER,
USER_LIFECYCLE_EVENT_STATE_FINISH);
break;
case REPORT_LOCKED_BOOT_COMPLETE_MSG:
@@ -3100,7 +3103,7 @@ class UserController implements Handler.Callback {
logAndClearSessionId(msg.arg1);
break;
case COMPLETE_USER_SWITCH_MSG:
- completeUserSwitch(msg.arg1);
+ completeUserSwitch(msg.arg1, msg.arg2);
break;
}
return false;
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index f650255d75ea..cda18b07c02e 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -118,6 +118,14 @@ import java.util.Set;
*/
public final class GameManagerService extends IGameManagerService.Stub {
public static final String TAG = "GameManagerService";
+ // event strings used for logging
+ private static final String EVENT_SET_GAME_MODE = "SET_GAME_MODE";
+ private static final String EVENT_UPDATE_CUSTOM_GAME_MODE_CONFIG =
+ "UPDATE_CUSTOM_GAME_MODE_CONFIG";
+ private static final String EVENT_RECEIVE_SHUTDOWN_INDENT = "RECEIVE_SHUTDOWN_INDENT";
+ private static final String EVENT_ON_USER_STARTING = "ON_USER_STARTING";
+ private static final String EVENT_ON_USER_SWITCHING = "ON_USER_SWITCHING";
+ private static final String EVENT_ON_USER_STOPPING = "ON_USER_STOPPING";
private static final boolean DEBUG = false;
@@ -1154,9 +1162,9 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
}
}
- sendUserMessage(userId, WRITE_SETTINGS, "SET_GAME_MODE", WRITE_DELAY_MILLIS);
+ sendUserMessage(userId, WRITE_SETTINGS, EVENT_SET_GAME_MODE, WRITE_DELAY_MILLIS);
sendUserMessage(userId, WRITE_GAME_MODE_INTERVENTION_LIST_FILE,
- "SET_GAME_MODE", 0 /*delayMillis*/);
+ EVENT_SET_GAME_MODE, 0 /*delayMillis*/);
int gameUid = -1;
try {
gameUid = mPackageManager.getPackageUidAsUser(packageName, userId);
@@ -1399,6 +1407,11 @@ public final class GameManagerService extends IGameManagerService.Stub {
Slog.i(TAG, "Updated custom game mode config for package: " + packageName
+ " with FPS=" + internalConfig.getFps() + ";Scaling="
+ internalConfig.getScaling() + " under user " + userId);
+
+ sendUserMessage(userId, WRITE_SETTINGS, EVENT_UPDATE_CUSTOM_GAME_MODE_CONFIG,
+ WRITE_DELAY_MILLIS);
+ sendUserMessage(userId, WRITE_GAME_MODE_INTERVENTION_LIST_FILE,
+ EVENT_UPDATE_CUSTOM_GAME_MODE_CONFIG, WRITE_DELAY_MILLIS /*delayMillis*/);
}
/**
@@ -1473,9 +1486,10 @@ public final class GameManagerService extends IGameManagerService.Stub {
for (Map.Entry<Integer, GameManagerSettings> entry : mSettings.entrySet()) {
final int userId = entry.getKey();
sendUserMessage(userId, WRITE_SETTINGS,
- Intent.ACTION_SHUTDOWN, 0 /*delayMillis*/);
+ EVENT_RECEIVE_SHUTDOWN_INDENT, 0 /*delayMillis*/);
sendUserMessage(userId,
- WRITE_GAME_MODE_INTERVENTION_LIST_FILE, Intent.ACTION_SHUTDOWN,
+ WRITE_GAME_MODE_INTERVENTION_LIST_FILE,
+ EVENT_RECEIVE_SHUTDOWN_INDENT,
0 /*delayMillis*/);
}
}
@@ -1500,7 +1514,8 @@ public final class GameManagerService extends IGameManagerService.Stub {
userSettings.readPersistentDataLocked();
}
}
- sendUserMessage(userId, POPULATE_GAME_MODE_SETTINGS, "ON_USER_STARTING", 0 /*delayMillis*/);
+ sendUserMessage(userId, POPULATE_GAME_MODE_SETTINGS, EVENT_ON_USER_STARTING,
+ 0 /*delayMillis*/);
if (mGameServiceController != null) {
mGameServiceController.notifyUserStarted(user);
@@ -1520,7 +1535,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
if (!mSettings.containsKey(userId)) {
return;
}
- sendUserMessage(userId, REMOVE_SETTINGS, "ON_USER_STOPPING", 0 /*delayMillis*/);
+ sendUserMessage(userId, REMOVE_SETTINGS, EVENT_ON_USER_STOPPING, 0 /*delayMillis*/);
}
if (mGameServiceController != null) {
@@ -1533,7 +1548,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
// we want to re-populate the setting when switching user as the device config may have
// changed, which will only update for the previous user, see
// DeviceConfigListener#onPropertiesChanged.
- sendUserMessage(toUserId, POPULATE_GAME_MODE_SETTINGS, "ON_USER_SWITCHING",
+ sendUserMessage(toUserId, POPULATE_GAME_MODE_SETTINGS, EVENT_ON_USER_SWITCHING,
0 /*delayMillis*/);
if (mGameServiceController != null) {
@@ -1617,6 +1632,34 @@ public final class GameManagerService extends IGameManagerService.Stub {
public void setGameModeConfigOverride(String packageName, @UserIdInt int userId,
@GameMode int gameMode, String fpsStr, String scaling) throws SecurityException {
checkPermission(Manifest.permission.MANAGE_GAME_MODE);
+ int gameUid = -1;
+ try {
+ gameUid = mPackageManager.getPackageUidAsUser(packageName, userId);
+ } catch (NameNotFoundException ex) {
+ Slog.d(TAG, "Cannot find the UID for package " + packageName + " under user " + userId);
+ }
+ GamePackageConfiguration pkgConfig = getConfig(packageName, userId);
+ if (pkgConfig != null && pkgConfig.getGameModeConfiguration(gameMode) != null) {
+ final GamePackageConfiguration.GameModeConfiguration currentModeConfig =
+ pkgConfig.getGameModeConfiguration(gameMode);
+ FrameworkStatsLog.write(FrameworkStatsLog.GAME_MODE_CONFIGURATION_CHANGED, gameUid,
+ Binder.getCallingUid(), gameModeToStatsdGameMode(gameMode),
+ currentModeConfig.getScaling() /* fromScaling */,
+ scaling == null ? currentModeConfig.getScaling()
+ : Float.parseFloat(scaling) /* toScaling */,
+ currentModeConfig.getFps() /* fromFps */,
+ fpsStr == null ? currentModeConfig.getFps()
+ : Integer.parseInt(fpsStr)) /* toFps */;
+ } else {
+ FrameworkStatsLog.write(FrameworkStatsLog.GAME_MODE_CONFIGURATION_CHANGED, gameUid,
+ Binder.getCallingUid(), gameModeToStatsdGameMode(gameMode),
+ GamePackageConfiguration.GameModeConfiguration.DEFAULT_SCALING /* fromScaling*/,
+ scaling == null ? GamePackageConfiguration.GameModeConfiguration.DEFAULT_SCALING
+ : Float.parseFloat(scaling) /* toScaling */,
+ 0 /* fromFps */,
+ fpsStr == null ? 0 : Integer.parseInt(fpsStr) /* toFps */);
+ }
+
// Adding game mode config override of the given package name
GamePackageConfiguration configOverride;
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/app/GameManagerSettings.java b/services/core/java/com/android/server/app/GameManagerSettings.java
index 638bc4e7a070..5189017f5bf0 100644
--- a/services/core/java/com/android/server/app/GameManagerSettings.java
+++ b/services/core/java/com/android/server/app/GameManagerSettings.java
@@ -19,6 +19,7 @@ package com.android.server.app;
import android.app.GameManager;
import android.os.FileUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.Xml;
@@ -37,7 +38,6 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.util.Map;
/**
* Persists all GameService related settings.
@@ -49,7 +49,11 @@ public class GameManagerSettings {
// The XML file follows the below format:
// <?xml>
// <packages>
- // <package></package>
+ // <package name="" gameMode="">
+ // <gameModeConfig gameMode="" fps="" scaling="" useAngle="" loadingBoost="">
+ // </gameModeConfig>
+ // ...
+ // </package>
// ...
// </packages>
private static final String GAME_SERVICE_FILE_NAME = "game-manager-service.xml";
@@ -155,11 +159,14 @@ public class GameManagerSettings {
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
serializer.startTag(null, TAG_PACKAGES);
- for (Map.Entry<String, Integer> entry : mGameModes.entrySet()) {
- String packageName = entry.getKey();
+ final ArraySet<String> packageNames = new ArraySet<>(mGameModes.keySet());
+ packageNames.addAll(mConfigOverrides.keySet());
+ for (String packageName : packageNames) {
serializer.startTag(null, TAG_PACKAGE);
serializer.attribute(null, ATTR_NAME, packageName);
- serializer.attributeInt(null, ATTR_GAME_MODE, entry.getValue());
+ if (mGameModes.containsKey(packageName)) {
+ serializer.attributeInt(null, ATTR_GAME_MODE, mGameModes.get(packageName));
+ }
writeGameModeConfigTags(serializer, mConfigOverrides.get(packageName));
serializer.endTag(null, TAG_PACKAGE);
}
@@ -224,7 +231,7 @@ public class GameManagerSettings {
// Do nothing
}
if (type != XmlPullParser.START_TAG) {
- Slog.wtf(TAG, "No start tag found in package manager settings");
+ Slog.wtf(TAG, "No start tag found in game manager settings");
return false;
}
@@ -245,7 +252,7 @@ public class GameManagerSettings {
}
}
} catch (XmlPullParserException | java.io.IOException e) {
- Slog.wtf(TAG, "Error reading package manager settings", e);
+ Slog.wtf(TAG, "Error reading game manager settings", e);
return false;
}
return true;
@@ -260,15 +267,12 @@ public class GameManagerSettings {
XmlUtils.skipCurrentTag(parser);
return;
}
- int gameMode;
try {
- gameMode = parser.getAttributeInt(null, ATTR_GAME_MODE);
+ final int gameMode = parser.getAttributeInt(null, ATTR_GAME_MODE);
+ mGameModes.put(name, gameMode);
} catch (XmlPullParserException e) {
- Slog.wtf(TAG, "Invalid game mode in package tag: "
- + parser.getAttributeValue(null, ATTR_GAME_MODE), e);
- return;
+ Slog.v(TAG, "No game mode selected by user for package" + name);
}
- mGameModes.put(name, gameMode);
final int packageTagDepth = parser.getDepth();
int type;
final GamePackageConfiguration config = new GamePackageConfiguration(name);
diff --git a/services/core/java/com/android/server/app/GameManagerShellCommand.java b/services/core/java/com/android/server/app/GameManagerShellCommand.java
index aa9b77c42e09..abab0e7ae3b9 100644
--- a/services/core/java/com/android/server/app/GameManagerShellCommand.java
+++ b/services/core/java/com/android/server/app/GameManagerShellCommand.java
@@ -27,6 +27,7 @@ import android.os.ShellCommand;
import java.io.PrintWriter;
import java.util.Locale;
+import java.util.StringJoiner;
/**
* ShellCommands for GameManagerService.
@@ -34,8 +35,20 @@ import java.util.Locale;
* Use with {@code adb shell cmd game ...}.
*/
public class GameManagerShellCommand extends ShellCommand {
+ private static final String STANDARD_MODE_STR = "standard";
+ private static final String STANDARD_MODE_NUM = "1";
+ private static final String PERFORMANCE_MODE_STR = "performance";
+ private static final String PERFORMANCE_MODE_NUM = "2";
+ private static final String BATTERY_MODE_STR = "battery";
+ private static final String BATTERY_MODE_NUM = "3";
+ private static final String CUSTOM_MODE_STR = "custom";
+ private static final String CUSTOM_MODE_NUM = "4";
+ private static final String UNSUPPORTED_MODE_STR = "unsupported";
+ private static final String UNSUPPORTED_MODE_NUM = String.valueOf(
+ GameManager.GAME_MODE_UNSUPPORTED);
- public GameManagerShellCommand() {}
+ public GameManagerShellCommand() {
+ }
@Override
public int onCommand(String cmd) {
@@ -46,10 +59,10 @@ public class GameManagerShellCommand extends ShellCommand {
try {
switch (cmd) {
case "set": {
- return runSetGameMode(pw);
+ return runSetGameModeConfig(pw);
}
case "reset": {
- return runResetGameMode(pw);
+ return runResetGameModeConfig(pw);
}
case "mode": {
/** The "mode" command allows setting a package's current game mode outside of
@@ -61,10 +74,13 @@ public class GameManagerShellCommand extends ShellCommand {
* <PACKAGE_NAME> <CONFIG_STRING>`
* see: {@link GameManagerServiceTests#mockDeviceConfigAll()}
*/
- return runGameMode(pw);
+ return runSetGameMode(pw);
+ }
+ case "list-modes": {
+ return runListGameModes(pw);
}
- case "list": {
- return runGameList(pw);
+ case "list-configs": {
+ return runListGameModeConfigs(pw);
}
default:
return handleDefaultCommands(cmd);
@@ -75,7 +91,21 @@ public class GameManagerShellCommand extends ShellCommand {
return -1;
}
- private int runGameList(PrintWriter pw) throws ServiceNotFoundException, RemoteException {
+ private int runListGameModes(PrintWriter pw) throws ServiceNotFoundException, RemoteException {
+ final String packageName = getNextArgRequired();
+ final GameManagerService gameManagerService = (GameManagerService)
+ ServiceManager.getService(Context.GAME_SERVICE);
+ final StringJoiner sj = new StringJoiner(",");
+ for (int mode : gameManagerService.getAvailableGameModes(packageName,
+ ActivityManager.getCurrentUser())) {
+ sj.add(gameModeIntToString(mode));
+ }
+ pw.println(packageName + " has available game modes: [" + sj + "]");
+ return 0;
+ }
+
+ private int runListGameModeConfigs(PrintWriter pw)
+ throws ServiceNotFoundException, RemoteException {
final String packageName = getNextArgRequired();
final GameManagerService gameManagerService = (GameManagerService)
@@ -92,7 +122,7 @@ public class GameManagerShellCommand extends ShellCommand {
return 0;
}
- private int runGameMode(PrintWriter pw) throws ServiceNotFoundException, RemoteException {
+ private int runSetGameMode(PrintWriter pw) throws ServiceNotFoundException, RemoteException {
final String option = getNextOption();
String userIdStr = null;
if (option != null && option.equals("--user")) {
@@ -116,34 +146,46 @@ public class GameManagerShellCommand extends ShellCommand {
}
}
switch (gameMode.toLowerCase()) {
- case "1":
- case "standard":
+ case STANDARD_MODE_NUM:
+ case STANDARD_MODE_STR:
// Standard mode can be used to specify loading ANGLE as the default OpenGL ES
// driver, so it should always be available.
service.setGameMode(packageName, GameManager.GAME_MODE_STANDARD, userId);
+ pw.println("Set game mode to `STANDARD` for user `" + userId + "` in game `"
+ + packageName + "`");
break;
- case "2":
- case "performance":
+ case PERFORMANCE_MODE_NUM:
+ case PERFORMANCE_MODE_STR:
if (perfModeSupported) {
service.setGameMode(packageName, GameManager.GAME_MODE_PERFORMANCE,
userId);
+ pw.println("Set game mode to `PERFORMANCE` for user `" + userId + "` in game `"
+ + packageName + "`");
} else {
pw.println("Game mode: " + gameMode + " not supported by "
+ packageName);
return -1;
}
break;
- case "3":
- case "battery":
+ case BATTERY_MODE_NUM:
+ case BATTERY_MODE_STR:
if (batteryModeSupported) {
service.setGameMode(packageName, GameManager.GAME_MODE_BATTERY,
userId);
+ pw.println("Set game mode to `BATTERY` for user `" + userId + "` in game `"
+ + packageName + "`");
} else {
pw.println("Game mode: " + gameMode + " not supported by "
+ packageName);
return -1;
}
break;
+ case CUSTOM_MODE_NUM:
+ case CUSTOM_MODE_STR:
+ service.setGameMode(packageName, GameManager.GAME_MODE_CUSTOM, userId);
+ pw.println("Set game mode to `CUSTOM` for user `" + userId + "` in game `"
+ + packageName + "`");
+ break;
default:
pw.println("Invalid game mode: " + gameMode);
return -1;
@@ -151,15 +193,9 @@ public class GameManagerShellCommand extends ShellCommand {
return 0;
}
- private int runSetGameMode(PrintWriter pw) throws ServiceNotFoundException, RemoteException {
- String option = getNextArgRequired();
- if (!option.equals("--mode")) {
- pw.println("Invalid option '" + option + "'");
- return -1;
- }
-
- final String gameMode = getNextArgRequired();
-
+ private int runSetGameModeConfig(PrintWriter pw)
+ throws ServiceNotFoundException, RemoteException {
+ String option;
/**
* handling optional input
* "--user", "--downscale" and "--fps" can come in any order
@@ -167,8 +203,12 @@ public class GameManagerShellCommand extends ShellCommand {
String userIdStr = null;
String fpsStr = null;
String downscaleRatio = null;
+ int gameMode = GameManager.GAME_MODE_CUSTOM;
while ((option = getNextOption()) != null) {
switch (option) {
+ case "--mode":
+ gameMode = Integer.parseInt(getNextArgRequired());
+ break;
case "--user":
if (userIdStr == null) {
userIdStr = getNextArgRequired();
@@ -220,50 +260,21 @@ public class GameManagerShellCommand extends ShellCommand {
final GameManagerService gameManagerService = (GameManagerService)
ServiceManager.getService(Context.GAME_SERVICE);
-
- boolean batteryModeSupported = false;
- boolean perfModeSupported = false;
- int [] modes = gameManagerService.getAvailableGameModes(packageName, userId);
-
- for (int mode : modes) {
- if (mode == GameManager.GAME_MODE_PERFORMANCE) {
- perfModeSupported = true;
- } else if (mode == GameManager.GAME_MODE_BATTERY) {
- batteryModeSupported = true;
- }
- }
-
- switch (gameMode.toLowerCase(Locale.getDefault())) {
- case "2":
- case "performance":
- if (perfModeSupported) {
- gameManagerService.setGameModeConfigOverride(packageName, userId,
- GameManager.GAME_MODE_PERFORMANCE, fpsStr, downscaleRatio);
- } else {
- pw.println("Game mode: " + gameMode + " not supported by "
- + packageName);
- return -1;
- }
- break;
- case "3":
- case "battery":
- if (batteryModeSupported) {
- gameManagerService.setGameModeConfigOverride(packageName, userId,
- GameManager.GAME_MODE_BATTERY, fpsStr, downscaleRatio);
- } else {
- pw.println("Game mode: " + gameMode + " not supported by "
- + packageName);
- return -1;
- }
- break;
- default:
- pw.println("Invalid game mode: " + gameMode);
- return -1;
+ if (gameManagerService == null) {
+ pw.println("Failed to find GameManagerService on device");
+ return -1;
}
+ gameManagerService.setGameModeConfigOverride(packageName, userId, gameMode,
+ fpsStr, downscaleRatio);
+ pw.println("Set custom mode intervention config for user `" + userId + "` in game `"
+ + packageName + "` as: `"
+ + "downscaling-ratio: " + downscaleRatio + ";"
+ + "fps-override: " + fpsStr + "`");
return 0;
}
- private int runResetGameMode(PrintWriter pw) throws ServiceNotFoundException, RemoteException {
+ private int runResetGameModeConfig(PrintWriter pw)
+ throws ServiceNotFoundException, RemoteException {
String option = null;
String gameMode = null;
String userIdStr = null;
@@ -305,13 +316,13 @@ public class GameManagerShellCommand extends ShellCommand {
}
switch (gameMode.toLowerCase(Locale.getDefault())) {
- case "2":
- case "performance":
+ case PERFORMANCE_MODE_NUM:
+ case PERFORMANCE_MODE_STR:
gameManagerService.resetGameModeConfigOverride(packageName, userId,
GameManager.GAME_MODE_PERFORMANCE);
break;
- case "3":
- case "battery":
+ case BATTERY_MODE_NUM:
+ case BATTERY_MODE_STR:
gameManagerService.resetGameModeConfigOverride(packageName, userId,
GameManager.GAME_MODE_BATTERY);
break;
@@ -322,6 +333,22 @@ public class GameManagerShellCommand extends ShellCommand {
return 0;
}
+ private static String gameModeIntToString(@GameManager.GameMode int gameMode) {
+ switch (gameMode) {
+ case GameManager.GAME_MODE_BATTERY:
+ return BATTERY_MODE_STR;
+ case GameManager.GAME_MODE_PERFORMANCE:
+ return PERFORMANCE_MODE_STR;
+ case GameManager.GAME_MODE_CUSTOM:
+ return CUSTOM_MODE_STR;
+ case GameManager.GAME_MODE_STANDARD:
+ return STANDARD_MODE_STR;
+ case GameManager.GAME_MODE_UNSUPPORTED:
+ return UNSUPPORTED_MODE_STR;
+ }
+ return "";
+ }
+
@Override
public void onHelp() {
PrintWriter pw = getOutPrintWriter();
@@ -329,21 +356,28 @@ public class GameManagerShellCommand extends ShellCommand {
pw.println(" help");
pw.println(" Print this help text.");
pw.println(" downscale");
- pw.println(" Deprecated. Please use `set` command.");
- pw.println(" mode [--user <USER_ID>] [1|2|3|standard|performance|battery] <PACKAGE_NAME>");
+ pw.println(" Deprecated. Please use `custom` command.");
+ pw.println(" list-configs <PACKAGE_NAME>");
+ pw.println(" Lists the current intervention configs of an app.");
+ pw.println(" list-modes <PACKAGE_NAME>");
+ pw.println(" Lists the current available game modes of an app.");
+ pw.println(" mode [--user <USER_ID>] [1|2|3|4|standard|performance|battery|custom] "
+ + "<PACKAGE_NAME>");
pw.println(" Set app to run in the specified game mode, if supported.");
pw.println(" --user <USER_ID>: apply for the given user,");
pw.println(" the current user is used when unspecified.");
- pw.println(" set --mode [2|3|performance|battery] [intervention configs] <PACKAGE_NAME>");
- pw.println(" Set app to run at given game mode with configs, if supported.");
+ pw.println(" set [intervention configs] <PACKAGE_NAME>");
+ pw.println(" Set app to run at custom mode using provided intervention configs");
pw.println(" Intervention configs consists of:");
pw.println(" --downscale [0.3|0.35|0.4|0.45|0.5|0.55|0.6|0.65");
- pw.println(" |0.7|0.75|0.8|0.85|0.9|disable]");
- pw.println(" Set app to run at the specified scaling ratio.");
- pw.println(" --fps [30|45|60|90|120|disable]");
- pw.println(" Set app to run at the specified fps, if supported.");
+ pw.println(" |0.7|0.75|0.8|0.85|0.9|disable]: Set app to run at the");
+ pw.println(" specified scaling ratio.");
+ pw.println(" --fps [30|45|60|90|120|disable]: Set app to run at the specified fps,");
+ pw.println(" if supported.");
pw.println(" reset [--mode [2|3|performance|battery] --user <USER_ID>] <PACKAGE_NAME>");
pw.println(" Resets the game mode of the app to device configuration.");
+ pw.println(" This should only be used to reset any override to non custom game mode");
+ pw.println(" applied using the deprecated `set` command");
pw.println(" --mode [2|3|performance|battery]: apply for the given mode,");
pw.println(" resets all modes when unspecified.");
pw.println(" --user <USER_ID>: apply for the given user,");
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index 2f147c4252cd..cb409fef6fa2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -278,13 +278,6 @@ public class FaceService extends SystemService {
return -1;
}
- if (!Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)) {
- // If this happens, something in KeyguardUpdateMonitor is wrong. This should only
- // ever be invoked when the user is encrypted or lockdown.
- Slog.e(TAG, "detectFace invoked when user is not encrypted or lockdown");
- return -1;
- }
-
final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for detectFace");
diff --git a/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java b/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java
index a8e4034e3f86..408fba1bff3b 100644
--- a/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java
+++ b/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java
@@ -27,6 +27,7 @@ import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.broadcastradio.hal2.AnnouncementAggregator;
@@ -51,15 +52,17 @@ final class IRadioServiceHidlImpl extends IRadioService.Stub {
private final Object mLock = new Object();
private final BroadcastRadioService mService;
+
+ @GuardedBy("mLock")
private final List<RadioManager.ModuleProperties> mV1Modules;
IRadioServiceHidlImpl(BroadcastRadioService service) {
mService = Objects.requireNonNull(service, "broadcast radio service cannot be null");
- mHal1 = new com.android.server.broadcastradio.hal1.BroadcastRadioService(mLock);
+ mHal1 = new com.android.server.broadcastradio.hal1.BroadcastRadioService();
mV1Modules = mHal1.loadModules();
OptionalInt max = mV1Modules.stream().mapToInt(RadioManager.ModuleProperties::getId).max();
mHal2 = new com.android.server.broadcastradio.hal2.BroadcastRadioService(
- max.isPresent() ? max.getAsInt() + 1 : 0, mLock);
+ max.isPresent() ? max.getAsInt() + 1 : 0);
}
@VisibleForTesting
@@ -78,9 +81,11 @@ final class IRadioServiceHidlImpl extends IRadioService.Stub {
public List<RadioManager.ModuleProperties> listModules() {
mService.enforcePolicyAccess();
Collection<RadioManager.ModuleProperties> v2Modules = mHal2.listModules();
- List<RadioManager.ModuleProperties> modules = new ArrayList<>(
- mV1Modules.size() + v2Modules.size());
- modules.addAll(mV1Modules);
+ List<RadioManager.ModuleProperties> modules;
+ synchronized (mLock) {
+ modules = new ArrayList<>(mV1Modules.size() + v2Modules.size());
+ modules.addAll(mV1Modules);
+ }
modules.addAll(v2Modules);
return modules;
}
@@ -131,7 +136,9 @@ final class IRadioServiceHidlImpl extends IRadioService.Stub {
radioPw.printf("HAL1: %s\n", mHal1);
radioPw.increaseIndent();
- radioPw.printf("Modules of HAL1: %s\n", mV1Modules);
+ synchronized (mLock) {
+ radioPw.printf("Modules of HAL1: %s\n", mV1Modules);
+ }
radioPw.decreaseIndent();
radioPw.printf("HAL2:\n");
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java b/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java
index 1d7112133b48..03acf72725e7 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java
@@ -77,7 +77,7 @@ public final class BroadcastRadioServiceImpl {
}
RadioModule radioModule =
- RadioModule.tryLoadingModule(moduleId, name, newBinder, mLock);
+ RadioModule.tryLoadingModule(moduleId, name, newBinder);
if (radioModule == null) {
Slogf.w(TAG, "No module %s with id %d (HAL AIDL)", name, moduleId);
return;
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
index eb9dafbe5281..e956a9c2038c 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
@@ -55,7 +55,7 @@ final class RadioModule {
private final IBroadcastRadio mService;
- private final Object mLock;
+ private final Object mLock = new Object();
private final Handler mHandler;
private final RadioLogger mLogger;
private final RadioManager.ModuleProperties mProperties;
@@ -165,18 +165,15 @@ final class RadioModule {
};
@VisibleForTesting
- RadioModule(IBroadcastRadio service,
- RadioManager.ModuleProperties properties, Object lock) {
+ RadioModule(IBroadcastRadio service, RadioManager.ModuleProperties properties) {
mProperties = Objects.requireNonNull(properties, "properties cannot be null");
mService = Objects.requireNonNull(service, "service cannot be null");
- mLock = Objects.requireNonNull(lock, "lock cannot be null");
mHandler = new Handler(Looper.getMainLooper());
mLogger = new RadioLogger(TAG, RADIO_EVENT_LOGGER_QUEUE_SIZE);
}
@Nullable
- static RadioModule tryLoadingModule(int moduleId, String moduleName,
- IBinder serviceBinder, Object lock) {
+ static RadioModule tryLoadingModule(int moduleId, String moduleName, IBinder serviceBinder) {
try {
Slogf.i(TAG, "Try loading module for module id = %d, module name = %s",
moduleId, moduleName);
@@ -206,7 +203,7 @@ final class RadioModule {
RadioManager.ModuleProperties prop = ConversionUtils.propertiesFromHalProperties(
moduleId, moduleName, service.getProperties(), amfmConfig, dabConfig);
- return new RadioModule(service, prop, lock);
+ return new RadioModule(service, prop);
} catch (RemoteException ex) {
Slogf.e(TAG, ex, "Failed to load module %s", moduleName);
return null;
@@ -222,9 +219,7 @@ final class RadioModule {
}
void setInternalHalCallback() throws RemoteException {
- synchronized (mLock) {
- mService.setTunerCallback(mHalTunerCallback);
- }
+ mService.setTunerCallback(mHalTunerCallback);
}
TunerSession openSession(android.hardware.radio.ITunerCallback userCb)
@@ -234,7 +229,7 @@ final class RadioModule {
Boolean antennaConnected;
RadioManager.ProgramInfo currentProgramInfo;
synchronized (mLock) {
- tunerSession = new TunerSession(this, mService, userCb, mLock);
+ tunerSession = new TunerSession(this, mService, userCb);
mAidlTunerSessions.add(tunerSession);
antennaConnected = mAntennaConnected;
currentProgramInfo = mCurrentProgramInfo;
@@ -356,14 +351,14 @@ final class RadioModule {
// Otherwise, update the HAL's filter, and AIDL clients will be updated when
// mHalTunerCallback.onProgramListUpdated() is called.
mUnionOfAidlProgramFilters = newFilter;
- try {
- mService.startProgramListUpdates(
- ConversionUtils.filterToHalProgramFilter(newFilter));
- } catch (RuntimeException ex) {
- throw ConversionUtils.throwOnError(ex, /* action= */ "Start Program ListUpdates");
- } catch (RemoteException ex) {
- Slogf.e(TAG, ex, "mHalTunerSession.startProgramListUpdates() failed");
- }
+ }
+ try {
+ mService.startProgramListUpdates(
+ ConversionUtils.filterToHalProgramFilter(newFilter));
+ } catch (RuntimeException ex) {
+ throw ConversionUtils.throwOnError(ex, /* action= */ "Start Program ListUpdates");
+ } catch (RemoteException ex) {
+ Slogf.e(TAG, ex, "mHalTunerSession.startProgramListUpdates() failed");
}
}
@@ -453,12 +448,10 @@ final class RadioModule {
}
};
- synchronized (mLock) {
- try {
- hwCloseHandle[0] = mService.registerAnnouncementListener(hwListener, enabledList);
- } catch (RuntimeException ex) {
- throw ConversionUtils.throwOnError(ex, /* action= */ "AnnouncementListener");
- }
+ try {
+ hwCloseHandle[0] = mService.registerAnnouncementListener(hwListener, enabledList);
+ } catch (RuntimeException ex) {
+ throw ConversionUtils.throwOnError(ex, /* action= */ "AnnouncementListener");
}
return new android.hardware.radio.ICloseHandle.Stub() {
@@ -478,12 +471,10 @@ final class RadioModule {
if (id == 0) throw new IllegalArgumentException("Image ID is missing");
byte[] rawImage;
- synchronized (mLock) {
- try {
- rawImage = mService.getImage(id);
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
+ try {
+ rawImage = mService.getImage(id);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
}
if (rawImage == null || rawImage.length == 0) return null;
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java b/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java
index d33633c435b1..1ce4044d4c51 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java
@@ -42,7 +42,7 @@ final class TunerSession extends ITuner.Stub {
private static final String TAG = "BcRadioAidlSrv.session";
private static final int TUNER_EVENT_LOGGER_QUEUE_SIZE = 25;
- private final Object mLock;
+ private final Object mLock = new Object();
private final RadioLogger mLogger;
private final RadioModule mModule;
@@ -61,12 +61,10 @@ final class TunerSession extends ITuner.Stub {
private RadioManager.BandConfig mPlaceHolderConfig;
TunerSession(RadioModule radioModule, IBroadcastRadio service,
- android.hardware.radio.ITunerCallback callback,
- Object lock) {
+ android.hardware.radio.ITunerCallback callback) {
mModule = Objects.requireNonNull(radioModule, "radioModule cannot be null");
mService = Objects.requireNonNull(service, "service cannot be null");
mCallback = Objects.requireNonNull(callback, "callback cannot be null");
- mLock = Objects.requireNonNull(lock, "lock cannot be null");
mLogger = new RadioLogger(TAG, TUNER_EVENT_LOGGER_QUEUE_SIZE);
}
@@ -91,17 +89,19 @@ final class TunerSession extends ITuner.Stub {
mLogger.logRadioEvent("Close tuner session on error %d", error);
}
synchronized (mLock) {
- if (mIsClosed) return;
- if (error != null) {
- try {
- mCallback.onError(error);
- } catch (RemoteException ex) {
- Slogf.w(TAG, ex, "mCallback.onError(%s) failed", error);
- }
+ if (mIsClosed) {
+ return;
}
mIsClosed = true;
- mModule.onTunerSessionClosed(this);
}
+ if (error != null) {
+ try {
+ mCallback.onError(error);
+ } catch (RemoteException ex) {
+ Slogf.w(TAG, ex, "mCallback.onError(%s) failed", error);
+ }
+ }
+ mModule.onTunerSessionClosed(this);
}
@Override
diff --git a/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java
index e50c6e8c21b8..fb42c94b56f4 100644
--- a/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java
@@ -36,7 +36,7 @@ public class BroadcastRadioService {
*/
private final long mNativeContext = nativeInit();
- private final Object mLock;
+ private final Object mLock = new Object();
@Override
protected void finalize() throws Throwable {
@@ -50,14 +50,6 @@ public class BroadcastRadioService {
private native Tuner nativeOpenTuner(long nativeContext, int moduleId,
RadioManager.BandConfig config, boolean withAudio, ITunerCallback callback);
- /**
- * Constructor. should pass
- * {@code com.android.server.broadcastradio.BroadcastRadioService#mLock} for lock.
- */
- public BroadcastRadioService(Object lock) {
- mLock = lock;
- }
-
public @NonNull List<RadioManager.ModuleProperties> loadModules() {
synchronized (mLock) {
return Objects.requireNonNull(nativeLoadModules(mNativeContext));
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
index 3d6962783f4a..984bf5125582 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
@@ -43,13 +43,16 @@ import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
-public class BroadcastRadioService {
+/**
+ * Broadcast radio service using BroadcastRadio HIDL 2.0 HAL
+ */
+public final class BroadcastRadioService {
private static final String TAG = "BcRadio2Srv";
- private final Object mLock;
+ private final Object mLock = new Object();
@GuardedBy("mLock")
- private int mNextModuleId = 0;
+ private int mNextModuleId;
@GuardedBy("mLock")
private final Map<String, Integer> mServiceNameToModuleIdMap = new HashMap<>();
@@ -72,7 +75,7 @@ public class BroadcastRadioService {
moduleId = mNextModuleId;
}
- RadioModule module = RadioModule.tryLoadingModule(moduleId, serviceName, mLock);
+ RadioModule module = RadioModule.tryLoadingModule(moduleId, serviceName);
if (module == null) {
return;
}
@@ -120,9 +123,8 @@ public class BroadcastRadioService {
}
};
- public BroadcastRadioService(int nextModuleId, Object lock) {
+ public BroadcastRadioService(int nextModuleId) {
mNextModuleId = nextModuleId;
- mLock = lock;
try {
IServiceManager manager = IServiceManager.getService();
if (manager == null) {
@@ -136,9 +138,8 @@ public class BroadcastRadioService {
}
@VisibleForTesting
- BroadcastRadioService(int nextModuleId, Object lock, IServiceManager manager) {
+ BroadcastRadioService(int nextModuleId, IServiceManager manager) {
mNextModuleId = nextModuleId;
- mLock = lock;
Objects.requireNonNull(manager, "Service manager cannot be null");
try {
manager.registerForNotifications(IBroadcastRadio.kInterfaceName, "", mServiceListener);
@@ -180,7 +181,7 @@ public class BroadcastRadioService {
throw new IllegalArgumentException("Non-audio sessions not supported with HAL 2.0");
}
- RadioModule module = null;
+ RadioModule module;
synchronized (mLock) {
module = mModules.get(moduleId);
if (module == null) {
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
index cf1b504037bc..0ea5f0fc1d6a 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
@@ -60,7 +60,7 @@ final class RadioModule {
@NonNull private final IBroadcastRadio mService;
@NonNull private final RadioManager.ModuleProperties mProperties;
- private final Object mLock;
+ private final Object mLock = new Object();
@NonNull private final Handler mHandler;
@NonNull private final RadioEventLogger mEventLogger;
@@ -75,7 +75,7 @@ final class RadioModule {
private RadioManager.ProgramInfo mCurrentProgramInfo = null;
@GuardedBy("mLock")
- private final ProgramInfoCache mProgramInfoCache = new ProgramInfoCache(null);
+ private final ProgramInfoCache mProgramInfoCache = new ProgramInfoCache(/* filter= */ null);
@GuardedBy("mLock")
private android.hardware.radio.ProgramList.Filter mUnionOfAidlProgramFilters = null;
@@ -84,47 +84,59 @@ final class RadioModule {
private final ITunerCallback mHalTunerCallback = new ITunerCallback.Stub() {
@Override
public void onTuneFailed(int result, ProgramSelector programSelector) {
- lockAndFireLater(() -> {
+ fireLater(() -> {
android.hardware.radio.ProgramSelector csel =
Convert.programSelectorFromHal(programSelector);
- fanoutAidlCallbackLocked(cb -> cb.onTuneFailed(result, csel));
+ synchronized (mLock) {
+ fanoutAidlCallbackLocked(cb -> cb.onTuneFailed(result, csel));
+ }
});
}
@Override
public void onCurrentProgramInfoChanged(ProgramInfo halProgramInfo) {
- lockAndFireLater(() -> {
- mCurrentProgramInfo = Convert.programInfoFromHal(halProgramInfo);
- fanoutAidlCallbackLocked(cb -> cb.onCurrentProgramInfoChanged(mCurrentProgramInfo));
+ fireLater(() -> {
+ synchronized (mLock) {
+ mCurrentProgramInfo = Convert.programInfoFromHal(halProgramInfo);
+ RadioManager.ProgramInfo currentProgramInfo = mCurrentProgramInfo;
+ fanoutAidlCallbackLocked(cb -> cb.onCurrentProgramInfoChanged(
+ currentProgramInfo));
+ }
});
}
@Override
public void onProgramListUpdated(ProgramListChunk programListChunk) {
- lockAndFireLater(() -> {
+ fireLater(() -> {
android.hardware.radio.ProgramList.Chunk chunk =
Convert.programListChunkFromHal(programListChunk);
- mProgramInfoCache.filterAndApplyChunk(chunk);
+ synchronized (mLock) {
+ mProgramInfoCache.filterAndApplyChunk(chunk);
- for (TunerSession tunerSession : mAidlTunerSessions) {
- tunerSession.onMergedProgramListUpdateFromHal(chunk);
+ for (TunerSession tunerSession : mAidlTunerSessions) {
+ tunerSession.onMergedProgramListUpdateFromHal(chunk);
+ }
}
});
}
@Override
public void onAntennaStateChange(boolean connected) {
- lockAndFireLater(() -> {
- mAntennaConnected = connected;
- fanoutAidlCallbackLocked(cb -> cb.onAntennaState(connected));
+ fireLater(() -> {
+ synchronized (mLock) {
+ mAntennaConnected = connected;
+ fanoutAidlCallbackLocked(cb -> cb.onAntennaState(connected));
+ }
});
}
@Override
public void onParametersUpdated(ArrayList<VendorKeyValue> parameters) {
- lockAndFireLater(() -> {
+ fireLater(() -> {
Map<String, String> cparam = Convert.vendorInfoFromHal(parameters);
- fanoutAidlCallbackLocked(cb -> cb.onParametersUpdated(cparam));
+ synchronized (mLock) {
+ fanoutAidlCallbackLocked(cb -> cb.onParametersUpdated(cparam));
+ }
});
}
};
@@ -135,17 +147,15 @@ final class RadioModule {
@VisibleForTesting
RadioModule(@NonNull IBroadcastRadio service,
- @NonNull RadioManager.ModuleProperties properties, @NonNull Object lock) {
+ @NonNull RadioManager.ModuleProperties properties) {
mProperties = Objects.requireNonNull(properties);
mService = Objects.requireNonNull(service);
- mLock = Objects.requireNonNull(lock);
mHandler = new Handler(Looper.getMainLooper());
mEventLogger = new RadioEventLogger(TAG, RADIO_EVENT_LOGGER_QUEUE_SIZE);
}
@Nullable
- static RadioModule tryLoadingModule(int idx, @NonNull String fqName,
- Object lock) {
+ static RadioModule tryLoadingModule(int idx, @NonNull String fqName) {
try {
Slog.i(TAG, "Try loading module for idx " + idx + ", fqName " + fqName);
IBroadcastRadio service = IBroadcastRadio.getService(fqName);
@@ -167,7 +177,7 @@ final class RadioModule {
RadioManager.ModuleProperties prop = Convert.propertiesFromHal(idx, fqName,
service.getProperties(), amfmConfig.value, dabConfig.value);
- return new RadioModule(service, prop, lock);
+ return new RadioModule(service, prop);
} catch (RemoteException ex) {
Slog.e(TAG, "Failed to load module " + fqName, ex);
return null;
@@ -196,8 +206,7 @@ final class RadioModule {
});
mHalTunerSession = Objects.requireNonNull(hwSession.value);
}
- TunerSession tunerSession = new TunerSession(this, mHalTunerSession, userCb,
- mLock);
+ TunerSession tunerSession = new TunerSession(this, mHalTunerSession, userCb);
mAidlTunerSessions.add(tunerSession);
// Propagate state to new client. Note: These callbacks are invoked while holding mLock
@@ -229,6 +238,7 @@ final class RadioModule {
}
}
+ @GuardedBy("mLock")
@Nullable
private android.hardware.radio.ProgramList.Filter
buildUnionOfTunerSessionFiltersLocked() {
@@ -281,6 +291,7 @@ final class RadioModule {
}
}
+ @GuardedBy("mLock")
private void onTunerSessionProgramListFilterChangedLocked(@Nullable TunerSession session) {
android.hardware.radio.ProgramList.Filter newFilter =
buildUnionOfTunerSessionFiltersLocked();
@@ -325,6 +336,7 @@ final class RadioModule {
}
}
+ @GuardedBy("mLock")
private void onTunerSessionsClosedLocked(TunerSession... tunerSessions) {
for (TunerSession tunerSession : tunerSessions) {
mAidlTunerSessions.remove(tunerSession);
@@ -342,12 +354,8 @@ final class RadioModule {
}
// add to mHandler queue, but ensure the runnable holds mLock when it gets executed
- private void lockAndFireLater(Runnable r) {
- mHandler.post(() -> {
- synchronized (mLock) {
- r.run();
- }
- });
+ private void fireLater(Runnable r) {
+ mHandler.post(() -> r.run());
}
interface AidlCallbackRunnable {
@@ -356,9 +364,14 @@ final class RadioModule {
// Invokes runnable with each TunerSession currently open.
void fanoutAidlCallback(AidlCallbackRunnable runnable) {
- lockAndFireLater(() -> fanoutAidlCallbackLocked(runnable));
+ fireLater(() -> {
+ synchronized (mLock) {
+ fanoutAidlCallbackLocked(runnable);
+ }
+ });
}
+ @GuardedBy("mLock")
private void fanoutAidlCallbackLocked(AidlCallbackRunnable runnable) {
List<TunerSession> deadSessions = null;
for (TunerSession tunerSession : mAidlTunerSessions) {
@@ -399,12 +412,10 @@ final class RadioModule {
}
};
- synchronized (mLock) {
- mService.registerAnnouncementListener(enabledList, hwListener, (result, closeHnd) -> {
- halResult.value = result;
- hwCloseHandle.value = closeHnd;
- });
- }
+ mService.registerAnnouncementListener(enabledList, hwListener, (result, closeHandle) -> {
+ halResult.value = result;
+ hwCloseHandle.value = closeHandle;
+ });
Convert.throwOnError("addAnnouncementListener", halResult.value);
return new android.hardware.radio.ICloseHandle.Stub() {
@@ -424,12 +435,10 @@ final class RadioModule {
if (id == 0) throw new IllegalArgumentException("Image ID is missing");
byte[] rawImage;
- synchronized (mLock) {
- List<Byte> rawList = Utils.maybeRethrow(() -> mService.getImage(id));
- rawImage = new byte[rawList.size()];
- for (int i = 0; i < rawList.size(); i++) {
- rawImage[i] = rawList.get(i);
- }
+ List<Byte> rawList = Utils.maybeRethrow(() -> mService.getImage(id));
+ rawImage = new byte[rawList.size()];
+ for (int i = 0; i < rawList.size(); i++) {
+ rawImage[i] = rawList.get(i);
}
if (rawImage == null || rawImage.length == 0) return null;
@@ -440,17 +449,17 @@ final class RadioModule {
void dumpInfo(IndentingPrintWriter pw) {
pw.printf("RadioModule\n");
pw.increaseIndent();
+ pw.printf("BroadcastRadioService: %s\n", mService);
+ pw.printf("Properties: %s\n", mProperties);
synchronized (mLock) {
- pw.printf("BroadcastRadioService: %s\n", mService);
- pw.printf("Properties: %s\n", mProperties);
- pw.printf("HIDL2.0 HAL TunerSession: %s\n", mHalTunerSession);
+ pw.printf("HIDL 2.0 HAL TunerSession: %s\n", mHalTunerSession);
pw.printf("Is antenna connected? ");
if (mAntennaConnected == null) {
pw.printf("null\n");
} else {
pw.printf("%s\n", mAntennaConnected ? "Yes" : "No");
}
- pw.printf("current ProgramInfo: %s\n", mCurrentProgramInfo);
+ pw.printf("Current ProgramInfo: %s\n", mCurrentProgramInfo);
pw.printf("ProgramInfoCache: %s\n", mProgramInfoCache);
pw.printf("Union of AIDL ProgramFilters: %s\n", mUnionOfAidlProgramFilters);
pw.printf("AIDL TunerSessions:\n");
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
index 12211eed47fd..7afee277fe1c 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
@@ -33,6 +33,7 @@ import android.util.MutableBoolean;
import android.util.MutableInt;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
import com.android.server.broadcastradio.RadioServiceUserController;
import com.android.server.utils.Slogf;
@@ -46,26 +47,28 @@ class TunerSession extends ITuner.Stub {
private static final String kAudioDeviceName = "Radio tuner source";
private static final int TUNER_EVENT_LOGGER_QUEUE_SIZE = 25;
- private final Object mLock;
+ private final Object mLock = new Object();
@NonNull private final RadioEventLogger mEventLogger;
private final RadioModule mModule;
private final ITunerSession mHwSession;
final android.hardware.radio.ITunerCallback mCallback;
+
+ @GuardedBy("mLock")
private boolean mIsClosed = false;
+ @GuardedBy("mLock")
private boolean mIsMuted = false;
+ @GuardedBy("mLock")
private ProgramInfoCache mProgramInfoCache = null;
// necessary only for older APIs compatibility
private RadioManager.BandConfig mDummyConfig = null;
TunerSession(@NonNull RadioModule module, @NonNull ITunerSession hwSession,
- @NonNull android.hardware.radio.ITunerCallback callback,
- @NonNull Object lock) {
+ @NonNull android.hardware.radio.ITunerCallback callback) {
mModule = Objects.requireNonNull(module);
mHwSession = Objects.requireNonNull(hwSession);
mCallback = Objects.requireNonNull(callback);
- mLock = Objects.requireNonNull(lock);
mEventLogger = new RadioEventLogger(TAG, TUNER_EVENT_LOGGER_QUEUE_SIZE);
}
@@ -86,23 +89,26 @@ class TunerSession extends ITuner.Stub {
mEventLogger.logRadioEvent("Close on error %d", error);
synchronized (mLock) {
if (mIsClosed) return;
- if (error != null) {
- try {
- mCallback.onError(error);
- } catch (RemoteException ex) {
- Slog.w(TAG, "mCallback.onError() failed: ", ex);
- }
- }
mIsClosed = true;
- mModule.onTunerSessionClosed(this);
}
+ if (error != null) {
+ try {
+ mCallback.onError(error);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "mCallback.onError() failed: ", ex);
+ }
+ }
+ mModule.onTunerSessionClosed(this);
}
@Override
public boolean isClosed() {
- return mIsClosed;
+ synchronized (mLock) {
+ return mIsClosed;
+ }
}
+ @GuardedBy("mLock")
private void checkNotClosedLocked() {
if (mIsClosed) {
throw new IllegalStateException("Tuner is closed, no further operations are allowed");
@@ -118,9 +124,9 @@ class TunerSession extends ITuner.Stub {
synchronized (mLock) {
checkNotClosedLocked();
mDummyConfig = Objects.requireNonNull(config);
- Slog.i(TAG, "Ignoring setConfiguration - not applicable for broadcastradio HAL 2.0");
- mModule.fanoutAidlCallback(cb -> cb.onConfigurationChanged(config));
}
+ Slog.i(TAG, "Ignoring setConfiguration - not applicable for broadcastradio HAL 2.0");
+ mModule.fanoutAidlCallback(cb -> cb.onConfigurationChanged(config));
}
@Override
@@ -137,8 +143,8 @@ class TunerSession extends ITuner.Stub {
checkNotClosedLocked();
if (mIsMuted == mute) return;
mIsMuted = mute;
- Slog.w(TAG, "Mute via RadioService is not implemented - please handle it via app");
}
+ Slog.w(TAG, "Mute via RadioService is not implemented - please handle it via app");
}
@Override
@@ -383,8 +389,8 @@ class TunerSession extends ITuner.Stub {
void dumpInfo(IndentingPrintWriter pw) {
pw.printf("TunerSession\n");
pw.increaseIndent();
+ pw.printf("HIDL HAL Session: %s\n", mHwSession);
synchronized (mLock) {
- pw.printf("HIDL HAL Session: %s\n", mHwSession);
pw.printf("Is session closed? %s\n", mIsClosed ? "Yes" : "No");
pw.printf("Is muted? %s\n", mIsMuted ? "Yes" : "No");
pw.printf("ProgramInfoCache: %s\n", mProgramInfoCache);
diff --git a/services/core/java/com/android/server/cpu/CpuAvailabilityInfo.java b/services/core/java/com/android/server/cpu/CpuAvailabilityInfo.java
new file mode 100644
index 000000000000..06b45bf0fb4b
--- /dev/null
+++ b/services/core/java/com/android/server/cpu/CpuAvailabilityInfo.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.cpu;
+
+import static com.android.server.cpu.CpuAvailabilityMonitoringConfig.CPUSET_ALL;
+import static com.android.server.cpu.CpuAvailabilityMonitoringConfig.CPUSET_BACKGROUND;
+
+import com.android.internal.util.Preconditions;
+
+/** CPU availability information. */
+public final class CpuAvailabilityInfo {
+ /** Constant to indicate missing CPU availability percent. */
+ public static final int MISSING_CPU_AVAILABILITY_PERCENT = -1;
+
+ /**
+ * The CPUSET whose availability info is recorded in this object.
+ *
+ * <p>The contained value is one of the CPUSET_* constants from the
+ * {@link CpuAvailabilityMonitoringConfig}.
+ */
+ @CpuAvailabilityMonitoringConfig.Cpuset
+ public final int cpuset;
+
+ /** The latest average CPU availability percent. */
+ public final int latestAvgAvailabilityPercent;
+
+ /** The past N-second average CPU availability percent. */
+ public final int pastNSecAvgAvailabilityPercent;
+
+ /** The duration over which the {@link pastNSecAvgAvailabilityPercent} was calculated. */
+ public final int avgAvailabilityDurationSec;
+
+ @Override
+ public String toString() {
+ return "CpuAvailabilityInfo{" + "cpuset=" + cpuset + ", latestAvgAvailabilityPercent="
+ + latestAvgAvailabilityPercent + ", pastNSecAvgAvailabilityPercent="
+ + pastNSecAvgAvailabilityPercent + ", avgAvailabilityDurationSec="
+ + avgAvailabilityDurationSec + '}';
+ }
+
+ CpuAvailabilityInfo(int cpuset, int latestAvgAvailabilityPercent,
+ int pastNSecAvgAvailabilityPercent, int avgAvailabilityDurationSec) {
+ this.cpuset = Preconditions.checkArgumentInRange(cpuset, CPUSET_ALL, CPUSET_BACKGROUND,
+ "cpuset");
+ this.latestAvgAvailabilityPercent = latestAvgAvailabilityPercent;
+ this.pastNSecAvgAvailabilityPercent = pastNSecAvgAvailabilityPercent;
+ this.avgAvailabilityDurationSec = avgAvailabilityDurationSec;
+ }
+}
diff --git a/services/core/java/com/android/server/cpu/CpuAvailabilityMonitoringConfig.java b/services/core/java/com/android/server/cpu/CpuAvailabilityMonitoringConfig.java
new file mode 100644
index 000000000000..a3c4c9e828b4
--- /dev/null
+++ b/services/core/java/com/android/server/cpu/CpuAvailabilityMonitoringConfig.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.cpu;
+
+import android.annotation.IntDef;
+import android.util.IntArray;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** CPU availability monitoring config. */
+public final class CpuAvailabilityMonitoringConfig {
+ /** Constant to monitor all cpusets. */
+ public static final int CPUSET_ALL = 1;
+
+ /** Constant to monitor background cpusets. */
+ public static final int CPUSET_BACKGROUND = 2;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"CPUSET_"}, value = {
+ CPUSET_ALL,
+ CPUSET_BACKGROUND
+ })
+ public @interface Cpuset {
+ }
+
+ /**
+ * The CPUSET to monitor.
+ *
+ * <p>The value must be one of the {@code CPUSET_*} constants.
+ */
+ @Cpuset
+ public final int cpuset;
+
+ /**
+ * CPU availability percent thresholds.
+ *
+ * <p>CPU availability change notifications are sent when the latest or last N seconds average
+ * CPU availability percent crosses any of these thresholds since the last notification.
+ */
+ private final IntArray mThresholds;
+
+ public IntArray getThresholds() {
+ return mThresholds;
+ }
+
+ /**
+ * Builder for the construction of {@link CpuAvailabilityMonitoringConfig} objects.
+ *
+ * <p>The builder must contain at least one threshold before calling {@link build}.
+ */
+ public static final class Builder {
+ private final int mCpuset;
+ private final IntArray mThresholds = new IntArray();
+
+ public Builder(int cpuset, int... thresholds) {
+ mCpuset = cpuset;
+ for (int threshold : thresholds) {
+ addThreshold(threshold);
+ }
+ }
+
+ /** Adds the given threshold to the builder object. */
+ public Builder addThreshold(int threshold) {
+ if (mThresholds.indexOf(threshold) == -1) {
+ mThresholds.add(threshold);
+ }
+ return this;
+ }
+
+ /** Returns the {@link CpuAvailabilityMonitoringConfig} object. */
+ public CpuAvailabilityMonitoringConfig build() {
+ return new CpuAvailabilityMonitoringConfig(this);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "CpuAvailabilityMonitoringConfig{cpuset=" + cpuset + ", mThresholds=" + mThresholds
+ + ')';
+ }
+
+ private CpuAvailabilityMonitoringConfig(Builder builder) {
+ if (builder.mCpuset != CPUSET_ALL && builder.mCpuset != CPUSET_BACKGROUND) {
+ throw new IllegalStateException("Cpuset must be either CPUSET_ALL (" + CPUSET_ALL
+ + ") or CPUSET_BACKGROUND (" + CPUSET_BACKGROUND + "). Builder contains "
+ + builder.mCpuset);
+ }
+ if (builder.mThresholds.size() == 0) {
+ throw new IllegalStateException("Must provide at least one threshold");
+ }
+ this.cpuset = builder.mCpuset;
+ this.mThresholds = builder.mThresholds.clone();
+ }
+}
diff --git a/services/core/java/com/android/server/cpu/CpuInfoReader.java b/services/core/java/com/android/server/cpu/CpuInfoReader.java
new file mode 100644
index 000000000000..680829d2bfc3
--- /dev/null
+++ b/services/core/java/com/android/server/cpu/CpuInfoReader.java
@@ -0,0 +1,453 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.cpu;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.system.Os;
+import android.system.OsConstants;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.utils.Slogf;
+
+import java.io.File;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/** Reader to read CPU information from proc and sys fs files exposed by the Kernel. */
+public final class CpuInfoReader {
+ static final String TAG = CpuInfoReader.class.getSimpleName();
+ static final int FLAG_CPUSET_CATEGORY_TOP_APP = 1 << 0;
+ static final int FLAG_CPUSET_CATEGORY_BACKGROUND = 1 << 1;
+
+ private static final String CPUFREQ_DIR_PATH = "/sys/devices/system/cpu/cpufreq";
+ private static final String POLICY_DIR_PREFIX = "policy";
+ private static final String RELATED_CPUS_FILE = "related_cpus";
+ private static final String MAX_CPUFREQ_FILE = "cpuinfo_max_freq";
+ private static final String MAX_SCALING_FREQ_FILE = "scaling_max_freq";
+ private static final String CPUSET_DIR_PATH = "/dev/cpuset";
+ private static final String CPUSET_TOP_APP_DIR = "top-app";
+ private static final String CPUSET_BACKGROUND_DIR = "background";
+ private static final String CPUS_FILE = "cpus";
+ private static final String PROC_STAT_FILE_PATH = "/proc/stat";
+ private static final Pattern PROC_STAT_PATTERN =
+ Pattern.compile("cpu(?<core>[0-9]+)\\s(?<userClockTicks>[0-9]+)\\s"
+ + "(?<niceClockTicks>[0-9]+)\\s(?<sysClockTicks>[0-9]+)\\s"
+ + "(?<idleClockTicks>[0-9]+)\\s(?<iowaitClockTicks>[0-9]+)\\s"
+ + "(?<irqClockTicks>[0-9]+)\\s(?<softirqClockTicks>[0-9]+)\\s"
+ + "(?<stealClockTicks>[0-9]+)\\s(?<guestClockTicks>[0-9]+)\\s"
+ + "(?<guestNiceClockTicks>[0-9]+)");
+ private static final long MILLIS_PER_JIFFY = 1000L / Os.sysconf(OsConstants._SC_CLK_TCK);
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"FLAG_CPUSET_CATEGORY_"}, flag = true, value = {
+ FLAG_CPUSET_CATEGORY_TOP_APP,
+ FLAG_CPUSET_CATEGORY_BACKGROUND
+ })
+ private @interface CpusetCategory{}
+
+ private final File mCpusetDir;
+ private final File mCpuFreqDir;
+ private final File mProcStatFile;
+ private final SparseIntArray mCpusetCategoriesByCpus = new SparseIntArray();
+ private final SparseArray<Long> mMaxCpuFrequenciesByCpus = new SparseArray<>();
+
+ private File[] mCpuFreqPolicyDirs;
+ private SparseArray<CpuUsageStats> mCumulativeCpuUsageStats = new SparseArray<>();
+ private boolean mIsEnabled;
+
+ public CpuInfoReader() {
+ this(new File(CPUSET_DIR_PATH), new File(CPUFREQ_DIR_PATH), new File(PROC_STAT_FILE_PATH));
+ }
+
+ @VisibleForTesting
+ CpuInfoReader(File cpusetDir, File cpuFreqDir, File procStatFile) {
+ mCpusetDir = cpusetDir;
+ mCpuFreqDir = cpuFreqDir;
+ mProcStatFile = procStatFile;
+ }
+
+ /** Inits CpuInfoReader and returns a boolean to indicate whether the reader is enabled. */
+ public boolean init() {
+ mCpuFreqPolicyDirs = mCpuFreqDir.listFiles(
+ file -> file.isDirectory() && file.getName().startsWith(POLICY_DIR_PREFIX));
+ if (mCpuFreqPolicyDirs == null || mCpuFreqPolicyDirs.length == 0) {
+ Slogf.w(TAG, "Missing CPU frequency policy directories at %s",
+ mCpuFreqDir.getAbsolutePath());
+ return false;
+ }
+ if (!mProcStatFile.exists()) {
+ Slogf.e(TAG, "Missing proc stat file at %s", mProcStatFile.getAbsolutePath());
+ return false;
+ }
+ readCpusetCategories();
+ if (mCpusetCategoriesByCpus.size() == 0) {
+ Slogf.e(TAG, "Failed to read cpuset information read from %s",
+ mCpusetDir.getAbsolutePath());
+ return false;
+ }
+ readMaxCpuFrequencies();
+ if (mMaxCpuFrequenciesByCpus.size() == 0) {
+ Slogf.e(TAG, "Failed to read max CPU frequencies from policy directories at %s",
+ mCpuFreqDir.getAbsolutePath());
+ return false;
+ }
+ mIsEnabled = true;
+ return true;
+ }
+
+ /** Reads CPU information from proc and sys fs files exposed by the Kernel. */
+ public List<CpuInfo> readCpuInfos() {
+ if (!mIsEnabled) {
+ return Collections.emptyList();
+ }
+ SparseArray<CpuUsageStats> latestCpuUsageStats = readLatestCpuUsageStats();
+ if (latestCpuUsageStats == null) {
+ Slogf.e(TAG, "Failed to read latest CPU usage stats");
+ return Collections.emptyList();
+ }
+ // TODO(b/217422127): Read current CPU frequencies and populate the CpuInfo.
+ return Collections.emptyList();
+ }
+
+ private void readCpusetCategories() {
+ File[] cpusetDirs = mCpusetDir.listFiles(File::isDirectory);
+ if (cpusetDirs == null) {
+ Slogf.e(TAG, "Missing cpuset directories at %s", mCpusetDir.getAbsolutePath());
+ return;
+ }
+ for (int i = 0; i < cpusetDirs.length; i++) {
+ File dir = cpusetDirs[i];
+ @CpusetCategory int cpusetCategory;
+ switch (dir.getName()) {
+ case CPUSET_TOP_APP_DIR:
+ cpusetCategory = FLAG_CPUSET_CATEGORY_TOP_APP;
+ break;
+ case CPUSET_BACKGROUND_DIR:
+ cpusetCategory = FLAG_CPUSET_CATEGORY_BACKGROUND;
+ break;
+ default:
+ continue;
+ }
+ File cpuCoresFile = new File(dir.getPath(), CPUS_FILE);
+ List<Integer> cpuCores = readCpuCores(cpuCoresFile);
+ if (cpuCores.isEmpty()) {
+ Slogf.e(TAG, "Failed to read CPU cores from %s", cpuCoresFile.getAbsolutePath());
+ continue;
+ }
+ for (int j = 0; j < cpuCores.size(); j++) {
+ int categories = mCpusetCategoriesByCpus.get(cpuCores.get(j));
+ categories |= cpusetCategory;
+ mCpusetCategoriesByCpus.append(cpuCores.get(j), categories);
+ }
+ }
+ }
+
+ private void readMaxCpuFrequencies() {
+ for (int i = 0; i < mCpuFreqPolicyDirs.length; i++) {
+ File policyDir = mCpuFreqPolicyDirs[i];
+ long maxCpuFreqKHz = readMaxCpuFrequency(policyDir);
+ if (maxCpuFreqKHz == 0) {
+ Slogf.w(TAG, "Invalid max CPU frequency read from %s", policyDir.getAbsolutePath());
+ continue;
+ }
+ File cpuCoresFile = new File(policyDir, RELATED_CPUS_FILE);
+ List<Integer> cpuCores = readCpuCores(cpuCoresFile);
+ if (cpuCores.isEmpty()) {
+ Slogf.e(TAG, "Failed to read CPU cores from %s", cpuCoresFile.getAbsolutePath());
+ continue;
+ }
+ for (int j = 0; j < cpuCores.size(); j++) {
+ mMaxCpuFrequenciesByCpus.append(cpuCores.get(j), maxCpuFreqKHz);
+ }
+ }
+ }
+
+ private long readMaxCpuFrequency(File policyDir) {
+ long curCpuFreqKHz = readCpuFreqKHz(new File(policyDir, MAX_CPUFREQ_FILE));
+ return curCpuFreqKHz > 0 ? curCpuFreqKHz
+ : readCpuFreqKHz(new File(policyDir, MAX_SCALING_FREQ_FILE));
+ }
+
+ private static long readCpuFreqKHz(File file) {
+ if (!file.exists()) {
+ Slogf.e(TAG, "CPU frequency file %s doesn't exist", file.getAbsolutePath());
+ return 0;
+ }
+ try {
+ List<String> lines = Files.readAllLines(file.toPath());
+ if (!lines.isEmpty()) {
+ long frequency = Long.parseLong(lines.get(0).trim());
+ return frequency > 0 ? frequency : 0;
+ }
+ } catch (Exception e) {
+ Slogf.e(TAG, e, "Failed to read integer content from file: %s", file.getAbsolutePath());
+ }
+ return 0;
+ }
+
+ /**
+ * Reads the list of CPU cores from the given file.
+ *
+ * Reads CPU cores represented in one of the below formats.
+ * <ul>
+ * <li> Single core id. Eg: 1
+ * <li> Core id range. Eg: 1-4
+ * <li> Comma separated values. Eg: 1, 3-5, 7
+ * </ul>
+ */
+ private static List<Integer> readCpuCores(File file) {
+ if (!file.exists()) {
+ Slogf.e(TAG, "Failed to read CPU cores as the file '%s' doesn't exist",
+ file.getAbsolutePath());
+ return Collections.emptyList();
+ }
+ try {
+ List<String> lines = Files.readAllLines(file.toPath());
+ List<Integer> cpuCores = new ArrayList<>();
+ for (int i = 0; i < lines.size(); i++) {
+ String[] pairs = lines.get(i).trim().split(",");
+ for (int j = 0; j < pairs.length; j++) {
+ String[] minMaxPairs = pairs[j].split("-");
+ if (minMaxPairs.length >= 2) {
+ int min = Integer.parseInt(minMaxPairs[0]);
+ int max = Integer.parseInt(minMaxPairs[1]);
+ if (min > max) {
+ continue;
+ }
+ for (int id = min; id <= max; id++) {
+ cpuCores.add(id);
+ }
+ } else if (minMaxPairs.length == 1) {
+ cpuCores.add(Integer.parseInt(minMaxPairs[0]));
+ } else {
+ Slogf.w(TAG, "Invalid CPU core range format %s", pairs[j]);
+ }
+ }
+ }
+ return cpuCores;
+ } catch (Exception e) {
+ Slogf.e(TAG, e, "Failed to read CPU cores from %s", file.getAbsolutePath());
+ }
+ return Collections.emptyList();
+ }
+
+ @Nullable
+ private SparseArray<CpuUsageStats> readLatestCpuUsageStats() {
+ SparseArray<CpuUsageStats> cumulativeCpuUsageStats = readCumulativeCpuUsageStats();
+ if (cumulativeCpuUsageStats.size() == 0) {
+ Slogf.e(TAG, "Failed to read cumulative CPU usage stats");
+ return null;
+ }
+ SparseArray<CpuUsageStats> deltaCpuUsageStats = new SparseArray();
+ for (int i = 0; i < cumulativeCpuUsageStats.size(); i++) {
+ int cpu = cumulativeCpuUsageStats.keyAt(i);
+ CpuUsageStats newStats = cumulativeCpuUsageStats.valueAt(i);
+ CpuUsageStats oldStats = mCumulativeCpuUsageStats.get(cpu);
+ deltaCpuUsageStats.append(cpu, oldStats == null ? newStats : newStats.delta(oldStats));
+ }
+ mCumulativeCpuUsageStats = cumulativeCpuUsageStats;
+ return deltaCpuUsageStats;
+ }
+
+ private SparseArray<CpuUsageStats> readCumulativeCpuUsageStats() {
+ SparseArray<CpuUsageStats> cpuUsageStats = new SparseArray<>();
+ try {
+ List<String> lines = Files.readAllLines(mProcStatFile.toPath());
+ for (int i = 0; i < lines.size(); i++) {
+ Matcher m = PROC_STAT_PATTERN.matcher(lines.get(i).trim());
+ if (!m.find()) {
+ continue;
+ }
+ cpuUsageStats.append(Integer.parseInt(Objects.requireNonNull(m.group("core"))),
+ new CpuUsageStats(jiffyStrToMillis(m.group("userClockTicks")),
+ jiffyStrToMillis(m.group("niceClockTicks")),
+ jiffyStrToMillis(m.group("sysClockTicks")),
+ jiffyStrToMillis(m.group("idleClockTicks")),
+ jiffyStrToMillis(m.group("iowaitClockTicks")),
+ jiffyStrToMillis(m.group("irqClockTicks")),
+ jiffyStrToMillis(m.group("softirqClockTicks")),
+ jiffyStrToMillis(m.group("stealClockTicks")),
+ jiffyStrToMillis(m.group("guestClockTicks")),
+ jiffyStrToMillis(m.group("guestNiceClockTicks"))));
+ }
+ } catch (Exception e) {
+ Slogf.e(TAG, e, "Failed to read cpu usage stats from %s",
+ mProcStatFile.getAbsolutePath());
+ }
+ return cpuUsageStats;
+ }
+
+ private static long jiffyStrToMillis(String jiffyStr) {
+ return Long.parseLong(Objects.requireNonNull(jiffyStr)) * MILLIS_PER_JIFFY;
+ }
+
+ /** Contains information for each CPU core on the system. */
+ public static final class CpuInfo {
+ public final int cpuCore;
+ public final @CpusetCategory int cpusetCategories;
+ public final long curCpuFreqKHz;
+ public final long maxCpuFreqKHz;
+ public final CpuUsageStats latestCpuUsageStats;
+
+ CpuInfo(int cpuCore, @CpusetCategory int cpusetCategories, long curCpuFreqKHz,
+ long maxCpuFreqKHz, CpuUsageStats latestCpuUsageStats) {
+ this.cpuCore = cpuCore;
+ this.cpusetCategories = cpusetCategories;
+ this.curCpuFreqKHz = curCpuFreqKHz;
+ this.maxCpuFreqKHz = maxCpuFreqKHz;
+ this.latestCpuUsageStats = latestCpuUsageStats;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder("CpuInfo{ cpuCore = ").append(cpuCore)
+ .append(", cpusetCategories = ").append(cpusetCategories)
+ .append(", curCpuFreqKHz = ").append(curCpuFreqKHz)
+ .append(", maxCpuFreqKHz = ").append(maxCpuFreqKHz)
+ .append(", latestCpuUsageStats = ").append(latestCpuUsageStats)
+ .append(" }").toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof CpuInfo)) {
+ return false;
+ }
+ CpuInfo other = (CpuInfo) obj;
+ return cpuCore == other.cpuCore && cpusetCategories == other.cpusetCategories
+ && curCpuFreqKHz == other.curCpuFreqKHz
+ && maxCpuFreqKHz == other.maxCpuFreqKHz
+ && latestCpuUsageStats.equals(other.latestCpuUsageStats);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(cpuCore, cpusetCategories, curCpuFreqKHz, maxCpuFreqKHz,
+ latestCpuUsageStats);
+ }
+ }
+
+ /** CPU time spent in different modes. */
+ public static final class CpuUsageStats {
+ public final long userTimeMillis;
+ public final long niceTimeMillis;
+ public final long systemTimeMillis;
+ public final long idleTimeMillis;
+ public final long iowaitTimeMillis;
+ public final long irqTimeMillis;
+ public final long softirqTimeMillis;
+ public final long stealTimeMillis;
+ public final long guestTimeMillis;
+ public final long guestNiceTimeMillis;
+
+ public CpuUsageStats(long userTimeMillis, long niceTimeMillis, long systemTimeMillis,
+ long idleTimeMillis, long iowaitTimeMillis, long irqTimeMillis,
+ long softirqTimeMillis, long stealTimeMillis, long guestTimeMillis,
+ long guestNiceTimeMillis) {
+ this.userTimeMillis = userTimeMillis;
+ this.niceTimeMillis = niceTimeMillis;
+ this.systemTimeMillis = systemTimeMillis;
+ this.idleTimeMillis = idleTimeMillis;
+ this.iowaitTimeMillis = iowaitTimeMillis;
+ this.irqTimeMillis = irqTimeMillis;
+ this.softirqTimeMillis = softirqTimeMillis;
+ this.stealTimeMillis = stealTimeMillis;
+ this.guestTimeMillis = guestTimeMillis;
+ this.guestNiceTimeMillis = guestNiceTimeMillis;
+ }
+
+ public long getTotalTime() {
+ return userTimeMillis + niceTimeMillis + systemTimeMillis + idleTimeMillis
+ + iowaitTimeMillis + irqTimeMillis + softirqTimeMillis + stealTimeMillis
+ + guestTimeMillis + guestNiceTimeMillis;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder("CpuUsageStats{ userTimeMillis = ")
+ .append(userTimeMillis)
+ .append(", niceTimeMillis = ").append(niceTimeMillis)
+ .append(", systemTimeMillis = ").append(systemTimeMillis)
+ .append(", idleTimeMillis = ").append(idleTimeMillis)
+ .append(", iowaitTimeMillis = ").append(iowaitTimeMillis)
+ .append(", irqTimeMillis = ").append(irqTimeMillis)
+ .append(", softirqTimeMillis = ").append(softirqTimeMillis)
+ .append(", stealTimeMillis = ").append(stealTimeMillis)
+ .append(", guestTimeMillis = ").append(guestTimeMillis)
+ .append(", guestNiceTimeMillis = ").append(guestNiceTimeMillis)
+ .append(" }").toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof CpuUsageStats)) {
+ return false;
+ }
+ CpuUsageStats other = (CpuUsageStats) obj;
+ return userTimeMillis == other.userTimeMillis && niceTimeMillis == other.niceTimeMillis
+ && systemTimeMillis == other.systemTimeMillis
+ && idleTimeMillis == other.idleTimeMillis
+ && iowaitTimeMillis == other.iowaitTimeMillis
+ && irqTimeMillis == other.irqTimeMillis
+ && softirqTimeMillis == other.softirqTimeMillis
+ && stealTimeMillis == other.stealTimeMillis
+ && guestTimeMillis == other.guestTimeMillis
+ && guestNiceTimeMillis == other.guestNiceTimeMillis;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(userTimeMillis, niceTimeMillis, systemTimeMillis, idleTimeMillis,
+ iowaitTimeMillis, irqTimeMillis, softirqTimeMillis, stealTimeMillis,
+ guestTimeMillis,
+ guestNiceTimeMillis);
+ }
+
+ CpuUsageStats delta(CpuUsageStats rhs) {
+ return new CpuUsageStats(diff(userTimeMillis, rhs.userTimeMillis),
+ diff(niceTimeMillis, rhs.niceTimeMillis),
+ diff(systemTimeMillis, rhs.systemTimeMillis),
+ diff(idleTimeMillis, rhs.idleTimeMillis),
+ diff(iowaitTimeMillis, rhs.iowaitTimeMillis),
+ diff(irqTimeMillis, rhs.irqTimeMillis),
+ diff(softirqTimeMillis, rhs.softirqTimeMillis),
+ diff(stealTimeMillis, rhs.stealTimeMillis),
+ diff(guestTimeMillis, rhs.guestTimeMillis),
+ diff(guestNiceTimeMillis, rhs.guestNiceTimeMillis));
+ }
+
+ private static long diff(long lhs, long rhs) {
+ return lhs > rhs ? lhs - rhs : 0;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/cpu/CpuMonitorInternal.java b/services/core/java/com/android/server/cpu/CpuMonitorInternal.java
new file mode 100644
index 000000000000..849a20be0cf8
--- /dev/null
+++ b/services/core/java/com/android/server/cpu/CpuMonitorInternal.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.cpu;
+
+import android.annotation.CallbackExecutor;
+
+import java.util.concurrent.Executor;
+
+/** CpuMonitorInternal hosts internal APIs to monitor CPU. */
+public abstract class CpuMonitorInternal {
+ /** Callback to get CPU availability change notifications. */
+ public interface CpuAvailabilityCallback {
+ /**
+ * Called when the CPU availability crosses the provided thresholds.
+ *
+ * <p>Called when the latest or past N-second (which will be specified in the
+ * {@link CpuAvailabilityInfo}) average CPU availability percent has crossed
+ * (either goes above or drop below) the {@link CpuAvailabilityMonitoringConfig#thresholds}
+ * since the last notification. Also called when a callback is added to the service.
+ *
+ * <p>The callback is called at the executor which is specified in
+ * {@link addCpuAvailabilityCallback} or at the service handler thread.
+ *
+ * @param info CPU availability information.
+ */
+ void onAvailabilityChanged(CpuAvailabilityInfo info);
+
+ /**
+ * Called when the CPU monitoring interval changes.
+ *
+ * <p>Also called when a callback is added to the service.
+ *
+ * @param intervalMilliseconds CPU monitoring interval in milliseconds.
+ */
+ void onMonitoringIntervalChanged(long intervalMilliseconds);
+ }
+
+ /**
+ * Adds the {@link CpuAvailabilityCallback} for the caller.
+ *
+ * <p>When the callback is added, the callback will be called to notify the current CPU
+ * availability and monitoring interval.
+ *
+ * <p>When the client needs to update the {@link config} for a previously added callback,
+ * the client has to remove the callback and add the callback with a new {@link config}.
+ *
+ * @param executor Executor to execute the callback. If an executor is not provided,
+ * the callback will be executed on the service handler thread.
+ * @param config CPU availability monitoring config.
+ * @param callback Callback implementing {@link CpuAvailabilityCallback}
+ * interface.
+ *
+ * @throws IllegalStateException if {@code callback} is already added.
+ */
+ public abstract void addCpuAvailabilityCallback(@CallbackExecutor Executor executor,
+ CpuAvailabilityMonitoringConfig config, CpuAvailabilityCallback callback);
+
+ /**
+ * Removes the {@link CpuAvailabilityCallback} for the caller.
+ *
+ * @param callback Callback implementing {@link CpuAvailabilityCallback}
+ * interface.
+ *
+ * @throws IllegalArgumentException if {@code callback} is not previously added.
+ */
+ public abstract void removeCpuAvailabilityCallback(CpuAvailabilityCallback callback);
+}
diff --git a/services/core/java/com/android/server/cpu/CpuMonitorService.java b/services/core/java/com/android/server/cpu/CpuMonitorService.java
new file mode 100644
index 000000000000..b0dfb8467fa7
--- /dev/null
+++ b/services/core/java/com/android/server/cpu/CpuMonitorService.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.cpu;
+
+import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
+
+import android.content.Context;
+import android.os.Binder;
+import android.util.ArrayMap;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.DumpUtils;
+import com.android.server.SystemService;
+import com.android.server.utils.PriorityDump;
+import com.android.server.utils.Slogf;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/** Service to monitor CPU availability and usage. */
+public final class CpuMonitorService extends SystemService {
+ static final String TAG = CpuMonitorService.class.getSimpleName();
+ static final boolean DEBUG = Slogf.isLoggable(TAG, Log.DEBUG);
+ // TODO(b/242722241): Make this a resource overlay property.
+ // Maintain 3 monitoring intervals:
+ // * One to poll very frequently when mCpuAvailabilityCallbackInfoByCallbacks are available and
+ // CPU availability is above a threshold (such as at least 10% of CPU is available).
+ // * One to poll less frequently when mCpuAvailabilityCallbackInfoByCallbacks are available
+ // and CPU availability is below a threshold (such as less than 10% of CPU is available).
+ // * One to poll very less frequently when no callbacks are available and the build is either
+ // user-debug or eng. This will be useful for debugging in development environment.
+ static final int DEFAULT_CPU_MONITORING_INTERVAL_MILLISECONDS = 5_000;
+
+ private final Context mContext;
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private final ArrayMap<CpuMonitorInternal.CpuAvailabilityCallback, CpuAvailabilityCallbackInfo>
+ mCpuAvailabilityCallbackInfoByCallbacks = new ArrayMap<>();
+ @GuardedBy("mLock")
+ private long mMonitoringIntervalMilliseconds = DEFAULT_CPU_MONITORING_INTERVAL_MILLISECONDS;
+
+ private final CpuMonitorInternal mLocalService = new CpuMonitorInternal() {
+ @Override
+ public void addCpuAvailabilityCallback(Executor executor,
+ CpuAvailabilityMonitoringConfig config, CpuAvailabilityCallback callback) {
+ Objects.requireNonNull(callback, "Callback must be non-null");
+ Objects.requireNonNull(config, "Config must be non-null");
+ synchronized (mLock) {
+ if (mCpuAvailabilityCallbackInfoByCallbacks.containsKey(callback)) {
+ Slogf.i(TAG, "Overwriting the existing CpuAvailabilityCallback %s",
+ mCpuAvailabilityCallbackInfoByCallbacks.get(callback));
+ // TODO(b/242722241): Overwrite any internal cache (will be added in future CLs)
+ // that maps callbacks based on the CPU availability thresholds.
+ }
+ CpuAvailabilityCallbackInfo info = new CpuAvailabilityCallbackInfo(config,
+ executor);
+ mCpuAvailabilityCallbackInfoByCallbacks.put(callback, info);
+ if (DEBUG) {
+ Slogf.d(TAG, "Added a CPU availability callback: %s", info);
+ }
+ }
+ // TODO(b/242722241):
+ // * On the executor or on the handler thread, call the callback with the latest CPU
+ // availability info and monitoring interval.
+ // * Monitor the CPU stats more frequently when the first callback is added.
+ }
+
+ @Override
+ public void removeCpuAvailabilityCallback(CpuAvailabilityCallback callback) {
+ synchronized (mLock) {
+ if (!mCpuAvailabilityCallbackInfoByCallbacks.containsKey(callback)) {
+ Slogf.i(TAG, "CpuAvailabilityCallback was not previously added."
+ + " Ignoring the remove request");
+ return;
+ }
+ CpuAvailabilityCallbackInfo info =
+ mCpuAvailabilityCallbackInfoByCallbacks.remove(callback);
+ if (DEBUG) {
+ Slogf.d(TAG, "Removed a CPU availability callback: %s", info);
+ }
+ }
+ // TODO(b/242722241): Increase CPU monitoring interval when all callbacks are removed.
+ }
+ };
+
+ public CpuMonitorService(Context context) {
+ super(context);
+ mContext = context;
+ }
+
+ @Override
+ public void onStart() {
+ publishLocalService(CpuMonitorInternal.class, mLocalService);
+ publishBinderService("cpu_monitor", new CpuMonitorBinder(), /* allowIsolated= */ false,
+ DUMP_FLAG_PRIORITY_CRITICAL);
+ }
+
+ private void doDump(IndentingPrintWriter writer) {
+ writer.printf("*%s*\n", getClass().getSimpleName());
+ writer.increaseIndent();
+ synchronized (mLock) {
+ writer.printf("CPU monitoring interval: %d ms\n", mMonitoringIntervalMilliseconds);
+ if (!mCpuAvailabilityCallbackInfoByCallbacks.isEmpty()) {
+ writer.println("CPU availability change callbacks:");
+ writer.increaseIndent();
+ for (int i = 0; i < mCpuAvailabilityCallbackInfoByCallbacks.size(); i++) {
+ writer.printf("%s: %s\n", mCpuAvailabilityCallbackInfoByCallbacks.keyAt(i),
+ mCpuAvailabilityCallbackInfoByCallbacks.valueAt(i));
+ }
+ writer.decreaseIndent();
+ }
+ }
+ // TODO(b/242722241): Print the recent past CPU stats.
+ writer.decreaseIndent();
+ }
+
+ private static final class CpuAvailabilityCallbackInfo {
+ public final CpuAvailabilityMonitoringConfig config;
+ public final Executor executor;
+
+ CpuAvailabilityCallbackInfo(CpuAvailabilityMonitoringConfig config,
+ Executor executor) {
+ this.config = config;
+ this.executor = executor;
+ }
+
+ @Override
+ public String toString() {
+ return "CpuAvailabilityCallbackInfo{" + "config=" + config + ", mExecutor=" + executor
+ + '}';
+ }
+ }
+
+ private final class CpuMonitorBinder extends Binder {
+ private final PriorityDump.PriorityDumper mPriorityDumper =
+ new PriorityDump.PriorityDumper() {
+ @Override
+ public void dumpCritical(FileDescriptor fd, PrintWriter pw, String[] args,
+ boolean asProto) {
+ if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)
+ || asProto) {
+ return;
+ }
+ try (IndentingPrintWriter ipw = new IndentingPrintWriter(pw)) {
+ doDump(ipw);
+ }
+ }
+ };
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ PriorityDump.dump(mPriorityDumper, fd, pw, args);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 523a2dca7a18..c1e9526a9127 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -1941,8 +1941,8 @@ public class DisplayDeviceConfig {
}
private void setProxSensorUnspecified() {
- mProximitySensor.name = "";
- mProximitySensor.type = "";
+ mProximitySensor.name = null;
+ mProximitySensor.type = null;
}
private void loadProxSensorFromDdc(DisplayConfiguration config) {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index c5cb08db934f..1d04f2ef99c0 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1573,7 +1573,7 @@ public final class DisplayManagerService extends SystemService {
mSyncRoot.notifyAll();
}
- sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
+ sendDisplayEventLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
Runnable work = updateDisplayStateLocked(device);
if (work != null) {
@@ -1592,7 +1592,7 @@ public final class DisplayManagerService extends SystemService {
// We don't bother invalidating the display info caches here because any changes to the
// display info will trigger a cache invalidation inside of LogicalDisplay before we hit
// this point.
- sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+ sendDisplayEventLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
scheduleTraversalLocked(false);
mPersistentDataStore.saveIfNeeded();
@@ -1622,7 +1622,7 @@ public final class DisplayManagerService extends SystemService {
mDisplayStates.delete(displayId);
mDisplayBrightnesses.delete(displayId);
DisplayManagerGlobal.invalidateLocalDisplayInfoCaches();
- sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
+ sendDisplayEventLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
scheduleTraversalLocked(false);
if (mDisplayWindowPolicyControllers.contains(displayId)) {
@@ -1638,23 +1638,13 @@ public final class DisplayManagerService extends SystemService {
}
private void handleLogicalDisplaySwappedLocked(@NonNull LogicalDisplay display) {
- final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
- final Runnable work = updateDisplayStateLocked(device);
- if (work != null) {
- mHandler.post(work);
- }
- final int displayId = display.getDisplayIdLocked();
+ handleLogicalDisplayChangedLocked(display);
+ final int displayId = display.getDisplayIdLocked();
if (displayId == Display.DEFAULT_DISPLAY) {
notifyDefaultDisplayDeviceUpdated(display);
}
- DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
- if (dpc != null) {
- dpc.onDisplayChanged();
- }
- mPersistentDataStore.saveIfNeeded();
mHandler.sendEmptyMessage(MSG_LOAD_BRIGHTNESS_CONFIGURATIONS);
- handleLogicalDisplayChangedLocked(display);
}
private void notifyDefaultDisplayDeviceUpdated(LogicalDisplay display) {
@@ -1666,7 +1656,7 @@ public final class DisplayManagerService extends SystemService {
final int displayId = display.getDisplayIdLocked();
final DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
- dpc.onDeviceStateTransition();
+ dpc.onDisplayChanged();
}
}
@@ -2366,9 +2356,13 @@ public final class DisplayManagerService extends SystemService {
}
}
- private void sendDisplayEventLocked(int displayId, @DisplayEvent int event) {
- Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT, displayId, event);
- mHandler.sendMessage(msg);
+ private void sendDisplayEventLocked(@NonNull LogicalDisplay display, @DisplayEvent int event) {
+ // Only send updates outside of DisplayManagerService for enabled displays
+ if (display.isEnabledLocked()) {
+ int displayId = display.getDisplayIdLocked();
+ Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT, displayId, event);
+ mHandler.sendMessage(msg);
+ }
}
private void sendDisplayGroupEvent(int groupId, int event) {
@@ -2653,8 +2647,7 @@ public final class DisplayManagerService extends SystemService {
}
private void handleBrightnessChange(LogicalDisplay display) {
- sendDisplayEventLocked(display.getDisplayIdLocked(),
- DisplayManagerGlobal.EVENT_DISPLAY_BRIGHTNESS_CHANGED);
+ sendDisplayEventLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_BRIGHTNESS_CHANGED);
}
private DisplayDevice getDeviceForDisplayLocked(int displayId) {
@@ -2871,12 +2864,12 @@ public final class DisplayManagerService extends SystemService {
* Returns the list of all display ids.
*/
@Override // Binder call
- public int[] getDisplayIds() {
+ public int[] getDisplayIds(boolean includeDisabled) {
final int callingUid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
- return mLogicalDisplayMapper.getDisplayIdsLocked(callingUid);
+ return mLogicalDisplayMapper.getDisplayIdsLocked(callingUid, includeDisabled);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -3367,6 +3360,11 @@ public final class DisplayManagerService extends SystemService {
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
+ LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(
+ displayId, /* includeDisabled= */ false);
+ if (display == null || !display.isEnabledLocked()) {
+ return null;
+ }
DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
return dpc.getBrightnessInfo();
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 306b8cf4e0ee..405a2b958dd1 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -16,6 +16,7 @@
package com.android.server.display;
+import static android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED;
import static android.hardware.display.DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE;
import static android.os.PowerManager.BRIGHTNESS_INVALID;
@@ -48,7 +49,7 @@ import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.provider.DeviceConfigInterface;
import android.provider.Settings;
-import android.sysprop.DisplayProperties;
+import android.sysprop.SurfaceFlingerProperties;
import android.text.TextUtils;
import android.util.IndentingPrintWriter;
import android.util.Pair;
@@ -137,8 +138,7 @@ public class DisplayModeDirector {
private boolean mAlwaysRespectAppRequest;
- // TODO(b/241447632): remove the flag once SF changes are ready
- private final boolean mRenderFrameRateIsPhysicalRefreshRate;
+ private final boolean mSupportsFrameRateOverride;
/**
* The allowed refresh rate switching type. This is used by SurfaceFlinger.
@@ -175,7 +175,7 @@ public class DisplayModeDirector {
mHbmObserver = new HbmObserver(injector, ballotBox, BackgroundThread.getHandler(),
mDeviceConfigDisplaySettings);
mAlwaysRespectAppRequest = false;
- mRenderFrameRateIsPhysicalRefreshRate = injector.renderFrameRateIsPhysicalRefreshRate();
+ mSupportsFrameRateOverride = injector.supportsFrameRateOverride();
}
/**
@@ -237,21 +237,6 @@ public class DisplayModeDirector {
}
}
- if (mRenderFrameRateIsPhysicalRefreshRate) {
- for (int i = 0; i < votes.size(); i++) {
-
- Vote vote = votes.valueAt(i);
- vote.refreshRateRanges.physical.min = Math.max(vote.refreshRateRanges.physical.min,
- vote.refreshRateRanges.render.min);
- vote.refreshRateRanges.physical.max = Math.min(vote.refreshRateRanges.physical.max,
- vote.refreshRateRanges.render.max);
- vote.refreshRateRanges.render.min = Math.max(vote.refreshRateRanges.physical.min,
- vote.refreshRateRanges.render.min);
- vote.refreshRateRanges.render.max = Math.min(vote.refreshRateRanges.physical.max,
- vote.refreshRateRanges.render.max);
- }
- }
-
return votes;
}
@@ -279,6 +264,18 @@ public class DisplayModeDirector {
disableRefreshRateSwitching = false;
appRequestBaseModeRefreshRate = 0f;
}
+
+ @Override
+ public String toString() {
+ return "minPhysicalRefreshRate=" + minPhysicalRefreshRate
+ + ", maxPhysicalRefreshRate=" + maxPhysicalRefreshRate
+ + ", minRenderFrameRate=" + minRenderFrameRate
+ + ", maxRenderFrameRate=" + maxRenderFrameRate
+ + ", width=" + width
+ + ", height=" + height
+ + ", disableRefreshRateSwitching=" + disableRefreshRateSwitching
+ + ", appRequestBaseModeRefreshRate=" + appRequestBaseModeRefreshRate;
+ }
}
// VoteSummary is returned as an output param to cut down a bit on the number of temporary
@@ -332,18 +329,8 @@ public class DisplayModeDirector {
}
if (mLoggingEnabled) {
- Slog.w(TAG, "Vote summary for priority "
- + Vote.priorityToString(priority)
- + ": width=" + summary.width
- + ", height=" + summary.height
- + ", minPhysicalRefreshRate=" + summary.minPhysicalRefreshRate
- + ", maxPhysicalRefreshRate=" + summary.maxPhysicalRefreshRate
- + ", minRenderFrameRate=" + summary.minRenderFrameRate
- + ", maxRenderFrameRate=" + summary.maxRenderFrameRate
- + ", disableRefreshRateSwitching="
- + summary.disableRefreshRateSwitching
- + ", appRequestBaseModeRefreshRate="
- + summary.appRequestBaseModeRefreshRate);
+ Slog.w(TAG, "Vote summary for priority " + Vote.priorityToString(priority)
+ + ": " + summary);
}
}
}
@@ -377,6 +364,23 @@ public class DisplayModeDirector {
return !availableModes.isEmpty() ? availableModes.get(0) : null;
}
+ private void disableModeSwitching(VoteSummary summary, float fps) {
+ summary.minPhysicalRefreshRate = summary.maxPhysicalRefreshRate = fps;
+ summary.maxRenderFrameRate = Math.min(summary.maxRenderFrameRate, fps);
+
+ if (mLoggingEnabled) {
+ Slog.i(TAG, "Disabled mode switching on summary: " + summary);
+ }
+ }
+
+ private void disableRenderRateSwitching(VoteSummary summary) {
+ summary.minRenderFrameRate = summary.maxRenderFrameRate;
+
+ if (mLoggingEnabled) {
+ Slog.i(TAG, "Disabled render rate switching on summary: " + summary);
+ }
+ }
+
/**
* Calculates the refresh rate ranges and display modes that the system is allowed to freely
* switch between based on global and display-specific constraints.
@@ -405,7 +409,7 @@ public class DisplayModeDirector {
int highestConsideredPriority = Vote.MAX_PRIORITY;
if (mAlwaysRespectAppRequest) {
- lowestConsideredPriority = Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE;
+ lowestConsideredPriority = Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE;
highestConsideredPriority = Vote.PRIORITY_APP_REQUEST_SIZE;
}
@@ -533,19 +537,15 @@ public class DisplayModeDirector {
if (modeSwitchingDisabled || primarySummary.disableRefreshRateSwitching) {
float fps = baseMode.getRefreshRate();
- primarySummary.minPhysicalRefreshRate = primarySummary.maxPhysicalRefreshRate = fps;
+ disableModeSwitching(primarySummary, fps);
if (modeSwitchingDisabled) {
- appRequestSummary.minPhysicalRefreshRate =
- appRequestSummary.maxPhysicalRefreshRate = fps;
- }
- }
+ disableModeSwitching(appRequestSummary, fps);
+ disableRenderRateSwitching(primarySummary);
- if (mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE
- || mRenderFrameRateIsPhysicalRefreshRate) {
- primarySummary.minRenderFrameRate = primarySummary.minPhysicalRefreshRate;
- primarySummary.maxRenderFrameRate = primarySummary.maxPhysicalRefreshRate;
- appRequestSummary.minRenderFrameRate = appRequestSummary.minPhysicalRefreshRate;
- appRequestSummary.maxRenderFrameRate = appRequestSummary.maxPhysicalRefreshRate;
+ if (mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE) {
+ disableRenderRateSwitching(appRequestSummary);
+ }
+ }
}
boolean allowGroupSwitching =
@@ -611,6 +611,22 @@ public class DisplayModeDirector {
continue;
}
+ // The physical refresh rate must be in the render frame rate range, unless
+ // frame rate override is supported.
+ if (!mSupportsFrameRateOverride) {
+ if (physicalRefreshRate < (summary.minRenderFrameRate - FLOAT_TOLERANCE)
+ || physicalRefreshRate > (summary.maxRenderFrameRate + FLOAT_TOLERANCE)) {
+ if (mLoggingEnabled) {
+ Slog.w(TAG, "Discarding mode " + mode.getModeId()
+ + ", outside render rate bounds"
+ + ": minPhysicalRefreshRate=" + summary.minPhysicalRefreshRate
+ + ", maxPhysicalRefreshRate=" + summary.maxPhysicalRefreshRate
+ + ", modeRefreshRate=" + physicalRefreshRate);
+ }
+ continue;
+ }
+ }
+
// Check whether the render frame rate range is achievable by the mode's physical
// refresh rate, meaning that if a divisor of the physical refresh rate is in range
// of the render frame rate.
@@ -1640,7 +1656,7 @@ public class DisplayModeDirector {
SparseArray<Display.Mode[]> modes = new SparseArray<>();
SparseArray<Display.Mode> defaultModes = new SparseArray<>();
DisplayInfo info = new DisplayInfo();
- Display[] displays = dm.getDisplays();
+ Display[] displays = dm.getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED);
for (Display d : displays) {
final int displayId = d.getDisplayId();
d.getDisplayInfo(info);
@@ -2517,7 +2533,8 @@ public class DisplayModeDirector {
sensorManager.addProximityActiveListener(BackgroundThread.getExecutor(), this);
synchronized (mSensorObserverLock) {
- for (Display d : mDisplayManager.getDisplays()) {
+ for (Display d : mDisplayManager.getDisplays(
+ DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)) {
mDozeStateByDisplay.put(d.getDisplayId(), mInjector.isDozeState(d));
}
}
@@ -2528,7 +2545,8 @@ public class DisplayModeDirector {
}
private void recalculateVotesLocked() {
- final Display[] displays = mDisplayManager.getDisplays();
+ final Display[] displays = mDisplayManager.getDisplays(
+ DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED);
for (Display d : displays) {
int displayId = d.getDisplayId();
Vote vote = null;
@@ -2976,7 +2994,7 @@ public class DisplayModeDirector {
IThermalService getThermalService();
- boolean renderFrameRateIsPhysicalRefreshRate();
+ boolean supportsFrameRateOverride();
}
@VisibleForTesting
@@ -3031,9 +3049,11 @@ public class DisplayModeDirector {
}
@Override
- public boolean renderFrameRateIsPhysicalRefreshRate() {
- return DisplayProperties
- .debug_render_frame_rate_is_physical_refresh_rate().orElse(true);
+ public boolean supportsFrameRateOverride() {
+ return SurfaceFlingerProperties.enable_frame_rate_override().orElse(false)
+ && !SurfaceFlingerProperties.frame_rate_override_for_native_rates()
+ .orElse(true)
+ && SurfaceFlingerProperties.frame_rate_override_global().orElse(false);
}
private DisplayManager getDisplayManager() {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index d6f0fd070f94..81245001eaf6 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -497,6 +497,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
private final String mSuspendBlockerIdProxNegative;
private final String mSuspendBlockerIdProxDebounce;
+ private boolean mIsEnabled;
+ private boolean mIsInTransition;
+
/**
* Creates the display power controller.
*/
@@ -520,6 +523,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mDisplayDevice = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
mUniqueDisplayId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
mDisplayStatsId = mUniqueDisplayId.hashCode();
+ mIsEnabled = logicalDisplay.isEnabledLocked();
+ mIsInTransition = logicalDisplay.isInTransitionLocked();
mHandler = new DisplayControllerHandler(handler.getLooper());
mLastBrightnessEvent = new BrightnessEvent(mDisplayId);
mTempBrightnessEvent = new BrightnessEvent(mDisplayId);
@@ -807,29 +812,36 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
final DisplayDeviceConfig config = device.getDisplayDeviceConfig();
final IBinder token = device.getDisplayTokenLocked();
final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
+ final boolean isEnabled = mLogicalDisplay.isEnabledLocked();
+ final boolean isInTransition = mLogicalDisplay.isInTransitionLocked();
mHandler.post(() -> {
+ boolean changed = false;
if (mDisplayDevice != device) {
+ changed = true;
mDisplayDevice = device;
mUniqueDisplayId = uniqueId;
mDisplayStatsId = mUniqueDisplayId.hashCode();
mDisplayDeviceConfig = config;
loadFromDisplayDeviceConfig(token, info);
+
+ // Since the underlying display-device changed, we really don't know the
+ // last command that was sent to change it's state. Lets assume it is unknown so
+ // that we trigger a change immediately.
+ mPowerState.resetScreenState();
+ }
+ if (mIsEnabled != isEnabled || mIsInTransition != isInTransition) {
+ changed = true;
+ mIsEnabled = isEnabled;
+ mIsInTransition = isInTransition;
+ }
+
+ if (changed) {
updatePowerState();
}
});
}
/**
- * Called when the displays are preparing to transition from one device state to another.
- * This process involves turning off some displays so we need updatePowerState() to run and
- * calculate the new state.
- */
- @Override
- public void onDeviceStateTransition() {
- sendUpdatePowerState();
- }
-
- /**
* Unregisters all listeners and interrupts all running threads; halting future work.
*
* This method should be called when the DisplayPowerController is no longer in use; i.e. when
@@ -1316,8 +1328,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mIgnoreProximityUntilChanged = false;
}
- if (!mLogicalDisplay.isEnabled()
- || mLogicalDisplay.getPhase() == LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION
+ if (!mIsEnabled
+ || mIsInTransition
|| mScreenOffBecauseOfProximity) {
state = Display.STATE_OFF;
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 1f58a1c40fd8..9a594e8e059e 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -422,6 +422,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
private DisplayDeviceConfig mDisplayDeviceConfig;
+ private boolean mIsEnabled;
+ private boolean mIsInTransition;
/**
* Creates the display power controller.
*/
@@ -439,6 +441,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
mHandler = new DisplayControllerHandler(handler.getLooper());
mDisplayDeviceConfig = logicalDisplay.getPrimaryDisplayDeviceLocked()
.getDisplayDeviceConfig();
+ mIsEnabled = logicalDisplay.isEnabledLocked();
+ mIsInTransition = logicalDisplay.isInTransitionLocked();
mWakelockController = mInjector.getWakelockController(mDisplayId, callbacks);
mDisplayPowerProximityStateController = mInjector.getDisplayPowerProximityStateController(
mWakelockController, mDisplayDeviceConfig, mHandler.getLooper(),
@@ -721,30 +725,37 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
final DisplayDeviceConfig config = device.getDisplayDeviceConfig();
final IBinder token = device.getDisplayTokenLocked();
final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
+ final boolean isEnabled = mLogicalDisplay.isEnabledLocked();
+ final boolean isInTransition = mLogicalDisplay.isInTransitionLocked();
mHandler.post(() -> {
+ boolean changed = false;
if (mDisplayDevice != device) {
+ changed = true;
mDisplayDevice = device;
mUniqueDisplayId = uniqueId;
mDisplayStatsId = mUniqueDisplayId.hashCode();
mDisplayDeviceConfig = config;
loadFromDisplayDeviceConfig(token, info);
mDisplayPowerProximityStateController.notifyDisplayDeviceChanged(config);
+
+ // Since the underlying display-device changed, we really don't know the
+ // last command that was sent to change it's state. Lets assume it is unknown so
+ // that we trigger a change immediately.
+ mPowerState.resetScreenState();
+ }
+ if (mIsEnabled != isEnabled || mIsInTransition != isInTransition) {
+ changed = true;
+ mIsEnabled = isEnabled;
+ mIsInTransition = isInTransition;
+ }
+
+ if (changed) {
updatePowerState();
}
});
}
/**
- * Called when the displays are preparing to transition from one device state to another.
- * This process involves turning off some displays so we need updatePowerState() to run and
- * calculate the new state.
- */
- @Override
- public void onDeviceStateTransition() {
- sendUpdatePowerState();
- }
-
- /**
* Unregisters all listeners and interrupts all running threads; halting future work.
*
* This method should be called when the DisplayPowerController2 is no longer in use; i.e. when
@@ -1165,8 +1176,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
mDisplayPowerProximityStateController.updateProximityState(mPowerRequest, state);
- if (!mLogicalDisplay.isEnabled()
- || mLogicalDisplay.getPhase() == LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION
+ if (!mIsEnabled
+ || mIsInTransition
|| mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity()) {
state = Display.STATE_OFF;
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
index 6677f358557d..46f1343ceeb8 100644
--- a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
+++ b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
@@ -45,11 +45,6 @@ public interface DisplayPowerControllerInterface {
void stop();
/**
- * Used to manage the displays preparing to transition from one device state to another.
- */
- void onDeviceStateTransition();
-
- /**
* Used to update the display's BrightnessConfiguration
* @param config The new BrightnessConfiguration
*/
diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index 2f22d33f552a..f650b118b815 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -145,7 +145,7 @@ final class DisplayPowerState {
public void setScreenState(int state) {
if (mScreenState != state) {
if (DEBUG) {
- Slog.d(TAG, "setScreenState: state=" + state);
+ Slog.w(TAG, "setScreenState: state=" + Display.stateToString(state));
}
mScreenState = state;
@@ -339,6 +339,15 @@ final class DisplayPowerState {
if (mColorFade != null) mColorFade.dump(pw);
}
+ /**
+ * Resets the screen state to unknown. Useful when the underlying display-device changes for the
+ * LogicalDisplay and we do not know the last state that was sent to it.
+ */
+ void resetScreenState() {
+ mScreenState = Display.STATE_UNKNOWN;
+ mScreenReady = false;
+ }
+
private void scheduleScreenUpdate() {
if (!mScreenUpdatePending) {
mScreenUpdatePending = true;
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 28bdce3d8b34..8dd169bf4bf6 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -18,7 +18,6 @@ package com.android.server.display;
import static com.android.server.display.DisplayDeviceInfo.TOUCH_NONE;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Point;
@@ -68,33 +67,6 @@ import java.util.Objects;
final class LogicalDisplay {
private static final String TAG = "LogicalDisplay";
- /**
- * Phase indicating the logical display's existence is hidden from the rest of the framework.
- * This can happen if the current layout has specifically requested to keep this display
- * disabled.
- */
- static final int DISPLAY_PHASE_DISABLED = -1;
-
- /**
- * Phase indicating that the logical display is going through a layout transition.
- * When in this phase, other systems can choose to special case power-state handling of a
- * display that might be in a transition.
- */
- static final int DISPLAY_PHASE_LAYOUT_TRANSITION = 0;
-
- /**
- * The display is exposed to the rest of the system and its power state is determined by a
- * power-request from PowerManager.
- */
- static final int DISPLAY_PHASE_ENABLED = 1;
-
- @IntDef(prefix = {"DISPLAY_PHASE" }, value = {
- DISPLAY_PHASE_DISABLED,
- DISPLAY_PHASE_LAYOUT_TRANSITION,
- DISPLAY_PHASE_ENABLED
- })
- @interface DisplayPhase {}
-
// The layer stack we use when the display has been blanked to prevent any
// of its content from appearing.
private static final int BLANK_LAYER_STACK = -1;
@@ -159,14 +131,6 @@ final class LogicalDisplay {
private final Rect mTempDisplayRect = new Rect();
/**
- * Indicates the current phase of the display. Generally, phases supersede any
- * requests from PowerManager in DPC's calculation for the display state. Only when the
- * phase is ENABLED does PowerManager's request for the display take effect.
- */
- @DisplayPhase
- private int mPhase = DISPLAY_PHASE_ENABLED;
-
- /**
* The UID mappings for refresh rate override
*/
private DisplayEventReceiver.FrameRateOverride[] mFrameRateOverrides;
@@ -181,12 +145,22 @@ final class LogicalDisplay {
*/
private final SparseArray<Float> mTempFrameRateOverride;
+ // Indicates the display is enabled (allowed to be ON).
+ private boolean mIsEnabled;
+
+ // Indicates the display is part of a transition from one device-state ({@link
+ // DeviceStateManager}) to another. Being a "part" of a transition means that either
+ // the {@link mIsEnabled} is changing, or the underlying mPrimiaryDisplayDevice is changing.
+ private boolean mIsInTransition;
+
public LogicalDisplay(int displayId, int layerStack, DisplayDevice primaryDisplayDevice) {
mDisplayId = displayId;
mLayerStack = layerStack;
mPrimaryDisplayDevice = primaryDisplayDevice;
mPendingFrameRateOverrideUids = new ArraySet<>();
mTempFrameRateOverride = new SparseArray<>();
+ mIsEnabled = true;
+ mIsInTransition = false;
}
/**
@@ -531,7 +505,7 @@ final class LogicalDisplay {
// Prevent displays that are disabled from receiving input.
// TODO(b/188914255): Remove once input can dispatch against device vs layerstack.
device.setDisplayFlagsLocked(t,
- (isEnabled() && device.getDisplayDeviceInfoLocked().touch != TOUCH_NONE)
+ (isEnabledLocked() && device.getDisplayDeviceInfoLocked().touch != TOUCH_NONE)
? SurfaceControl.DISPLAY_RECEIVES_INPUT
: 0);
@@ -773,32 +747,45 @@ final class LogicalDisplay {
return old;
}
- public void setPhase(@DisplayPhase int phase) {
- mPhase = phase;
+ /**
+ * @return {@code true} if the LogicalDisplay is enabled or {@code false}
+ * if disabled indicating that the display should be hidden from the rest of the apps and
+ * framework.
+ */
+ public boolean isEnabledLocked() {
+ return mIsEnabled;
+ }
+
+ /**
+ * Sets the display as enabled.
+ *
+ * @param enable True if enabled, false otherwise.
+ */
+ public void setEnabledLocked(boolean enabled) {
+ mIsEnabled = enabled;
}
/**
- * Returns the currently set phase for this LogicalDisplay. Phases are used when transitioning
- * from one device state to another. {@see LogicalDisplayMapper}.
+ * @return {@code true} if the LogicalDisplay is in a transition phase. This is used to indicate
+ * that we are getting ready to swap the underlying display-device and the display should be
+ * rendered appropriately to reduce jank.
*/
- @DisplayPhase
- public int getPhase() {
- return mPhase;
+ public boolean isInTransitionLocked() {
+ return mIsInTransition;
}
/**
- * @return {@code true} if the LogicalDisplay is enabled or {@code false}
- * if disabled indicating that the display should be hidden from the rest of the apps and
- * framework.
+ * Sets the transition phase.
+ * @param isInTransition True if it display is in transition.
*/
- public boolean isEnabled() {
- // DISPLAY_PHASE_LAYOUT_TRANSITION is still considered an 'enabled' phase.
- return mPhase == DISPLAY_PHASE_ENABLED || mPhase == DISPLAY_PHASE_LAYOUT_TRANSITION;
+ public void setIsInTransitionLocked(boolean isInTransition) {
+ mIsInTransition = isInTransition;
}
public void dumpLocked(PrintWriter pw) {
pw.println("mDisplayId=" + mDisplayId);
- pw.println("mPhase=" + mPhase);
+ pw.println("mIsEnabled=" + mIsEnabled);
+ pw.println("mIsInTransition=" + mIsInTransition);
pw.println("mLayerStack=" + mLayerStack);
pw.println("mHasContent=" + mHasContent);
pw.println("mDesiredDisplayModeSpecs={" + mDesiredDisplayModeSpecs + "}");
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index cb97e2832854..66073c2abdec 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -40,7 +40,6 @@ import android.view.DisplayAddress;
import android.view.DisplayInfo;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.display.LogicalDisplay.DisplayPhase;
import com.android.server.display.layout.Layout;
import java.io.PrintWriter;
@@ -180,6 +179,12 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
LogicalDisplayMapper(@NonNull Context context, @NonNull DisplayDeviceRepository repo,
@NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot,
@NonNull Handler handler) {
+ this(context, repo, listener, syncRoot, handler, new DeviceStateToLayoutMap());
+ }
+
+ LogicalDisplayMapper(@NonNull Context context, @NonNull DisplayDeviceRepository repo,
+ @NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot,
+ @NonNull Handler handler, @NonNull DeviceStateToLayoutMap deviceStateToLayoutMap) {
mSyncRoot = syncRoot;
mPowerManager = context.getSystemService(PowerManager.class);
mInteractive = mPowerManager.isInteractive();
@@ -194,7 +199,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
mDeviceStatesOnWhichToSleep = toSparseBooleanArray(context.getResources().getIntArray(
com.android.internal.R.array.config_deviceStatesOnWhichToSleep));
mDisplayDeviceRepo.addListener(this);
- mDeviceStateToLayoutMap = new DeviceStateToLayoutMap();
+ mDeviceStateToLayoutMap = deviceStateToLayoutMap;
}
@Override
@@ -231,10 +236,22 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
}
public LogicalDisplay getDisplayLocked(int displayId) {
- return mLogicalDisplays.get(displayId);
+ return getDisplayLocked(displayId, /* includeDisabled= */ true);
+ }
+
+ public LogicalDisplay getDisplayLocked(int displayId, boolean includeDisabled) {
+ LogicalDisplay display = mLogicalDisplays.get(displayId);
+ if (display == null || display.isEnabledLocked() || includeDisabled) {
+ return display;
+ }
+ return null;
}
public LogicalDisplay getDisplayLocked(DisplayDevice device) {
+ return getDisplayLocked(device, /* includeDisabled= */ true);
+ }
+
+ public LogicalDisplay getDisplayLocked(DisplayDevice device, boolean includeDisabled) {
if (device == null) {
return null;
}
@@ -242,21 +259,26 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
for (int i = 0; i < count; i++) {
final LogicalDisplay display = mLogicalDisplays.valueAt(i);
if (display.getPrimaryDisplayDeviceLocked() == device) {
- return display;
+ if (display.isEnabledLocked() || includeDisabled) {
+ return display;
+ }
+ return null;
}
}
return null;
}
- public int[] getDisplayIdsLocked(int callingUid) {
+ public int[] getDisplayIdsLocked(int callingUid, boolean includeDisabled) {
final int count = mLogicalDisplays.size();
int[] displayIds = new int[count];
int n = 0;
for (int i = 0; i < count; i++) {
LogicalDisplay display = mLogicalDisplays.valueAt(i);
- DisplayInfo info = display.getDisplayInfoLocked();
- if (info.hasAccess(callingUid)) {
- displayIds[n++] = mLogicalDisplays.keyAt(i);
+ if (display.isEnabledLocked() || includeDisabled) {
+ DisplayInfo info = display.getDisplayInfoLocked();
+ if (info.hasAccess(callingUid)) {
+ displayIds[n++] = mLogicalDisplays.keyAt(i);
+ }
}
}
if (n != count) {
@@ -390,14 +412,12 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
void setDeviceStateLocked(int state, boolean isOverrideActive) {
Slog.i(TAG, "Requesting Transition to state: " + state + ", from state=" + mDeviceState
- + ", interactive=" + mInteractive);
+ + ", interactive=" + mInteractive + ", mBootCompleted=" + mBootCompleted);
// As part of a state transition, we may need to turn off some displays temporarily so that
// the transition is smooth. Plus, on some devices, only one internal displays can be
- // on at a time. We use DISPLAY_PHASE_LAYOUT_TRANSITION to mark a display that needs to be
+ // on at a time. We use LogicalDisplay.setIsInTransition to mark a display that needs to be
// temporarily turned off.
- if (mDeviceState != DeviceStateManager.INVALID_DEVICE_STATE) {
- resetLayoutLocked(mDeviceState, state, LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION);
- }
+ resetLayoutLocked(mDeviceState, state, /* transitionValue= */ true);
mPendingDeviceState = state;
final boolean wakeDevice = shouldDeviceBeWoken(mPendingDeviceState, mDeviceState,
mInteractive, mBootCompleted);
@@ -507,7 +527,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
final int count = mLogicalDisplays.size();
for (int i = 0; i < count; i++) {
final LogicalDisplay display = mLogicalDisplays.valueAt(i);
- if (display.getPhase() != LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION) {
+ if (!display.isInTransitionLocked()) {
continue;
}
@@ -523,7 +543,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
}
private void transitionToPendingStateLocked() {
- resetLayoutLocked(mDeviceState, mPendingDeviceState, LogicalDisplay.DISPLAY_PHASE_ENABLED);
+ resetLayoutLocked(mDeviceState, mPendingDeviceState, /* transitionValue= */ false);
mDeviceState = mPendingDeviceState;
mPendingDeviceState = DeviceStateManager.INVALID_DEVICE_STATE;
applyLayoutLocked();
@@ -838,17 +858,17 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
/**
* Goes through all the displays used in the layouts for the specified {@code fromState} and
- * {@code toState} and applies the specified {@code phase}. When a new layout is requested, we
- * put the displays that will change into a transitional phase so that they can all be turned
- * OFF. Once all are confirmed OFF, then this method gets called again to reset the phase to
- * normal operation. This helps to ensure that all display-OFF requests are made before
+ * {@code toState} and un/marks them for transition. When a new layout is requested, we
+ * mark the displays that will change into a transitional phase so that they can all be turned
+ * OFF. Once all are confirmed OFF, then this method gets called again to reset transition
+ * marker. This helps to ensure that all display-OFF requests are made before
* display-ON which in turn hides any resizing-jank windows might incur when switching displays.
*
* @param fromState The state we are switching from.
* @param toState The state we are switching to.
- * @param phase The new phase to apply to the displays.
+ * @param transitionValue The value to mark the transition state: true == transitioning.
*/
- private void resetLayoutLocked(int fromState, int toState, @DisplayPhase int phase) {
+ private void resetLayoutLocked(int fromState, int toState, boolean transitionValue) {
final Layout fromLayout = mDeviceStateToLayoutMap.get(fromState);
final Layout toLayout = mDeviceStateToLayoutMap.get(toState);
@@ -866,12 +886,16 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
// new layout.
final DisplayAddress address = device.getDisplayDeviceInfoLocked().address;
- // Virtual displays do not have addresses.
+ // Virtual displays do not have addresses, so account for nulls.
final Layout.Display fromDisplay =
address != null ? fromLayout.getByAddress(address) : null;
final Layout.Display toDisplay =
address != null ? toLayout.getByAddress(address) : null;
+ // If the display is in one of the layouts but not the other, then the content will
+ // change, so in this case we also want to blank the displays to avoid jank.
+ final boolean displayNotInBothLayouts = (fromDisplay == null) != (toDisplay == null);
+
// If a layout doesn't mention a display-device at all, then the display-device defaults
// to enabled. This is why we treat null as "enabled" in the code below.
final boolean wasEnabled = fromDisplay == null || fromDisplay.isEnabled();
@@ -886,16 +910,23 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
// 3) It's enabled, but it's mapped to a new logical display ID. To the user this
// would look like apps moving from one screen to another since task-stacks stay
// with the logical display [ID].
+ // 4) It's in one layout but not the other, so the content will change.
final boolean isTransitioning =
- (logicalDisplay.getPhase() == LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION)
+ logicalDisplay.isInTransitionLocked()
|| (wasEnabled != willBeEnabled)
- || deviceHasNewLogicalDisplayId;
+ || deviceHasNewLogicalDisplayId
+ || displayNotInBothLayouts;
if (isTransitioning) {
- setDisplayPhase(logicalDisplay, phase);
- if (phase == LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION) {
- mUpdatedLogicalDisplays.put(displayId, UPDATE_STATE_TRANSITION);
+ if (transitionValue != logicalDisplay.isInTransitionLocked()) {
+ Slog.i(TAG, "Set isInTransition on display " + displayId + ": "
+ + transitionValue);
}
+ // This will either mark the display as "transitioning" if we are starting to change
+ // the device state, or remove the transitioning marker if the state change is
+ // ending.
+ logicalDisplay.setIsInTransitionLocked(transitionValue);
+ mUpdatedLogicalDisplays.put(displayId, UPDATE_STATE_TRANSITION);
}
}
}
@@ -940,9 +971,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
newDisplay.swapDisplaysLocked(oldDisplay);
}
- if (!displayLayout.isEnabled()) {
- setDisplayPhase(newDisplay, LogicalDisplay.DISPLAY_PHASE_DISABLED);
- }
+ setEnabledLocked(newDisplay, displayLayout.isEnabled());
}
}
@@ -961,23 +990,25 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
final LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device);
display.updateLocked(mDisplayDeviceRepo);
mLogicalDisplays.put(displayId, display);
- setDisplayPhase(display, LogicalDisplay.DISPLAY_PHASE_ENABLED);
return display;
}
- private void setDisplayPhase(LogicalDisplay display, @DisplayPhase int phase) {
+ private void setEnabledLocked(LogicalDisplay display, boolean isEnabled) {
final int displayId = display.getDisplayIdLocked();
final DisplayInfo info = display.getDisplayInfoLocked();
final boolean disallowSecondaryDisplay = mSingleDisplayDemoMode
&& (info.type != Display.TYPE_INTERNAL);
- if (phase != LogicalDisplay.DISPLAY_PHASE_DISABLED && disallowSecondaryDisplay) {
+ if (isEnabled && disallowSecondaryDisplay) {
Slog.i(TAG, "Not creating a logical display for a secondary display because single"
+ " display demo mode is enabled: " + display.getDisplayInfoLocked());
- phase = LogicalDisplay.DISPLAY_PHASE_DISABLED;
+ isEnabled = false;
}
- display.setPhase(phase);
+ if (display.isEnabledLocked() != isEnabled) {
+ Slog.i(TAG, "SetEnabled on display " + displayId + ": " + isEnabled);
+ display.setEnabledLocked(isEnabled);
+ }
}
private int assignDisplayGroupIdLocked(
diff --git a/services/core/java/com/android/server/display/utils/SensorUtils.java b/services/core/java/com/android/server/display/utils/SensorUtils.java
index cb40b406899f..4924ad525fcc 100644
--- a/services/core/java/com/android/server/display/utils/SensorUtils.java
+++ b/services/core/java/com/android/server/display/utils/SensorUtils.java
@@ -33,6 +33,9 @@ public class SensorUtils {
*/
public static Sensor findSensor(SensorManager sensorManager, String sensorType,
String sensorName, int fallbackType) {
+ if ("".equals(sensorName) && "".equals(sensorType)) {
+ return null;
+ }
final boolean isNameSpecified = !TextUtils.isEmpty(sensorName);
final boolean isTypeSpecified = !TextUtils.isEmpty(sensorType);
if (isNameSpecified || isTypeSpecified) {
diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
index fac001e7828f..c2157a6497de 100644
--- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java
+++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
@@ -79,9 +79,10 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
// (requires restart)
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final int MSG_SWITCH_KEYBOARD_LAYOUT = 1;
- private static final int MSG_RELOAD_KEYBOARD_LAYOUTS = 2;
- private static final int MSG_UPDATE_KEYBOARD_LAYOUTS = 3;
+ private static final int MSG_UPDATE_EXISTING_DEVICES = 1;
+ private static final int MSG_SWITCH_KEYBOARD_LAYOUT = 2;
+ private static final int MSG_RELOAD_KEYBOARD_LAYOUTS = 3;
+ private static final int MSG_UPDATE_KEYBOARD_LAYOUTS = 4;
private final Context mContext;
private final NativeInputManagerService mNative;
@@ -121,10 +122,10 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
InputManager inputManager = Objects.requireNonNull(
mContext.getSystemService(InputManager.class));
inputManager.registerInputDeviceListener(this, mHandler);
- // Circle through all the already added input devices
- for (int deviceId : inputManager.getInputDeviceIds()) {
- onInputDeviceAdded(deviceId);
- }
+
+ Message msg = Message.obtain(mHandler, MSG_UPDATE_EXISTING_DEVICES,
+ inputManager.getInputDeviceIds());
+ mHandler.sendMessage(msg);
}
@Override
@@ -682,6 +683,13 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
private boolean handleMessage(Message msg) {
switch (msg.what) {
+ case MSG_UPDATE_EXISTING_DEVICES:
+ // Circle through all the already added input devices
+ // Need to do it on handler thread and not block IMS thread
+ for (int deviceId : (int[]) msg.obj) {
+ onInputDeviceAdded(deviceId);
+ }
+ return true;
case MSG_SWITCH_KEYBOARD_LAYOUT:
handleSwitchKeyboardLayout(msg.arg1, msg.arg2);
return true;
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index 90245b5ebda9..7f6c2d6ececc 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -73,6 +73,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledThreadPoolExecutor;
@@ -147,7 +148,6 @@ public class ContextHubService extends IContextHubService.Stub {
private final ScheduledThreadPoolExecutor mDailyMetricTimer =
new ScheduledThreadPoolExecutor(1);
-
// The period of the recurring time
private static final int PERIOD_METRIC_QUERY_DAYS = 1;
@@ -363,11 +363,13 @@ public class ContextHubService extends IContextHubService.Stub {
*/
private void initDefaultClientMap() {
HashMap<Integer, IContextHubClient> defaultClientMap = new HashMap<>();
- for (int contextHubId : mContextHubIdToInfoMap.keySet()) {
+ for (Map.Entry<Integer, ContextHubInfo> entry: mContextHubIdToInfoMap.entrySet()) {
+ int contextHubId = entry.getKey();
+ ContextHubInfo contextHubInfo = entry.getValue();
+
mLastRestartTimestampMap.put(contextHubId,
new AtomicLong(SystemClock.elapsedRealtimeNanos()));
- ContextHubInfo contextHubInfo = mContextHubIdToInfoMap.get(contextHubId);
IContextHubClient client = mClientManager.registerClient(
contextHubInfo, createDefaultClientCallback(contextHubId),
/* attributionTag= */ null, mTransactionManager, mContext.getPackageName());
@@ -1133,6 +1135,26 @@ public class ContextHubService extends IContextHubService.Stub {
mTransactionManager.addTransaction(transaction);
}
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
+ /**
+ * Queries for a list of preloaded nanoapp IDs from the specified Context Hub.
+ *
+ * @param hubInfo The Context Hub to query a list of nanoapps from.
+ * @return The list of 64-bit IDs of the preloaded nanoapps.
+ * @throws NullPointerException if hubInfo is null
+ */
+ @Override
+ public long[] getPreloadedNanoAppIds(ContextHubInfo hubInfo) throws RemoteException {
+ super.getPreloadedNanoAppIds_enforcePermission();
+ Objects.requireNonNull(hubInfo, "hubInfo cannot be null");
+
+ long[] nanoappIds = mContextHubWrapper.getPreloadedNanoappIds();
+ if (nanoappIds == null) {
+ return new long[0];
+ }
+ return nanoappIds;
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
@@ -1160,6 +1182,10 @@ public class ContextHubService extends IContextHubService.Stub {
mNanoAppStateManager.foreachNanoAppInstanceInfo((info) -> pw.println(info));
pw.println("");
+ pw.println("=================== PRELOADED NANOAPPS ====================");
+ dumpPreloadedNanoapps(pw);
+
+ pw.println("");
pw.println("=================== CLIENTS ====================");
pw.println(mClientManager);
@@ -1201,6 +1227,21 @@ public class ContextHubService extends IContextHubService.Stub {
proto.flush();
}
+ /**
+ * Dumps preloaded nanoapps to the console
+ */
+ private void dumpPreloadedNanoapps(PrintWriter pw) {
+ if (mContextHubWrapper == null) {
+ return;
+ }
+
+ long[] preloadedNanoappIds = mContextHubWrapper.getPreloadedNanoappIds();
+ for (long preloadedNanoappId: preloadedNanoappIds) {
+ pw.print("ID: 0x");
+ pw.println(Long.toHexString(preloadedNanoappId));
+ }
+ }
+
private void checkPermissions() {
ContextHubServiceUtil.checkPermissions(mContext);
}
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index 48152b40aaf6..f55ae6e8aed4 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -359,6 +359,14 @@ public abstract class IContextHubWrapper {
public abstract int queryNanoapps(int contextHubId) throws RemoteException;
/**
+ * Provides the list of preloaded nanoapp IDs on the system. The output of this API must
+ * not change.
+ *
+ * @return The list of preloaded nanoapp IDs
+ */
+ public abstract long[] getPreloadedNanoappIds();
+
+ /**
* Registers a callback with the Context Hub.
*
* @param contextHubId The ID of the Context Hub to register the callback with.
@@ -683,6 +691,20 @@ public abstract class IContextHubWrapper {
}
}
+ public long[] getPreloadedNanoappIds() {
+ android.hardware.contexthub.IContextHub hub = getHub();
+ if (hub == null) {
+ return null;
+ }
+
+ try {
+ return hub.getPreloadedNanoappIds();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception while getting preloaded nanoapp IDs: " + e.getMessage());
+ return null;
+ }
+ }
+
public void registerExistingCallback(int contextHubId) {
android.hardware.contexthub.IContextHub hub = getHub();
if (hub == null) {
@@ -863,6 +885,10 @@ public abstract class IContextHubWrapper {
mHub.queryApps(contextHubId));
}
+ public long[] getPreloadedNanoappIds() {
+ return new long[0];
+ }
+
public void registerCallback(int contextHubId, ICallback callback) throws RemoteException {
mHidlCallbackMap.put(contextHubId,
new ContextHubWrapperHidlCallback(contextHubId, callback));
diff --git a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
index dcdb881df12b..72ce38b72340 100644
--- a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
+++ b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
@@ -275,6 +275,10 @@ final class MediaButtonReceiverHolder {
String.valueOf(mComponentType));
}
+ public ComponentName getComponentName() {
+ return mComponentName;
+ }
+
@ComponentType
private static int getComponentType(PendingIntent pendingIntent) {
if (pendingIntent.isBroadcast()) {
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 283640d7613a..5d01aebab29b 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -3923,13 +3923,20 @@ final class InstallPackageHelper {
&& !pkgSetting.getPathString().equals(parsedPackage.getPath());
final boolean newPkgVersionGreater = pkgAlreadyExists
&& parsedPackage.getLongVersionCode() > pkgSetting.getVersionCode();
+ final boolean newSharedUserSetting = pkgAlreadyExists
+ && (initialScanRequest.mOldSharedUserSetting
+ != initialScanRequest.mSharedUserSetting);
final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated
- && newPkgChangedPaths && newPkgVersionGreater;
+ && newPkgChangedPaths && (newPkgVersionGreater || newSharedUserSetting);
if (isSystemPkgBetter) {
// The version of the application on /system is greater than the version on
// /data. Switch back to the application on /system.
// It's safe to assume the application on /system will correctly scan. If not,
// there won't be a working copy of the application.
+ // Also, if the sharedUserSetting of the application on /system is different
+ // from the sharedUserSetting on /data, switch back to the application on /system.
+ // We should trust the sharedUserSetting on /system, even if the application
+ // version on /system is smaller than the version on /data.
synchronized (mPm.mLock) {
// just remove the loaded entries from package lists
mPm.mPackages.remove(pkgSetting.getPackageName());
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 0bdd98038f83..046db921e245 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -320,15 +320,13 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
switch (profileType) {
case ArtManager.PROFILE_APPS :
- return SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false);
+ return true;
case ArtManager.PROFILE_BOOT_IMAGE:
// The device config property overrides the system property version.
boolean profileBootClassPath = SystemProperties.getBoolean(
"persist.device_config.runtime_native_boot.profilebootclasspath",
SystemProperties.getBoolean("dalvik.vm.profilebootclasspath", false));
- return (Build.IS_USERDEBUG || Build.IS_ENG) &&
- SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false) &&
- profileBootClassPath;
+ return (Build.IS_USERDEBUG || Build.IS_ENG) && profileBootClassPath;
default:
throw new IllegalArgumentException("Invalid profile type:" + profileType);
}
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 558202b99de2..a3fa25d2c03d 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -533,7 +533,7 @@ public class PackageInfoUtils {
ai.metaData = null;
}
ai.applicationInfo = applicationInfo;
- ai.targetDisplayCategory = a.getTargetDisplayCategory();
+ ai.requiredDisplayCategory = a.getRequiredDisplayCategory();
ai.setKnownActivityEmbeddingCerts(a.getKnownActivityEmbeddingCerts());
assignFieldsComponentInfoParsedMainComponent(ai, a, pkgSetting, userId);
return ai;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java b/services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java
index e019215f1ad8..1826f7a38e26 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java
@@ -98,8 +98,8 @@ public interface ParsedActivity extends ParsedMainComponent {
boolean isSupportsSizeChanges();
/**
- * Gets the category of the target display this activity is supposed to run on.
+ * Gets the required category of the display this activity is supposed to run on.
*/
@Nullable
- String getTargetDisplayCategory();
+ String getRequiredDisplayCategory();
}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java
index 278e547dd742..68d5428d6604 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java
@@ -97,7 +97,7 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
private ActivityInfo.WindowLayout windowLayout;
@Nullable
- private String mTargetDisplayCategory;
+ private String mRequiredDisplayCategory;
public ParsedActivityImpl(ParsedActivityImpl other) {
super(other);
@@ -125,7 +125,7 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
this.colorMode = other.colorMode;
this.windowLayout = other.windowLayout;
this.mKnownActivityEmbeddingCerts = other.mKnownActivityEmbeddingCerts;
- this.mTargetDisplayCategory = other.mTargetDisplayCategory;
+ this.mRequiredDisplayCategory = other.mRequiredDisplayCategory;
}
/**
@@ -193,7 +193,7 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
alias.requestedVrComponent = target.getRequestedVrComponent();
alias.setDirectBootAware(target.isDirectBootAware());
alias.setProcessName(target.getProcessName());
- alias.setTargetDisplayCategory(target.getTargetDisplayCategory());
+ alias.setRequiredDisplayCategory(target.getRequiredDisplayCategory());
return alias;
// Not all attributes from the target ParsedActivity are copied to the alias.
@@ -321,7 +321,7 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
dest.writeBoolean(false);
}
sForStringSet.parcel(this.mKnownActivityEmbeddingCerts, dest, flags);
- dest.writeString8(this.mTargetDisplayCategory);
+ dest.writeString8(this.mRequiredDisplayCategory);
}
public ParsedActivityImpl() {
@@ -356,7 +356,7 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
windowLayout = new ActivityInfo.WindowLayout(in);
}
this.mKnownActivityEmbeddingCerts = sForStringSet.unparcel(in);
- this.mTargetDisplayCategory = in.readString8();
+ this.mRequiredDisplayCategory = in.readString8();
}
@NonNull
@@ -414,7 +414,7 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
int rotationAnimation,
int colorMode,
@Nullable ActivityInfo.WindowLayout windowLayout,
- @Nullable String targetDisplayCategory) {
+ @Nullable String requiredDisplayCategory) {
this.theme = theme;
this.uiOptions = uiOptions;
this.targetActivity = targetActivity;
@@ -439,7 +439,7 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
this.rotationAnimation = rotationAnimation;
this.colorMode = colorMode;
this.windowLayout = windowLayout;
- this.mTargetDisplayCategory = targetDisplayCategory;
+ this.mRequiredDisplayCategory = requiredDisplayCategory;
// onConstructed(); // You can define this method to get a callback
}
@@ -560,8 +560,8 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
}
@DataClass.Generated.Member
- public @Nullable String getTargetDisplayCategory() {
- return mTargetDisplayCategory;
+ public @Nullable String getRequiredDisplayCategory() {
+ return mRequiredDisplayCategory;
}
@DataClass.Generated.Member
@@ -691,16 +691,16 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
}
@DataClass.Generated.Member
- public @NonNull ParsedActivityImpl setTargetDisplayCategory(@NonNull String value) {
- mTargetDisplayCategory = value;
+ public @NonNull ParsedActivityImpl setRequiredDisplayCategory(@NonNull String value) {
+ mRequiredDisplayCategory = value;
return this;
}
@DataClass.Generated(
- time = 1664805688714L,
+ time = 1669437519576L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java",
- inputSignatures = "private int theme\nprivate int uiOptions\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetActivity\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String parentActivityName\nprivate @android.annotation.Nullable java.lang.String taskAffinity\nprivate int privateFlags\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> mKnownActivityEmbeddingCerts\nprivate int launchMode\nprivate int documentLaunchMode\nprivate int maxRecents\nprivate int configChanges\nprivate int softInputMode\nprivate int persistableMode\nprivate int lockTaskLaunchMode\nprivate int screenOrientation\nprivate int resizeMode\nprivate float maxAspectRatio\nprivate float minAspectRatio\nprivate boolean supportsSizeChanges\nprivate @android.annotation.Nullable java.lang.String requestedVrComponent\nprivate int rotationAnimation\nprivate int colorMode\nprivate @android.annotation.Nullable android.content.pm.ActivityInfo.WindowLayout windowLayout\nprivate @android.annotation.Nullable java.lang.String mTargetDisplayCategory\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.server.pm.pkg.component.ParsedActivityImpl> CREATOR\nstatic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedActivityImpl makeAppDetailsActivity(java.lang.String,java.lang.String,int,java.lang.String,boolean)\nstatic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedActivityImpl makeAlias(java.lang.String,com.android.server.pm.pkg.component.ParsedActivity)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setMaxAspectRatio(int,float)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setMinAspectRatio(int,float)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setTargetActivity(java.lang.String)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setPermission(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override java.util.Set<java.lang.String> getKnownActivityEmbeddingCerts()\npublic void setKnownActivityEmbeddingCerts(java.util.Set<java.lang.String>)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedActivityImpl extends com.android.server.pm.pkg.component.ParsedMainComponentImpl implements [com.android.server.pm.pkg.component.ParsedActivity, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
+ inputSignatures = "private int theme\nprivate int uiOptions\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetActivity\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String parentActivityName\nprivate @android.annotation.Nullable java.lang.String taskAffinity\nprivate int privateFlags\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> mKnownActivityEmbeddingCerts\nprivate int launchMode\nprivate int documentLaunchMode\nprivate int maxRecents\nprivate int configChanges\nprivate int softInputMode\nprivate int persistableMode\nprivate int lockTaskLaunchMode\nprivate int screenOrientation\nprivate int resizeMode\nprivate float maxAspectRatio\nprivate float minAspectRatio\nprivate boolean supportsSizeChanges\nprivate @android.annotation.Nullable java.lang.String requestedVrComponent\nprivate int rotationAnimation\nprivate int colorMode\nprivate @android.annotation.Nullable android.content.pm.ActivityInfo.WindowLayout windowLayout\nprivate @android.annotation.Nullable java.lang.String mRequiredDisplayCategory\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.server.pm.pkg.component.ParsedActivityImpl> CREATOR\nstatic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedActivityImpl makeAppDetailsActivity(java.lang.String,java.lang.String,int,java.lang.String,boolean)\nstatic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedActivityImpl makeAlias(java.lang.String,com.android.server.pm.pkg.component.ParsedActivity)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setMaxAspectRatio(int,float)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setMinAspectRatio(int,float)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setTargetActivity(java.lang.String)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setPermission(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override java.util.Set<java.lang.String> getKnownActivityEmbeddingCerts()\npublic void setKnownActivityEmbeddingCerts(java.util.Set<java.lang.String>)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedActivityImpl extends com.android.server.pm.pkg.component.ParsedMainComponentImpl implements [com.android.server.pm.pkg.component.ParsedActivity, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
index 305062b4ce7e..ea791e1f29db 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
@@ -220,17 +220,17 @@ public class ParsedActivityUtils {
pkg.setVisibleToInstantApps(true);
}
- String targetDisplayCategory = sa.getNonConfigurationString(
- R.styleable.AndroidManifestActivity_targetDisplayCategory, 0);
+ String requiredDisplayCategory = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestActivity_requiredDisplayCategory, 0);
- if (targetDisplayCategory != null
- && FrameworkParsingPackageUtils.validateName(targetDisplayCategory,
+ if (requiredDisplayCategory != null
+ && FrameworkParsingPackageUtils.validateName(requiredDisplayCategory,
false /* requireSeparator */, false /* requireFilename */) != null) {
- return input.error("targetDisplayCategory attribute can only consists of "
- + "alphanumeric characters, '_', and '.'");
+ return input.error("requiredDisplayCategory attribute can only consist "
+ + "of alphanumeric characters, '_', and '.'");
}
- activity.setTargetDisplayCategory(targetDisplayCategory);
+ activity.setRequiredDisplayCategory(requiredDisplayCategory);
return parseActivityOrAlias(activity, pkg, tag, parser, res, sa, receiver,
false /*isAlias*/, visibleToEphemeral, input,
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java
index 9953ca8f9b65..135841729e69 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsService.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java
@@ -83,6 +83,9 @@ public class PowerStatsService extends SystemService {
@Nullable
@GuardedBy("this")
private Looper mLooper;
+ @Nullable
+ @GuardedBy("this")
+ private EnergyConsumer[] mEnergyConsumers = null;
@VisibleForTesting
static class Injector {
@@ -260,6 +263,15 @@ public class PowerStatsService extends SystemService {
}
}
+ private EnergyConsumer[] getEnergyConsumerInfo() {
+ synchronized (this) {
+ if (mEnergyConsumers == null) {
+ mEnergyConsumers = getPowerStatsHal().getEnergyConsumerInfo();
+ }
+ return mEnergyConsumers;
+ }
+ }
+
public PowerStatsService(Context context) {
this(context, new Injector());
}
@@ -327,7 +339,69 @@ public class PowerStatsService extends SystemService {
private void getEnergyConsumedAsync(CompletableFuture<EnergyConsumerResult[]> future,
int[] energyConsumerIds) {
- future.complete(getPowerStatsHal().getEnergyConsumed(energyConsumerIds));
+ EnergyConsumerResult[] results = getPowerStatsHal().getEnergyConsumed(energyConsumerIds);
+
+ // STOPSHIP(253292374): Remove once missing EnergyConsumer results issue is resolved.
+ EnergyConsumer[] energyConsumers = getEnergyConsumerInfo();
+ if (energyConsumers != null) {
+ final int expectedLength;
+ if (energyConsumerIds.length == 0) {
+ // Empty request is a request for all available EnergyConsumers.
+ expectedLength = energyConsumers.length;
+ } else {
+ expectedLength = energyConsumerIds.length;
+ }
+
+ if (results == null || expectedLength != results.length) {
+ // Mismatch in requested/received energy consumer data.
+ StringBuilder sb = new StringBuilder();
+ sb.append("Requested ids:");
+ if (energyConsumerIds.length == 0) {
+ sb.append("ALL");
+ }
+ sb.append("[");
+ for (int i = 0; i < expectedLength; i++) {
+ final int id = energyConsumerIds[i];
+ sb.append(id);
+ sb.append("(type:");
+ sb.append(energyConsumers[id].type);
+ sb.append(",ord:");
+ sb.append(energyConsumers[id].ordinal);
+ sb.append(",name:");
+ sb.append(energyConsumers[id].name);
+ sb.append(")");
+ if (i != expectedLength - 1) {
+ sb.append(", ");
+ }
+ }
+ sb.append("]");
+
+ sb.append(", Received result ids:");
+ if (results == null) {
+ sb.append("null");
+ } else {
+ sb.append("[");
+ final int resultLength = results.length;
+ for (int i = 0; i < resultLength; i++) {
+ final int id = results[i].id;
+ sb.append(id);
+ sb.append("(type:");
+ sb.append(energyConsumers[id].type);
+ sb.append(",ord:");
+ sb.append(energyConsumers[id].ordinal);
+ sb.append(",name:");
+ sb.append(energyConsumers[id].name);
+ sb.append(")");
+ if (i != resultLength - 1) {
+ sb.append(", ");
+ }
+ }
+ sb.append("]");
+ }
+ Slog.wtf(TAG, "Missing result from getEnergyConsumedAsync call. " + sb);
+ }
+ }
+ future.complete(results);
}
private void getStateResidencyAsync(CompletableFuture<StateResidencyResult[]> future,
diff --git a/services/core/java/com/android/server/utils/EventLogger.java b/services/core/java/com/android/server/utils/EventLogger.java
index 770cb7258319..4772bbfe97dd 100644
--- a/services/core/java/com/android/server/utils/EventLogger.java
+++ b/services/core/java/com/android/server/utils/EventLogger.java
@@ -36,8 +36,11 @@ import java.util.Locale;
*/
public class EventLogger {
+ /** Prefix for the title added at the beginning of a {@link #dump(PrintWriter)} operation */
+ private static final String DUMP_TITLE_PREFIX = "Events log: ";
+
/** Identifies the source of events. */
- private final String mTag;
+ @Nullable private final String mTag;
/** Stores the events using a ring buffer. */
private final ArrayDeque<Event> mEvents;
@@ -55,7 +58,7 @@ public class EventLogger {
* @param size the maximum number of events to keep in log
* @param tag the string displayed before the recorded log
*/
- public EventLogger(int size, String tag) {
+ public EventLogger(int size, @Nullable String tag) {
mEvents = new ArrayDeque<>(size);
mMemSize = size;
mTag = tag;
@@ -64,10 +67,10 @@ public class EventLogger {
/** Enqueues {@code event} to be logged. */
public synchronized void enqueue(Event event) {
if (mEvents.size() >= mMemSize) {
- mEvents.removeLast();
+ mEvents.removeFirst();
}
- mEvents.addFirst(event);
+ mEvents.addLast(event);
}
/**
@@ -91,13 +94,19 @@ public class EventLogger {
dump(pw, "" /* prefix */);
}
+ protected String getDumpTitle() {
+ if (mTag == null) {
+ return DUMP_TITLE_PREFIX;
+ }
+ return DUMP_TITLE_PREFIX + mTag;
+ }
+
/** Dumps events using {@link PrintWriter} with a certain indent. */
public synchronized void dump(PrintWriter pw, String indent) {
- pw.println(indent + "Events log: " + mTag);
+ pw.println(getDumpTitle());
- String childrenIndention = indent + " ";
for (Event evt : mEvents) {
- pw.println(childrenIndention + evt.toString());
+ pw.println(indent + evt.toString());
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 12424c0c6a6f..e099aac8c73e 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -8918,9 +8918,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN)
- && getParent().getConfiguration().orientation == ORIENTATION_PORTRAIT
- && getParent().getWindowConfiguration().getWindowingMode()
- == WINDOWING_MODE_FULLSCREEN) {
+ && isParentFullscreenPortrait()) {
// We are using the parent configuration here as this is the most recent one that gets
// passed to onConfigurationChanged when a relevant change takes place
return info.getMinAspectRatio();
@@ -8943,6 +8941,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return info.getMinAspectRatio();
}
+ private boolean isParentFullscreenPortrait() {
+ final WindowContainer parent = getParent();
+ return parent != null
+ && parent.getConfiguration().orientation == ORIENTATION_PORTRAIT
+ && parent.getWindowConfiguration().getWindowingMode() == WINDOWING_MODE_FULLSCREEN;
+ }
+
/**
* Returns true if the activity has maximum or minimum aspect ratio.
*/
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 0b16a4d47a7d..5c83c4f940d3 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -53,7 +53,9 @@ import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
import static android.content.pm.ActivityInfo.launchModeToString;
import static android.os.Process.INVALID_UID;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
@@ -2139,6 +2141,11 @@ class ActivityStarter {
if (actuallyMoved) {
// Only record if the activity actually moved.
mMovedToTopActivity = act;
+ if (mNoAnimation) {
+ act.mDisplayContent.prepareAppTransition(TRANSIT_NONE);
+ } else {
+ act.mDisplayContent.prepareAppTransition(TRANSIT_TO_FRONT);
+ }
}
act.updateOptionsLocked(mOptions);
deliverNewIntent(act, intentGrants);
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index d3452277a29f..cd26e2eb9c53 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -226,6 +226,9 @@ class BLASTSyncEngine {
}
private void setReady(boolean ready) {
+ if (mReady == ready) {
+ return;
+ }
ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Set ready", mSyncId);
mReady = ready;
if (!ready) return;
@@ -239,7 +242,9 @@ class BLASTSyncEngine {
ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Adding to group: %s", mSyncId, wc);
wc.setSyncGroup(this);
wc.prepareSync();
- mWm.mWindowPlacerLocked.requestTraversal();
+ if (mReady) {
+ mWm.mWindowPlacerLocked.requestTraversal();
+ }
}
void onCancelSync(WindowContainer wc) {
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index bedeabeb6141..89f1bd043556 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -343,7 +343,11 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
if (childArea == null) {
continue;
}
- pw.println(prefix + "* " + childArea.getName());
+ pw.print(prefix + "* " + childArea.getName());
+ if (childArea.isOrganized()) {
+ pw.print(" (organized)");
+ }
+ pw.println();
if (childArea.isTaskDisplayArea()) {
// TaskDisplayArea can only contain task. And it is already printed by display.
continue;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index d1122e14c57b..690779d8951e 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3516,9 +3516,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
@Override
public void dump(PrintWriter pw, String prefix, boolean dumpAll) {
- super.dump(pw, prefix, dumpAll);
pw.print(prefix);
- pw.println("Display: mDisplayId=" + mDisplayId + " rootTasks=" + getRootTaskCount());
+ pw.println("Display: mDisplayId=" + mDisplayId + (isOrganized() ? " (organized)" : ""));
final String subPrefix = " " + prefix;
pw.print(subPrefix); pw.print("init="); pw.print(mInitialDisplayWidth); pw.print("x");
pw.print(mInitialDisplayHeight); pw.print(" "); pw.print(mInitialDisplayDensity);
@@ -3549,6 +3548,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
pw.println(" mTouchExcludeRegion=" + mTouchExcludeRegion);
pw.println();
+ super.dump(pw, prefix, dumpAll);
pw.print(prefix); pw.print("mLayoutSeq="); pw.println(mLayoutSeq);
pw.print(" mCurrentFocus="); pw.println(mCurrentFocus);
@@ -3640,6 +3640,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
pw.println();
mInsetsStateController.dump(prefix, pw);
mDwpcHelper.dump(prefix, pw);
+ pw.println();
}
@Override
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index d53ee1e9fa51..64cca87db65a 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -3407,7 +3407,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
final DisplayContent display = getChildAt(i);
display.dump(pw, prefix, dumpAll);
}
- pw.println();
}
/**
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index d3a3cf5c6468..443e3049b2ea 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1151,8 +1151,16 @@ class TaskFragment extends WindowContainer<WindowContainer> {
}
next.delayedResume = false;
- final TaskDisplayArea taskDisplayArea = getDisplayArea();
+ // If we are currently pausing an activity, then don't do anything until that is done.
+ final boolean allPausedComplete = mRootWindowContainer.allPausedActivitiesComplete();
+ if (!allPausedComplete) {
+ ProtoLog.v(WM_DEBUG_STATES,
+ "resumeTopActivity: Skip resume: some activity pausing.");
+ return false;
+ }
+
+ final TaskDisplayArea taskDisplayArea = getDisplayArea();
// If the top activity is the resumed one, nothing to do.
if (mResumedActivity == next && next.isState(RESUMED)
&& taskDisplayArea.allResumedActivitiesComplete()) {
@@ -1175,14 +1183,6 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return false;
}
- // If we are currently pausing an activity, then don't do anything until that is done.
- final boolean allPausedComplete = mRootWindowContainer.allPausedActivitiesComplete();
- if (!allPausedComplete) {
- ProtoLog.v(WM_DEBUG_STATES,
- "resumeTopActivity: Skip resume: some activity pausing.");
- return false;
- }
-
// If we are sleeping, and there is no resumed activity, and the top activity is paused,
// well that is the state we want.
if (mLastPausedActivity == next && shouldSleepOrShutDownActivities()) {
@@ -2580,6 +2580,14 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return false;
}
+ @Override
+ boolean canCustomizeAppTransition() {
+ // This is only called when the app transition is going to be played by system server. In
+ // this case, we should allow custom app transition for fullscreen embedded TaskFragment
+ // just like Activity.
+ return isEmbedded() && matchParentBounds();
+ }
+
/** Clear {@link #mLastPausedActivity} for all {@link TaskFragment} children */
void clearLastPausedActivity() {
forAllTaskFragments(taskFragment -> taskFragment.mLastPausedActivity = null);
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 6d149da99cb0..da73fad99eb7 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -278,31 +278,29 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
// is set with the suggestedDisplayArea. If it is set, but the eventual TaskDisplayArea is
// different, we should recalcuating the bounds.
boolean hasInitialBoundsForSuggestedDisplayAreaInFreeformMode = false;
- // shouldSetAsOverrideWindowingMode is set if the task needs to retain the launchMode
- // regardless of the windowing mode of the parent.
- boolean shouldSetAsOverrideWindowingMode = false;
- if (launchMode == WINDOWING_MODE_PINNED) {
- if (DEBUG) appendLog("picture-in-picture");
- } else if (!root.isResizeable()) {
- if (shouldLaunchUnresizableAppInFreeformInFreeformMode(root, suggestedDisplayArea,
- options)) {
- launchMode = WINDOWING_MODE_UNDEFINED;
- if (outParams.mBounds.isEmpty()) {
- getTaskBounds(root, suggestedDisplayArea, layout, launchMode, hasInitialBounds,
- outParams.mBounds);
- hasInitialBoundsForSuggestedDisplayAreaInFreeformMode = true;
+ if (suggestedDisplayArea.inFreeformWindowingMode()) {
+ if (launchMode == WINDOWING_MODE_PINNED) {
+ if (DEBUG) appendLog("picture-in-picture");
+ } else if (!root.isResizeable()) {
+ if (shouldLaunchUnresizableAppInFreeform(root, suggestedDisplayArea, options)) {
+ launchMode = WINDOWING_MODE_FREEFORM;
+ if (outParams.mBounds.isEmpty()) {
+ getTaskBounds(root, suggestedDisplayArea, layout, launchMode,
+ hasInitialBounds, outParams.mBounds);
+ hasInitialBoundsForSuggestedDisplayAreaInFreeformMode = true;
+ }
+ if (DEBUG) appendLog("unresizable-freeform");
+ } else {
+ launchMode = WINDOWING_MODE_FULLSCREEN;
+ outParams.mBounds.setEmpty();
+ if (DEBUG) appendLog("unresizable-forced-maximize");
}
- if (DEBUG) appendLog("unresizable-freeform");
- } else {
- launchMode = WINDOWING_MODE_FULLSCREEN;
- outParams.mBounds.setEmpty();
- shouldSetAsOverrideWindowingMode = true;
- if (DEBUG) appendLog("unresizable-forced-maximize");
}
+ } else {
+ if (DEBUG) appendLog("non-freeform-task-display-area");
}
// If launch mode matches display windowing mode, let it inherit from display.
outParams.mWindowingMode = launchMode == suggestedDisplayArea.getWindowingMode()
- && !shouldSetAsOverrideWindowingMode
? WINDOWING_MODE_UNDEFINED : launchMode;
if (phase == PHASE_WINDOWING_MODE) {
@@ -669,7 +667,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
inOutBounds.offset(xOffset, yOffset);
}
- private boolean shouldLaunchUnresizableAppInFreeformInFreeformMode(ActivityRecord activity,
+ private boolean shouldLaunchUnresizableAppInFreeform(ActivityRecord activity,
TaskDisplayArea displayArea, @Nullable ActivityOptions options) {
if (options != null && options.getLaunchWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
// Do not launch the activity in freeform if it explicitly requested fullscreen mode.
@@ -682,7 +680,8 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
final int displayOrientation = orientationFromBounds(displayArea.getBounds());
final int activityOrientation = resolveOrientation(activity, displayArea,
displayArea.getBounds());
- if (displayOrientation != activityOrientation) {
+ if (displayArea.getWindowingMode() == WINDOWING_MODE_FREEFORM
+ && displayOrientation != activityOrientation) {
return true;
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 9cb13e420184..2b11d54898e2 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -160,9 +160,9 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
private SurfaceControl.Transaction mFinishTransaction = null;
/**
- * Contains change infos for both participants and all ancestors. We have to track ancestors
- * because they are all promotion candidates and thus we need their start-states
- * to be captured.
+ * Contains change infos for both participants and all remote-animatable ancestors. The
+ * ancestors can be the promotion candidates so their start-states need to be captured.
+ * @see #getAnimatableParent
*/
final ArrayMap<WindowContainer, ChangeInfo> mChanges = new ArrayMap<>();
@@ -221,7 +221,8 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
}
@Nullable
- static Transition fromBinder(@NonNull IBinder token) {
+ static Transition fromBinder(@Nullable IBinder token) {
+ if (token == null) return null;
try {
return ((Token) token).mTransition.get();
} catch (ClassCastException e) {
@@ -410,8 +411,9 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
mSyncId, wc);
// "snapshot" all parents (as potential promotion targets). Do this before checking
// if this is already a participant in case it has since been re-parented.
- for (WindowContainer curr = wc.getParent(); curr != null && !mChanges.containsKey(curr);
- curr = curr.getParent()) {
+ for (WindowContainer<?> curr = getAnimatableParent(wc);
+ curr != null && !mChanges.containsKey(curr);
+ curr = getAnimatableParent(curr)) {
mChanges.put(curr, new ChangeInfo(curr));
if (isReadyGroup(curr)) {
mReadyTracker.addGroup(curr);
@@ -1271,6 +1273,16 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
return sb.toString();
}
+ /** Returns the parent that the remote animator can animate or control. */
+ private static WindowContainer<?> getAnimatableParent(WindowContainer<?> wc) {
+ WindowContainer<?> parent = wc.getParent();
+ while (parent != null
+ && (!parent.canCreateRemoteAnimationTarget() && !parent.isOrganized())) {
+ parent = parent.getParent();
+ }
+ return parent;
+ }
+
private static boolean reportIfNotTop(WindowContainer wc) {
// Organized tasks need to be reported anyways because Core won't show() their surfaces
// and we can't rely on onTaskAppeared because it isn't in sync.
@@ -1494,7 +1506,8 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
intermediates.clear();
boolean foundParentInTargets = false;
// Collect the intermediate parents between target and top changed parent.
- for (WindowContainer<?> p = wc.getParent(); p != null; p = p.getParent()) {
+ for (WindowContainer<?> p = getAnimatableParent(wc); p != null;
+ p = getAnimatableParent(p)) {
final ChangeInfo parentChange = changes.get(p);
if (parentChange == null || !parentChange.hasChanged(p)) break;
if (p.mRemoteToken == null) {
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 6522d93d5267..3b30dd136c73 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -114,12 +114,6 @@ class WallpaperController {
private boolean mShouldUpdateZoom;
- /**
- * Temporary storage for taking a screenshot of the wallpaper.
- * @see #screenshotWallpaperLocked()
- */
- private WindowState mTmpTopWallpaper;
-
@Nullable private Point mLargestDisplaySize = null;
private final FindWallpaperTargetResult mFindResults = new FindWallpaperTargetResult();
@@ -965,21 +959,16 @@ class WallpaperController {
}
WindowState getTopVisibleWallpaper() {
- mTmpTopWallpaper = null;
-
for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
- token.forAllWindows(w -> {
- final WindowStateAnimator winAnim = w.mWinAnimator;
- if (winAnim != null && winAnim.getShown() && winAnim.mLastAlpha > 0f) {
- mTmpTopWallpaper = w;
- return true;
+ for (int i = token.getChildCount() - 1; i >= 0; i--) {
+ final WindowState w = token.getChildAt(i);
+ if (w.mWinAnimator.getShown() && w.mWinAnimator.mLastAlpha > 0f) {
+ return w;
}
- return false;
- }, true /* traverseTopToBottom */);
+ }
}
-
- return mTmpTopWallpaper;
+ return null;
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 19409b1f3636..73759d3a3362 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -4927,7 +4927,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// animation on the keyguard but seeing the IME window that originally on the app
// which behinds the keyguard.
final WindowState imeInputTarget = getImeInputTarget();
- if (imeInputTarget != null && !(imeInputTarget.isDrawn() || imeInputTarget.isVisible())) {
+ if (imeInputTarget != null
+ && !(imeInputTarget.isDrawn() || imeInputTarget.isVisibleRequested())) {
return false;
}
return mDisplayContent.forAllImeWindows(callback, traverseTopToBottom);
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 5d0551b4b54c..969056e7c8b4 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -310,7 +310,7 @@ public:
const InputDeviceIdentifier& identifier) override;
std::string getDeviceAlias(const InputDeviceIdentifier& identifier) override;
TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor,
- int32_t surfaceRotation) override;
+ ui::Rotation surfaceRotation) override;
TouchAffineTransformation getTouchAffineTransformation(JNIEnv* env, jfloatArray matrixArr);
void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) override;
@@ -1153,7 +1153,7 @@ TouchAffineTransformation NativeInputManager::getTouchAffineTransformation(
}
TouchAffineTransformation NativeInputManager::getTouchAffineTransformation(
- const std::string& inputDeviceDescriptor, int32_t surfaceRotation) {
+ const std::string& inputDeviceDescriptor, ui::Rotation surfaceRotation) {
JNIEnv* env = jniEnv();
ScopedLocalRef<jstring> descriptorObj(env, env->NewStringUTF(inputDeviceDescriptor.c_str()));
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/PackageParserTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/PackageParserTest.java
index 59f27ec2766e..c8e26766646f 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/PackageParserTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/PackageParserTest.java
@@ -334,7 +334,7 @@ public class PackageParserTest {
}
@Test
- public void testParseActivityTargetDisplayCategoryValid() throws Exception {
+ public void testParseActivityRequiredDisplayCategoryValid() throws Exception {
final File testFile = extractFile(TEST_APP4_APK);
String actualDisplayCategory = null;
try {
@@ -342,7 +342,7 @@ public class PackageParserTest {
final List<ParsedActivity> activities = pkg.getActivities();
for (ParsedActivity activity : activities) {
if ((PACKAGE_NAME + ".MyActivity").equals(activity.getName())) {
- actualDisplayCategory = activity.getTargetDisplayCategory();
+ actualDisplayCategory = activity.getRequiredDisplayCategory();
}
}
} finally {
@@ -352,7 +352,7 @@ public class PackageParserTest {
}
@Test
- public void testParseActivityTargetDisplayCategoryInvalid() throws Exception {
+ public void testParseActivityRequiredDisplayCategoryInvalid() throws Exception {
final File testFile = extractFile(TEST_APP6_APK);
String actualDisplayCategory = null;
try {
@@ -360,12 +360,12 @@ public class PackageParserTest {
final List<ParsedActivity> activities = pkg.getActivities();
for (ParsedActivity activity : activities) {
if ((PACKAGE_NAME + ".MyActivity").equals(activity.getName())) {
- actualDisplayCategory = activity.getTargetDisplayCategory();
+ actualDisplayCategory = activity.getRequiredDisplayCategory();
}
}
} catch (PackageManagerException e) {
assertThat(e.getMessage()).contains(
- "targetDisplayCategory attribute can only consists"
+ "requiredDisplayCategory attribute can only consist"
+ " of alphanumeric characters, '_', and '.'");
} finally {
testFile.delete();
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
index 4ceae966b7e3..0e2e35f25b15 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
@@ -54,7 +54,7 @@ class ParsedActivityTest : ParsedMainComponentTest(
ParsedActivity::getTheme,
ParsedActivity::getUiOptions,
ParsedActivity::isSupportsSizeChanges,
- ParsedActivity::getTargetDisplayCategory
+ ParsedActivity::getRequiredDisplayCategory
)
override fun mainComponentSubclassExtraParams() = listOf(
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
index 9f0d759da121..a8d894511213 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -20,6 +20,8 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSess
import static com.android.server.app.GameManagerService.CANCEL_GAME_LOADING_MODE;
import static com.android.server.app.GameManagerService.LOADING_BOOST_MAX_DURATION;
import static com.android.server.app.GameManagerService.SET_GAME_STATE;
+import static com.android.server.app.GameManagerService.WRITE_DELAY_MILLIS;
+import static com.android.server.app.GameManagerService.WRITE_GAME_MODE_INTERVENTION_LIST_FILE;
import static com.android.server.app.GameManagerService.WRITE_SETTINGS;
import static org.junit.Assert.assertArrayEquals;
@@ -1835,9 +1837,7 @@ public class GameManagerServiceTests {
public void testUpdateCustomGameModeConfiguration_permissionDenied() {
mockModifyGameModeDenied();
mockDeviceConfigAll();
- GameManagerService gameManagerService =
- new GameManagerService(mMockContext, mTestLooper.getLooper());
- startUser(gameManagerService, USER_ID_1);
+ GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
assertThrows(SecurityException.class, () -> {
gameManagerService.updateCustomGameModeConfiguration(mPackageName,
new GameModeConfiguration.Builder().setScalingFactor(0.5f).build(),
@@ -1848,9 +1848,7 @@ public class GameManagerServiceTests {
@Test
public void testUpdateCustomGameModeConfiguration_noUserId() {
mockModifyGameModeGranted();
- GameManagerService gameManagerService =
- new GameManagerService(mMockContext, mTestLooper.getLooper());
- startUser(gameManagerService, USER_ID_2);
+ GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_2);
assertThrows(IllegalArgumentException.class, () -> {
gameManagerService.updateCustomGameModeConfiguration(mPackageName,
new GameModeConfiguration.Builder().setScalingFactor(0.5f).build(),
@@ -1874,6 +1872,48 @@ public class GameManagerServiceTests {
}
@Test
+ public void testUpdateCustomGameModeConfiguration() throws InterruptedException {
+ mockModifyGameModeGranted();
+ GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+ gameManagerService.updateCustomGameModeConfiguration(mPackageName,
+ new GameModeConfiguration.Builder().setScalingFactor(0.35f).setFpsOverride(
+ 60).build(),
+ USER_ID_1);
+
+ assertTrue(gameManagerService.mHandler.hasEqualMessages(WRITE_SETTINGS, USER_ID_1));
+ assertTrue(
+ gameManagerService.mHandler.hasEqualMessages(WRITE_GAME_MODE_INTERVENTION_LIST_FILE,
+ USER_ID_1));
+
+ GameManagerService.GamePackageConfiguration pkgConfig = gameManagerService.getConfig(
+ mPackageName, USER_ID_1);
+ assertNotNull(pkgConfig);
+ GameManagerService.GamePackageConfiguration.GameModeConfiguration modeConfig =
+ pkgConfig.getGameModeConfiguration(GameManager.GAME_MODE_CUSTOM);
+ assertNotNull(modeConfig);
+ assertEquals(modeConfig.getScaling(), 0.35f, 0.01f);
+ assertEquals(modeConfig.getFps(), 60);
+ // creates a new service to check that no data has been stored
+ mTestLooper.dispatchAll();
+ gameManagerService = createServiceAndStartUser(USER_ID_1);
+ pkgConfig = gameManagerService.getConfig(mPackageName, USER_ID_1);
+ assertNull(pkgConfig);
+
+ mTestLooper.moveTimeForward(WRITE_DELAY_MILLIS + 500);
+ mTestLooper.dispatchAll();
+ // creates a new service to check that data is persisted after delay
+ gameManagerService = createServiceAndStartUser(USER_ID_1);
+ assertEquals(GameManager.GAME_MODE_STANDARD,
+ gameManagerService.getGameMode(mPackageName, USER_ID_1));
+ pkgConfig = gameManagerService.getConfig(mPackageName, USER_ID_1);
+ assertNotNull(pkgConfig);
+ modeConfig = pkgConfig.getGameModeConfiguration(GameManager.GAME_MODE_CUSTOM);
+ assertNotNull(modeConfig);
+ assertEquals(modeConfig.getScaling(), 0.35f, 0.01f);
+ assertEquals(modeConfig.getFps(), 60);
+ }
+
+ @Test
public void testWritingSettingFile_onShutdown() throws InterruptedException {
mockModifyGameModeGranted();
mockDeviceConfigAll();
diff --git a/services/tests/mockingservicestests/src/com/android/server/cpu/CpuMonitorServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/cpu/CpuMonitorServiceTest.java
new file mode 100644
index 000000000000..7ab1363b5087
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/cpu/CpuMonitorServiceTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.cpu;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.server.cpu.CpuAvailabilityMonitoringConfig.CPUSET_ALL;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Looper;
+import android.os.ServiceManager;
+
+import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
+import com.android.server.ExtendedMockitoTestCase;
+import com.android.server.LocalServices;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+public final class CpuMonitorServiceTest extends ExtendedMockitoTestCase {
+ private static final CpuAvailabilityMonitoringConfig TEST_CPU_AVAILABILITY_MONITORING_CONFIG =
+ new CpuAvailabilityMonitoringConfig.Builder(CPUSET_ALL)
+ .addThreshold(30).addThreshold(70).build();
+
+ private static final CpuAvailabilityMonitoringConfig TEST_CPU_AVAILABILITY_MONITORING_CONFIG_2 =
+ new CpuAvailabilityMonitoringConfig.Builder(CPUSET_ALL)
+ .addThreshold(10).addThreshold(90).build();
+
+ @Mock
+ private Context mContext;
+ private CpuMonitorService mService;
+ private HandlerExecutor mHandlerExecutor;
+ private CpuMonitorInternal mLocalService;
+
+ @Override
+ protected void initializeSession(StaticMockitoSessionBuilder builder) {
+ builder.mockStatic(ServiceManager.class);
+ }
+
+ @Before
+ public void setUp() {
+ mService = new CpuMonitorService(mContext);
+ mHandlerExecutor = new HandlerExecutor(new Handler(Looper.getMainLooper()));
+ doNothing().when(() -> ServiceManager.addService(eq("cpu_monitor"), any(Binder.class),
+ anyBoolean(), anyInt()));
+ mService.onStart();
+ mLocalService = LocalServices.getService(CpuMonitorInternal.class);
+ }
+
+ @After
+ public void tearDown() {
+ // The CpuMonitorInternal.class service is added by the mService.onStart call.
+ // Remove the service to ensure the setUp procedure can add this service again.
+ LocalServices.removeServiceForTest(CpuMonitorInternal.class);
+ }
+
+ @Test
+ public void testAddRemoveCpuAvailabilityCallback() {
+ CpuMonitorInternal.CpuAvailabilityCallback mockCallback = mock(
+ CpuMonitorInternal.CpuAvailabilityCallback.class);
+
+ mLocalService.addCpuAvailabilityCallback(mHandlerExecutor,
+ TEST_CPU_AVAILABILITY_MONITORING_CONFIG, mockCallback);
+
+ // TODO(b/242722241): Verify that {@link mockCallback.onAvailabilityChanged} and
+ // {@link mockCallback.onMonitoringIntervalChanged} are called when the callback is added.
+
+ mLocalService.removeCpuAvailabilityCallback(mockCallback);
+ }
+
+
+ @Test
+ public void testDuplicateAddCpuAvailabilityCallback() {
+ CpuMonitorInternal.CpuAvailabilityCallback mockCallback = mock(
+ CpuMonitorInternal.CpuAvailabilityCallback.class);
+
+ mLocalService.addCpuAvailabilityCallback(mHandlerExecutor,
+ TEST_CPU_AVAILABILITY_MONITORING_CONFIG, mockCallback);
+
+ mLocalService.addCpuAvailabilityCallback(mHandlerExecutor,
+ TEST_CPU_AVAILABILITY_MONITORING_CONFIG_2, mockCallback);
+
+ // TODO(b/242722241): Verify that {@link mockCallback} is called only when CPU availability
+ // thresholds cross the bounds specified in the
+ // {@link TEST_CPU_AVAILABILITY_MONITORING_CONFIG_2} config.
+
+ mLocalService.removeCpuAvailabilityCallback(mockCallback);
+ }
+
+ @Test
+ public void testRemoveInvalidCpuAvailabilityCallback() {
+ CpuMonitorInternal.CpuAvailabilityCallback mockCallback = mock(
+ CpuMonitorInternal.CpuAvailabilityCallback.class);
+
+ mLocalService.removeCpuAvailabilityCallback(mockCallback);
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
index 4c28c51f7e62..f2cba40685e4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -237,8 +237,8 @@ public final class DisplayPowerController2Test {
when(mLogicalDisplayMock.getDisplayIdLocked()).thenReturn(displayId);
when(mLogicalDisplayMock.getPrimaryDisplayDeviceLocked()).thenReturn(mDisplayDeviceMock);
when(mLogicalDisplayMock.getDisplayInfoLocked()).thenReturn(info);
- when(mLogicalDisplayMock.isEnabled()).thenReturn(true);
- when(mLogicalDisplayMock.getPhase()).thenReturn(LogicalDisplay.DISPLAY_PHASE_ENABLED);
+ when(mLogicalDisplayMock.isEnabledLocked()).thenReturn(true);
+ when(mLogicalDisplayMock.isInTransitionLocked()).thenReturn(false);
when(mDisplayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo);
when(mDisplayDeviceMock.getUniqueId()).thenReturn(uniqueId);
when(mDisplayDeviceMock.getDisplayDeviceConfig()).thenReturn(mDisplayDeviceConfigMock);
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
index 9a4bb22d5195..4f8cb8876b3f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -219,8 +219,8 @@ public final class DisplayPowerControllerTest {
when(mLogicalDisplayMock.getDisplayIdLocked()).thenReturn(displayId);
when(mLogicalDisplayMock.getPrimaryDisplayDeviceLocked()).thenReturn(mDisplayDeviceMock);
when(mLogicalDisplayMock.getDisplayInfoLocked()).thenReturn(info);
- when(mLogicalDisplayMock.isEnabled()).thenReturn(true);
- when(mLogicalDisplayMock.getPhase()).thenReturn(LogicalDisplay.DISPLAY_PHASE_ENABLED);
+ when(mLogicalDisplayMock.isEnabledLocked()).thenReturn(true);
+ when(mLogicalDisplayMock.isInTransitionLocked()).thenReturn(false);
when(mDisplayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo);
when(mDisplayDeviceMock.getUniqueId()).thenReturn(uniqueId);
when(mDisplayDeviceMock.getDisplayDeviceConfig()).thenReturn(mDisplayDeviceConfigMock);
@@ -233,4 +233,4 @@ public final class DisplayPowerControllerTest {
});
when(mDisplayDeviceConfigMock.getNits()).thenReturn(new float[]{2, 500});
}
-} \ No newline at end of file
+}
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 1c4309759186..80305fa7d8fb 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -114,6 +114,7 @@ android_test {
":SimpleServiceTestApp3",
":StubTestApp",
":SuspendTestApp",
+ ":MediaButtonReceiverHolderTestHelperApp",
],
java_resources: [
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 0be321ac5249..9eb3b92abc6f 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -110,6 +110,9 @@
<queries>
<package android:name="com.android.servicestests.apps.suspendtestapp" />
+ <intent>
+ <action android:name="android.media.browse.MediaBrowserService" />
+ </intent>
</queries>
<!-- Uses API introduced in O (26) -->
diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml
index 9052f58b2a8b..d9676470aca3 100644
--- a/services/tests/servicestests/AndroidTest.xml
+++ b/services/tests/servicestests/AndroidTest.xml
@@ -33,6 +33,7 @@
<option name="test-file-name" value="SimpleServiceTestApp1.apk" />
<option name="test-file-name" value="SimpleServiceTestApp2.apk" />
<option name="test-file-name" value="SimpleServiceTestApp3.apk" />
+ <option name="test-file-name" value="MediaButtonReceiverHolderTestHelperApp.apk" />
</target_preparer>
<!-- Create place to store apks -->
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index e8b8253f0611..537d04fa35e9 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -113,6 +113,8 @@ import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
/**
* Tests for {@link UserController}.
@@ -146,9 +148,11 @@ public class UserControllerTest {
private static final List<String> START_FOREGROUND_USER_ACTIONS = newArrayList(
Intent.ACTION_USER_STARTED,
- Intent.ACTION_USER_SWITCHED,
Intent.ACTION_USER_STARTING);
+ private static final List<String> START_FOREGROUND_USER_DEFERRED_ACTIONS = newArrayList(
+ Intent.ACTION_USER_SWITCHED);
+
private static final List<String> START_BACKGROUND_USER_ACTIONS = newArrayList(
Intent.ACTION_USER_STARTED,
Intent.ACTION_LOCKED_BOOT_COMPLETED,
@@ -397,11 +401,11 @@ public class UserControllerTest {
private void continueAndCompleteUserSwitch(UserState userState, int oldUserId, int newUserId) {
mUserController.continueUserSwitch(userState, oldUserId, newUserId);
mInjector.mHandler.removeMessages(UserController.COMPLETE_USER_SWITCH_MSG);
- mUserController.completeUserSwitch(newUserId);
+ mUserController.completeUserSwitch(oldUserId, newUserId);
}
@Test
- public void testContinueUserSwitch() throws RemoteException {
+ public void testContinueUserSwitch() {
mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
/* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
// Start user -- this will update state of mUserController
@@ -416,12 +420,12 @@ public class UserControllerTest {
continueAndCompleteUserSwitch(userState, oldUserId, newUserId);
verify(mInjector, times(0)).dismissKeyguard(any(), anyString());
verify(mInjector.getWindowManager(), times(1)).stopFreezingScreen();
- continueUserSwitchAssertions(TEST_USER_ID, false);
+ continueUserSwitchAssertions(oldUserId, TEST_USER_ID, false);
verifySystemUserVisibilityChangesNeverNotified();
}
@Test
- public void testContinueUserSwitchDismissKeyguard() throws RemoteException {
+ public void testContinueUserSwitchDismissKeyguard() {
when(mInjector.mKeyguardManagerMock.isDeviceSecure(anyInt())).thenReturn(false);
mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
/* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
@@ -437,12 +441,12 @@ public class UserControllerTest {
continueAndCompleteUserSwitch(userState, oldUserId, newUserId);
verify(mInjector, times(1)).dismissKeyguard(any(), anyString());
verify(mInjector.getWindowManager(), times(1)).stopFreezingScreen();
- continueUserSwitchAssertions(TEST_USER_ID, false);
+ continueUserSwitchAssertions(oldUserId, TEST_USER_ID, false);
verifySystemUserVisibilityChangesNeverNotified();
}
@Test
- public void testContinueUserSwitchUIDisabled() throws RemoteException {
+ public void testContinueUserSwitchUIDisabled() {
mUserController.setInitialConfig(/* userSwitchUiEnabled= */ false,
/* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
@@ -457,11 +461,11 @@ public class UserControllerTest {
// Verify that continueUserSwitch worked as expected
continueAndCompleteUserSwitch(userState, oldUserId, newUserId);
verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
- continueUserSwitchAssertions(TEST_USER_ID, false);
+ continueUserSwitchAssertions(oldUserId, TEST_USER_ID, false);
}
- private void continueUserSwitchAssertions(int expectedUserId, boolean backgroundUserStopping)
- throws RemoteException {
+ private void continueUserSwitchAssertions(int expectedOldUserId, int expectedNewUserId,
+ boolean backgroundUserStopping) {
Set<Integer> expectedCodes = new LinkedHashSet<>();
expectedCodes.add(COMPLETE_USER_SWITCH_MSG);
expectedCodes.add(REPORT_USER_SWITCH_COMPLETE_MSG);
@@ -473,7 +477,8 @@ public class UserControllerTest {
assertEquals("Unexpected message sent", expectedCodes, actualCodes);
Message msg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_COMPLETE_MSG);
assertNotNull(msg);
- assertEquals("Unexpected userId", expectedUserId, msg.arg1);
+ assertEquals("Unexpected oldUserId", expectedOldUserId, msg.arg1);
+ assertEquals("Unexpected newUserId", expectedNewUserId, msg.arg2);
}
@Test
@@ -486,16 +491,21 @@ public class UserControllerTest {
mUserController.startUser(TEST_USER_ID, true);
Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG);
assertNotNull(reportMsg);
+ int oldUserId = reportMsg.arg1;
int newUserId = reportMsg.arg2;
mInjector.mHandler.clearAllRecordedMessages();
// Mockito can't reset only interactions, so just verify that this hasn't been
// called with 'false' until after dispatchUserSwitchComplete.
verify(mInjector.getWindowManager(), never()).setSwitchingUser(false);
// Call dispatchUserSwitchComplete
- mUserController.dispatchUserSwitchComplete(newUserId);
+ mUserController.dispatchUserSwitchComplete(oldUserId, newUserId);
verify(observer, times(1)).onUserSwitchComplete(anyInt());
verify(observer).onUserSwitchComplete(TEST_USER_ID);
verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(false);
+ startUserAssertions(Stream.concat(
+ START_FOREGROUND_USER_ACTIONS.stream(),
+ START_FOREGROUND_USER_DEFERRED_ACTIONS.stream()
+ ).collect(Collectors.toList()), Collections.emptySet());
}
@Test
@@ -902,8 +912,7 @@ public class UserControllerTest {
}
private void addForegroundUserAndContinueUserSwitch(int newUserId, int expectedOldUserId,
- int expectedNumberOfCalls, boolean expectOldUserStopping)
- throws RemoteException {
+ int expectedNumberOfCalls, boolean expectOldUserStopping) {
// Start user -- this will update state of mUserController
mUserController.startUser(newUserId, true);
Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG);
@@ -918,7 +927,7 @@ public class UserControllerTest {
continueAndCompleteUserSwitch(userState, oldUserId, newUserId);
verify(mInjector.getWindowManager(), times(expectedNumberOfCalls))
.stopFreezingScreen();
- continueUserSwitchAssertions(newUserId, expectOldUserStopping);
+ continueUserSwitchAssertions(oldUserId, newUserId, expectOldUserStopping);
}
private void setUpUser(@UserIdInt int userId, @UserInfoFlag int flags) {
diff --git a/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java
index 582c78bce54c..fde3422b1ff3 100644
--- a/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceSettingsTests.java
@@ -52,6 +52,8 @@ public class GameManagerServiceSettingsTests {
private static final String PACKAGE_NAME_1 = "com.android.app1";
private static final String PACKAGE_NAME_2 = "com.android.app2";
private static final String PACKAGE_NAME_3 = "com.android.app3";
+ private static final String PACKAGE_NAME_4 = "com.android.app4";
+
private void writeFile(File file, byte[] data) {
file.mkdirs();
@@ -69,16 +71,23 @@ public class GameManagerServiceSettingsTests {
writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(),
"system/game-manager-service.xml"),
("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<packages>\n"
- + " <package name=\"com.android.app1\" gameMode=\"1\">\n"
- + " </package>\n"
+ + "<packages>"
+ + "\n" // app1: no package config setting
+ + "\n" // app2: performance mode is selected with override
+ " <package name=\"com.android.app2\" gameMode=\"2\">\n"
+ " <gameModeConfig gameMode=\"2\" scaling=\"0.99\" "
+ "useAngle=\"true\" fps=\"90\" loadingBoost=\"123\"></gameModeConfig>\n"
+ " <gameModeConfig gameMode=\"3\"></gameModeConfig>\n"
- + " </package>\n"
+ + " </package>"
+ + "\n" // app3: only battery mode is selected
+ " <package name=\"com.android.app3\" gameMode=\"3\">\n"
- + " </package>\n"
+ + " </package>"
+ + "\n" // app4: no game mode selected but custom game mode config
+ + " <package name=\"com.android.app4\">\n"
+ + " <gameModeConfig gameMode=\"4\" scaling=\"0.4\" "
+ + "fps=\"30\"></gameModeConfig>\n"
+ + " </package>"
+ + "\n"
+ "</packages>\n").getBytes());
}
@@ -115,14 +124,15 @@ public class GameManagerServiceSettingsTests {
assertTrue(settings.readPersistentDataLocked());
// test game modes
- assertEquals(1, settings.getGameModeLocked(PACKAGE_NAME_1));
- assertEquals(2, settings.getGameModeLocked(PACKAGE_NAME_2));
- assertEquals(3, settings.getGameModeLocked(PACKAGE_NAME_3));
+ assertEquals(GameManager.GAME_MODE_STANDARD, settings.getGameModeLocked(PACKAGE_NAME_1));
+ assertEquals(GameManager.GAME_MODE_PERFORMANCE, settings.getGameModeLocked(PACKAGE_NAME_2));
+ assertEquals(GameManager.GAME_MODE_BATTERY, settings.getGameModeLocked(PACKAGE_NAME_3));
+ assertEquals(GameManager.GAME_MODE_STANDARD, settings.getGameModeLocked(PACKAGE_NAME_4));
// test game mode configs
assertNull(settings.getConfigOverride(PACKAGE_NAME_1));
assertNull(settings.getConfigOverride(PACKAGE_NAME_3));
- final GamePackageConfiguration config = settings.getConfigOverride(PACKAGE_NAME_2);
+ GamePackageConfiguration config = settings.getConfigOverride(PACKAGE_NAME_2);
assertNotNull(config);
assertNull(config.getGameModeConfiguration(GameManager.GAME_MODE_STANDARD));
@@ -141,6 +151,14 @@ public class GameManagerServiceSettingsTests {
GameModeConfiguration.DEFAULT_LOADING_BOOST_DURATION);
assertEquals(batteryConfig.getFpsStr(), GameModeConfiguration.DEFAULT_FPS);
assertFalse(batteryConfig.getUseAngle());
+
+ config = settings.getConfigOverride(PACKAGE_NAME_4);
+ assertNotNull(config);
+ GameModeConfiguration customConfig = config.getGameModeConfiguration(
+ GameManager.GAME_MODE_CUSTOM);
+ assertNotNull(customConfig);
+ assertEquals(customConfig.getScaling(), 0.4f, 0.1f);
+ assertEquals(customConfig.getFps(), 30);
}
@Test
@@ -176,16 +194,20 @@ public class GameManagerServiceSettingsTests {
writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(),
"system/game-manager-service.xml"),
("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<packages>\n"
+ + "<packages>"
+ + "\n" // missing package name
+ " <package gameMode=\"1\">\n"
- + " </package>\n"
+ + " </package>"
+ + "\n" // app2 with unknown sub element
+ " <package name=\"com.android.app2\" gameMode=\"2\">\n"
+ " <unknown></unknown>"
+ " <gameModeConfig gameMode=\"3\" fps=\"90\"></gameModeConfig>\n"
+ " foo bar"
- + " </package>\n"
+ + " </package>"
+ + "\n" // unknown package element
+ " <unknownTag></unknownTag>\n"
- + " foo bar\n"
+ + " foo bar"
+ + "\n" // app3 after unknown element
+ " <package name=\"com.android.app3\" gameMode=\"3\">\n"
+ " </package>\n"
+ "</packages>\n").getBytes());
@@ -214,6 +236,8 @@ public class GameManagerServiceSettingsTests {
settings.setGameModeLocked(PACKAGE_NAME_1, GameManager.GAME_MODE_BATTERY);
settings.setGameModeLocked(PACKAGE_NAME_2, GameManager.GAME_MODE_PERFORMANCE);
settings.setGameModeLocked(PACKAGE_NAME_3, GameManager.GAME_MODE_STANDARD);
+
+ // set config for app2
GamePackageConfiguration config = new GamePackageConfiguration(PACKAGE_NAME_2);
GameModeConfiguration performanceConfig = config.getOrAddDefaultGameModeConfiguration(
GameManager.GAME_MODE_PERFORMANCE);
@@ -225,18 +249,29 @@ public class GameManagerServiceSettingsTests {
GameManager.GAME_MODE_BATTERY);
batteryConfig.setScaling(0.77f);
settings.setConfigOverride(PACKAGE_NAME_2, config);
+
+ // set config for app4
+ config = new GamePackageConfiguration(PACKAGE_NAME_4);
+ GameModeConfiguration customConfig = config.getOrAddDefaultGameModeConfiguration(
+ GameManager.GAME_MODE_CUSTOM);
+ customConfig.setScaling(0.4f);
+ customConfig.setFpsStr("30");
+ settings.setConfigOverride(PACKAGE_NAME_4, config);
+
settings.writePersistentDataLocked();
// clear the settings in memory
settings.removeGame(PACKAGE_NAME_1);
settings.removeGame(PACKAGE_NAME_2);
settings.removeGame(PACKAGE_NAME_3);
+ settings.removeGame(PACKAGE_NAME_4);
// read back in and verify
assertTrue(settings.readPersistentDataLocked());
assertEquals(3, settings.getGameModeLocked(PACKAGE_NAME_1));
assertEquals(2, settings.getGameModeLocked(PACKAGE_NAME_2));
assertEquals(1, settings.getGameModeLocked(PACKAGE_NAME_3));
+ assertEquals(1, settings.getGameModeLocked(PACKAGE_NAME_4));
config = settings.getConfigOverride(PACKAGE_NAME_1);
assertNull(config);
@@ -256,5 +291,14 @@ public class GameManagerServiceSettingsTests {
assertEquals(performanceConfig.getLoadingBoostDuration(), 321);
assertEquals(performanceConfig.getFpsStr(), "60");
assertTrue(performanceConfig.getUseAngle());
+
+ config = settings.getConfigOverride(PACKAGE_NAME_4);
+ assertNotNull(config);
+ customConfig = config.getGameModeConfiguration(GameManager.GAME_MODE_CUSTOM);
+ assertNotNull(customConfig);
+ assertEquals(customConfig.getScaling(), 0.4f, 0.1f);
+ assertEquals(customConfig.getFps(), 30);
+ assertNull(config.getGameModeConfiguration(GameManager.GAME_MODE_PERFORMANCE));
+ assertNull(config.getGameModeConfiguration(GameManager.GAME_MODE_BATTERY));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 3e8a07021d6b..3daf0f8bcb3b 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -212,14 +212,14 @@ public class VirtualDeviceManagerServiceTest {
private ArrayList<ActivityInfo> getActivityInfoList(
String packageName, String name, boolean displayOnRemoveDevices,
- String targetDisplayCategory) {
+ String requiredDisplayCategory) {
ActivityInfo activityInfo = new ActivityInfo();
activityInfo.packageName = packageName;
activityInfo.name = name;
activityInfo.flags = displayOnRemoveDevices
? FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES : FLAG_CANNOT_DISPLAY_ON_REMOTE_DEVICES;
activityInfo.applicationInfo = mApplicationInfoMock;
- activityInfo.targetDisplayCategory = targetDisplayCategory;
+ activityInfo.requiredDisplayCategory = requiredDisplayCategory;
return new ArrayList<>(Arrays.asList(activityInfo));
}
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index ce35626a5106..109abd03747c 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -319,7 +319,7 @@ public class DisplayManagerServiceTest {
when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
- final int displayIds[] = bs.getDisplayIds();
+ final int[] displayIds = bs.getDisplayIds(/* includeDisabled= */ true);
final int size = displayIds.length;
assertTrue(size > 0);
@@ -1417,7 +1417,8 @@ public class DisplayManagerServiceTest {
DisplayManagerService.BinderService displayManagerBinderService,
FakeDisplayDevice displayDevice) {
- final int[] displayIds = displayManagerBinderService.getDisplayIds();
+ final int[] displayIds = displayManagerBinderService.getDisplayIds(
+ /* includeDisabled= */ true);
assertTrue(displayIds.length > 0);
int displayId = Display.INVALID_DISPLAY;
for (int i = 0; i < displayIds.length; i++) {
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index 71f3d15e76fb..865bc987cb2c 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -200,12 +200,7 @@ public class DisplayModeDirectorTest {
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testDisplayModeVoting(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testDisplayModeVoting() {
// With no votes present, DisplayModeDirector should allow any refresh rate.
DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
DesiredDisplayModeSpecs modeSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
@@ -242,12 +237,7 @@ public class DisplayModeDirectorTest {
.isEqualTo((float) (minFps + i));
assertThat(modeSpecs.primary.physical.max)
.isEqualTo((float) (maxFps - i));
- if (frameRateIsRefreshRate) {
- assertThat(modeSpecs.primary.render.min)
- .isEqualTo((float) (minFps + i));
- } else {
- assertThat(modeSpecs.primary.render.min).isZero();
- }
+ assertThat(modeSpecs.primary.render.min).isZero();
assertThat(modeSpecs.primary.render.max)
.isEqualTo((float) (maxFps - i));
if (priority >= Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF) {
@@ -255,12 +245,7 @@ public class DisplayModeDirectorTest {
.isEqualTo((float) (minFps + i));
assertThat(modeSpecs.appRequest.physical.max)
.isEqualTo((float) (maxFps - i));
- if (frameRateIsRefreshRate) {
- assertThat(modeSpecs.appRequest.render.min).isEqualTo(
- (float) (minFps + i));
- } else {
- assertThat(modeSpecs.appRequest.render.min).isZero();
- }
+ assertThat(modeSpecs.appRequest.render.min).isZero();
assertThat(modeSpecs.appRequest.render.max).isEqualTo(
(float) (maxFps - i));
} else {
@@ -292,12 +277,7 @@ public class DisplayModeDirectorTest {
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testVotingWithFloatingPointErrors(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testVotingWithFloatingPointErrors() {
DisplayModeDirector director = createDirectorFromFpsRange(50, 90);
SparseArray<Vote> votes = new SparseArray<>();
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
@@ -318,12 +298,7 @@ public class DisplayModeDirectorTest {
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testFlickerHasLowerPriorityThanUserAndRangeIsSingle(
- boolean frameRateIsRefreshRate) {
+ public void testFlickerHasLowerPriorityThanUserAndRangeIsSingle() {
assertTrue(Vote.PRIORITY_FLICKER_REFRESH_RATE
< Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
assertTrue(Vote.PRIORITY_FLICKER_REFRESH_RATE
@@ -332,7 +307,6 @@ public class DisplayModeDirectorTest {
assertTrue(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH
> Vote.PRIORITY_LOW_POWER_MODE);
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
Display.Mode[] modes = new Display.Mode[4];
modes[0] = new Display.Mode(
/*modeId=*/1, /*width=*/1000, /*height=*/1000, 60);
@@ -408,17 +382,12 @@ public class DisplayModeDirectorTest {
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testLPMHasHigherPriorityThanUser(boolean frameRateIsRefreshRate) {
+ public void testLPMHasHigherPriorityThanUser() {
assertTrue(Vote.PRIORITY_LOW_POWER_MODE
> Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
assertTrue(Vote.PRIORITY_LOW_POWER_MODE
> Vote.PRIORITY_APP_REQUEST_SIZE);
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
Display.Mode[] modes = new Display.Mode[4];
modes[0] = new Display.Mode(
/*modeId=*/1, /*width=*/1000, /*height=*/1000, 60);
@@ -443,11 +412,7 @@ public class DisplayModeDirectorTest {
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.baseModeId).isEqualTo(2);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
- } else {
- assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
- }
+ assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
@@ -462,11 +427,7 @@ public class DisplayModeDirectorTest {
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.baseModeId).isEqualTo(4);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(90);
- } else {
- assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
- }
+ assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(90);
assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(90);
@@ -481,11 +442,7 @@ public class DisplayModeDirectorTest {
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.baseModeId).isEqualTo(2);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
- } else {
- assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
- }
+ assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
@@ -500,21 +457,13 @@ public class DisplayModeDirectorTest {
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.baseModeId).isEqualTo(4);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(90);
- } else {
- assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
- }
+ assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(90);
assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(90);
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testAppRequestRefreshRateRange(boolean frameRateIsRefreshRate) {
+ public void testAppRequestRefreshRateRange() {
// Confirm that the app request range doesn't include flicker or min refresh rate settings,
// but does include everything else.
assertTrue(
@@ -525,7 +474,6 @@ public class DisplayModeDirectorTest {
assertTrue(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE
>= Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
Display.Mode[] modes = new Display.Mode[3];
modes[0] = new Display.Mode(
/*modeId=*/60, /*width=*/1000, /*height=*/1000, 60);
@@ -582,12 +530,7 @@ public class DisplayModeDirectorTest {
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testSpecsFromRefreshRateSettings(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testSpecsFromRefreshRateSettings() {
// Confirm that, with varying settings for min, peak, and default refresh rate,
// DesiredDisplayModeSpecs is calculated correctly.
float[] refreshRates = {30.f, 60.f, 90.f, 120.f, 150.f};
@@ -607,27 +550,12 @@ public class DisplayModeDirectorTest {
RefreshRateRanges frameRateAll = new RefreshRateRanges(rangeAll, rangeAll);
RefreshRateRanges frameRate90toInf = new RefreshRateRanges(range90toInf, range90toInf);
- RefreshRateRanges frameRate0to60;
- RefreshRateRanges frameRate0to90;
- RefreshRateRanges frameRate0to120;
- RefreshRateRanges frameRate60to90;
- RefreshRateRanges frameRate90to90;
- RefreshRateRanges frameRate90to120;
- if (frameRateIsRefreshRate) {
- frameRate0to60 = new RefreshRateRanges(range0to60, range0to60);
- frameRate0to90 = new RefreshRateRanges(range0to90, range0to90);
- frameRate0to120 = new RefreshRateRanges(range0to120, range0to120);
- frameRate60to90 = new RefreshRateRanges(range60to90, range60to90);
- frameRate90to90 = new RefreshRateRanges(range90to90, range90to90);
- frameRate90to120 = new RefreshRateRanges(range90to120, range90to120);
- } else {
- frameRate0to60 = new RefreshRateRanges(rangeAll, range0to60);
- frameRate0to90 = new RefreshRateRanges(rangeAll, range0to90);
- frameRate0to120 = new RefreshRateRanges(rangeAll, range0to120);
- frameRate60to90 = new RefreshRateRanges(range60toInf, range60to90);
- frameRate90to90 = new RefreshRateRanges(range90toInf, range90to90);
- frameRate90to120 = new RefreshRateRanges(range90toInf, range90to120);
- }
+ RefreshRateRanges frameRate0to60 = new RefreshRateRanges(rangeAll, range0to60);
+ RefreshRateRanges frameRate0to90 = new RefreshRateRanges(rangeAll, range0to90);
+ RefreshRateRanges frameRate0to120 = new RefreshRateRanges(rangeAll, range0to120);
+ RefreshRateRanges frameRate60to90 = new RefreshRateRanges(range60toInf, range60to90);
+ RefreshRateRanges frameRate90to90 = new RefreshRateRanges(range90toInf, range90to90);
+ RefreshRateRanges frameRate90to120 = new RefreshRateRanges(range90toInf, range90to120);
verifySpecsWithRefreshRateSettings(director, 0, 0, 0, frameRateAll, frameRateAll);
verifySpecsWithRefreshRateSettings(director, 0, 0, 90, frameRate0to90, frameRateAll);
@@ -657,12 +585,7 @@ public class DisplayModeDirectorTest {
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testBrightnessObserverCallWithRefreshRateSettings(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testBrightnessObserverCallWithRefreshRateSettings() {
// Confirm that, with varying settings for min, peak, and default refresh rate, we make the
// correct call to the brightness observer.
float[] refreshRates = {60.f, 90.f, 120.f};
@@ -677,12 +600,7 @@ public class DisplayModeDirectorTest {
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testVotingWithAlwaysRespectAppRequest(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testVotingWithAlwaysRespectAppRequest() {
Display.Mode[] modes = new Display.Mode[3];
modes[0] = new Display.Mode(
/*modeId=*/50, /*width=*/1000, /*height=*/1000, 50);
@@ -711,11 +629,7 @@ public class DisplayModeDirectorTest {
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
- } else {
- assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
- }
+ assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.baseModeId).isEqualTo(60);
@@ -734,23 +648,14 @@ public class DisplayModeDirectorTest {
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
- } else {
- assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
- }
+ assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.baseModeId).isEqualTo(60);
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testVotingWithSwitchingTypeNone(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testVotingWithSwitchingTypeNone() {
DisplayModeDirector director = createDirectorFromFpsRange(0, 90);
SparseArray<Vote> votes = new SparseArray<>();
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
@@ -765,20 +670,11 @@ public class DisplayModeDirectorTest {
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(30);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
- } else {
- assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
- }
+ assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(30);
assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(
- 60);
- } else {
- assertThat(desiredSpecs.appRequest.physical.max).isPositiveInfinity();
- }
+ assertThat(desiredSpecs.appRequest.physical.max).isPositiveInfinity();
assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(0);
assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.baseModeId).isEqualTo(30);
@@ -800,12 +696,7 @@ public class DisplayModeDirectorTest {
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testVotingWithSwitchingTypeRenderFrameRateOnly(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testVotingWithSwitchingTypeRenderFrameRateOnly() {
DisplayModeDirector director = createDirectorFromFpsRange(0, 90);
SparseArray<Vote> votes = new SparseArray<>();
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
@@ -816,24 +707,15 @@ public class DisplayModeDirectorTest {
director.injectVotesByDisplay(votesByDisplay);
assertThat(director.getModeSwitchingType())
- .isNotEqualTo(DisplayManager.SWITCHING_TYPE_NONE);
+ .isNotEqualTo(DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY);
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(30);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
- } else {
- assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
- }
+ assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(30);
assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(
- 60);
- } else {
- assertThat(desiredSpecs.appRequest.physical.max).isPositiveInfinity();
- }
+ assertThat(desiredSpecs.appRequest.physical.max).isPositiveInfinity();
assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(0);
assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.baseModeId).isEqualTo(30);
@@ -846,25 +728,58 @@ public class DisplayModeDirectorTest {
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(30);
assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(30);
assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(30);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(30);
- } else {
- assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
- }
+ assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(30);
assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(30);
assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(30);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(30);
- assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(30);
- } else {
- assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(0);
- assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
- }
+ assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(0);
+ assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(30);
assertThat(desiredSpecs.baseModeId).isEqualTo(30);
}
@Test
+ public void testVotingWithSwitchingTypeRenderFrameRateOnlyRenderRateIsNotPhysicalRefreshRate() {
+ DisplayModeDirector director = createDirectorFromFpsRange(90, 120);
+ SparseArray<Vote> votes = new SparseArray<>();
+ SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
+ votesByDisplay.put(DISPLAY_ID, votes);
+ votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
+ Vote.forRenderFrameRates(30, 90));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+
+ director.injectVotesByDisplay(votesByDisplay);
+ assertThat(director.getModeSwitchingType())
+ .isNotEqualTo(DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY);
+ DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+
+ assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(30);
+ assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
+ assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(30);
+ assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
+ assertThat(desiredSpecs.appRequest.physical.max).isPositiveInfinity();
+ assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(0);
+ assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(90);
+
+ director.setModeSwitchingType(DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY);
+ assertThat(director.getModeSwitchingType())
+ .isEqualTo(DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY);
+
+ desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
+ assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(90);
+ assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
+ assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(90);
+ assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(0);
+ assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
+
+ assertThat(desiredSpecs.baseModeId).isEqualTo(90);
+ }
+
+ @Test
public void testVotingWithSwitchingTypeWithinGroups() {
DisplayModeDirector director = createDirectorFromFpsRange(0, 90);
@@ -887,12 +802,7 @@ public class DisplayModeDirectorTest {
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testDefaultDisplayModeIsSelectedIfAvailable(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testDefaultDisplayModeIsSelectedIfAvailable() {
final float[] refreshRates = new float[]{24f, 25f, 30f, 60f, 90f};
final int defaultModeId = 3;
DisplayModeDirector director = createDirectorFromRefreshRateArray(
@@ -1173,17 +1083,12 @@ public class DisplayModeDirectorTest {
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testAppRequestMinRefreshRate(boolean frameRateIsRefreshRate) {
+ public void testAppRequestMinRefreshRate() {
// Confirm that the app min request range doesn't include flicker or min refresh rate
// settings but does include everything else.
assertTrue(Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE
>= Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
Display.Mode[] modes = new Display.Mode[3];
modes[0] = new Display.Mode(
/*modeId=*/60, /*width=*/1000, /*height=*/1000, 60);
@@ -1225,11 +1130,7 @@ public class DisplayModeDirectorTest {
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testAppRequestMaxRefreshRate(boolean frameRateIsRefreshRate) {
+ public void testAppRequestMaxRefreshRate() {
// Confirm that the app max request range doesn't include flicker or min refresh rate
// settings but does include everything else.
assertTrue(Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE
@@ -1243,7 +1144,6 @@ public class DisplayModeDirectorTest {
modes[2] = new Display.Mode(
/*modeId=*/90, /*width=*/1000, /*height=*/1000, 90);
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
DisplayModeDirector director = createDirectorFromModeArray(modes, modes[1]);
SparseArray<Vote> votes = new SparseArray<>();
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
@@ -1254,19 +1154,11 @@ public class DisplayModeDirectorTest {
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(60);
- } else {
- assertThat(desiredSpecs.primary.render.min).isZero();
- }
+ assertThat(desiredSpecs.primary.render.min).isZero();
assertThat(desiredSpecs.primary.render.max).isAtMost(60);
assertThat(desiredSpecs.appRequest.physical.min).isAtMost(60f);
assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(90f);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.appRequest.render.min).isAtMost(60f);
- } else {
- assertThat(desiredSpecs.appRequest.render.min).isZero();
- }
+ assertThat(desiredSpecs.appRequest.render.min).isZero();
assertThat(desiredSpecs.appRequest.render.max).isAtLeast(90f);
votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
@@ -1288,30 +1180,16 @@ public class DisplayModeDirectorTest {
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(75);
assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(75);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(75);
- } else {
- assertThat(desiredSpecs.primary.render.min).isZero();
- }
+ assertThat(desiredSpecs.primary.render.min).isZero();
assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(75);
assertThat(desiredSpecs.appRequest.physical.min).isZero();
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(
- 75);
- } else {
- assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(90f);
- }
+ assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(90f);
assertThat(desiredSpecs.appRequest.render.min).isZero();
assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(75);
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testAppRequestObserver_modeId(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testAppRequestObserver_modeId() {
DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 60, 0, 0);
@@ -1373,12 +1251,7 @@ public class DisplayModeDirectorTest {
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testAppRequestObserver_minRefreshRate(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testAppRequestObserver_minRefreshRate() {
DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 60, 0);
Vote appRequestRefreshRate =
@@ -1391,15 +1264,9 @@ public class DisplayModeDirectorTest {
Vote appRequestRefreshRateRange =
director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
assertNotNull(appRequestRefreshRateRange);
- if (frameRateIsRefreshRate) {
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min)
- .isWithin(FLOAT_TOLERANCE).of(60);
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max).isAtLeast(90);
- } else {
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
- .isPositiveInfinity();
- }
+ assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
+ assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
+ .isPositiveInfinity();
assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min)
.isWithin(FLOAT_TOLERANCE).of(60);
assertThat(appRequestRefreshRateRange.refreshRateRanges.render.max).isAtLeast(90);
@@ -1417,15 +1284,9 @@ public class DisplayModeDirectorTest {
appRequestRefreshRateRange =
director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
assertNotNull(appRequestRefreshRateRange);
- if (frameRateIsRefreshRate) {
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isWithin(
- FLOAT_TOLERANCE).of(90);
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max).isAtLeast(90);
- } else {
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
- .isPositiveInfinity();
- }
+ assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
+ assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
+ .isPositiveInfinity();
assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min)
.isWithin(FLOAT_TOLERANCE).of(90);
@@ -1435,12 +1296,7 @@ public class DisplayModeDirectorTest {
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testAppRequestObserver_maxRefreshRate(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testAppRequestObserver_maxRefreshRate() {
DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 0, 90);
Vote appRequestRefreshRate =
@@ -1454,13 +1310,8 @@ public class DisplayModeDirectorTest {
director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
assertNotNull(appRequestRefreshRateRange);
assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
- if (frameRateIsRefreshRate) {
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
- .isWithin(FLOAT_TOLERANCE).of(90);
- } else {
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
- .isPositiveInfinity();
- }
+ assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
+ .isPositiveInfinity();
assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min).isZero();
assertThat(appRequestRefreshRateRange.refreshRateRanges.render.max)
@@ -1480,13 +1331,8 @@ public class DisplayModeDirectorTest {
director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
assertNotNull(appRequestRefreshRateRange);
assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
- if (frameRateIsRefreshRate) {
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
- .isWithin(FLOAT_TOLERANCE).of(60);
- } else {
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
- .isPositiveInfinity();
- }
+ assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
+ .isPositiveInfinity();
assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min).isZero();
assertThat(appRequestRefreshRateRange.refreshRateRanges.render.max)
@@ -1512,12 +1358,7 @@ public class DisplayModeDirectorTest {
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testAppRequestObserver_modeIdAndRefreshRateRange(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testAppRequestObserver_modeIdAndRefreshRateRange() {
DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 60, 90, 90);
@@ -1547,16 +1388,9 @@ public class DisplayModeDirectorTest {
Vote appRequestRefreshRateRange =
director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
assertNotNull(appRequestRefreshRateRange);
- if (frameRateIsRefreshRate) {
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min)
- .isWithin(FLOAT_TOLERANCE).of(90);
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
- .isWithin(FLOAT_TOLERANCE).of(90);
- } else {
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
- .isPositiveInfinity();
- }
+ assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
+ assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
+ .isPositiveInfinity();
assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min)
.isWithin(FLOAT_TOLERANCE).of(90);
assertThat(appRequestRefreshRateRange.refreshRateRanges.render.max)
@@ -1566,12 +1400,7 @@ public class DisplayModeDirectorTest {
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testAppRequestsIsTheDefaultMode(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testAppRequestsIsTheDefaultMode() {
Display.Mode[] modes = new Display.Mode[2];
modes[0] = new Display.Mode(
/*modeId=*/1, /*width=*/1000, /*height=*/1000, 60);
@@ -1600,12 +1429,7 @@ public class DisplayModeDirectorTest {
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testDisableRefreshRateSwitchingVote(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testDisableRefreshRateSwitchingVote() {
DisplayModeDirector director = createDirectorFromFpsRange(50, 90);
SparseArray<Vote> votes = new SparseArray<>();
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
@@ -1650,8 +1474,8 @@ public class DisplayModeDirectorTest {
"true",
"false"
})
- public void testBaseModeIdInPrimaryRange(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testBaseModeIdInPrimaryRange(boolean supportsFrameRateOverride) {
+ when(mInjector.supportsFrameRateOverride()).thenReturn(supportsFrameRateOverride);
DisplayModeDirector director = createDirectorFromFpsRange(50, 90);
SparseArray<Vote> votes = new SparseArray<>();
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
@@ -1662,12 +1486,12 @@ public class DisplayModeDirectorTest {
director.injectVotesByDisplay(votesByDisplay);
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
- assertThat(desiredSpecs.baseModeId).isEqualTo(50);
- } else {
- assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
+ assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
+ if (supportsFrameRateOverride) {
assertThat(desiredSpecs.baseModeId).isEqualTo(70);
+ } else {
+ assertThat(desiredSpecs.baseModeId).isEqualTo(50);
+
}
assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(0);
assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
@@ -1679,11 +1503,7 @@ public class DisplayModeDirectorTest {
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
- } else {
- assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
- }
+ assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(0);
assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.baseModeId).isEqualTo(55);
@@ -1697,12 +1517,11 @@ public class DisplayModeDirectorTest {
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
- assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
- } else {
- assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
+ assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
+ if (supportsFrameRateOverride) {
assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(52);
+ } else {
+ assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
}
assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(0);
assertThat(desiredSpecs.baseModeId).isEqualTo(55);
@@ -1716,23 +1535,14 @@ public class DisplayModeDirectorTest {
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(58);
- } else {
- assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
- }
+ assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(0);
assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(58);
assertThat(desiredSpecs.baseModeId).isEqualTo(55);
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testStaleAppVote(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testStaleAppVote() {
Display.Mode[] modes = new Display.Mode[4];
modes[0] = new Display.Mode(
/*modeId=*/1, /*width=*/1000, /*height=*/1000, 60);
@@ -1782,8 +1592,8 @@ public class DisplayModeDirectorTest {
"true",
"false"
})
- public void testRefreshRateIsSubsetOfFrameRate(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testRefreshRateIsSubsetOfFrameRate(boolean supportsFrameRateOverride) {
+ when(mInjector.supportsFrameRateOverride()).thenReturn(supportsFrameRateOverride);
DisplayModeDirector director = createDirectorFromFpsRange(60, 120);
SparseArray<Vote> votes = new SparseArray<>();
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
@@ -1795,11 +1605,7 @@ public class DisplayModeDirectorTest {
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(120);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(90);
- } else {
- assertThat(desiredSpecs.appRequest.render.min).isZero();
- }
+ assertThat(desiredSpecs.appRequest.render.min).isZero();
assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(120);
votes.clear();
@@ -1810,13 +1616,11 @@ public class DisplayModeDirectorTest {
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(120);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(90);
- assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(
- 120);
- } else {
- assertThat(desiredSpecs.appRequest.render.min).isZero();
+ assertThat(desiredSpecs.appRequest.render.min).isZero();
+ if (supportsFrameRateOverride) {
assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
+ } else {
+ assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(120);
}
votes.clear();
@@ -1827,13 +1631,12 @@ public class DisplayModeDirectorTest {
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(120);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(90);
- assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(
- 120);
- } else {
+ if (supportsFrameRateOverride) {
assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
+ } else {
+ assertThat(desiredSpecs.appRequest.render.min).isZero();
+ assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(120);
}
votes.clear();
@@ -1844,17 +1647,12 @@ public class DisplayModeDirectorTest {
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(120);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(90);
- } else {
- assertThat(desiredSpecs.appRequest.render.min).isZero();
- }
+ assertThat(desiredSpecs.appRequest.render.min).isZero();
assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(120);
}
@Test
public void testRenderFrameRateIsAchievableByPhysicalRefreshRate() {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(false);
DisplayModeDirector director = createDirectorFromFpsRange(60, 120);
SparseArray<Vote> votes = new SparseArray<>();
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
@@ -1872,8 +1670,34 @@ public class DisplayModeDirectorTest {
}
@Test
+ @Parameters({
+ "true",
+ "false"
+ })
+ public void testRenderFrameRateIncludesPhysicalRefreshRate(boolean supportsFrameRateOverride) {
+ when(mInjector.supportsFrameRateOverride()).thenReturn(supportsFrameRateOverride);
+ DisplayModeDirector director = createDirectorFromFpsRange(60, 120);
+ SparseArray<Vote> votes = new SparseArray<>();
+ SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
+ votesByDisplay.put(DISPLAY_ID, votes);
+
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+ votes.put(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
+ Vote.forRenderFrameRates(0, 30));
+ director.injectVotesByDisplay(votesByDisplay);
+ DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(desiredSpecs.appRequest.physical.min).isZero();
+ assertThat(desiredSpecs.appRequest.physical.max).isPositiveInfinity();
+ assertThat(desiredSpecs.appRequest.render.min).isZero();
+ if (supportsFrameRateOverride) {
+ assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(30);
+ } else {
+ assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
+ }
+ }
+
+ @Test
public void testRenderFrameRateIsDroppedIfLowerPriorityThenBaseModeRefreshRate() {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(false);
DisplayModeDirector director = createDirectorFromFpsRange(60, 120);
SparseArray<Vote> votes = new SparseArray<>();
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
@@ -2692,7 +2516,7 @@ public class DisplayModeDirectorTest {
}
@Override
- public boolean renderFrameRateIsPhysicalRefreshRate() {
+ public boolean supportsFrameRateOverride() {
return true;
}
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
index 657bda633ab5..246945c2d968 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -30,6 +30,8 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
@@ -52,6 +54,8 @@ import android.view.DisplayInfo;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.display.layout.Layout;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -59,6 +63,7 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
import java.io.InputStream;
import java.io.OutputStream;
@@ -83,6 +88,7 @@ public class LogicalDisplayMapperTest {
@Mock Resources mResourcesMock;
@Mock IPowerManager mIPowerManagerMock;
@Mock IThermalService mIThermalServiceMock;
+ @Spy DeviceStateToLayoutMap mDeviceStateToLayoutMapSpy = new DeviceStateToLayoutMap();
@Captor ArgumentCaptor<LogicalDisplay> mDisplayCaptor;
@@ -132,7 +138,8 @@ public class LogicalDisplayMapperTest {
mLooper = new TestLooper();
mHandler = new Handler(mLooper.getLooper());
mLogicalDisplayMapper = new LogicalDisplayMapper(mContextMock, mDisplayDeviceRepo,
- mListenerMock, new DisplayManagerService.SyncRoot(), mHandler);
+ mListenerMock, new DisplayManagerService.SyncRoot(), mHandler,
+ mDeviceStateToLayoutMapSpy);
}
@@ -259,7 +266,8 @@ public class LogicalDisplayMapperTest {
add(createDisplayDevice(Display.TYPE_EXTERNAL, 600, 800, 0));
add(createDisplayDevice(Display.TYPE_VIRTUAL, 600, 800, 0));
- int [] ids = mLogicalDisplayMapper.getDisplayIdsLocked(Process.SYSTEM_UID);
+ int [] ids = mLogicalDisplayMapper.getDisplayIdsLocked(Process.SYSTEM_UID,
+ /* includeDisabled= */ true);
assertEquals(3, ids.length);
Arrays.sort(ids);
assertEquals(DEFAULT_DISPLAY, ids[0]);
@@ -503,10 +511,183 @@ public class LogicalDisplayMapperTest {
/* isBootCompleted= */true));
}
+ @Test
+ public void testDeviceStateLocked() {
+ DisplayDevice device1 = createDisplayDevice(Display.TYPE_INTERNAL, 600, 800,
+ DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY);
+ DisplayDevice device2 = createDisplayDevice(Display.TYPE_INTERNAL, 600, 800,
+ DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY);
+
+ Layout layout = new Layout();
+ layout.createDisplayLocked(device1.getDisplayDeviceInfoLocked().address, true, true);
+ layout.createDisplayLocked(device2.getDisplayDeviceInfoLocked().address, false, false);
+ when(mDeviceStateToLayoutMapSpy.get(0)).thenReturn(layout);
+
+ layout = new Layout();
+ layout.createDisplayLocked(device1.getDisplayDeviceInfoLocked().address, false, false);
+ layout.createDisplayLocked(device2.getDisplayDeviceInfoLocked().address, true, true);
+ when(mDeviceStateToLayoutMapSpy.get(1)).thenReturn(layout);
+ when(mDeviceStateToLayoutMapSpy.get(2)).thenReturn(layout);
+
+ LogicalDisplay display1 = add(device1);
+ assertEquals(info(display1).address, info(device1).address);
+ assertEquals(DEFAULT_DISPLAY, id(display1));
+
+ LogicalDisplay display2 = add(device2);
+ assertEquals(info(display2).address, info(device2).address);
+ // We can only have one default display
+ assertEquals(DEFAULT_DISPLAY, id(display1));
+
+ mLogicalDisplayMapper.setDeviceStateLocked(0, false);
+ advanceTime(1000);
+ assertTrue(mLogicalDisplayMapper.getDisplayLocked(device1).isEnabledLocked());
+ assertFalse(mLogicalDisplayMapper.getDisplayLocked(device2).isEnabledLocked());
+ assertFalse(mLogicalDisplayMapper.getDisplayLocked(device1).isInTransitionLocked());
+ assertFalse(mLogicalDisplayMapper.getDisplayLocked(device2).isInTransitionLocked());
+
+ mLogicalDisplayMapper.setDeviceStateLocked(1, false);
+ advanceTime(1000);
+ assertFalse(mLogicalDisplayMapper.getDisplayLocked(device1).isEnabledLocked());
+ assertTrue(mLogicalDisplayMapper.getDisplayLocked(device2).isEnabledLocked());
+ assertFalse(mLogicalDisplayMapper.getDisplayLocked(device1).isInTransitionLocked());
+ assertFalse(mLogicalDisplayMapper.getDisplayLocked(device2).isInTransitionLocked());
+
+ mLogicalDisplayMapper.setDeviceStateLocked(2, false);
+ advanceTime(1000);
+ assertFalse(mLogicalDisplayMapper.getDisplayLocked(device1).isEnabledLocked());
+ assertTrue(mLogicalDisplayMapper.getDisplayLocked(device2).isEnabledLocked());
+ assertFalse(mLogicalDisplayMapper.getDisplayLocked(device1).isInTransitionLocked());
+ assertFalse(mLogicalDisplayMapper.getDisplayLocked(device2).isInTransitionLocked());
+ }
+
+ @Test
+ public void testEnabledAndDisabledDisplays() {
+ DisplayAddress displayAddressOne = new TestUtils.TestDisplayAddress();
+ DisplayAddress displayAddressTwo = new TestUtils.TestDisplayAddress();
+ DisplayAddress displayAddressThree = new TestUtils.TestDisplayAddress();
+
+ TestDisplayDevice device1 = createDisplayDevice(displayAddressOne, "one",
+ Display.TYPE_INTERNAL, 600, 800,
+ DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY);
+ TestDisplayDevice device2 = createDisplayDevice(displayAddressTwo, "two",
+ Display.TYPE_INTERNAL, 200, 800,
+ DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP);
+ TestDisplayDevice device3 = createDisplayDevice(displayAddressThree, "three",
+ Display.TYPE_INTERNAL, 600, 900,
+ DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP);
+
+ Layout threeDevicesEnabledLayout = new Layout();
+ threeDevicesEnabledLayout.createDisplayLocked(
+ displayAddressOne,
+ /* isDefault= */ true,
+ /* isEnabled= */ true);
+ threeDevicesEnabledLayout.createDisplayLocked(
+ displayAddressTwo,
+ /* isDefault= */ false,
+ /* isEnabled= */ true);
+ threeDevicesEnabledLayout.createDisplayLocked(
+ displayAddressThree,
+ /* isDefault= */ false,
+ /* isEnabled= */ true);
+
+ when(mDeviceStateToLayoutMapSpy.get(DeviceStateToLayoutMap.STATE_DEFAULT))
+ .thenReturn(threeDevicesEnabledLayout);
+
+ LogicalDisplay display1 = add(device1);
+ LogicalDisplay display2 = add(device2);
+ LogicalDisplay display3 = add(device3);
+
+ // ensure 3 displays are returned
+ int [] ids = mLogicalDisplayMapper.getDisplayIdsLocked(Process.SYSTEM_UID, false);
+ assertEquals(3, ids.length);
+ Arrays.sort(ids);
+ assertEquals(DEFAULT_DISPLAY, ids[0]);
+ assertNotNull(mLogicalDisplayMapper.getDisplayLocked(device1,
+ /* includeDisabled= */ false));
+ assertNotNull(mLogicalDisplayMapper.getDisplayLocked(device2,
+ /* includeDisabled= */ false));
+ assertNotNull(mLogicalDisplayMapper.getDisplayLocked(device3,
+ /* includeDisabled= */ false));
+ assertNotNull(mLogicalDisplayMapper.getDisplayLocked(
+ threeDevicesEnabledLayout.getByAddress(displayAddressOne).getLogicalDisplayId(),
+ /* includeDisabled= */ false));
+ assertNotNull(mLogicalDisplayMapper.getDisplayLocked(
+ threeDevicesEnabledLayout.getByAddress(displayAddressTwo).getLogicalDisplayId(),
+ /* includeDisabled= */ false));
+ assertNotNull(mLogicalDisplayMapper.getDisplayLocked(
+ threeDevicesEnabledLayout.getByAddress(displayAddressThree).getLogicalDisplayId(),
+ /* includeDisabled= */ false));
+
+ Layout oneDeviceEnabledLayout = new Layout();
+ oneDeviceEnabledLayout.createDisplayLocked(
+ displayAddressOne,
+ /* isDefault= */ true,
+ /* isEnabled= */ true);
+ oneDeviceEnabledLayout.createDisplayLocked(
+ displayAddressTwo,
+ /* isDefault= */ false,
+ /* isEnabled= */ false);
+ oneDeviceEnabledLayout.createDisplayLocked(
+ displayAddressThree,
+ /* isDefault= */ false,
+ /* isEnabled= */ false);
+
+ when(mDeviceStateToLayoutMapSpy.get(0)).thenReturn(oneDeviceEnabledLayout);
+ when(mDeviceStateToLayoutMapSpy.get(1)).thenReturn(threeDevicesEnabledLayout);
+
+ // 1) Set the new state
+ // 2) Mark the displays as STATE_OFF so that it can continue with transition
+ // 3) Send DISPLAY_DEVICE_EVENT_CHANGE to inform the mapper of the new display state
+ // 4) Dispatch handler events.
+ mLogicalDisplayMapper.setDeviceStateLocked(0, false);
+ mDisplayDeviceRepo.onDisplayDeviceEvent(device3, DISPLAY_DEVICE_EVENT_CHANGED);
+ advanceTime(1000);
+ final int[] allDisplayIds = mLogicalDisplayMapper.getDisplayIdsLocked(
+ Process.SYSTEM_UID, false);
+ if (allDisplayIds.length != 1) {
+ throw new RuntimeException("Displays: \n"
+ + mLogicalDisplayMapper.getDisplayLocked(device1).toString()
+ + "\n" + mLogicalDisplayMapper.getDisplayLocked(device2).toString()
+ + "\n" + mLogicalDisplayMapper.getDisplayLocked(device3).toString());
+ }
+ // ensure only one display is returned
+ assertEquals(1, allDisplayIds.length);
+ assertNotNull(mLogicalDisplayMapper.getDisplayLocked(device1,
+ /* includeDisabled= */ false));
+ assertNull(mLogicalDisplayMapper.getDisplayLocked(device2,
+ /* includeDisabled= */ false));
+ assertNull(mLogicalDisplayMapper.getDisplayLocked(device3,
+ /* includeDisabled= */ false));
+ assertNotNull(mLogicalDisplayMapper.getDisplayLocked(
+ oneDeviceEnabledLayout.getByAddress(displayAddressOne).getLogicalDisplayId(),
+ /* includeDisabled= */ false));
+ assertNull(mLogicalDisplayMapper.getDisplayLocked(
+ oneDeviceEnabledLayout.getByAddress(displayAddressTwo).getLogicalDisplayId(),
+ /* includeDisabled= */ false));
+ assertNull(mLogicalDisplayMapper.getDisplayLocked(
+ oneDeviceEnabledLayout.getByAddress(displayAddressThree).getLogicalDisplayId(),
+ /* includeDisabled= */ false));
+
+ // Now do it again to go back to state 1
+ mLogicalDisplayMapper.setDeviceStateLocked(1, false);
+ mDisplayDeviceRepo.onDisplayDeviceEvent(device3, DISPLAY_DEVICE_EVENT_CHANGED);
+ advanceTime(1000);
+ final int[] threeDisplaysEnabled = mLogicalDisplayMapper.getDisplayIdsLocked(
+ Process.SYSTEM_UID, false);
+
+ // ensure all three displays are returned
+ assertEquals(3, threeDisplaysEnabled.length);
+ }
+
/////////////////
// Helper Methods
/////////////////
+ private void advanceTime(long timeMs) {
+ mLooper.moveTimeForward(1000);
+ mLooper.dispatchAll();
+ }
+
private TestDisplayDevice createDisplayDevice(int type, int width, int height, int flags) {
return createDisplayDevice(
new TestUtils.TestDisplayAddress(), /* uniqueId */ "", type, width, height, flags);
@@ -575,6 +756,7 @@ public class LogicalDisplayMapperTest {
class TestDisplayDevice extends DisplayDevice {
private DisplayDeviceInfo mInfo;
private DisplayDeviceInfo mSentInfo;
+ private int mState;
TestDisplayDevice() {
super(null, null, "test_display_" + sUniqueTestDisplayId++, mContextMock);
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
index 5a43530d44dd..1d70fc61c937 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
@@ -126,12 +126,12 @@ public class LogicalDisplayTest {
verify(t).setDisplayFlags(any(), eq(SurfaceControl.DISPLAY_RECEIVES_INPUT));
reset(t);
- mLogicalDisplay.setPhase(LogicalDisplay.DISPLAY_PHASE_DISABLED);
+ mLogicalDisplay.setEnabledLocked(false);
mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false);
verify(t).setDisplayFlags(any(), eq(0));
reset(t);
- mLogicalDisplay.setPhase(LogicalDisplay.DISPLAY_PHASE_ENABLED);
+ mLogicalDisplay.setEnabledLocked(true);
mDisplayDeviceInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false);
verify(t).setDisplayFlags(any(), eq(SurfaceControl.DISPLAY_RECEIVES_INPUT));
diff --git a/services/tests/servicestests/src/com/android/server/media/MediaButtonReceiverHolderTest.java b/services/tests/servicestests/src/com/android/server/media/MediaButtonReceiverHolderTest.java
new file mode 100644
index 000000000000..1c4ee691fc77
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/media/MediaButtonReceiverHolderTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.google.common.truth.Truth;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class MediaButtonReceiverHolderTest {
+
+ @Test
+ public void createMediaButtonReceiverHolder_resolvesNullComponentName() {
+ Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+ PendingIntent pi = PendingIntent.getBroadcast(context, /* requestCode= */ 0, intent,
+ PendingIntent.FLAG_IMMUTABLE);
+ MediaButtonReceiverHolder a = MediaButtonReceiverHolder.create(/* userId= */ 0, pi,
+ context.getPackageName());
+ Truth.assertWithMessage("Component name must match PendingIntent creator package.").that(
+ a.getComponentName()).isNull();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/media/OWNERS b/services/tests/servicestests/src/com/android/server/media/OWNERS
new file mode 100644
index 000000000000..55ffde223374
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/media/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 137631
+include platform/frameworks/av:/media/janitors/media_solutions_OWNERS \ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
index 136e79e02de6..261156611a06 100644
--- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
@@ -456,9 +456,8 @@ public final class DeviceStateProviderImplTest {
new DeviceState(1, "CLOSED", 0 /* flags */),
new DeviceState(2, "HALF_OPENED", 0 /* flags */)
}, mDeviceStateArrayCaptor.getValue());
- // onStateChanged() should be called because the provider could not find the sensor.
- verify(listener).onStateChanged(mIntegerCaptor.capture());
- assertEquals(1, mIntegerCaptor.getValue().intValue());
+ // onStateChanged() should not be called because the provider could not find the sensor.
+ verify(listener, never()).onStateChanged(mIntegerCaptor.capture());
}
private static Sensor newSensor(String name, String type) throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/utils/EventLoggerTest.java b/services/tests/servicestests/src/com/android/server/utils/EventLoggerTest.java
index aafc16db50da..febbffea50cd 100644
--- a/services/tests/servicestests/src/com/android/server/utils/EventLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/utils/EventLoggerTest.java
@@ -71,9 +71,10 @@ public class EventLoggerTest {
}
@Test
- public void testThatPrintWriterProducesEmptyListFromEmptyLog() {
+ public void testThatPrintWriterProducesOnlyTitleFromEmptyLog() {
mEventLogger.dump(mTestPrintWriter);
- assertThat(mTestStringWriter.toString()).isEmpty();
+ assertThat(mTestStringWriter.toString())
+ .isEqualTo(mEventLogger.getDumpTitle() + "\n");
}
}
@@ -87,27 +88,27 @@ public class EventLoggerTest {
// insertion order, max size is 3
new EventLogger.Event[] { TEST_EVENT_1, TEST_EVENT_2 },
// expected events
- new EventLogger.Event[] { TEST_EVENT_2, TEST_EVENT_1 }
+ new EventLogger.Event[] { TEST_EVENT_1, TEST_EVENT_2 }
},
{
// insertion order, max size is 3
new EventLogger.Event[] { TEST_EVENT_1, TEST_EVENT_3, TEST_EVENT_2 },
// expected events
- new EventLogger.Event[] { TEST_EVENT_2, TEST_EVENT_3, TEST_EVENT_1 }
+ new EventLogger.Event[] { TEST_EVENT_1, TEST_EVENT_3, TEST_EVENT_2 }
},
{
// insertion order, max size is 3
new EventLogger.Event[] { TEST_EVENT_1, TEST_EVENT_2, TEST_EVENT_3,
TEST_EVENT_4 },
// expected events
- new EventLogger.Event[] { TEST_EVENT_4, TEST_EVENT_3, TEST_EVENT_2 }
+ new EventLogger.Event[] { TEST_EVENT_2, TEST_EVENT_3, TEST_EVENT_4 }
},
{
// insertion order, max size is 3
new EventLogger.Event[] { TEST_EVENT_1, TEST_EVENT_2, TEST_EVENT_3,
TEST_EVENT_4, TEST_EVENT_5 },
// expected events
- new EventLogger.Event[] { TEST_EVENT_5, TEST_EVENT_4, TEST_EVENT_3 }
+ new EventLogger.Event[] { TEST_EVENT_3, TEST_EVENT_4, TEST_EVENT_5 }
}
});
}
diff --git a/services/tests/servicestests/test-apps/MediaButtonReceiverHolderTestHelperApp/Android.bp b/services/tests/servicestests/test-apps/MediaButtonReceiverHolderTestHelperApp/Android.bp
new file mode 100644
index 000000000000..f376b6fc9cf8
--- /dev/null
+++ b/services/tests/servicestests/test-apps/MediaButtonReceiverHolderTestHelperApp/Android.bp
@@ -0,0 +1,37 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // 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_helper_app {
+ name: "MediaButtonReceiverHolderTestHelperApp",
+
+ sdk_version: "current",
+
+ srcs: ["**/*.java"],
+
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/services/tests/servicestests/test-apps/MediaButtonReceiverHolderTestHelperApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/MediaButtonReceiverHolderTestHelperApp/AndroidManifest.xml
new file mode 100644
index 000000000000..3ba3dc27f10a
--- /dev/null
+++ b/services/tests/servicestests/test-apps/MediaButtonReceiverHolderTestHelperApp/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.servicestests.apps.mediabuttonreceiverholdertesthelperapp">
+
+ <application>
+ <receiver
+ android:name=".FakeMediaButtonBroadcastReceiver"
+ android:enabled="true"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MEDIA_BUTTON" />
+ </intent-filter>
+ </receiver>
+ </application>
+
+</manifest>
diff --git a/services/tests/servicestests/test-apps/MediaButtonReceiverHolderTestHelperApp/OWNERS b/services/tests/servicestests/test-apps/MediaButtonReceiverHolderTestHelperApp/OWNERS
new file mode 100644
index 000000000000..55ffde223374
--- /dev/null
+++ b/services/tests/servicestests/test-apps/MediaButtonReceiverHolderTestHelperApp/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 137631
+include platform/frameworks/av:/media/janitors/media_solutions_OWNERS \ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/MediaButtonReceiverHolderTestHelperApp/src/FakeMediaButtonBroadcastReceiver.java b/services/tests/servicestests/test-apps/MediaButtonReceiverHolderTestHelperApp/src/FakeMediaButtonBroadcastReceiver.java
new file mode 100644
index 000000000000..6fdd8be790cc
--- /dev/null
+++ b/services/tests/servicestests/test-apps/MediaButtonReceiverHolderTestHelperApp/src/FakeMediaButtonBroadcastReceiver.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.servicestests.apps.mediabuttonreceiverholdertesthelperapp;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+public class FakeMediaButtonBroadcastReceiver extends BroadcastReceiver {
+
+ private static final String TAG = "FakeMediaButtonBroadcastReceiver";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.v(TAG, "onReceive not expected");
+ }
+}
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp4.xml b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp4.xml
index 4dcb442ebee2..5451735b2e86 100644
--- a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp4.xml
+++ b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp4.xml
@@ -32,7 +32,7 @@
<activity android:name="com.android.servicestests.apps.packageparserapp.MyActivity"
android:exported="true"
- android:targetDisplayCategory="automotive">
+ android:requiredDisplayCategory="automotive">
<property android:name="android.cts.PROPERTY_ACTIVITY" android:value="@integer/integer_property" />
<property android:name="android.cts.PROPERTY_COMPONENT" android:value="@integer/integer_property" />
<property android:name="android.cts.PROPERTY_STRING" android:value="koala activity" />
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp6.xml b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp6.xml
index 8e694e1a1eb0..601479d82aff 100644
--- a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp6.xml
+++ b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp6.xml
@@ -21,7 +21,7 @@
<application>
<activity android:name="com.android.servicestests.apps.packageparserapp.MyActivity"
android:exported="true"
- android:targetDisplayCategory="$automotive">
+ android:requiredDisplayCategory="$automotive">
</activity>
</application>
</manifest>
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index c898119ea991..cdb264222a7e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -443,6 +443,44 @@ public class InsetsStateControllerTest extends WindowTestsBase {
}
@Test
+ public void testUpdateAboveInsetsState_imeTargetOnScreenBehavior() {
+ final WindowToken imeToken = createTestWindowToken(TYPE_INPUT_METHOD, mDisplayContent);
+ final WindowState ime = createWindow(null, TYPE_INPUT_METHOD, imeToken, "ime");
+ final WindowState app = createTestWindow("app");
+
+ getController().getSourceProvider(ITYPE_IME).setWindowContainer(ime, null, null);
+ ime.getControllableInsetProvider().setServerVisible(true);
+
+ app.mActivityRecord.setVisibility(true);
+ mDisplayContent.setImeLayeringTarget(app);
+ mDisplayContent.updateImeInputAndControlTarget(app);
+
+ app.setRequestedVisibleTypes(ime(), ime());
+ getController().onInsetsModified(app);
+ assertTrue(ime.getControllableInsetProvider().getSource().isVisible());
+
+ getController().updateAboveInsetsState(true /* notifyInsetsChange */);
+ assertNotNull(app.getInsetsState().peekSource(ITYPE_IME));
+ verify(app, atLeastOnce()).notifyInsetsChanged();
+
+ // Expect the app will still get IME insets even when the app was invisible.
+ // (i.e. app invisible after locking the device)
+ app.mActivityRecord.setVisible(false);
+ app.setHasSurface(false);
+ getController().updateAboveInsetsState(true /* notifyInsetsChange */);
+ assertNotNull(app.getInsetsState().peekSource(ITYPE_IME));
+ verify(app, atLeastOnce()).notifyInsetsChanged();
+
+ // Expect the app will get IME insets when the app is requesting visible.
+ // (i.e. app is going to visible when unlocking the device)
+ app.mActivityRecord.setVisibility(true);
+ assertTrue(app.isVisibleRequested());
+ getController().updateAboveInsetsState(true /* notifyInsetsChange */);
+ assertNotNull(app.getInsetsState().peekSource(ITYPE_IME));
+ verify(app, atLeastOnce()).notifyInsetsChanged();
+ }
+
+ @Test
public void testDispatchGlobalInsets() {
final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindowContainer(navBar, null,
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index c548dc3aebd5..eb26415c2b21 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -772,6 +772,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
// Simulating now win1 is being covered by the lockscreen which has no surface,
// and then launching an activity win2 with the remote animation
win1.mHasSurface = false;
+ win1.mActivityRecord.setVisibility(false);
mDisplayContent.mOpeningApps.add(win2.mActivityRecord);
final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
win2.mActivityRecord, new Point(50, 100), null,
diff --git a/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
index d3aa073c84d8..df7b3cdebe28 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
@@ -74,15 +74,15 @@ public class SyncEngineTests extends WindowTestsBase {
int id = startSyncSet(bse, listener);
bse.addToSyncSet(id, mockWC);
- // Make sure a traversal is requested
- verify(mWm.mWindowPlacerLocked, times(1)).requestTraversal();
+ // The traversal is not requested because ready is not set.
+ verify(mWm.mWindowPlacerLocked, times(0)).requestTraversal();
bse.onSurfacePlacement();
verify(listener, times(0)).onTransactionReady(anyInt(), any());
bse.setReady(id);
// Make sure a traversal is requested
- verify(mWm.mWindowPlacerLocked, times(2)).requestTraversal();
+ verify(mWm.mWindowPlacerLocked).requestTraversal();
bse.onSurfacePlacement();
verify(listener, times(1)).onTransactionReady(eq(id), notNull());
@@ -103,14 +103,14 @@ public class SyncEngineTests extends WindowTestsBase {
int id = startSyncSet(bse, listener);
bse.addToSyncSet(id, mockWC);
bse.setReady(id);
- // Make sure traversals requested (one for add and another for setReady)
- verify(mWm.mWindowPlacerLocked, times(2)).requestTraversal();
+ // Make sure traversals requested.
+ verify(mWm.mWindowPlacerLocked).requestTraversal();
bse.onSurfacePlacement();
verify(listener, times(0)).onTransactionReady(anyInt(), any());
mockWC.onSyncFinishedDrawing();
- // Make sure a (third) traversal is requested.
- verify(mWm.mWindowPlacerLocked, times(3)).requestTraversal();
+ // Make sure the second traversal is requested.
+ verify(mWm.mWindowPlacerLocked, times(2)).requestTraversal();
bse.onSurfacePlacement();
verify(listener, times(1)).onTransactionReady(eq(id), notNull());
}
@@ -127,8 +127,8 @@ public class SyncEngineTests extends WindowTestsBase {
int id = startSyncSet(bse, listener);
bse.addToSyncSet(id, mockWC);
bse.setReady(id);
- // Make sure traversals requested (one for add and another for setReady)
- verify(mWm.mWindowPlacerLocked, times(2)).requestTraversal();
+ // Make sure traversals requested.
+ verify(mWm.mWindowPlacerLocked).requestTraversal();
bse.onSurfacePlacement();
verify(listener, times(0)).onTransactionReady(anyInt(), any());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index a4cc09a205f6..e7813ffb8e1a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -893,11 +893,10 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase {
}
@Test
- public void testLaunchesPortraitUnresizableOnFreeformLandscapeDisplay() {
+ public void testLaunchesPortraitUnresizableOnFreeformDisplayWithFreeformSizeCompat() {
mAtm.mDevEnableNonResizableMultiWindow = true;
final TestDisplayContent freeformDisplay = createNewDisplayContent(
WINDOWING_MODE_FREEFORM);
- assertTrue(freeformDisplay.getBounds().width() > freeformDisplay.getBounds().height());
final ActivityOptions options = ActivityOptions.makeBasic();
mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
@@ -905,42 +904,12 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase {
assertEquals(RESULT_CONTINUE,
new CalculateRequestBuilder().setOptions(options).calculate());
- assertEquals(WINDOWING_MODE_UNDEFINED, mResult.mWindowingMode);
- }
-
- @Test
- public void testLaunchesLandscapeUnresizableOnFreeformLandscapeDisplay() {
- mAtm.mDevEnableNonResizableMultiWindow = true;
- final TestDisplayContent freeformDisplay = createNewDisplayContent(
- WINDOWING_MODE_FREEFORM);
- assertTrue(freeformDisplay.getBounds().width() > freeformDisplay.getBounds().height());
- final ActivityOptions options = ActivityOptions.makeBasic();
- mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
- mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
- mActivity.info.screenOrientation = SCREEN_ORIENTATION_LANDSCAPE;
- assertEquals(RESULT_CONTINUE,
- new CalculateRequestBuilder().setOptions(options).calculate());
-
- assertEquals(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode);
- }
-
- @Test
- public void testLaunchesUndefinedUnresizableOnFreeformLandscapeDisplay() {
- mAtm.mDevEnableNonResizableMultiWindow = true;
- final TestDisplayContent freeformDisplay = createNewDisplayContent(
+ assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode,
WINDOWING_MODE_FREEFORM);
- assertTrue(freeformDisplay.getBounds().width() > freeformDisplay.getBounds().height());
- final ActivityOptions options = ActivityOptions.makeBasic();
- mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
- mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
- assertEquals(RESULT_CONTINUE,
- new CalculateRequestBuilder().setOptions(options).calculate());
-
- assertEquals(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode);
}
@Test
- public void testForceMaximizingAppsOnNonFreeformDisplay() {
+ public void testSkipsForceMaximizingAppsOnNonFreeformDisplay() {
final ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
options.setLaunchBounds(new Rect(0, 0, 200, 100));
@@ -954,9 +923,8 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase {
assertEquals(RESULT_CONTINUE,
new CalculateRequestBuilder().setOptions(options).calculate());
- // Non-resizable apps must be launched in fullscreen in a fullscreen display regardless of
- // other properties.
- assertEquals(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode);
+ assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode,
+ WINDOWING_MODE_FULLSCREEN);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 35b9710f5528..59a31b105717 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -479,6 +479,8 @@ public class TransitionTests extends WindowTestsBase {
wallpaperWindow.mHasSurface = true;
doReturn(true).when(mDisplayContent).isAttached();
transition.collect(mDisplayContent);
+ assertFalse("The change of non-interesting window container should be skipped",
+ transition.mChanges.containsKey(mDisplayContent.getParent()));
mDisplayContent.getWindowConfiguration().setRotation(
(mDisplayContent.getWindowConfiguration().getRotation() + 1) % 4);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamManager.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamCopier.java
index d5eea1f3ff35..76574542da4f 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamManager.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamCopier.java
@@ -17,16 +17,16 @@
package com.android.server.voiceinteraction;
import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.service.voice.HotwordAudioStream.KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES;
import static com.android.server.voiceinteraction.HotwordDetectionConnection.DEBUG;
import android.annotation.NonNull;
import android.app.AppOpsManager;
-import android.media.permission.Identity;
import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
import android.service.voice.HotwordAudioStream;
import android.service.voice.HotwordDetectedResult;
-import android.util.Pair;
import android.util.Slog;
import java.io.IOException;
@@ -39,21 +39,38 @@ import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
-final class HotwordAudioStreamManager {
+/**
+ * Copies the audio streams in {@link HotwordDetectedResult}s. This allows the system to manage the
+ * lifetime of the {@link ParcelFileDescriptor}s and ensures that the flow of data is in the right
+ * direction from the {@link android.service.voice.HotwordDetectionService} to the client (i.e., the
+ * voice interactor).
+ *
+ * @hide
+ */
+final class HotwordAudioStreamCopier {
- private static final String TAG = "HotwordAudioStreamManager";
+ private static final String TAG = "HotwordAudioStreamCopier";
private static final String OP_MESSAGE = "Streaming hotword audio to VoiceInteractionService";
private static final String TASK_ID_PREFIX = "HotwordDetectedResult@";
private static final String THREAD_NAME_PREFIX = "Copy-";
+ private static final int DEFAULT_COPY_BUFFER_LENGTH_BYTES = 2_560;
+
+ // Corresponds to the OS pipe capacity in bytes
+ private static final int MAX_COPY_BUFFER_LENGTH_BYTES = 65_536;
private final AppOpsManager mAppOpsManager;
- private final Identity mVoiceInteractorIdentity;
+ private final int mVoiceInteractorUid;
+ private final String mVoiceInteractorPackageName;
+ private final String mVoiceInteractorAttributionTag;
private final ExecutorService mExecutorService = Executors.newCachedThreadPool();
- HotwordAudioStreamManager(@NonNull AppOpsManager appOpsManager,
- @NonNull Identity voiceInteractorIdentity) {
+ HotwordAudioStreamCopier(@NonNull AppOpsManager appOpsManager,
+ int voiceInteractorUid, @NonNull String voiceInteractorPackageName,
+ @NonNull String voiceInteractorAttributionTag) {
mAppOpsManager = appOpsManager;
- mVoiceInteractorIdentity = voiceInteractorIdentity;
+ mVoiceInteractorUid = voiceInteractorUid;
+ mVoiceInteractorPackageName = voiceInteractorPackageName;
+ mVoiceInteractorAttributionTag = voiceInteractorAttributionTag;
}
/**
@@ -61,7 +78,7 @@ final class HotwordAudioStreamManager {
* <p>
* The returned {@link HotwordDetectedResult} is identical the one that was passed in, except
* that the {@link ParcelFileDescriptor}s within {@link HotwordDetectedResult#getAudioStreams()}
- * are replaced with descriptors from pipes managed by {@link HotwordAudioStreamManager}. The
+ * are replaced with descriptors from pipes managed by {@link HotwordAudioStreamCopier}. The
* returned value should be passed on to the client (i.e., the voice interactor).
* </p>
*
@@ -76,8 +93,7 @@ final class HotwordAudioStreamManager {
}
List<HotwordAudioStream> newAudioStreams = new ArrayList<>(audioStreams.size());
- List<Pair<ParcelFileDescriptor, ParcelFileDescriptor>> sourcesAndSinks = new ArrayList<>(
- audioStreams.size());
+ List<CopyTaskInfo> copyTaskInfos = new ArrayList<>(audioStreams.size());
for (HotwordAudioStream audioStream : audioStreams) {
ParcelFileDescriptor[] clientPipe = ParcelFileDescriptor.createReliablePipe();
ParcelFileDescriptor clientAudioSource = clientPipe[0];
@@ -87,46 +103,69 @@ final class HotwordAudioStreamManager {
clientAudioSource).build();
newAudioStreams.add(newAudioStream);
+ int copyBufferLength = DEFAULT_COPY_BUFFER_LENGTH_BYTES;
+ PersistableBundle metadata = audioStream.getMetadata();
+ if (metadata.containsKey(KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES)) {
+ copyBufferLength = metadata.getInt(KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES, -1);
+ if (copyBufferLength < 1 || copyBufferLength > MAX_COPY_BUFFER_LENGTH_BYTES) {
+ Slog.w(TAG, "Attempted to set an invalid copy buffer length ("
+ + copyBufferLength + ") for: " + audioStream);
+ copyBufferLength = DEFAULT_COPY_BUFFER_LENGTH_BYTES;
+ } else if (DEBUG) {
+ Slog.i(TAG, "Copy buffer length set to " + copyBufferLength + " for: "
+ + audioStream);
+ }
+ }
+
ParcelFileDescriptor serviceAudioSource =
audioStream.getAudioStreamParcelFileDescriptor();
- sourcesAndSinks.add(new Pair<>(serviceAudioSource, clientAudioSink));
+ copyTaskInfos.add(new CopyTaskInfo(serviceAudioSource, clientAudioSink,
+ copyBufferLength));
}
String resultTaskId = TASK_ID_PREFIX + System.identityHashCode(result);
- mExecutorService.execute(new HotwordDetectedResultCopyTask(resultTaskId, sourcesAndSinks));
+ mExecutorService.execute(new HotwordDetectedResultCopyTask(resultTaskId, copyTaskInfos));
return result.buildUpon().setAudioStreams(newAudioStreams).build();
}
+ private static class CopyTaskInfo {
+ private final ParcelFileDescriptor mSource;
+ private final ParcelFileDescriptor mSink;
+ private final int mCopyBufferLength;
+
+ CopyTaskInfo(ParcelFileDescriptor source, ParcelFileDescriptor sink, int copyBufferLength) {
+ mSource = source;
+ mSink = sink;
+ mCopyBufferLength = copyBufferLength;
+ }
+ }
+
private class HotwordDetectedResultCopyTask implements Runnable {
private final String mResultTaskId;
- private final List<Pair<ParcelFileDescriptor, ParcelFileDescriptor>> mSourcesAndSinks;
+ private final List<CopyTaskInfo> mCopyTaskInfos;
private final ExecutorService mExecutorService = Executors.newCachedThreadPool();
- HotwordDetectedResultCopyTask(String resultTaskId,
- List<Pair<ParcelFileDescriptor, ParcelFileDescriptor>> sourcesAndSinks) {
+ HotwordDetectedResultCopyTask(String resultTaskId, List<CopyTaskInfo> copyTaskInfos) {
mResultTaskId = resultTaskId;
- mSourcesAndSinks = sourcesAndSinks;
+ mCopyTaskInfos = copyTaskInfos;
}
@Override
public void run() {
Thread.currentThread().setName(THREAD_NAME_PREFIX + mResultTaskId);
- int size = mSourcesAndSinks.size();
+ int size = mCopyTaskInfos.size();
List<SingleAudioStreamCopyTask> tasks = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
- Pair<ParcelFileDescriptor, ParcelFileDescriptor> sourceAndSink =
- mSourcesAndSinks.get(i);
- ParcelFileDescriptor serviceAudioSource = sourceAndSink.first;
- ParcelFileDescriptor clientAudioSink = sourceAndSink.second;
+ CopyTaskInfo copyTaskInfo = mCopyTaskInfos.get(i);
String streamTaskId = mResultTaskId + "@" + i;
- tasks.add(new SingleAudioStreamCopyTask(streamTaskId, serviceAudioSource,
- clientAudioSink));
+ tasks.add(new SingleAudioStreamCopyTask(streamTaskId, copyTaskInfo.mSource,
+ copyTaskInfo.mSink, copyTaskInfo.mCopyBufferLength));
}
if (mAppOpsManager.startOpNoThrow(AppOpsManager.OPSTR_RECORD_AUDIO_HOTWORD,
- mVoiceInteractorIdentity.uid, mVoiceInteractorIdentity.packageName,
- mVoiceInteractorIdentity.attributionTag, OP_MESSAGE) == MODE_ALLOWED) {
+ mVoiceInteractorUid, mVoiceInteractorPackageName,
+ mVoiceInteractorAttributionTag, OP_MESSAGE) == MODE_ALLOWED) {
try {
// TODO(b/244599891): Set timeout, close after inactivity
mExecutorService.invokeAll(tasks);
@@ -135,25 +174,23 @@ final class HotwordAudioStreamManager {
bestEffortPropagateError(e.getMessage());
} finally {
mAppOpsManager.finishOp(AppOpsManager.OPSTR_RECORD_AUDIO_HOTWORD,
- mVoiceInteractorIdentity.uid, mVoiceInteractorIdentity.packageName,
- mVoiceInteractorIdentity.attributionTag);
+ mVoiceInteractorUid, mVoiceInteractorPackageName,
+ mVoiceInteractorAttributionTag);
}
} else {
bestEffortPropagateError(
- "Failed to obtain RECORD_AUDIO_HOTWORD permission for "
- + SoundTriggerSessionPermissionsDecorator.toString(
- mVoiceInteractorIdentity));
+ "Failed to obtain RECORD_AUDIO_HOTWORD permission for voice interactor with"
+ + " uid=" + mVoiceInteractorUid
+ + " packageName=" + mVoiceInteractorPackageName
+ + " attributionTag=" + mVoiceInteractorAttributionTag);
}
}
private void bestEffortPropagateError(@NonNull String errorMessage) {
try {
- for (Pair<ParcelFileDescriptor, ParcelFileDescriptor> sourceAndSink :
- mSourcesAndSinks) {
- ParcelFileDescriptor serviceAudioSource = sourceAndSink.first;
- ParcelFileDescriptor clientAudioSink = sourceAndSink.second;
- serviceAudioSource.closeWithError(errorMessage);
- clientAudioSink.closeWithError(errorMessage);
+ for (CopyTaskInfo copyTaskInfo : mCopyTaskInfos) {
+ copyTaskInfo.mSource.closeWithError(errorMessage);
+ copyTaskInfo.mSink.closeWithError(errorMessage);
}
} catch (IOException e) {
Slog.e(TAG, mResultTaskId + ": Failed to propagate error", e);
@@ -162,18 +199,17 @@ final class HotwordAudioStreamManager {
}
private static class SingleAudioStreamCopyTask implements Callable<Void> {
- // TODO: Make this buffer size customizable from updateState()
- private static final int COPY_BUFFER_LENGTH = 2_560;
-
private final String mStreamTaskId;
private final ParcelFileDescriptor mAudioSource;
private final ParcelFileDescriptor mAudioSink;
+ private final int mCopyBufferLength;
SingleAudioStreamCopyTask(String streamTaskId, ParcelFileDescriptor audioSource,
- ParcelFileDescriptor audioSink) {
+ ParcelFileDescriptor audioSink, int copyBufferLength) {
mStreamTaskId = streamTaskId;
mAudioSource = audioSource;
mAudioSink = audioSink;
+ mCopyBufferLength = copyBufferLength;
}
@Override
@@ -189,7 +225,7 @@ final class HotwordAudioStreamManager {
try {
fis = new ParcelFileDescriptor.AutoCloseInputStream(mAudioSource);
fos = new ParcelFileDescriptor.AutoCloseOutputStream(mAudioSink);
- byte[] buffer = new byte[COPY_BUFFER_LENGTH];
+ byte[] buffer = new byte[mCopyBufferLength];
while (true) {
if (Thread.interrupted()) {
Slog.e(TAG,
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 415166328519..3bcba6c1af65 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -183,7 +183,7 @@ final class HotwordDetectionConnection {
private final ScheduledExecutorService mScheduledExecutorService =
Executors.newSingleThreadScheduledExecutor();
private final AppOpsManager mAppOpsManager;
- private final HotwordAudioStreamManager mHotwordAudioStreamManager;
+ private final HotwordAudioStreamCopier mHotwordAudioStreamCopier;
@Nullable private final ScheduledFuture<?> mCancellationTaskFuture;
private final AtomicBoolean mUpdateStateAfterStartFinished = new AtomicBoolean(false);
private final IBinder.DeathRecipient mAudioServerDeathRecipient = this::audioServerDied;
@@ -245,8 +245,9 @@ final class HotwordDetectionConnection {
mVoiceInteractionServiceUid = voiceInteractionServiceUid;
mVoiceInteractorIdentity = voiceInteractorIdentity;
mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
- mHotwordAudioStreamManager = new HotwordAudioStreamManager(mAppOpsManager,
- mVoiceInteractorIdentity);
+ mHotwordAudioStreamCopier = new HotwordAudioStreamCopier(mAppOpsManager,
+ mVoiceInteractorIdentity.uid, mVoiceInteractorIdentity.packageName,
+ mVoiceInteractorIdentity.attributionTag);
mDetectionComponentName = serviceName;
mUser = userId;
mCallback = callback;
@@ -506,7 +507,7 @@ final class HotwordDetectionConnection {
saveProximityValueToBundle(result);
HotwordDetectedResult newResult;
try {
- newResult = mHotwordAudioStreamManager.startCopyingAudioStreams(result);
+ newResult = mHotwordAudioStreamCopier.startCopyingAudioStreams(result);
} catch (IOException e) {
// TODO: Write event
mSoftwareCallback.onError();
@@ -641,7 +642,7 @@ final class HotwordDetectionConnection {
saveProximityValueToBundle(result);
HotwordDetectedResult newResult;
try {
- newResult = mHotwordAudioStreamManager.startCopyingAudioStreams(result);
+ newResult = mHotwordAudioStreamCopier.startCopyingAudioStreams(result);
} catch (IOException e) {
// TODO: Write event
externalCallback.onError(CALLBACK_ONDETECTED_STREAM_COPY_ERROR);
@@ -1000,7 +1001,7 @@ final class HotwordDetectionConnection {
HotwordDetectedResult newResult;
try {
newResult =
- mHotwordAudioStreamManager.startCopyingAudioStreams(
+ mHotwordAudioStreamCopier.startCopyingAudioStreams(
triggerResult);
} catch (IOException e) {
// TODO: Write event
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/TrustedHotwordDetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/TrustedHotwordDetectorSession.java
index b87b8f790338..eeafe910e13e 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/TrustedHotwordDetectorSession.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/TrustedHotwordDetectorSession.java
@@ -183,7 +183,7 @@ final class TrustedHotwordDetectorSession {
private final ScheduledExecutorService mScheduledExecutorService =
Executors.newSingleThreadScheduledExecutor();
private final AppOpsManager mAppOpsManager;
- private final HotwordAudioStreamManager mHotwordAudioStreamManager;
+ private final HotwordAudioStreamCopier mHotwordAudioStreamCopier;
@Nullable private final ScheduledFuture<?> mCancellationTaskFuture;
private final AtomicBoolean mUpdateStateAfterStartFinished = new AtomicBoolean(false);
private final IBinder.DeathRecipient mAudioServerDeathRecipient = this::audioServerDied;
@@ -245,8 +245,9 @@ final class TrustedHotwordDetectorSession {
mVoiceInteractionServiceUid = voiceInteractionServiceUid;
mVoiceInteractorIdentity = voiceInteractorIdentity;
mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
- mHotwordAudioStreamManager = new HotwordAudioStreamManager(mAppOpsManager,
- mVoiceInteractorIdentity);
+ mHotwordAudioStreamCopier = new HotwordAudioStreamCopier(mAppOpsManager,
+ mVoiceInteractorIdentity.uid, mVoiceInteractorIdentity.packageName,
+ mVoiceInteractorIdentity.attributionTag);
mDetectionComponentName = serviceName;
mUser = userId;
mCallback = callback;
@@ -506,7 +507,7 @@ final class TrustedHotwordDetectorSession {
saveProximityValueToBundle(result);
HotwordDetectedResult newResult;
try {
- newResult = mHotwordAudioStreamManager.startCopyingAudioStreams(result);
+ newResult = mHotwordAudioStreamCopier.startCopyingAudioStreams(result);
} catch (IOException e) {
// TODO: Write event
mSoftwareCallback.onError();
@@ -641,7 +642,7 @@ final class TrustedHotwordDetectorSession {
saveProximityValueToBundle(result);
HotwordDetectedResult newResult;
try {
- newResult = mHotwordAudioStreamManager.startCopyingAudioStreams(result);
+ newResult = mHotwordAudioStreamCopier.startCopyingAudioStreams(result);
} catch (IOException e) {
// TODO: Write event
externalCallback.onError(CALLBACK_ONDETECTED_STREAM_COPY_ERROR);
@@ -1000,7 +1001,7 @@ final class TrustedHotwordDetectorSession {
HotwordDetectedResult newResult;
try {
newResult =
- mHotwordAudioStreamManager.startCopyingAudioStreams(
+ mHotwordAudioStreamCopier.startCopyingAudioStreams(
triggerResult);
} catch (IOException e) {
// TODO: Write event
diff --git a/startop/view_compiler/TEST_MAPPING b/startop/view_compiler/TEST_MAPPING
deleted file mode 100644
index 791e47105ff9..000000000000
--- a/startop/view_compiler/TEST_MAPPING
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "presubmit": [
- {
- "name": "dex-builder-test"
- },
- {
- "name": "CtsViewTestCases",
- "options": [
- {
- "include-filter": "android.view.cts.PrecompiledLayoutTest"
- }
- ]
- }
- ]
-}
diff --git a/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl b/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl
index 85191734872a..ea4480dc7958 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl
@@ -65,6 +65,7 @@ interface IImsMmTelFeature {
oneway void sendSms(in int token, int messageRef, String format, String smsc, boolean retry,
in byte[] pdu);
oneway void acknowledgeSms(int token, int messageRef, int result);
+ oneway void acknowledgeSmsWithPdu(int token, int messageRef, int result, in byte[] pdu);
oneway void acknowledgeSmsReport(int token, int messageRef, int result);
String getSmsFormat();
oneway void onSmsReady();
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index 8147759769e6..d776928965e5 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -275,6 +275,12 @@ public class MmTelFeature extends ImsFeature {
}
@Override
+ public void acknowledgeSmsWithPdu(int token, int messageRef, int result, byte[] pdu) {
+ executeMethodAsyncNoException(() -> MmTelFeature.this
+ .acknowledgeSms(token, messageRef, result, pdu), "acknowledgeSms");
+ }
+
+ @Override
public void acknowledgeSmsReport(int token, int messageRef, int result) {
executeMethodAsyncNoException(() -> MmTelFeature.this
.acknowledgeSmsReport(token, messageRef, result), "acknowledgeSmsReport");
@@ -1087,6 +1093,11 @@ public class MmTelFeature extends ImsFeature {
getSmsImplementation().acknowledgeSms(token, messageRef, result);
}
+ private void acknowledgeSms(int token, int messageRef,
+ @ImsSmsImplBase.DeliverStatusResult int result, byte[] pdu) {
+ getSmsImplementation().acknowledgeSms(token, messageRef, result, pdu);
+ }
+
private void acknowledgeSmsReport(int token, int messageRef,
@ImsSmsImplBase.StatusReportResult int result) {
getSmsImplementation().acknowledgeSmsReport(token, messageRef, result);
diff --git a/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
index fb997d118419..66833d18a945 100644
--- a/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
@@ -18,6 +18,7 @@ package android.telephony.ims.stub;
import android.annotation.IntDef;
import android.annotation.IntRange;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.RemoteException;
import android.telephony.SmsManager;
@@ -174,6 +175,9 @@ public class ImsSmsImplBase {
* {@link #onSmsReceived(int, String, byte[])} has been called to deliver the result to the IMS
* provider.
*
+ * If the framework needs to provide the PDU used to acknowledge the SMS,
+ * {@link #acknowledgeSms(int, int, int, byte[])} will be called.
+ *
* @param token token provided in {@link #onSmsReceived(int, String, byte[])}
* @param messageRef the message reference, which may be 1 byte if it is in
* {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
@@ -186,6 +190,27 @@ public class ImsSmsImplBase {
}
/**
+ * This method will be called by the platform after
+ * {@link #onSmsReceived(int, String, byte[])} has been called to acknowledge an incoming SMS.
+ *
+ * This method is only called in cases where the framework needs to provide the PDU such as the
+ * case where we provide the Short Message Transfer Layer PDU (see 3GPP TS 23.040). Otherwise,
+ * {@link #acknowledgeSms(int, int, int)} will be used.
+ *
+ * @param token token provided in {@link #onSmsReceived(int, String, byte[])}
+ * @param messageRef the message reference, which may be 1 byte if it is in
+ * {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
+ * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
+ * @param result result of delivering the message.
+ * @param pdu PDU representing the contents of the message.
+ */
+ public void acknowledgeSms(int token, @IntRange(from = 0, to = 65535) int messageRef,
+ @DeliverStatusResult int result, @NonNull byte[] pdu) {
+ Log.e(LOG_TAG, "acknowledgeSms() not implemented. acknowledgeSms(int, int, int) called.");
+ acknowledgeSms(token, messageRef, result);
+ }
+
+ /**
* This method will be triggered by the platform after
* {@link #onSmsStatusReportReceived(int, int, String, byte[])} or
* {@link #onSmsStatusReportReceived(int, String, byte[])} has been called to provide the
diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
index 15a6afc5ff7c..7c5dcf8b95f7 100644
--- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
+++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
@@ -50,6 +50,7 @@ public final class FrameworksTestsFilter extends SelectTest {
"android.app.servertransaction.", // all tests under the package.
"android.view.CutoutSpecificationTest",
"android.view.DisplayCutoutTest",
+ "android.view.DisplayShapeTest",
"android.view.InsetsAnimationControlImplTest",
"android.view.InsetsControllerTest",
"android.view.InsetsFlagsTest",
diff --git a/tools/lint/common/src/main/java/com/google/android/lint/PermissionMethodUtils.kt b/tools/lint/common/src/main/java/com/google/android/lint/PermissionMethodUtils.kt
index 720f8356f050..01575963951b 100644
--- a/tools/lint/common/src/main/java/com/google/android/lint/PermissionMethodUtils.kt
+++ b/tools/lint/common/src/main/java/com/google/android/lint/PermissionMethodUtils.kt
@@ -17,6 +17,7 @@
package com.google.android.lint
import com.android.tools.lint.detector.api.getUMethod
+import org.jetbrains.uast.UAnnotation
import org.jetbrains.uast.UCallExpression
import org.jetbrains.uast.UMethod
import org.jetbrains.uast.UParameter
@@ -26,10 +27,11 @@ fun isPermissionMethodCall(callExpression: UCallExpression): Boolean {
return hasPermissionMethodAnnotation(method)
}
-fun hasPermissionMethodAnnotation(method: UMethod): Boolean = method.annotations
- .any {
- it.hasQualifiedName(ANNOTATION_PERMISSION_METHOD)
- }
+fun hasPermissionMethodAnnotation(method: UMethod): Boolean =
+ getPermissionMethodAnnotation(method) != null
+
+fun getPermissionMethodAnnotation(method: UMethod?): UAnnotation? = method?.uAnnotations
+ ?.firstOrNull { it.qualifiedName == ANNOTATION_PERMISSION_METHOD }
fun hasPermissionNameAnnotation(parameter: UParameter) = parameter.annotations.any {
it.hasQualifiedName(ANNOTATION_PERMISSION_NAME)
diff --git a/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt b/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
index 413e19717d50..c5cf0fb2f963 100644
--- a/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
+++ b/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
@@ -40,7 +40,7 @@ class AndroidFrameworkIssueRegistry : IssueRegistry() {
EnforcePermissionDetector.ISSUE_MISSING_ENFORCE_PERMISSION,
EnforcePermissionDetector.ISSUE_MISMATCHING_ENFORCE_PERMISSION,
EnforcePermissionHelperDetector.ISSUE_ENFORCE_PERMISSION_HELPER,
- SimpleManualPermissionEnforcementDetector.ISSUE_USE_ENFORCE_PERMISSION_ANNOTATION,
+ SimpleManualPermissionEnforcementDetector.ISSUE_SIMPLE_MANUAL_PERMISSION_ENFORCEMENT,
SaferParcelChecker.ISSUE_UNSAFE_API_USAGE,
PackageVisibilityDetector.ISSUE_PACKAGE_NAME_NO_PACKAGE_VISIBILITY_FILTERS,
RegisterReceiverFlagDetector.ISSUE_RECEIVER_EXPORTED_FLAG,
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt
index b377d503f318..a20266a9b140 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt
@@ -31,7 +31,7 @@ class AndroidGlobalIssueRegistry : IssueRegistry() {
EnforcePermissionDetector.ISSUE_MISSING_ENFORCE_PERMISSION,
EnforcePermissionDetector.ISSUE_MISMATCHING_ENFORCE_PERMISSION,
EnforcePermissionHelperDetector.ISSUE_ENFORCE_PERMISSION_HELPER,
- SimpleManualPermissionEnforcementDetector.ISSUE_USE_ENFORCE_PERMISSION_ANNOTATION,
+ SimpleManualPermissionEnforcementDetector.ISSUE_SIMPLE_MANUAL_PERMISSION_ENFORCEMENT,
)
override val api: Int
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt
index f1b634898ec9..ee7dd62aaa36 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt
@@ -17,10 +17,14 @@
package com.google.android.lint.aidl
import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.LintFix
import com.android.tools.lint.detector.api.Location
+import com.android.tools.lint.detector.api.UastLintUtils.Companion.getAnnotationBooleanValue
import com.android.tools.lint.detector.api.getUMethod
+import com.google.android.lint.getPermissionMethodAnnotation
import com.google.android.lint.hasPermissionNameAnnotation
import com.google.android.lint.isPermissionMethodCall
+import com.intellij.psi.PsiType
import org.jetbrains.kotlin.psi.psiUtil.parameterIndex
import org.jetbrains.uast.UCallExpression
import org.jetbrains.uast.evaluateString
@@ -36,13 +40,37 @@ import org.jetbrains.uast.visitor.AbstractUastVisitor
*/
data class EnforcePermissionFix(
val locations: List<Location>,
- val permissionNames: List<String>
+ val permissionNames: List<String>,
+ val errorLevel: Boolean,
) {
- val annotation: String
+ fun toLintFix(annotationLocation: Location): LintFix {
+ val removeFixes = this.locations.map {
+ LintFix.create()
+ .replace()
+ .reformat(true)
+ .range(it)
+ .with("")
+ .autoFix()
+ .build()
+ }
+
+ val annotateFix = LintFix.create()
+ .annotate(this.annotation)
+ .range(annotationLocation)
+ .autoFix()
+ .build()
+
+ return LintFix.create().composite(annotateFix, *removeFixes.toTypedArray())
+ }
+
+ private val annotation: String
get() {
val quotedPermissions = permissionNames.joinToString(", ") { """"$it"""" }
+
val annotationParameter =
- if (permissionNames.size > 1) "allOf={$quotedPermissions}" else quotedPermissions
+ if (permissionNames.size > 1) "allOf={$quotedPermissions}"
+ else quotedPermissions
+
return "@$ANNOTATION_ENFORCE_PERMISSION($annotationParameter)"
}
@@ -54,19 +82,28 @@ data class EnforcePermissionFix(
fun fromCallExpression(
context: JavaContext,
callExpression: UCallExpression
- ): EnforcePermissionFix? =
- if (isPermissionMethodCall(callExpression)) {
- EnforcePermissionFix(
+ ): EnforcePermissionFix? {
+ val method = callExpression.resolve()?.getUMethod() ?: return null
+ val annotation = getPermissionMethodAnnotation(method) ?: return null
+ val enforces = method.returnType == PsiType.VOID
+ val orSelf = getAnnotationBooleanValue(annotation, "orSelf") ?: false
+ return EnforcePermissionFix(
listOf(getPermissionCheckLocation(context, callExpression)),
- getPermissionCheckValues(callExpression)
- )
- } else null
+ getPermissionCheckValues(callExpression),
+ // If we detect that the PermissionMethod enforces that permission is granted,
+ // AND is of the "orSelf" variety, we are very confident that this is a behavior
+ // preserving migration to @EnforcePermission. Thus, the incident should be ERROR
+ // level.
+ errorLevel = enforces && orSelf
+ )
+ }
fun compose(individuals: List<EnforcePermissionFix>): EnforcePermissionFix =
EnforcePermissionFix(
individuals.flatMap { it.locations },
- individuals.flatMap { it.permissionNames }
+ individuals.flatMap { it.permissionNames },
+ errorLevel = individuals.all(EnforcePermissionFix::errorLevel)
)
/**
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetector.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetector.kt
index 4c0cbe7b3adf..9999a0ba7e06 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetector.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetector.kt
@@ -18,6 +18,7 @@ package com.google.android.lint.aidl
import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Incident
import com.android.tools.lint.detector.api.Issue
import com.android.tools.lint.detector.api.JavaContext
import com.android.tools.lint.detector.api.Scope
@@ -28,6 +29,7 @@ import org.jetbrains.uast.UElement
import org.jetbrains.uast.UIfExpression
import org.jetbrains.uast.UMethod
import org.jetbrains.uast.UQualifiedReferenceExpression
+import org.jetbrains.uast.skipParenthesizedExprDown
/**
* Looks for methods implementing generated AIDL interface stubs
@@ -44,30 +46,25 @@ class SimpleManualPermissionEnforcementDetector : AidlImplementationDetector() {
interfaceName: String,
body: UBlockExpression
) {
- val fix = accumulateSimplePermissionCheckFixes(body, context) ?: return
-
- val javaRemoveFixes = fix.locations.map {
- fix()
- .replace()
- .reformat(true)
- .range(it)
- .with("")
- .autoFix()
- .build()
+ val enforcePermissionFix = accumulateSimplePermissionCheckFixes(body, context) ?: return
+ val lintFix = enforcePermissionFix.toLintFix(context.getLocation(node))
+ val message =
+ "$interfaceName permission check ${
+ if (enforcePermissionFix.errorLevel) "should" else "can"
+ } be converted to @EnforcePermission annotation"
+
+ val incident = Incident(
+ ISSUE_SIMPLE_MANUAL_PERMISSION_ENFORCEMENT,
+ enforcePermissionFix.locations.last(),
+ message,
+ lintFix
+ )
+
+ if (enforcePermissionFix.errorLevel) {
+ incident.overrideSeverity(Severity.ERROR)
}
- val javaAnnotateFix = fix()
- .annotate(fix.annotation)
- .range(context.getLocation(node))
- .autoFix()
- .build()
-
- context.report(
- ISSUE_USE_ENFORCE_PERMISSION_ANNOTATION,
- fix.locations.last(),
- "$interfaceName permission check can be converted to @EnforcePermission annotation",
- fix().composite(*javaRemoveFixes.toTypedArray(), javaAnnotateFix)
- )
+ context.report(incident)
}
/**
@@ -89,7 +86,8 @@ class SimpleManualPermissionEnforcementDetector : AidlImplementationDetector() {
EnforcePermissionFix? {
val singleFixes = mutableListOf<EnforcePermissionFix>()
for (expression in methodBody.expressions) {
- singleFixes.add(getPermissionCheckFix(expression, context) ?: break)
+ singleFixes.add(getPermissionCheckFix(expression.skipParenthesizedExprDown(), context)
+ ?: break)
}
return when (singleFixes.size) {
0 -> null
@@ -133,7 +131,7 @@ class SimpleManualPermissionEnforcementDetector : AidlImplementationDetector() {
""".trimIndent()
@JvmField
- val ISSUE_USE_ENFORCE_PERMISSION_ANNOTATION = Issue.create(
+ val ISSUE_SIMPLE_MANUAL_PERMISSION_ENFORCEMENT = Issue.create(
id = "SimpleManualPermissionEnforcement",
briefDescription = "Manual permission check can be @EnforcePermission annotation",
explanation = EXPLANATION,
diff --git a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetectorTest.kt b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetectorTest.kt
index 150fc264506f..bdf9c897b946 100644
--- a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetectorTest.kt
+++ b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetectorTest.kt
@@ -18,7 +18,6 @@ package com.google.android.lint.aidl
import com.android.tools.lint.checks.infrastructure.LintDetectorTest
import com.android.tools.lint.checks.infrastructure.TestLintTask
-import com.android.tools.lint.checks.infrastructure.TestMode
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Issue
@@ -27,7 +26,7 @@ class SimpleManualPermissionEnforcementDetectorTest : LintDetectorTest() {
override fun getDetector(): Detector = SimpleManualPermissionEnforcementDetector()
override fun getIssues(): List<Issue> = listOf(
SimpleManualPermissionEnforcementDetector
- .ISSUE_USE_ENFORCE_PERMISSION_ANNOTATION
+ .ISSUE_SIMPLE_MANUAL_PERMISSION_ENFORCEMENT
)
override fun lint(): TestLintTask = super.lint().allowMissingSdk()
@@ -36,15 +35,15 @@ class SimpleManualPermissionEnforcementDetectorTest : LintDetectorTest() {
lint().files(
java(
"""
- import android.content.Context;
- import android.test.ITest;
- public class Foo extends ITest.Stub {
- private Context mContext;
- @Override
- public void test() throws android.os.RemoteException {
- mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
- }
+ import android.content.Context;
+ import android.test.ITest;
+ public class Foo extends ITest.Stub {
+ private Context mContext;
+ @Override
+ public void test() throws android.os.RemoteException {
+ mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
}
+ }
"""
).indented(),
*stubs
@@ -52,10 +51,10 @@ class SimpleManualPermissionEnforcementDetectorTest : LintDetectorTest() {
.run()
.expect(
"""
- src/Foo.java:7: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ src/Foo.java:7: Error: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- 0 errors, 1 warnings
+ 1 errors, 0 warnings
"""
)
.expectFixDiffs(
@@ -69,22 +68,96 @@ class SimpleManualPermissionEnforcementDetectorTest : LintDetectorTest() {
)
}
- fun testAnonClass() {
+ fun testClass_orSelfFalse_warning() {
lint().files(
- java(
- """
+ java(
+ """
import android.content.Context;
import android.test.ITest;
- public class Foo {
+ public class Foo extends ITest.Stub {
private Context mContext;
- private ITest itest = new ITest.Stub() {
- @Override
- public void test() throws android.os.RemoteException {
- mContext.enforceCallingOrSelfPermission(
- "android.permission.READ_CONTACTS", "foo");
- }
- };
+ @Override
+ public void test() throws android.os.RemoteException {
+ mContext.enforceCallingPermission("android.permission.READ_CONTACTS", "foo");
+ }
}
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/Foo.java:7: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ mContext.enforceCallingPermission("android.permission.READ_CONTACTS", "foo");
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 1 warnings
+ """
+ )
+ .expectFixDiffs(
+ """
+ Fix for src/Foo.java line 7: Annotate with @EnforcePermission:
+ @@ -5 +5
+ + @android.annotation.EnforcePermission("android.permission.READ_CONTACTS")
+ @@ -7 +8
+ - mContext.enforceCallingPermission("android.permission.READ_CONTACTS", "foo");
+ """
+ )
+ }
+
+ fun testClass_enforcesFalse_warning() {
+ lint().files(
+ java(
+ """
+ import android.content.Context;
+ import android.test.ITest;
+ public class Foo extends ITest.Stub {
+ private Context mContext;
+ @Override
+ public void test() throws android.os.RemoteException {
+ mContext.checkCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/Foo.java:7: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ mContext.checkCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 1 warnings
+ """
+ )
+ .expectFixDiffs(
+ """
+ Fix for src/Foo.java line 7: Annotate with @EnforcePermission:
+ @@ -5 +5
+ + @android.annotation.EnforcePermission("android.permission.READ_CONTACTS")
+ @@ -7 +8
+ - mContext.checkCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
+ """
+ )
+ }
+
+ fun testAnonClass() {
+ lint().files(
+ java(
+ """
+ import android.content.Context;
+ import android.test.ITest;
+ public class Foo {
+ private Context mContext;
+ private ITest itest = new ITest.Stub() {
+ @Override
+ public void test() throws android.os.RemoteException {
+ mContext.enforceCallingOrSelfPermission(
+ "android.permission.READ_CONTACTS", "foo");
+ }
+ };
+ }
"""
).indented(),
*stubs
@@ -92,10 +165,10 @@ class SimpleManualPermissionEnforcementDetectorTest : LintDetectorTest() {
.run()
.expect(
"""
- src/Foo.java:8: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ src/Foo.java:8: Error: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
mContext.enforceCallingOrSelfPermission(
^
- 0 errors, 1 warnings
+ 1 errors, 0 warnings
"""
)
.expectFixDiffs(
@@ -114,16 +187,16 @@ class SimpleManualPermissionEnforcementDetectorTest : LintDetectorTest() {
lint().files(
java(
"""
- import android.content.Context;
- import android.test.ITest;
+ import android.content.Context;
+ import android.test.ITest;
- public class Foo extends ITest.Stub {
- private Context mContext;
- @Override
- public void test() throws android.os.RemoteException {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_CONTACTS, "foo");
- }
+ public class Foo extends ITest.Stub {
+ private Context mContext;
+ @Override
+ public void test() throws android.os.RemoteException {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_CONTACTS, "foo");
}
+ }
"""
).indented(),
*stubs,
@@ -132,10 +205,10 @@ class SimpleManualPermissionEnforcementDetectorTest : LintDetectorTest() {
.run()
.expect(
"""
- src/Foo.java:8: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ src/Foo.java:8: Error: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_CONTACTS, "foo");
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- 0 errors, 1 warnings
+ 1 errors, 0 warnings
"""
)
.expectFixDiffs(
@@ -153,20 +226,20 @@ class SimpleManualPermissionEnforcementDetectorTest : LintDetectorTest() {
lint().files(
java(
"""
- import android.content.Context;
- import android.test.ITest;
- public class Foo {
- private Context mContext;
- private ITest itest = new ITest.Stub() {
- @Override
- public void test() throws android.os.RemoteException {
- mContext.enforceCallingOrSelfPermission(
- "android.permission.READ_CONTACTS", "foo");
- mContext.enforceCallingOrSelfPermission(
- "android.permission.WRITE_CONTACTS", "foo");
- }
- };
- }
+ import android.content.Context;
+ import android.test.ITest;
+ public class Foo {
+ private Context mContext;
+ private ITest itest = new ITest.Stub() {
+ @Override
+ public void test() throws android.os.RemoteException {
+ mContext.enforceCallingOrSelfPermission(
+ "android.permission.READ_CONTACTS", "foo");
+ mContext.enforceCallingOrSelfPermission(
+ "android.permission.WRITE_CONTACTS", "foo");
+ }
+ };
+ }
"""
).indented(),
*stubs
@@ -174,10 +247,10 @@ class SimpleManualPermissionEnforcementDetectorTest : LintDetectorTest() {
.run()
.expect(
"""
- src/Foo.java:10: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ src/Foo.java:10: Error: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
mContext.enforceCallingOrSelfPermission(
^
- 0 errors, 1 warnings
+ 1 errors, 0 warnings
"""
)
.expectFixDiffs(
@@ -194,20 +267,110 @@ class SimpleManualPermissionEnforcementDetectorTest : LintDetectorTest() {
)
}
+ fun testAllOf_mixedOrSelf_warning() {
+ lint().files(
+ java(
+ """
+ import android.content.Context;
+ import android.test.ITest;
+ public class Foo {
+ private Context mContext;
+ private ITest itest = new ITest.Stub() {
+ @Override
+ public void test() throws android.os.RemoteException {
+ mContext.enforceCallingOrSelfPermission(
+ "android.permission.READ_CONTACTS", "foo");
+ mContext.enforceCallingPermission(
+ "android.permission.WRITE_CONTACTS", "foo");
+ }
+ };
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/Foo.java:10: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ mContext.enforceCallingPermission(
+ ^
+ 0 errors, 1 warnings
+ """
+ )
+ .expectFixDiffs(
+ """
+ Fix for src/Foo.java line 10: Annotate with @EnforcePermission:
+ @@ -6 +6
+ + @android.annotation.EnforcePermission(allOf={"android.permission.READ_CONTACTS", "android.permission.WRITE_CONTACTS"})
+ @@ -8 +9
+ - mContext.enforceCallingOrSelfPermission(
+ - "android.permission.READ_CONTACTS", "foo");
+ - mContext.enforceCallingPermission(
+ - "android.permission.WRITE_CONTACTS", "foo");
+ """
+ )
+ }
+
+ fun testAllOf_mixedEnforces_warning() {
+ lint().files(
+ java(
+ """
+ import android.content.Context;
+ import android.test.ITest;
+ public class Foo {
+ private Context mContext;
+ private ITest itest = new ITest.Stub() {
+ @Override
+ public void test() throws android.os.RemoteException {
+ mContext.enforceCallingOrSelfPermission(
+ "android.permission.READ_CONTACTS", "foo");
+ mContext.checkCallingOrSelfPermission(
+ "android.permission.WRITE_CONTACTS", "foo");
+ }
+ };
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/Foo.java:10: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ mContext.checkCallingOrSelfPermission(
+ ^
+ 0 errors, 1 warnings
+ """
+ )
+ .expectFixDiffs(
+ """
+ Fix for src/Foo.java line 10: Annotate with @EnforcePermission:
+ @@ -6 +6
+ + @android.annotation.EnforcePermission(allOf={"android.permission.READ_CONTACTS", "android.permission.WRITE_CONTACTS"})
+ @@ -8 +9
+ - mContext.enforceCallingOrSelfPermission(
+ - "android.permission.READ_CONTACTS", "foo");
+ - mContext.checkCallingOrSelfPermission(
+ - "android.permission.WRITE_CONTACTS", "foo");
+ """
+ )
+ }
+
fun testPrecedingExpressions() {
lint().files(
java(
"""
- import android.os.Binder;
- import android.test.ITest;
- public class Foo extends ITest.Stub {
- private mContext Context;
- @Override
- public void test() throws android.os.RemoteException {
- long uid = Binder.getCallingUid();
- mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
- }
+ import android.os.Binder;
+ import android.test.ITest;
+ public class Foo extends ITest.Stub {
+ private mContext Context;
+ @Override
+ public void test() throws android.os.RemoteException {
+ long uid = Binder.getCallingUid();
+ mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
}
+ }
"""
).indented(),
*stubs
@@ -217,25 +380,25 @@ class SimpleManualPermissionEnforcementDetectorTest : LintDetectorTest() {
}
fun testPermissionHelper() {
- lint().skipTestModes(TestMode.PARENTHESIZED).files(
+ lint().files(
java(
"""
- import android.content.Context;
- import android.test.ITest;
+ import android.content.Context;
+ import android.test.ITest;
- public class Foo extends ITest.Stub {
- private Context mContext;
+ public class Foo extends ITest.Stub {
+ private Context mContext;
- @android.content.pm.PermissionMethod
- private void helper() {
- mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
- }
+ @android.content.pm.PermissionMethod(orSelf = true)
+ private void helper() {
+ mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
+ }
- @Override
- public void test() throws android.os.RemoteException {
- helper();
- }
+ @Override
+ public void test() throws android.os.RemoteException {
+ helper();
}
+ }
"""
).indented(),
*stubs
@@ -243,10 +406,10 @@ class SimpleManualPermissionEnforcementDetectorTest : LintDetectorTest() {
.run()
.expect(
"""
- src/Foo.java:14: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ src/Foo.java:14: Error: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
helper();
~~~~~~~~~
- 0 errors, 1 warnings
+ 1 errors, 0 warnings
"""
)
.expectFixDiffs(
@@ -260,8 +423,52 @@ class SimpleManualPermissionEnforcementDetectorTest : LintDetectorTest() {
)
}
+ fun testPermissionHelper_orSelfNotBubbledUp_warning() {
+ lint().files(
+ java(
+ """
+ import android.content.Context;
+ import android.test.ITest;
+
+ public class Foo extends ITest.Stub {
+ private Context mContext;
+
+ @android.content.pm.PermissionMethod
+ private void helper() {
+ mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
+ }
+
+ @Override
+ public void test() throws android.os.RemoteException {
+ helper();
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/Foo.java:14: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ helper();
+ ~~~~~~~~~
+ 0 errors, 1 warnings
+ """
+ )
+ .expectFixDiffs(
+ """
+ Fix for src/Foo.java line 14: Annotate with @EnforcePermission:
+ @@ -12 +12
+ + @android.annotation.EnforcePermission("android.permission.READ_CONTACTS")
+ @@ -14 +15
+ - helper();
+ """
+ )
+ }
+
fun testPermissionHelperAllOf() {
- lint().skipTestModes(TestMode.PARENTHESIZED).files(
+ lint().files(
java(
"""
import android.content.Context;
@@ -270,7 +477,7 @@ class SimpleManualPermissionEnforcementDetectorTest : LintDetectorTest() {
public class Foo extends ITest.Stub {
private Context mContext;
- @android.content.pm.PermissionMethod
+ @android.content.pm.PermissionMethod(orSelf = true)
private void helper() {
mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
mContext.enforceCallingOrSelfPermission("android.permission.WRITE_CONTACTS", "foo");
@@ -289,10 +496,10 @@ class SimpleManualPermissionEnforcementDetectorTest : LintDetectorTest() {
.run()
.expect(
"""
- src/Foo.java:16: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ src/Foo.java:16: Error: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
mContext.enforceCallingOrSelfPermission("FOO", "foo");
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- 0 errors, 1 warnings
+ 1 errors, 0 warnings
"""
)
.expectFixDiffs(
@@ -309,7 +516,7 @@ class SimpleManualPermissionEnforcementDetectorTest : LintDetectorTest() {
fun testPermissionHelperNested() {
- lint().skipTestModes(TestMode.PARENTHESIZED).files(
+ lint().files(
java(
"""
import android.content.Context;
@@ -318,12 +525,12 @@ class SimpleManualPermissionEnforcementDetectorTest : LintDetectorTest() {
public class Foo extends ITest.Stub {
private Context mContext;
- @android.content.pm.PermissionMethod
+ @android.content.pm.PermissionMethod(orSelf = true)
private void helperHelper() {
helper("android.permission.WRITE_CONTACTS");
}
- @android.content.pm.PermissionMethod
+ @android.content.pm.PermissionMethod(orSelf = true)
private void helper(@android.content.pm.PermissionName String extraPermission) {
mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
}
@@ -340,10 +547,10 @@ class SimpleManualPermissionEnforcementDetectorTest : LintDetectorTest() {
.run()
.expect(
"""
- src/Foo.java:19: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ src/Foo.java:19: Error: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
helperHelper();
~~~~~~~~~~~~~~~
- 0 errors, 1 warnings
+ 1 errors, 0 warnings
"""
)
.expectFixDiffs(
diff --git a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/Stubs.kt b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/Stubs.kt
index bd6b1952847c..5ac8a0ba2604 100644
--- a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/Stubs.kt
+++ b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/Stubs.kt
@@ -17,8 +17,12 @@ val contextStub: TestFile = java(
"""
package android.content;
public class Context {
- @android.content.pm.PermissionMethod
+ @android.content.pm.PermissionMethod(orSelf = true)
public void enforceCallingOrSelfPermission(@android.content.pm.PermissionName String permission, String message) {}
+ @android.content.pm.PermissionMethod
+ public void enforceCallingPermission(@android.content.pm.PermissionName String permission, String message) {}
+ @android.content.pm.PermissionMethod(orSelf = true)
+ public int checkCallingOrSelfPermission(@android.content.pm.PermissionName String permission, String message) {}
}
"""
).indented()