summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java15
-rw-r--r--apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java6
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java10
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java7
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java436
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java14
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java26
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/Agent.java98
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java6
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java4
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java38
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java139
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java6
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/Scribe.java9
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/TareShellCommand.java112
-rw-r--r--core/TEST_MAPPING3
-rw-r--r--core/api/current.txt7
-rw-r--r--core/api/test-current.txt1
-rw-r--r--core/java/android/attention/AttentionManagerInternal.java4
-rw-r--r--core/java/android/content/pm/ActivityInfo.java52
-rw-r--r--core/java/android/hardware/face/FaceManager.java40
-rw-r--r--core/java/android/hardware/face/IFaceAuthenticatorsRegisteredCallback.aidl35
-rw-r--r--core/java/android/hardware/face/IFaceService.aidl9
-rw-r--r--core/java/android/hardware/fingerprint/IFingerprintService.aidl2
-rw-r--r--core/java/android/provider/DeviceConfig.java7
-rw-r--r--core/java/android/provider/Settings.java19
-rw-r--r--core/java/android/service/voice/HotwordDetectedResult.java57
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java4
-rw-r--r--core/java/android/view/IWindowSession.aidl53
-rw-r--r--core/java/android/view/InsetsSourceConsumer.java4
-rw-r--r--core/java/android/view/SurfaceView.java42
-rw-r--r--core/java/android/view/ViewRootImpl.java134
-rw-r--r--core/java/android/view/WindowlessWindowManager.java39
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java15
-rw-r--r--core/java/android/widget/Editor.java28
-rw-r--r--core/java/android/widget/SelectionActionModeHelper.java6
-rw-r--r--core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java21
-rw-r--r--core/java/com/android/internal/policy/DecorContext.java9
-rw-r--r--core/java/com/android/internal/util/ScreenshotHelper.java79
-rw-r--r--core/res/res/layout/side_fps_toast.xml4
-rw-r--r--core/res/res/values-night/colors.xml5
-rw-r--r--core/res/res/values/colors.xml5
-rw-r--r--core/res/res/values/config.xml3
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java47
-rw-r--r--libs/WindowManager/Shell/res/animator/tv_window_menu_action_button_animator.xml (renamed from libs/WindowManager/Shell/res/animator/tv_pip_menu_action_button_animator.xml)0
-rw-r--r--libs/WindowManager/Shell/res/color/tv_window_menu_close_icon.xml (renamed from libs/WindowManager/Shell/res/color/tv_pip_menu_close_icon.xml)2
-rw-r--r--libs/WindowManager/Shell/res/color/tv_window_menu_close_icon_bg.xml (renamed from libs/WindowManager/Shell/res/color/tv_pip_menu_icon_bg.xml)6
-rw-r--r--libs/WindowManager/Shell/res/color/tv_window_menu_icon.xml (renamed from libs/WindowManager/Shell/res/color/tv_pip_menu_icon.xml)8
-rw-r--r--libs/WindowManager/Shell/res/color/tv_window_menu_icon_bg.xml (renamed from libs/WindowManager/Shell/res/color/tv_pip_menu_close_icon_bg.xml)4
-rw-r--r--libs/WindowManager/Shell/res/drawable/decor_minimize_button_dark.xml24
-rw-r--r--libs/WindowManager/Shell/res/drawable/tv_pip_button_bg.xml21
-rw-r--r--libs/WindowManager/Shell/res/drawable/tv_pip_menu_border.xml2
-rw-r--r--libs/WindowManager/Shell/res/drawable/tv_window_button_bg.xml21
-rw-r--r--libs/WindowManager/Shell/res/layout/caption_window_decoration.xml12
-rw-r--r--libs/WindowManager/Shell/res/layout/tv_pip_menu.xml10
-rw-r--r--libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml40
-rw-r--r--libs/WindowManager/Shell/res/layout/tv_window_menu_action_button.xml40
-rw-r--r--libs/WindowManager/Shell/res/values-es/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-television/dimen.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-tvdpi/dimen.xml16
-rw-r--r--libs/WindowManager/Shell/res/values/colors_tv.xml16
-rw-r--r--libs/WindowManager/Shell/res/values/strings.xml2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java43
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/TvWindowMenuActionButton.java (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java)38
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarter.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java77
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java3
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt86
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt1
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt64
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt187
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt196
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt7
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt193
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt53
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt194
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_splitscreen.xml1
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java7
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java234
-rw-r--r--libs/hwui/FrameInfoVisualizer.cpp2
-rw-r--r--libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp2
-rw-r--r--libs/hwui/pipeline/skia/SkiaProfileRenderer.cpp8
-rw-r--r--libs/hwui/pipeline/skia/SkiaProfileRenderer.h9
-rw-r--r--libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp4
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp18
-rw-r--r--location/java/android/location/Location.java9
-rw-r--r--media/java/android/media/IMediaRouter2.aidl4
-rw-r--r--media/java/android/media/IMediaRouter2Manager.aidl4
-rw-r--r--media/java/android/media/MediaRouter2.java92
-rw-r--r--media/java/android/media/MediaRouter2Manager.java85
-rw-r--r--media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java189
-rw-r--r--media/tests/MediaRouter/src/com/android/mediaroutertest/StubMediaRoute2ProviderService.java43
-rw-r--r--packages/SettingsLib/Spa/OWNERS4
-rw-r--r--packages/SettingsLib/Spa/build.gradle6
-rw-r--r--packages/SettingsLib/Spa/codelab/AndroidManifest.xml4
-rw-r--r--packages/SettingsLib/Spa/codelab/build.gradle4
-rw-r--r--packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/ArgumentPage.kt8
-rw-r--r--packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/FooterPage.kt71
-rw-r--r--packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/HomePage.kt11
-rw-r--r--packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PageRepository.kt13
-rw-r--r--packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PreferencePage.kt8
-rw-r--r--packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/SliderPage.kt107
-rw-r--r--packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/SwitchPreferencePage.kt129
-rw-r--r--packages/SettingsLib/Spa/spa/Android.bp5
-rw-r--r--packages/SettingsLib/Spa/spa/build.gradle16
-rw-r--r--packages/SettingsLib/Spa/spa/res/values-night/themes.xml4
-rw-r--r--packages/SettingsLib/Spa/spa/res/values/themes.xml6
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/SpaActivity.kt7
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/api/SettingsPageProvider.kt (renamed from packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/api/SettingsPageProvider.kt)2
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/api/SettingsPageRepository.kt (renamed from packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/api/SettingsPageRepository.kt)2
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/DrawablePainter.kt180
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavControllerWrapper.kt (renamed from packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/NavControllerWrapper.kt)2
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/RuntimeUtils.kt (renamed from packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/RuntimeUtils.kt)10
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/MaterialColors.kt (renamed from packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/MaterialColors.kt)2
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsColors.kt91
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt (renamed from packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsDimension.kt)3
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsOpacity.kt (renamed from packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsOpacity.kt)2
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt48
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTonalPalette.kt205
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTypography.kt144
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt17
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BasePreference.kt19
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt2
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SwitchPreference.kt146
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Footer.kt47
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Icon.kt38
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/SettingsSlider.kt185
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Switch.kt47
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt52
-rw-r--r--packages/SettingsLib/Spa/tests/build.gradle8
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/PreferenceTest.kt2
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/SwitchPreferenceTest.kt91
-rw-r--r--packages/SettingsLib/SpaPrivileged/Android.bp33
-rw-r--r--packages/SettingsLib/SpaPrivileged/AndroidManifest.xml18
-rw-r--r--packages/SettingsLib/SpaPrivileged/OWNERS1
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/AppRepository.kt58
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/PackageManagers.kt25
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt70
-rw-r--r--packages/SettingsLib/res/values-af/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-af/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-am/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-ar/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-as/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-az/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-az/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-b+sr+Latn/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-b+sr+Latn/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-be/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-bg/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-bg/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-bn/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-bn/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-bs/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-bs/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ca/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-ca/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-cs/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-da/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-da/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-de/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-de/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-el/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-el/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-en-rAU/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-en-rAU/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-en-rCA/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-en-rCA/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-en-rGB/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-en-rGB/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-en-rIN/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-en-rIN/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-en-rXC/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-en-rXC/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-es-rUS/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-es/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-es/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-et/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-et/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-eu/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-eu/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-fa/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-fa/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-fi/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-fr-rCA/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-fr-rCA/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-fr/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-fr/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-gl/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-gl/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-gu/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-gu/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-hi/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-hi/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-hr/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-hu/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-hu/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-hy/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-in/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-in/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-is/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-is/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-it/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-it/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-iw/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-iw/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-ja/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-ja/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-ka/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-ka/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-kk/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-kk/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-km/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-kn/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-ko/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-ko/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ky/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-ky/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-lo/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-lt/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-lt/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-lv/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-mk/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-mk/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-ml/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-ml/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-mn/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-mn/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-mr/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-mr/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-ms/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-ms/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-my/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-my/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-nb/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-nb/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-ne/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-ne/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-nl/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-nl/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-or/strings.xml11
-rw-r--r--packages/SettingsLib/res/values-pa/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-pa/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-pl/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-pt-rBR/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-pt-rBR/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-pt-rPT/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-pt-rPT/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-pt/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-pt/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ro/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-ro/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-ru/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-si/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-si/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-sk/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-sk/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-sl/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-sl/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-sq/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-sq/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-sr/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-sr/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-sv/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-sv/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-sw/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-sw/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-ta/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-ta/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-te/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-te/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-th/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-th/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-tl/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-tl/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-tr/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-tr/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-uk/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-ur/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-ur/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-uz/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-vi/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-vi/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-zh-rCN/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-zh-rCN/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-zh-rHK/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-zh-rHK/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-zh-rTW/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-zh-rTW/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-zu/arrays.xml24
-rw-r--r--packages/SettingsLib/res/values-zu/strings.xml4
-rw-r--r--packages/SettingsLib/res/values/strings.xml3
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java12
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java12
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java3
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java2
-rw-r--r--packages/SystemUI/checks/src/com/android/internal/systemui/lint/BindServiceViaContextDetector.kt67
-rw-r--r--packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt3
-rw-r--r--packages/SystemUI/checks/tests/com/android/systemui/lint/BindServiceViaContextDetectorTest.kt140
-rw-r--r--packages/SystemUI/res/values/strings.xml6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/LockIconViewController.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java86
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java93
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java92
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java44
-rw-r--r--packages/VpnDialogs/res/values-es/strings.xml2
-rw-r--r--proto/src/system_messages.proto4
-rw-r--r--services/core/java/com/android/server/DropBoxManagerService.java4
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java8
-rw-r--r--services/core/java/com/android/server/am/LmkdStatsReporter.java6
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java27
-rw-r--r--services/core/java/com/android/server/am/ProcessServiceRecord.java10
-rw-r--r--services/core/java/com/android/server/am/SettingsToPropertiesMapper.java1
-rw-r--r--services/core/java/com/android/server/attention/AttentionManagerService.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BiometricServiceProvider.java61
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java235
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java94
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceService.java221
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceServiceRegistry.java71
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java30
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java373
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistry.java72
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java37
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java5
-rw-r--r--services/core/java/com/android/server/display/AutomaticBrightnessController.java30
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java42
-rw-r--r--services/core/java/com/android/server/display/brightness/BrightnessEvent.java157
-rw-r--r--services/core/java/com/android/server/display/color/ColorDisplayService.java4
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java12
-rw-r--r--services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java319
-rw-r--r--services/core/java/com/android/server/wm/ActivityMetricsLogger.java28
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java31
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java27
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java13
-rw-r--r--services/core/java/com/android/server/wm/LaunchParamsUtil.java6
-rw-r--r--services/core/java/com/android/server/wm/LetterboxUiController.java4
-rw-r--r--services/core/java/com/android/server/wm/Session.java23
-rw-r--r--services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java7
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java106
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java39
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/BatteryControllerTest.java4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java457
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java79
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java28
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java (renamed from services/tests/servicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java)9
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceRegistryTest.java165
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java51
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistryTest.java161
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java41
-rw-r--r--services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java16
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java10
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java103
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java34
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java65
-rw-r--r--telephony/java/android/telephony/data/ApnSetting.java3
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl3
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt5
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt7
-rw-r--r--tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java23
-rw-r--r--tools/aapt/Command.cpp6
-rw-r--r--tools/aapt/ResourceTable.cpp8
-rw-r--r--tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt3
-rw-r--r--tools/lint/checks/src/main/java/com/google/android/lint/Constants.kt36
-rw-r--r--tools/lint/checks/src/main/java/com/google/android/lint/PackageVisibilityDetector.kt31
-rw-r--r--tools/lint/checks/src/main/java/com/google/android/lint/aidl/Constants.kt73
-rw-r--r--tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt (renamed from tools/lint/checks/src/main/java/com/google/android/lint/EnforcePermissionDetector.kt)19
-rw-r--r--tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt118
-rw-r--r--tools/lint/checks/src/main/java/com/google/android/lint/aidl/ManualPermissionCheckDetector.kt199
-rw-r--r--tools/lint/checks/src/main/java/com/google/android/lint/model/Method.kt (renamed from packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsTheme.kt)18
-rw-r--r--tools/lint/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt (renamed from tools/lint/checks/src/test/java/com/google/android/lint/EnforcePermissionDetectorTest.kt)2
-rw-r--r--tools/lint/checks/src/test/java/com/google/android/lint/aidl/ManualPermissionCheckDetectorTest.kt211
395 files changed, 10082 insertions, 2379 deletions
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java
index 1e2650dbfdc4..3a23b5491678 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java
@@ -28,6 +28,7 @@ import android.util.Log;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -50,6 +51,14 @@ public class TypefaceSerializationPerfTest {
@Rule
public PerfManualStatusReporter mPerfManualStatusReporter = new PerfManualStatusReporter();
+ @Before
+ public void setUp() {
+ // Parse and load the preinstalled fonts in the test process so that:
+ // (1) Updated fonts do not affect test results.
+ // (2) Lazy-loading of fonts does not affect test results (esp. testSerializeFontMap).
+ Typeface.loadPreinstalledSystemFontMap();
+ }
+
@ManualBenchmarkState.ManualBenchmarkTest(
warmupDurationNs = WARMUP_DURATION_NS,
targetTestDurationNs = TARGET_TEST_DURATION_NS)
@@ -61,8 +70,12 @@ public class TypefaceSerializationPerfTest {
long elapsedTime = 0;
while (state.keepRunning(elapsedTime)) {
long startTime = System.nanoTime();
- Typeface.serializeFontMap(systemFontMap);
+ SharedMemory sharedMemory = Typeface.serializeFontMap(systemFontMap);
elapsedTime = System.nanoTime() - startTime;
+ sharedMemory.close();
+ android.util.Log.i(TAG,
+ "testSerializeFontMap isWarmingUp=" + state.isWarmingUp()
+ + " elapsedTime=" + elapsedTime);
}
}
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
index c92c6340a6b4..fb62920681de 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
@@ -153,9 +153,9 @@ public class RelayoutPerfTest extends WindowManagerPerfTestBase
final IWindowSession session = WindowManagerGlobal.getWindowSession();
while (state.keepRunning()) {
session.relayout(mWindow, mParams, mWidth, mHeight,
- mViewVisibility.getAsInt(), mFlags, mOutFrames,
- mOutMergedConfiguration, mOutSurfaceControl, mOutInsetsState, mOutControls,
- new Bundle());
+ mViewVisibility.getAsInt(), mFlags, 0 /* seq */, 0 /* lastSyncSeqId */,
+ mOutFrames, mOutMergedConfiguration, mOutSurfaceControl, mOutInsetsState,
+ mOutControls, new Bundle());
}
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index d8e25b6bf5a2..c0a9e675dcb2 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -3078,15 +3078,15 @@ public class AlarmManagerService extends SystemService {
pw.decreaseIndent();
pw.println();
} else {
- if (mAppStateTracker != null) {
- mAppStateTracker.dump(pw);
- pw.println();
- }
-
pw.println("App Standby Parole: " + mAppStandbyParole);
pw.println();
}
+ if (mAppStateTracker != null) {
+ mAppStateTracker.dump(pw);
+ pw.println();
+ }
+
final long nowELAPSED = mInjector.getElapsedRealtime();
final long nowUPTIME = SystemClock.uptimeMillis();
final long nowRTC = mInjector.getCurrentTimeMillis();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 73508c8ecd80..794362bd682b 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1556,7 +1556,10 @@ public class JobSchedulerService extends com.android.server.SystemService
// Create the controllers.
mControllers = new ArrayList<StateController>();
- final FlexibilityController flexibilityController = new FlexibilityController(this);
+ mPrefetchController = new PrefetchController(this);
+ mControllers.add(mPrefetchController);
+ final FlexibilityController flexibilityController =
+ new FlexibilityController(this, mPrefetchController);
mControllers.add(flexibilityController);
final ConnectivityController connectivityController =
new ConnectivityController(this, flexibilityController);
@@ -1575,8 +1578,6 @@ public class JobSchedulerService extends com.android.server.SystemService
mControllers.add(new ContentObserverController(this));
mDeviceIdleJobsController = new DeviceIdleJobsController(this);
mControllers.add(mDeviceIdleJobsController);
- mPrefetchController = new PrefetchController(this);
- mControllers.add(mPrefetchController);
mQuotaController =
new QuotaController(this, backgroundJobsController, connectivityController);
mControllers.add(mQuotaController);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
index f4ee0aeb8c0c..2e41dfd2888c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
@@ -23,6 +23,7 @@ import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_BATTERY_NOT_LOW;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CHARGING;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CONNECTIVITY;
+import static com.android.server.job.controllers.JobStatus.CONSTRAINT_FLEXIBLE;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_IDLE;
import android.annotation.ElapsedRealtimeLong;
@@ -36,6 +37,7 @@ import android.provider.DeviceConfig;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.Slog;
+import android.util.SparseArrayMap;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -44,14 +46,13 @@ import com.android.server.job.JobSchedulerService;
import com.android.server.utils.AlarmQueue;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
/**
* Controller that tracks the number of flexible constraints being actively satisfied.
* Drops constraint for TOP apps and lowers number of required constraints with time.
- *
- * TODO(b/238887951): handle prefetch
*/
public final class FlexibilityController extends StateController {
private static final String TAG = "JobScheduler.Flexibility";
@@ -68,24 +69,15 @@ public final class FlexibilityController extends StateController {
private static final int FLEXIBLE_CONSTRAINTS =
JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS | SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS;
- @VisibleForTesting
- static final int NUM_JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS =
+ private static final int NUM_JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS =
Integer.bitCount(JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS);
static final int NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS =
Integer.bitCount(SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS);
- @VisibleForTesting
static final int NUM_FLEXIBLE_CONSTRAINTS = Integer.bitCount(FLEXIBLE_CONSTRAINTS);
- /** Hard cutoff to remove flexible constraints. */
- private static final long DEADLINE_PROXIMITY_LIMIT_MS = 15 * MINUTE_IN_MILLIS;
-
- /**
- * The default deadline that all flexible constraints should be dropped by if a job lacks
- * a deadline.
- */
- private static final long DEFAULT_FLEXIBILITY_DEADLINE = 72 * HOUR_IN_MILLIS;
+ private static final long NO_LIFECYCLE_END = Long.MAX_VALUE;
/**
* Keeps track of what flexible constraints are satisfied at the moment.
@@ -94,30 +86,98 @@ public final class FlexibilityController extends StateController {
@VisibleForTesting
@GuardedBy("mLock")
int mSatisfiedFlexibleConstraints;
+
+ /** Hard cutoff to remove flexible constraints. */
+ private long mDeadlineProximityLimitMs =
+ FcConfig.DEFAULT_DEADLINE_PROXIMITY_LIMIT_MS;
+
+ /**
+ * The default deadline that all flexible constraints should be dropped by if a job lacks
+ * a deadline.
+ */
+ private long mFallbackFlexibilityDeadlineMs =
+ FcConfig.DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS;
+
@GuardedBy("mLock")
- private boolean mFlexibilityEnabled = FcConstants.DEFAULT_FLEXIBILITY_ENABLED;
+ @VisibleForTesting
+ boolean mFlexibilityEnabled = FcConfig.DEFAULT_FLEXIBILITY_ENABLED;
+
+ private long mMinTimeBetweenFlexibilityAlarmsMs =
+ FcConfig.DEFAULT_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS;
+
+ /**
+ * The percent of a job's lifecycle to drop number of required constraints.
+ * mPercentToDropConstraints[i] denotes that at x% of a Jobs lifecycle,
+ * the controller should have i+1 constraints dropped.
+ */
+ private int[] mPercentToDropConstraints;
@VisibleForTesting
@GuardedBy("mLock")
final FlexibilityTracker mFlexibilityTracker;
- private final FcConstants mFcConstants;
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ final FlexibilityAlarmQueue mFlexibilityAlarmQueue;
+ @VisibleForTesting
+ final FcConfig mFcConfig;
- private final FlexibilityAlarmQueue mFlexibilityAlarmQueue;
- private static final long MIN_TIME_BETWEEN_ALARMS_MS = MINUTE_IN_MILLIS;
+ @VisibleForTesting
+ final PrefetchController mPrefetchController;
/**
- * The percent of a Jobs lifecycle to drop number of required constraints.
- * PERCENT_TO_DROP_CONSTRAINTS[i] denotes that at x% of a Jobs lifecycle,
- * the controller should have i+1 constraints dropped.
+ * Stores the beginning of prefetch jobs lifecycle per app as a maximum of
+ * the last time the app was used and the last time the launch time was updated.
*/
- private static final int[] PERCENT_TO_DROP_CONSTRAINTS = {50, 60, 70, 80};
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ final SparseArrayMap<String, Long> mPrefetchLifeCycleStart = new SparseArrayMap<>();
- public FlexibilityController(JobSchedulerService service) {
+ @VisibleForTesting
+ final PrefetchController.PrefetchChangedListener mPrefetchChangedListener =
+ new PrefetchController.PrefetchChangedListener() {
+ @Override
+ public void onPrefetchCacheUpdated(ArraySet<JobStatus> jobs, int userId,
+ String pkgName, long prevEstimatedLaunchTime, long newEstimatedLaunchTime) {
+ synchronized (mLock) {
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ final long prefetchThreshold =
+ mPrefetchController.getLaunchTimeThresholdMs();
+ boolean jobWasInPrefetchWindow = prevEstimatedLaunchTime
+ - prefetchThreshold < nowElapsed;
+ boolean jobIsInPrefetchWindow = newEstimatedLaunchTime
+ - prefetchThreshold < nowElapsed;
+ if (jobIsInPrefetchWindow != jobWasInPrefetchWindow) {
+ // If the job was in the window previously then changing the start
+ // of the lifecycle to the current moment without a large change in the
+ // end would squeeze the window too tight fail to drop constraints.
+ mPrefetchLifeCycleStart.add(userId, pkgName, Math.max(nowElapsed,
+ mPrefetchLifeCycleStart.getOrDefault(userId, pkgName, 0L)));
+ }
+ for (int i = 0; i < jobs.size(); i++) {
+ JobStatus js = jobs.valueAt(i);
+ if (!js.hasFlexibilityConstraint()) {
+ continue;
+ }
+ mFlexibilityTracker.resetJobNumDroppedConstraints(js);
+ mFlexibilityAlarmQueue.scheduleDropNumConstraintsAlarm(js);
+ }
+ }
+ }
+ };
+
+ public FlexibilityController(
+ JobSchedulerService service, PrefetchController prefetchController) {
super(service);
mFlexibilityTracker = new FlexibilityTracker(NUM_FLEXIBLE_CONSTRAINTS);
- mFcConstants = new FcConstants();
+ mFcConfig = new FcConfig();
mFlexibilityAlarmQueue = new FlexibilityAlarmQueue(
mContext, JobSchedulerBackgroundThread.get().getLooper());
+ mPercentToDropConstraints =
+ mFcConfig.DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS;
+ mPrefetchController = prefetchController;
+ if (mFlexibilityEnabled) {
+ mPrefetchController.registerPrefetchChangedListener(mPrefetchChangedListener);
+ }
}
/**
@@ -131,7 +191,7 @@ public final class FlexibilityController extends StateController {
js.setTrackingController(JobStatus.TRACKING_FLEXIBILITY);
final long nowElapsed = sElapsedRealtimeClock.millis();
js.setFlexibilityConstraintSatisfied(nowElapsed, isFlexibilitySatisfiedLocked(js));
- mFlexibilityAlarmQueue.addAlarm(js, getNextConstraintDropTimeElapsed(js));
+ mFlexibilityAlarmQueue.scheduleDropNumConstraintsAlarm(js);
}
}
@@ -144,6 +204,19 @@ public final class FlexibilityController extends StateController {
}
}
+ @Override
+ @GuardedBy("mLock")
+ public void onAppRemovedLocked(String packageName, int uid) {
+ final int userId = UserHandle.getUserId(uid);
+ mPrefetchLifeCycleStart.delete(userId, packageName);
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ public void onUserRemovedLocked(int userId) {
+ mPrefetchLifeCycleStart.delete(userId);
+ }
+
/** Checks if the flexibility constraint is actively satisfied for a given job. */
@GuardedBy("mLock")
boolean isFlexibilitySatisfiedLocked(JobStatus js) {
@@ -165,6 +238,7 @@ public final class FlexibilityController extends StateController {
* Sets the controller's constraint to a given state.
* Changes flexibility constraint satisfaction for affected jobs.
*/
+ @VisibleForTesting
void setConstraintSatisfied(int constraint, boolean state) {
synchronized (mLock) {
final boolean old = (mSatisfiedFlexibleConstraints & constraint) != 0;
@@ -187,21 +261,20 @@ public final class FlexibilityController extends StateController {
// of satisfied system-wide constraints and iterate to the max number of potentially
// satisfied constraints, determined by how many job-specific constraints exist.
for (int j = 0; j <= NUM_JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS; j++) {
- final ArraySet<JobStatus> jobs = mFlexibilityTracker
+ final ArraySet<JobStatus> jobsByNumConstraints = mFlexibilityTracker
.getJobsByNumRequiredConstraints(numConstraintsToUpdate + j);
- if (jobs == null) {
+ if (jobsByNumConstraints == null) {
// If there are no more jobs to iterate through we can just return.
return;
}
- for (int i = 0; i < jobs.size(); i++) {
- JobStatus js = jobs.valueAt(i);
+ for (int i = 0; i < jobsByNumConstraints.size(); i++) {
+ JobStatus js = jobsByNumConstraints.valueAt(i);
js.setFlexibilityConstraintSatisfied(
nowElapsed, isFlexibilitySatisfiedLocked(js));
}
}
-
}
}
@@ -211,16 +284,85 @@ public final class FlexibilityController extends StateController {
return (mSatisfiedFlexibleConstraints & constraint) != 0;
}
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ long getLifeCycleBeginningElapsedLocked(JobStatus js) {
+ if (js.getJob().isPrefetch()) {
+ final long earliestRuntime = Math.max(js.enqueueTime, js.getEarliestRunTime());
+ final long estimatedLaunchTime =
+ mPrefetchController.getNextEstimatedLaunchTimeLocked(js);
+ long prefetchWindowStart = mPrefetchLifeCycleStart.getOrDefault(
+ js.getSourceUserId(), js.getSourcePackageName(), 0L);
+ if (estimatedLaunchTime != Long.MAX_VALUE) {
+ prefetchWindowStart = Math.max(prefetchWindowStart,
+ estimatedLaunchTime - mPrefetchController.getLaunchTimeThresholdMs());
+ }
+ return Math.max(prefetchWindowStart, earliestRuntime);
+ }
+ return js.getEarliestRunTime() == JobStatus.NO_EARLIEST_RUNTIME
+ ? js.enqueueTime : js.getEarliestRunTime();
+ }
+
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ long getLifeCycleEndElapsedLocked(JobStatus js, long earliest) {
+ if (js.getJob().isPrefetch()) {
+ final long estimatedLaunchTime =
+ mPrefetchController.getNextEstimatedLaunchTimeLocked(js);
+ // Prefetch jobs aren't supposed to have deadlines after T.
+ // But some legacy apps might still schedule them with deadlines.
+ if (js.getLatestRunTimeElapsed() != JobStatus.NO_LATEST_RUNTIME) {
+ // If there is a deadline, the earliest time is the end of the lifecycle.
+ return Math.min(
+ estimatedLaunchTime - mConstants.PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS,
+ js.getLatestRunTimeElapsed());
+ }
+ if (estimatedLaunchTime != Long.MAX_VALUE) {
+ return estimatedLaunchTime - mConstants.PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS;
+ }
+ // There is no deadline and no estimated launch time.
+ return NO_LIFECYCLE_END;
+ }
+ return js.getLatestRunTimeElapsed() == JobStatus.NO_LATEST_RUNTIME
+ ? earliest + mFallbackFlexibilityDeadlineMs : js.getLatestRunTimeElapsed();
+ }
+
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ int getCurPercentOfLifecycleLocked(JobStatus js) {
+ final long earliest = getLifeCycleBeginningElapsedLocked(js);
+ final long latest = getLifeCycleEndElapsedLocked(js, earliest);
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ if (latest == NO_LIFECYCLE_END || earliest >= nowElapsed) {
+ return 0;
+ }
+ if (nowElapsed > latest || latest == earliest) {
+ return 100;
+ }
+ final int percentInTime = (int) ((nowElapsed - earliest) * 100 / (latest - earliest));
+ return percentInTime;
+ }
+
/** The elapsed time that marks when the next constraint should be dropped. */
@VisibleForTesting
@ElapsedRealtimeLong
- long getNextConstraintDropTimeElapsed(JobStatus js) {
- final long earliest = js.getEarliestRunTime() == JobStatus.NO_EARLIEST_RUNTIME
- ? js.enqueueTime : js.getEarliestRunTime();
- final long latest = js.getLatestRunTimeElapsed() == JobStatus.NO_LATEST_RUNTIME
- ? earliest + DEFAULT_FLEXIBILITY_DEADLINE
- : js.getLatestRunTimeElapsed();
- final int percent = PERCENT_TO_DROP_CONSTRAINTS[js.getNumDroppedFlexibleConstraints()];
+ @GuardedBy("mLock")
+ long getNextConstraintDropTimeElapsedLocked(JobStatus js) {
+ final long earliest = getLifeCycleBeginningElapsedLocked(js);
+ final long latest = getLifeCycleEndElapsedLocked(js, earliest);
+ return getNextConstraintDropTimeElapsedLocked(js, earliest, latest);
+ }
+
+ /** The elapsed time that marks when the next constraint should be dropped. */
+ @VisibleForTesting
+ @ElapsedRealtimeLong
+ @GuardedBy("mLock")
+ long getNextConstraintDropTimeElapsedLocked(JobStatus js, long earliest, long latest) {
+ if (latest == NO_LIFECYCLE_END
+ || js.getNumDroppedFlexibleConstraints() == mPercentToDropConstraints.length) {
+ return NO_LIFECYCLE_END;
+ }
+ final int percent = mPercentToDropConstraints[js.getNumDroppedFlexibleConstraints()];
final long percentInTime = ((latest - earliest) * percent) / 100;
return earliest + percentInTime;
}
@@ -233,10 +375,28 @@ public final class FlexibilityController extends StateController {
}
final long nowElapsed = sElapsedRealtimeClock.millis();
List<JobStatus> jobsByUid = mService.getJobStore().getJobsByUid(uid);
+ boolean hasPrefetch = false;
for (int i = 0; i < jobsByUid.size(); i++) {
JobStatus js = jobsByUid.get(i);
if (js.hasFlexibilityConstraint()) {
js.setFlexibilityConstraintSatisfied(nowElapsed, isFlexibilitySatisfiedLocked(js));
+ hasPrefetch |= js.getJob().isPrefetch();
+ }
+ }
+
+ // Prefetch jobs can't run when the app is TOP, so it should not be included in their
+ // lifecycle, and marks the beginning of a new lifecycle.
+ if (hasPrefetch && prevBias == JobInfo.BIAS_TOP_APP) {
+ final int userId = UserHandle.getUserId(uid);
+ final ArraySet<String> pkgs = mService.getPackagesForUidLocked(uid);
+ if (pkgs == null) {
+ return;
+ }
+ for (int i = 0; i < pkgs.size(); i++) {
+ String pkg = pkgs.valueAt(i);
+ mPrefetchLifeCycleStart.add(userId, pkg,
+ Math.max(mPrefetchLifeCycleStart.getOrDefault(userId, pkg, 0L),
+ nowElapsed));
}
}
}
@@ -244,8 +404,7 @@ public final class FlexibilityController extends StateController {
@Override
@GuardedBy("mLock")
public void onConstantsUpdatedLocked() {
- if (mFcConstants.mShouldReevaluateConstraints) {
- // Update job bookkeeping out of band.
+ if (mFcConfig.mShouldReevaluateConstraints) {
JobSchedulerBackgroundThread.getHandler().post(() -> {
final ArraySet<JobStatus> changedJobs = new ArraySet<>();
synchronized (mLock) {
@@ -255,6 +414,8 @@ public final class FlexibilityController extends StateController {
.getJobsByNumRequiredConstraints(j);
for (int i = 0; i < jobs.size(); i++) {
JobStatus js = jobs.valueAt(i);
+ mFlexibilityTracker.resetJobNumDroppedConstraints(js);
+ mFlexibilityAlarmQueue.scheduleDropNumConstraintsAlarm(js);
if (js.setFlexibilityConstraintSatisfied(
nowElapsed, isFlexibilitySatisfiedLocked(js))) {
changedJobs.add(js);
@@ -272,7 +433,13 @@ public final class FlexibilityController extends StateController {
@Override
@GuardedBy("mLock")
public void prepareForUpdatedConstantsLocked() {
- mFcConstants.mShouldReevaluateConstraints = false;
+ mFcConfig.mShouldReevaluateConstraints = false;
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ public void processConstantLocked(DeviceConfig.Properties properties, String key) {
+ mFcConfig.processConstantLocked(properties, key);
}
@VisibleForTesting
@@ -281,7 +448,7 @@ public final class FlexibilityController extends StateController {
FlexibilityTracker(int numFlexibleConstraints) {
mTrackedJobs = new ArrayList<>();
- for (int i = 0; i <= numFlexibleConstraints; i++) {
+ for (int i = 0; i < numFlexibleConstraints; i++) {
mTrackedJobs.add(new ArraySet<JobStatus>());
}
}
@@ -312,6 +479,19 @@ public final class FlexibilityController extends StateController {
mTrackedJobs.get(js.getNumRequiredFlexibleConstraints() - 1).remove(js);
}
+ public void resetJobNumDroppedConstraints(JobStatus js) {
+ final int curPercent = getCurPercentOfLifecycleLocked(js);
+ int toDrop = 0;
+ final int jsMaxFlexibleConstraints = NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS
+ + (js.getPreferUnmetered() ? 1 : 0);
+ for (int i = 0; i < jsMaxFlexibleConstraints; i++) {
+ if (curPercent >= mPercentToDropConstraints[i]) {
+ toDrop++;
+ }
+ }
+ adjustJobsRequiredConstraints(js, js.getNumDroppedFlexibleConstraints() - toDrop);
+ }
+
/** Returns all tracked jobs. */
public ArrayList<ArraySet<JobStatus>> getArrayList() {
return mTrackedJobs;
@@ -323,6 +503,9 @@ public final class FlexibilityController extends StateController {
* Jobs with 0 required flexible constraints are removed from the tracker.
*/
public boolean adjustJobsRequiredConstraints(JobStatus js, int n) {
+ if (n == 0) {
+ return false;
+ }
remove(js);
js.adjustNumRequiredFlexibleConstraints(n);
final long nowElapsed = sElapsedRealtimeClock.millis();
@@ -342,7 +525,7 @@ public final class FlexibilityController extends StateController {
public void dump(IndentingPrintWriter pw, Predicate<JobStatus> predicate) {
for (int i = 0; i < mTrackedJobs.size(); i++) {
ArraySet<JobStatus> jobs = mTrackedJobs.get(i);
- for (int j = 0; j < mTrackedJobs.size(); j++) {
+ for (int j = 0; j < jobs.size(); j++) {
final JobStatus js = jobs.valueAt(j);
if (!predicate.test(js)) {
continue;
@@ -357,11 +540,12 @@ public final class FlexibilityController extends StateController {
}
}
- private class FlexibilityAlarmQueue extends AlarmQueue<JobStatus> {
+ @VisibleForTesting
+ class FlexibilityAlarmQueue extends AlarmQueue<JobStatus> {
private FlexibilityAlarmQueue(Context context, Looper looper) {
super(context, looper, "*job.flexibility_check*",
"Flexible Constraint Check", false,
- MIN_TIME_BETWEEN_ALARMS_MS);
+ mMinTimeBetweenFlexibilityAlarmsMs);
}
@Override
@@ -369,38 +553,100 @@ public final class FlexibilityController extends StateController {
return js.getSourceUserId() == userId;
}
+ public void scheduleDropNumConstraintsAlarm(JobStatus js) {
+ long nextTimeElapsed;
+ synchronized (mLock) {
+ final long earliest = getLifeCycleBeginningElapsedLocked(js);
+ final long latest = getLifeCycleEndElapsedLocked(js, earliest);
+ nextTimeElapsed = getNextConstraintDropTimeElapsedLocked(js, earliest, latest);
+ if (nextTimeElapsed == NO_LIFECYCLE_END) {
+ // There is no known or estimated next time to drop a constraint.
+ removeAlarmForKey(js);
+ return;
+ }
+
+ if (latest - nextTimeElapsed < mDeadlineProximityLimitMs) {
+ mFlexibilityTracker.adjustJobsRequiredConstraints(
+ js, -js.getNumRequiredFlexibleConstraints());
+ return;
+ }
+ addAlarm(js, nextTimeElapsed);
+ }
+ }
+
@Override
protected void processExpiredAlarms(@NonNull ArraySet<JobStatus> expired) {
synchronized (mLock) {
- JobStatus js;
+ ArraySet<JobStatus> changedJobs = new ArraySet<>();
for (int i = 0; i < expired.size(); i++) {
- js = expired.valueAt(i);
- long time = getNextConstraintDropTimeElapsed(js);
- int toDecrease =
- js.getLatestRunTimeElapsed() - time < DEADLINE_PROXIMITY_LIMIT_MS
- ? -js.getNumRequiredFlexibleConstraints() : -1;
- if (mFlexibilityTracker.adjustJobsRequiredConstraints(js, toDecrease)) {
- mFlexibilityAlarmQueue.addAlarm(js, time);
+ JobStatus js = expired.valueAt(i);
+ boolean wasFlexibilitySatisfied = js.isConstraintSatisfied(CONSTRAINT_FLEXIBLE);
+
+ final long earliest = getLifeCycleBeginningElapsedLocked(js);
+ final long latest = getLifeCycleEndElapsedLocked(js, earliest);
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+
+ if (latest - nowElapsed < mDeadlineProximityLimitMs) {
+ mFlexibilityTracker.adjustJobsRequiredConstraints(js,
+ -js.getNumRequiredFlexibleConstraints());
+ } else {
+ long nextTimeElapsed =
+ getNextConstraintDropTimeElapsedLocked(js, earliest, latest);
+ if (mFlexibilityTracker.adjustJobsRequiredConstraints(js, -1)
+ && nextTimeElapsed != NO_LIFECYCLE_END) {
+ mFlexibilityAlarmQueue.addAlarm(js, nextTimeElapsed);
+ }
+ }
+ if (wasFlexibilitySatisfied != js.isConstraintSatisfied(CONSTRAINT_FLEXIBLE)) {
+ changedJobs.add(js);
}
}
+ mStateChangedListener.onControllerStateChanged(changedJobs);
}
}
}
@VisibleForTesting
- class FcConstants {
+ class FcConfig {
private boolean mShouldReevaluateConstraints = false;
+ /** Prefix to use with all constant keys in order to "sub-namespace" the keys. */
+ private static final String FC_CONFIG_PREFIX = "fc_";
+
+ static final String KEY_FLEXIBILITY_ENABLED = FC_CONFIG_PREFIX + "enable_flexibility";
+ static final String KEY_DEADLINE_PROXIMITY_LIMIT =
+ FC_CONFIG_PREFIX + "flexibility_deadline_proximity_limit_ms";
+ static final String KEY_FALLBACK_FLEXIBILITY_DEADLINE =
+ FC_CONFIG_PREFIX + "fallback_flexibility_deadline_ms";
+ static final String KEY_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS =
+ FC_CONFIG_PREFIX + "min_alarm_time_flexibility_ms";
+ static final String KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS =
+ FC_CONFIG_PREFIX + "percents_to_drop_num_flexible_constraints";
+
private static final boolean DEFAULT_FLEXIBILITY_ENABLED = false;
+ @VisibleForTesting
+ static final long DEFAULT_DEADLINE_PROXIMITY_LIMIT_MS = 15 * MINUTE_IN_MILLIS;
+ @VisibleForTesting
+ static final long DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS = 72 * HOUR_IN_MILLIS;
+ private static final long DEFAULT_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS = MINUTE_IN_MILLIS;
+ @VisibleForTesting
+ final int[] DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS = {50, 60, 70, 80};
+ /**
+ * If false the controller will not track new jobs
+ * and the flexibility constraint will always be satisfied.
+ */
public boolean FLEXIBILITY_ENABLED = DEFAULT_FLEXIBILITY_ENABLED;
+ /** How close to a jobs' deadline all flexible constraints will be dropped. */
+ public long DEADLINE_PROXIMITY_LIMIT_MS = DEFAULT_DEADLINE_PROXIMITY_LIMIT_MS;
+ /** For jobs that lack a deadline, the time that will be used to drop all constraints by. */
+ public long FALLBACK_FLEXIBILITY_DEADLINE_MS = DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS;
+ public long MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS =
+ DEFAULT_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS;
+ /** The percentages of a jobs' lifecycle to drop the number of required constraints. */
+ public int[] PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS =
+ DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS;
- /** Prefix to use with all constant keys in order to "sub-namespace" the keys. */
- private static final String FC_CONSTANT_PREFIX = "fc_";
-
- static final String KEY_FLEXIBILITY_ENABLED = FC_CONSTANT_PREFIX + "enable_flexibility";
-
- // TODO(b/239925946): properly handle DeviceConfig and changing variables
@GuardedBy("mLock")
public void processConstantLocked(@NonNull DeviceConfig.Properties properties,
@NonNull String key) {
@@ -410,9 +656,77 @@ public final class FlexibilityController extends StateController {
if (mFlexibilityEnabled != FLEXIBILITY_ENABLED) {
mFlexibilityEnabled = FLEXIBILITY_ENABLED;
mShouldReevaluateConstraints = true;
+ if (mFlexibilityEnabled) {
+ mPrefetchController
+ .registerPrefetchChangedListener(mPrefetchChangedListener);
+ } else {
+ mPrefetchController
+ .unRegisterPrefetchChangedListener(mPrefetchChangedListener);
+ }
+ }
+ break;
+ case KEY_DEADLINE_PROXIMITY_LIMIT:
+ DEADLINE_PROXIMITY_LIMIT_MS =
+ properties.getLong(key, DEFAULT_DEADLINE_PROXIMITY_LIMIT_MS);
+ if (mDeadlineProximityLimitMs != DEADLINE_PROXIMITY_LIMIT_MS) {
+ mDeadlineProximityLimitMs = DEADLINE_PROXIMITY_LIMIT_MS;
+ mShouldReevaluateConstraints = true;
}
break;
+ case KEY_FALLBACK_FLEXIBILITY_DEADLINE:
+ FALLBACK_FLEXIBILITY_DEADLINE_MS =
+ properties.getLong(key, DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS);
+ if (mFallbackFlexibilityDeadlineMs != FALLBACK_FLEXIBILITY_DEADLINE_MS) {
+ mFallbackFlexibilityDeadlineMs = FALLBACK_FLEXIBILITY_DEADLINE_MS;
+ mShouldReevaluateConstraints = true;
+ }
+ break;
+ case KEY_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS:
+ MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS =
+ properties.getLong(key, DEFAULT_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS);
+ if (mMinTimeBetweenFlexibilityAlarmsMs
+ != MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS) {
+ mMinTimeBetweenFlexibilityAlarmsMs = MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS;
+ mShouldReevaluateConstraints = true;
+ }
+ break;
+ case KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS:
+ String dropPercentString = properties.getString(key, "");
+ PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS =
+ parsePercentToDropString(dropPercentString);
+ if (PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS != null
+ && !Arrays.equals(mPercentToDropConstraints,
+ PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS)) {
+ mPercentToDropConstraints = PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS;
+ mShouldReevaluateConstraints = true;
+ }
+ break;
+ }
+ }
+
+ private int[] parsePercentToDropString(String s) {
+ String[] dropPercentString = s.split(",");
+ int[] dropPercentInt = new int[NUM_FLEXIBLE_CONSTRAINTS];
+ if (dropPercentInt.length != dropPercentString.length) {
+ return DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS;
}
+ int prevPercent = 0;
+ for (int i = 0; i < dropPercentString.length; i++) {
+ try {
+ dropPercentInt[i] =
+ Integer.parseInt(dropPercentString[i]);
+ } catch (NumberFormatException ex) {
+ Slog.e(TAG, "Provided string was improperly formatted.", ex);
+ return DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS;
+ }
+ if (dropPercentInt[i] < prevPercent) {
+ Slog.wtf(TAG, "Percents to drop constraints were not in increasing order.");
+ return DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS;
+ }
+ prevPercent = dropPercentInt[i];
+ }
+
+ return dropPercentInt;
}
private void dump(IndentingPrintWriter pw) {
@@ -429,8 +743,8 @@ public final class FlexibilityController extends StateController {
@VisibleForTesting
@NonNull
- FcConstants getFcConstants() {
- return mFcConstants;
+ FcConfig getFcConfig() {
+ return mFcConfig;
}
@Override
@@ -440,6 +754,6 @@ public final class FlexibilityController extends StateController {
pw.println();
mFlexibilityTracker.dump(pw, predicate);
- mFcConstants.dump(pw);
+ mFcConfig.dump(pw);
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 52882fe5d565..57c731757cc9 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -51,6 +51,7 @@ import android.util.Slog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
@@ -574,7 +575,6 @@ public final class JobStatus {
if (!isRequestedExpeditedJob()
&& satisfiesMinWindowException
- && !job.isPrefetch()
&& lacksSomeFlexibleConstraints) {
mNumRequiredFlexibleConstraints =
NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS + (mPreferUnmetered ? 1 : 0);
@@ -1669,7 +1669,8 @@ public final class JobStatus {
return readinessStatusWithConstraint(constraint, true);
}
- private boolean readinessStatusWithConstraint(int constraint, boolean value) {
+ @VisibleForTesting
+ boolean readinessStatusWithConstraint(int constraint, boolean value) {
boolean oldValue = false;
int satisfied = mSatisfiedConstraintsOfInterest;
switch (constraint) {
@@ -1705,6 +1706,15 @@ public final class JobStatus {
break;
}
+ // The flexibility constraint relies on other constraints to be satisfied.
+ // This function lacks the information to determine if flexibility will be satisfied.
+ // But for the purposes of this function it is still useful to know the jobs' readiness
+ // not including the flexibility constraint. If flexibility is the constraint in question
+ // we can proceed as normal.
+ if (constraint != CONSTRAINT_FLEXIBLE) {
+ satisfied |= CONSTRAINT_FLEXIBLE;
+ }
+
boolean toReturn = isReady(satisfied);
switch (constraint) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
index 0f385efae5cc..0945b7e796fd 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
@@ -81,6 +81,8 @@ public class PrefetchController extends StateController {
*/
@GuardedBy("mLock")
private final SparseArrayMap<String, Long> mEstimatedLaunchTimes = new SparseArrayMap<>();
+ @GuardedBy("mLock")
+ private final ArraySet<PrefetchChangedListener> mPrefetchChangedListeners = new ArraySet<>();
private final ThresholdAlarmListener mThresholdAlarmListener;
/**
@@ -99,6 +101,13 @@ public class PrefetchController extends StateController {
@GuardedBy("mLock")
private long mLaunchTimeAllowanceMs = PcConstants.DEFAULT_LAUNCH_TIME_ALLOWANCE_MS;
+ /** Called by Prefetch Controller after local cache has been updated */
+ public interface PrefetchChangedListener {
+ /** Callback to inform listeners when estimated launch times change. */
+ void onPrefetchCacheUpdated(ArraySet<JobStatus> jobs, int userId, String pkgName,
+ long prevEstimatedLaunchTime, long newEstimatedLaunchTime);
+ }
+
@SuppressWarnings("FieldCanBeLocal")
private final EstimatedLaunchTimeChangedListener mEstimatedLaunchTimeChangedListener =
new EstimatedLaunchTimeChangedListener() {
@@ -291,12 +300,17 @@ public class PrefetchController extends StateController {
// Don't bother caching the value unless the app has scheduled prefetch jobs
// before. This is based on the assumption that if an app has scheduled a
// prefetch job before, then it will probably schedule another one again.
+ final long prevEstimatedLaunchTime = mEstimatedLaunchTimes.get(userId, pkgName);
mEstimatedLaunchTimes.add(userId, pkgName, newEstimatedLaunchTime);
if (!jobs.isEmpty()) {
final long now = sSystemClock.millis();
final long nowElapsed = sElapsedRealtimeClock.millis();
updateThresholdAlarmLocked(userId, pkgName, now, nowElapsed);
+ for (int i = 0; i < mPrefetchChangedListeners.size(); i++) {
+ mPrefetchChangedListeners.valueAt(i).onPrefetchCacheUpdated(jobs,
+ userId, pkgName, prevEstimatedLaunchTime, newEstimatedLaunchTime);
+ }
if (maybeUpdateConstraintForPkgLocked(now, nowElapsed, userId, pkgName)) {
mStateChangedListener.onControllerStateChanged(jobs);
}
@@ -448,6 +462,18 @@ public class PrefetchController extends StateController {
}
}
+ void registerPrefetchChangedListener(PrefetchChangedListener listener) {
+ synchronized (mLock) {
+ mPrefetchChangedListeners.add(listener);
+ }
+ }
+
+ void unRegisterPrefetchChangedListener(PrefetchChangedListener listener) {
+ synchronized (mLock) {
+ mPrefetchChangedListeners.remove(listener);
+ }
+ }
+
private class PcHandler extends Handler {
PcHandler(Looper looper) {
super(looper);
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
index 8b8a57d248b9..e23860c1eca3 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
@@ -34,8 +34,6 @@ import static com.android.server.tare.TareUtils.getCurrentTimeMillis;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -46,6 +44,7 @@ import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArrayMap;
+import android.util.SparseSetArray;
import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
@@ -284,6 +283,7 @@ class Agent {
for (int i = 0; i < pkgNames.size(); ++i) {
final String pkgName = pkgNames.valueAt(i);
+ final boolean isVip = mIrs.isVip(userId, pkgName);
SparseArrayMap<String, OngoingEvent> ongoingEvents =
mCurrentOngoingEvents.get(userId, pkgName);
if (ongoingEvents != null) {
@@ -298,8 +298,8 @@ class Agent {
for (int n = 0; n < size; ++n) {
final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(n);
note.recalculateCosts(economicPolicy, userId, pkgName);
- final boolean isAffordable =
- isAffordableLocked(newBalance,
+ final boolean isAffordable = isVip
+ || isAffordableLocked(newBalance,
note.getCachedModifiedPrice(), note.getCtp());
if (note.isCurrentlyAffordable() != isAffordable) {
note.setNewAffordability(isAffordable);
@@ -313,6 +313,51 @@ class Agent {
}
@GuardedBy("mLock")
+ void onVipStatusChangedLocked(final int userId, @NonNull String pkgName) {
+ final long now = getCurrentTimeMillis();
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked();
+
+ final boolean isVip = mIrs.isVip(userId, pkgName);
+ SparseArrayMap<String, OngoingEvent> ongoingEvents =
+ mCurrentOngoingEvents.get(userId, pkgName);
+ if (ongoingEvents != null) {
+ mOngoingEventUpdater.reset(userId, pkgName, now, nowElapsed);
+ ongoingEvents.forEach(mOngoingEventUpdater);
+ }
+ final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes =
+ mActionAffordabilityNotes.get(userId, pkgName);
+ if (actionAffordabilityNotes != null) {
+ final int size = actionAffordabilityNotes.size();
+ final long newBalance =
+ mScribe.getLedgerLocked(userId, pkgName).getCurrentBalance();
+ for (int n = 0; n < size; ++n) {
+ final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(n);
+ note.recalculateCosts(economicPolicy, userId, pkgName);
+ final boolean isAffordable = isVip
+ || isAffordableLocked(newBalance,
+ note.getCachedModifiedPrice(), note.getCtp());
+ if (note.isCurrentlyAffordable() != isAffordable) {
+ note.setNewAffordability(isAffordable);
+ mIrs.postAffordabilityChanged(userId, pkgName, note);
+ }
+ }
+ }
+ scheduleBalanceCheckLocked(userId, pkgName);
+ }
+
+ @GuardedBy("mLock")
+ void onVipStatusChangedLocked(@NonNull SparseSetArray<String> pkgs) {
+ for (int u = pkgs.size() - 1; u >= 0; --u) {
+ final int userId = pkgs.keyAt(u);
+
+ for (int p = pkgs.sizeAt(u) - 1; p >= 0; --p) {
+ onVipStatusChangedLocked(userId, pkgs.valueAt(u, p));
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
private void onAnythingChangedLocked(final boolean updateOngoingEvents) {
final long now = getCurrentTimeMillis();
final long nowElapsed = SystemClock.elapsedRealtime();
@@ -349,11 +394,12 @@ class Agent {
if (actionAffordabilityNotes != null) {
final int size = actionAffordabilityNotes.size();
final long newBalance = getBalanceLocked(userId, pkgName);
+ final boolean isVip = mIrs.isVip(userId, pkgName);
for (int n = 0; n < size; ++n) {
final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(n);
note.recalculateCosts(economicPolicy, userId, pkgName);
- final boolean isAffordable =
- isAffordableLocked(newBalance,
+ final boolean isAffordable = isVip
+ || isAffordableLocked(newBalance,
note.getCachedModifiedPrice(), note.getCtp());
if (note.isCurrentlyAffordable() != isAffordable) {
note.setNewAffordability(isAffordable);
@@ -454,6 +500,14 @@ class Agent {
"Tried to adjust system balance for " + appToString(userId, pkgName));
return;
}
+ if (mIrs.isVip(userId, pkgName)) {
+ // This could happen if the app was made a VIP after it started performing actions.
+ // Continue recording the transaction for debugging purposes, but don't let it change
+ // any numbers.
+ transaction = new Ledger.Transaction(
+ transaction.startTimeMs, transaction.endTimeMs,
+ transaction.eventId, transaction.tag, 0 /* delta */, transaction.ctp);
+ }
final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked();
final long originalBalance = ledger.getCurrentBalance();
if (transaction.delta > 0
@@ -479,10 +533,11 @@ class Agent {
mActionAffordabilityNotes.get(userId, pkgName);
if (actionAffordabilityNotes != null) {
final long newBalance = ledger.getCurrentBalance();
+ final boolean isVip = mIrs.isVip(userId, pkgName);
for (int i = 0; i < actionAffordabilityNotes.size(); ++i) {
final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i);
- final boolean isAffordable =
- isAffordableLocked(newBalance,
+ final boolean isAffordable = isVip
+ || isAffordableLocked(newBalance,
note.getCachedModifiedPrice(), note.getCtp());
if (note.isCurrentlyAffordable() != isAffordable) {
note.setNewAffordability(isAffordable);
@@ -606,13 +661,12 @@ class Agent {
}
/** Returns true if an app should be given credits in the general distributions. */
- private boolean shouldGiveCredits(@NonNull PackageInfo packageInfo) {
- final ApplicationInfo applicationInfo = packageInfo.applicationInfo;
+ private boolean shouldGiveCredits(@NonNull InstalledPackageInfo packageInfo) {
// Skip apps that wouldn't be doing any work. Giving them ARCs would be wasteful.
- if (applicationInfo == null || !applicationInfo.hasCode()) {
+ if (!packageInfo.hasCode) {
return false;
}
- final int userId = UserHandle.getUserId(packageInfo.applicationInfo.uid);
+ final int userId = UserHandle.getUserId(packageInfo.uid);
// No point allocating ARCs to the system. It can do whatever it wants.
return !mIrs.isSystem(userId, packageInfo.packageName);
}
@@ -623,15 +677,15 @@ class Agent {
@GuardedBy("mLock")
void distributeBasicIncomeLocked(int batteryLevel) {
- List<PackageInfo> pkgs = mIrs.getInstalledPackages();
+ final List<InstalledPackageInfo> pkgs = mIrs.getInstalledPackages();
final long now = getCurrentTimeMillis();
for (int i = 0; i < pkgs.size(); ++i) {
- final PackageInfo pkgInfo = pkgs.get(i);
+ final InstalledPackageInfo pkgInfo = pkgs.get(i);
if (!shouldGiveCredits(pkgInfo)) {
continue;
}
- final int userId = UserHandle.getUserId(pkgInfo.applicationInfo.uid);
+ final int userId = UserHandle.getUserId(pkgInfo.uid);
final String pkgName = pkgInfo.packageName;
final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName);
final long minBalance = mIrs.getMinBalanceLocked(userId, pkgName);
@@ -659,11 +713,11 @@ class Agent {
@GuardedBy("mLock")
void grantBirthrightsLocked(final int userId) {
- final List<PackageInfo> pkgs = mIrs.getInstalledPackages(userId);
+ final List<InstalledPackageInfo> pkgs = mIrs.getInstalledPackages(userId);
final long now = getCurrentTimeMillis();
for (int i = 0; i < pkgs.size(); ++i) {
- final PackageInfo packageInfo = pkgs.get(i);
+ final InstalledPackageInfo packageInfo = pkgs.get(i);
if (!shouldGiveCredits(packageInfo)) {
continue;
}
@@ -869,7 +923,7 @@ class Agent {
private void scheduleBalanceCheckLocked(final int userId, @NonNull final String pkgName) {
SparseArrayMap<String, OngoingEvent> ongoingEvents =
mCurrentOngoingEvents.get(userId, pkgName);
- if (ongoingEvents == null) {
+ if (ongoingEvents == null || mIrs.isVip(userId, pkgName)) {
// No ongoing transactions. No reason to schedule
mBalanceThresholdAlarmQueue.removeAlarmForKey(new Package(userId, pkgName));
return;
@@ -1062,9 +1116,10 @@ class Agent {
note.setNewAffordability(true);
return;
}
+ final boolean isVip = mIrs.isVip(userId, pkgName);
note.recalculateCosts(economicPolicy, userId, pkgName);
- note.setNewAffordability(
- isAffordableLocked(getBalanceLocked(userId, pkgName),
+ note.setNewAffordability(isVip
+ || isAffordableLocked(getBalanceLocked(userId, pkgName),
note.getCachedModifiedPrice(), note.getCtp()));
mIrs.postAffordabilityChanged(userId, pkgName, note);
// Update ongoing alarm
@@ -1203,11 +1258,12 @@ class Agent {
if (actionAffordabilityNotes != null
&& actionAffordabilityNotes.size() > 0) {
final long newBalance = getBalanceLocked(userId, pkgName);
+ final boolean isVip = mIrs.isVip(userId, pkgName);
for (int i = 0; i < actionAffordabilityNotes.size(); ++i) {
final ActionAffordabilityNote note =
actionAffordabilityNotes.valueAt(i);
- final boolean isAffordable = isAffordableLocked(
+ final boolean isAffordable = isVip || isAffordableLocked(
newBalance, note.getCachedModifiedPrice(), note.getCtp());
if (note.isCurrentlyAffordable() != isAffordable) {
note.setNewAffordability(isAffordable);
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
index a46430feb688..aa66e92a091f 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
@@ -149,7 +149,6 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy {
private long mHardSatiatedConsumptionLimit;
private final KeyValueListParser mParser = new KeyValueListParser(',');
- private final InternalResourceService mInternalResourceService;
private final Injector mInjector;
private final SparseArray<Action> mActions = new SparseArray<>();
@@ -157,7 +156,6 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy {
AlarmManagerEconomicPolicy(InternalResourceService irs, Injector injector) {
super(irs);
- mInternalResourceService = irs;
mInjector = injector;
loadConstants("", null);
}
@@ -165,14 +163,14 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy {
@Override
void setup(@NonNull DeviceConfig.Properties properties) {
super.setup(properties);
- ContentResolver resolver = mInternalResourceService.getContext().getContentResolver();
+ ContentResolver resolver = mIrs.getContext().getContentResolver();
loadConstants(mInjector.getSettingsGlobalString(resolver, TARE_ALARM_MANAGER_CONSTANTS),
properties);
}
@Override
long getMinSatiatedBalance(final int userId, @NonNull final String pkgName) {
- if (mInternalResourceService.isPackageExempted(userId, pkgName)) {
+ if (mIrs.isPackageExempted(userId, pkgName)) {
return mMinSatiatedBalanceExempted;
}
// TODO: take other exemptions into account
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
index 0937e7ba4055..564ffb9c4169 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
@@ -169,9 +169,11 @@ public abstract class EconomicPolicy {
}
}
+ protected final InternalResourceService mIrs;
private static final Modifier[] COST_MODIFIER_BY_INDEX = new Modifier[NUM_COST_MODIFIERS];
EconomicPolicy(@NonNull InternalResourceService irs) {
+ mIrs = irs;
for (int mId : getCostModifiers()) {
initModifier(mId, irs);
}
@@ -240,7 +242,7 @@ public abstract class EconomicPolicy {
@NonNull
final Cost getCostOfAction(int actionId, int userId, @NonNull String pkgName) {
final Action action = getAction(actionId);
- if (action == null) {
+ if (action == null || mIrs.isVip(userId, pkgName)) {
return new Cost(0, 0);
}
long ctp = action.costToProduce;
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java b/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java
new file mode 100644
index 000000000000..da544bb6a2eb
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.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 com.android.server.tare;
+
+import android.annotation.NonNull;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.os.UserHandle;
+
+/** POJO to cache only the information about installed packages that TARE cares about. */
+class InstalledPackageInfo {
+ static final int NO_UID = -1;
+
+ public final int uid;
+ public final String packageName;
+ public final boolean hasCode;
+
+ InstalledPackageInfo(@NonNull PackageInfo packageInfo) {
+ final ApplicationInfo applicationInfo = packageInfo.applicationInfo;
+ this.uid = applicationInfo == null ? NO_UID : applicationInfo.uid;
+ this.packageName = packageInfo.packageName;
+ this.hasCode = applicationInfo != null && applicationInfo.hasCode();
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
index 6d5c16021ea9..7a7d669ae229 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -48,6 +48,7 @@ import android.os.Handler;
import android.os.IDeviceIdleController;
import android.os.Looper;
import android.os.Message;
+import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -131,7 +132,7 @@ public class InternalResourceService extends SystemService {
@NonNull
@GuardedBy("mLock")
- private final List<PackageInfo> mPkgCache = new ArrayList<>();
+ private final List<InstalledPackageInfo> mPkgCache = new ArrayList<>();
/** Cached mapping of UIDs (for all users) to a list of packages in the UID. */
@GuardedBy("mLock")
@@ -149,6 +150,9 @@ public class InternalResourceService extends SystemService {
@GuardedBy("mLock")
private ArraySet<String> mExemptedApps = new ArraySet<>();
+ @GuardedBy("mLock")
+ private final SparseArrayMap<String, Boolean> mVipOverrides = new SparseArrayMap<>();
+
private volatile boolean mIsEnabled;
private volatile int mBootPhase;
private volatile boolean mExemptListLoaded;
@@ -303,7 +307,7 @@ public class InternalResourceService extends SystemService {
}
@NonNull
- List<PackageInfo> getInstalledPackages() {
+ List<InstalledPackageInfo> getInstalledPackages() {
synchronized (mLock) {
return mPkgCache;
}
@@ -311,13 +315,12 @@ public class InternalResourceService extends SystemService {
/** Returns the installed packages for the specified user. */
@NonNull
- List<PackageInfo> getInstalledPackages(final int userId) {
- final List<PackageInfo> userPkgs = new ArrayList<>();
+ List<InstalledPackageInfo> getInstalledPackages(final int userId) {
+ final List<InstalledPackageInfo> userPkgs = new ArrayList<>();
synchronized (mLock) {
for (int i = 0; i < mPkgCache.size(); ++i) {
- final PackageInfo packageInfo = mPkgCache.get(i);
- if (packageInfo.applicationInfo != null
- && UserHandle.getUserId(packageInfo.applicationInfo.uid) == userId) {
+ final InstalledPackageInfo packageInfo = mPkgCache.get(i);
+ if (UserHandle.getUserId(packageInfo.uid) == userId) {
userPkgs.add(packageInfo);
}
}
@@ -369,6 +372,21 @@ public class InternalResourceService extends SystemService {
return UserHandle.isCore(getUid(userId, pkgName));
}
+ boolean isVip(final int userId, @NonNull String pkgName) {
+ synchronized (mLock) {
+ final Boolean override = mVipOverrides.get(userId, pkgName);
+ if (override != null) {
+ return override;
+ }
+ }
+ if (isSystem(userId, pkgName)) {
+ // The government, I mean the system, can create ARCs as it needs to in order to
+ // operate.
+ return true;
+ }
+ return false;
+ }
+
void onBatteryLevelChanged() {
synchronized (mLock) {
final int newBatteryLevel = getCurrentBatteryLevel();
@@ -451,7 +469,7 @@ public class InternalResourceService extends SystemService {
mPackageToUidCache.add(userId, pkgName, uid);
}
synchronized (mLock) {
- mPkgCache.add(packageInfo);
+ mPkgCache.add(new InstalledPackageInfo(packageInfo));
mUidToPackageCache.add(uid, pkgName);
// TODO: only do this when the user first launches the app (app leaves stopped state)
mAgent.grantBirthrightLocked(userId, pkgName);
@@ -471,9 +489,10 @@ public class InternalResourceService extends SystemService {
}
synchronized (mLock) {
mUidToPackageCache.remove(uid, pkgName);
+ mVipOverrides.delete(userId, pkgName);
for (int i = 0; i < mPkgCache.size(); ++i) {
- PackageInfo pkgInfo = mPkgCache.get(i);
- if (UserHandle.getUserId(pkgInfo.applicationInfo.uid) == userId
+ final InstalledPackageInfo pkgInfo = mPkgCache.get(i);
+ if (UserHandle.getUserId(pkgInfo.uid) == userId
&& pkgName.equals(pkgInfo.packageName)) {
mPkgCache.remove(i);
break;
@@ -496,20 +515,24 @@ public class InternalResourceService extends SystemService {
void onUserAdded(final int userId) {
synchronized (mLock) {
- mPkgCache.addAll(
- mPackageManager.getInstalledPackagesAsUser(PACKAGE_QUERY_FLAGS, userId));
+ final List<PackageInfo> pkgs =
+ mPackageManager.getInstalledPackagesAsUser(PACKAGE_QUERY_FLAGS, userId);
+ for (int i = pkgs.size() - 1; i >= 0; --i) {
+ mPkgCache.add(new InstalledPackageInfo(pkgs.get(i)));
+ }
mAgent.grantBirthrightsLocked(userId);
}
}
void onUserRemoved(final int userId) {
synchronized (mLock) {
+ mVipOverrides.delete(userId);
ArrayList<String> removedPkgs = new ArrayList<>();
for (int i = mPkgCache.size() - 1; i >= 0; --i) {
- PackageInfo pkgInfo = mPkgCache.get(i);
- if (UserHandle.getUserId(pkgInfo.applicationInfo.uid) == userId) {
+ final InstalledPackageInfo pkgInfo = mPkgCache.get(i);
+ if (UserHandle.getUserId(pkgInfo.uid) == userId) {
removedPkgs.add(pkgInfo.packageName);
- mUidToPackageCache.remove(pkgInfo.applicationInfo.uid);
+ mUidToPackageCache.remove(pkgInfo.uid);
mPkgCache.remove(i);
break;
}
@@ -659,8 +682,11 @@ public class InternalResourceService extends SystemService {
LocalServices.getService(UserManagerInternal.class);
final int[] userIds = userManagerInternal.getUserIds();
for (int userId : userIds) {
- mPkgCache.addAll(
- mPackageManager.getInstalledPackagesAsUser(PACKAGE_QUERY_FLAGS, userId));
+ final List<PackageInfo> pkgs =
+ mPackageManager.getInstalledPackagesAsUser(PACKAGE_QUERY_FLAGS, userId);
+ for (int i = pkgs.size() - 1; i >= 0; --i) {
+ mPkgCache.add(new InstalledPackageInfo(pkgs.get(i)));
+ }
}
}
@@ -881,6 +907,15 @@ public class InternalResourceService extends SystemService {
Binder.restoreCallingIdentity(identityToken);
}
}
+
+ @Override
+ public int handleShellCommand(@NonNull ParcelFileDescriptor in,
+ @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
+ @NonNull String[] args) {
+ return (new TareShellCommand(InternalResourceService.this)).exec(
+ this, in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(),
+ args);
+ }
}
private final class LocalService implements EconomyManagerInternal {
@@ -932,9 +967,9 @@ public class InternalResourceService extends SystemService {
if (!mIsEnabled) {
return true;
}
- if (isSystem(userId, pkgName)) {
+ if (isVip(userId, pkgName)) {
// The government, I mean the system, can create ARCs as it needs to in order to
- // operate.
+ // allow VIPs to operate.
return true;
}
// TODO: take temp-allowlist into consideration
@@ -960,7 +995,7 @@ public class InternalResourceService extends SystemService {
if (!mIsEnabled) {
return FOREVER_MS;
}
- if (isSystem(userId, pkgName)) {
+ if (isVip(userId, pkgName)) {
return FOREVER_MS;
}
long totalCostPerSecond = 0;
@@ -1131,6 +1166,47 @@ public class InternalResourceService extends SystemService {
}
}
+ // Shell command infrastructure
+ int executeClearVip(@NonNull PrintWriter pw) {
+ synchronized (mLock) {
+ final SparseSetArray<String> changedPkgs = new SparseSetArray<>();
+ for (int u = mVipOverrides.numMaps() - 1; u >= 0; --u) {
+ final int userId = mVipOverrides.keyAt(u);
+
+ for (int p = mVipOverrides.numElementsForKeyAt(u) - 1; p >= 0; --p) {
+ changedPkgs.add(userId, mVipOverrides.keyAt(u, p));
+ }
+ }
+ mVipOverrides.clear();
+ if (mIsEnabled) {
+ mAgent.onVipStatusChangedLocked(changedPkgs);
+ }
+ }
+ pw.println("Cleared all VIP statuses");
+ return TareShellCommand.COMMAND_SUCCESS;
+ }
+
+ int executeSetVip(@NonNull PrintWriter pw,
+ int userId, @NonNull String pkgName, @Nullable Boolean newVipState) {
+ final boolean changed;
+ synchronized (mLock) {
+ final boolean wasVip = isVip(userId, pkgName);
+ if (newVipState == null) {
+ mVipOverrides.delete(userId, pkgName);
+ } else {
+ mVipOverrides.add(userId, pkgName, newVipState);
+ }
+ changed = isVip(userId, pkgName) != wasVip;
+ if (mIsEnabled && changed) {
+ mAgent.onVipStatusChangedLocked(userId, pkgName);
+ }
+ }
+ pw.println(appToString(userId, pkgName) + " VIP status set to " + newVipState + "."
+ + " Final VIP state changed? " + changed);
+ return TareShellCommand.COMMAND_SUCCESS;
+ }
+
+ // Dump infrastructure
private static void dumpHelp(PrintWriter pw) {
pw.println("Resource Economy (economy) dump options:");
pw.println(" [-h|--help] [package] ...");
@@ -1168,6 +1244,29 @@ public class InternalResourceService extends SystemService {
pw.print("Exempted apps", mExemptedApps);
pw.println();
+ boolean printedVips = false;
+ pw.println();
+ pw.print("VIPs:");
+ for (int u = 0; u < mVipOverrides.numMaps(); ++u) {
+ final int userId = mVipOverrides.keyAt(u);
+
+ for (int p = 0; p < mVipOverrides.numElementsForKeyAt(u); ++p) {
+ final String pkgName = mVipOverrides.keyAt(u, p);
+
+ printedVips = true;
+ pw.println();
+ pw.print(appToString(userId, pkgName));
+ pw.print("=");
+ pw.print(mVipOverrides.valueAt(u, p));
+ }
+ }
+ if (printedVips) {
+ pw.println();
+ } else {
+ pw.print(" None");
+ }
+ pw.println();
+
pw.println();
mCompleteEconomicPolicy.dump(pw);
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
index e7db1adc859e..03c5fdd63250 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
@@ -151,7 +151,6 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy {
private long mHardSatiatedConsumptionLimit;
private final KeyValueListParser mParser = new KeyValueListParser(',');
- private final InternalResourceService mInternalResourceService;
private final Injector mInjector;
private final SparseArray<Action> mActions = new SparseArray<>();
@@ -159,7 +158,6 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy {
JobSchedulerEconomicPolicy(InternalResourceService irs, Injector injector) {
super(irs);
- mInternalResourceService = irs;
mInjector = injector;
loadConstants("", null);
}
@@ -167,14 +165,14 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy {
@Override
void setup(@NonNull DeviceConfig.Properties properties) {
super.setup(properties);
- ContentResolver resolver = mInternalResourceService.getContext().getContentResolver();
+ final ContentResolver resolver = mIrs.getContext().getContentResolver();
loadConstants(mInjector.getSettingsGlobalString(resolver, TARE_JOB_SCHEDULER_CONSTANTS),
properties);
}
@Override
long getMinSatiatedBalance(final int userId, @NonNull final String pkgName) {
- if (mInternalResourceService.isPackageExempted(userId, pkgName)) {
+ if (mIrs.isPackageExempted(userId, pkgName)) {
return mMinSatiatedBalanceExempted;
}
// TODO: take other exemptions into account
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
index 8f7657e6c414..ed915cd88ebe 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
@@ -22,7 +22,6 @@ import static com.android.server.tare.TareUtils.appToString;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.pm.PackageInfo;
import android.os.Environment;
import android.os.UserHandle;
import android.util.ArraySet;
@@ -210,11 +209,11 @@ public class Scribe {
mRemainingConsumableCakes = 0;
final SparseArray<ArraySet<String>> installedPackagesPerUser = new SparseArray<>();
- final List<PackageInfo> installedPackages = mIrs.getInstalledPackages();
+ final List<InstalledPackageInfo> installedPackages = mIrs.getInstalledPackages();
for (int i = 0; i < installedPackages.size(); ++i) {
- final PackageInfo packageInfo = installedPackages.get(i);
- if (packageInfo.applicationInfo != null) {
- final int userId = UserHandle.getUserId(packageInfo.applicationInfo.uid);
+ final InstalledPackageInfo packageInfo = installedPackages.get(i);
+ if (packageInfo.uid != InstalledPackageInfo.NO_UID) {
+ final int userId = UserHandle.getUserId(packageInfo.uid);
ArraySet<String> pkgsForUser = installedPackagesPerUser.get(userId);
if (pkgsForUser == null) {
pkgsForUser = new ArraySet<>();
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/TareShellCommand.java b/apex/jobscheduler/service/java/com/android/server/tare/TareShellCommand.java
new file mode 100644
index 000000000000..5e380b408d01
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/TareShellCommand.java
@@ -0,0 +1,112 @@
+/*
+ * 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.tare;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+
+import com.android.modules.utils.BasicShellCommandHandler;
+
+import java.io.PrintWriter;
+
+/**
+ * Shell command handler for TARE.
+ */
+public class TareShellCommand extends BasicShellCommandHandler {
+ static final int COMMAND_ERROR = -1;
+ static final int COMMAND_SUCCESS = 0;
+
+ private final InternalResourceService mIrs;
+
+ public TareShellCommand(@NonNull InternalResourceService irs) {
+ mIrs = irs;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ final PrintWriter pw = getOutPrintWriter();
+ try {
+ switch (cmd != null ? cmd : "") {
+ case "clear-vip":
+ return runClearVip(pw);
+ case "set-vip":
+ return runSetVip(pw);
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ } catch (Exception e) {
+ pw.println("Exception: " + e);
+ }
+ return COMMAND_ERROR;
+ }
+
+ @Override
+ public void onHelp() {
+ final PrintWriter pw = getOutPrintWriter();
+
+ pw.println("TARE commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println(" clear-vip");
+ pw.println(" Clears all VIP settings resulting from previous calls using `set-vip` and");
+ pw.println(" resets them all to default.");
+ pw.println(" set-vip <USER_ID> <PACKAGE> <true|false|default>");
+ pw.println(" Designate the app as a Very Important Package or not. A VIP is allowed to");
+ pw.println(" do as much work as it wants, regardless of TARE state.");
+ pw.println(" The user ID must be an explicit user ID. USER_ALL, CURRENT, etc. are not");
+ pw.println(" supported.");
+ pw.println();
+ }
+
+ private void checkPermission(@NonNull String operation) throws Exception {
+ final int perm = mIrs.getContext()
+ .checkCallingOrSelfPermission(Manifest.permission.CHANGE_APP_IDLE_STATE);
+ if (perm != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Uid " + Binder.getCallingUid()
+ + " not permitted to " + operation);
+ }
+ }
+
+ private int runClearVip(@NonNull PrintWriter pw) throws Exception {
+ checkPermission("clear vip");
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return mIrs.executeClearVip(pw);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private int runSetVip(@NonNull PrintWriter pw) throws Exception {
+ checkPermission("modify vip");
+
+ final int userId = Integer.parseInt(getNextArgRequired());
+ final String pkgName = getNextArgRequired();
+ final String vipState = getNextArgRequired();
+ final Boolean isVip = "default".equals(vipState) ? null : Boolean.valueOf(vipState);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return mIrs.executeSetVip(pw, userId, pkgName, isVip);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+}
diff --git a/core/TEST_MAPPING b/core/TEST_MAPPING
index 682d3ab6dd60..fd571c95f568 100644
--- a/core/TEST_MAPPING
+++ b/core/TEST_MAPPING
@@ -10,9 +10,6 @@
"include-filter": "com.android.internal.inputmethod"
},
{
- "include-filter": "android.accessibilityService.cts"
- },
- {
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
],
diff --git a/core/api/current.txt b/core/api/current.txt
index ea2ae0c9e15f..4734e8a75de6 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -23189,9 +23189,10 @@ package android.media {
public abstract static class MediaRouter2.RouteCallback {
ctor public MediaRouter2.RouteCallback();
- method public void onRoutesAdded(@NonNull java.util.List<android.media.MediaRoute2Info>);
- method public void onRoutesChanged(@NonNull java.util.List<android.media.MediaRoute2Info>);
- method public void onRoutesRemoved(@NonNull java.util.List<android.media.MediaRoute2Info>);
+ method @Deprecated public void onRoutesAdded(@NonNull java.util.List<android.media.MediaRoute2Info>);
+ method @Deprecated public void onRoutesChanged(@NonNull java.util.List<android.media.MediaRoute2Info>);
+ method @Deprecated public void onRoutesRemoved(@NonNull java.util.List<android.media.MediaRoute2Info>);
+ method public void onRoutesUpdated(@NonNull java.util.List<android.media.MediaRoute2Info>);
}
public class MediaRouter2.RoutingController {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 6c941e751b9e..c87ea2adbd77 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -798,6 +798,7 @@ package android.content.pm {
field public static final long OVERRIDE_MIN_ASPECT_RATIO_MEDIUM = 180326845L; // 0xabf91bdL
field public static final float OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE = 1.5f;
field public static final long OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY = 203647190L; // 0xc2368d6L
+ field public static final long OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN = 208648326L; // 0xc6fb886L
field public static final int RESIZE_MODE_RESIZEABLE = 2; // 0x2
}
diff --git a/core/java/android/attention/AttentionManagerInternal.java b/core/java/android/attention/AttentionManagerInternal.java
index 47bec618cfd9..24fe0dbe5760 100644
--- a/core/java/android/attention/AttentionManagerInternal.java
+++ b/core/java/android/attention/AttentionManagerInternal.java
@@ -83,11 +83,11 @@ public abstract class AttentionManagerInternal {
}
/** Internal interface for proximity callback. */
- public abstract static class ProximityUpdateCallbackInternal {
+ public interface ProximityUpdateCallbackInternal {
/**
* @param distance the estimated distance of the user (in meter)
* The distance will be PROXIMITY_UNKNOWN if the proximity sensing was inconclusive.
*/
- public abstract void onProximityUpdate(double distance);
+ void onProximityUpdate(double distance);
}
}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index a3d595ef6575..e92be7cb64e2 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1109,6 +1109,17 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
public static final float OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE = 16 / 9f;
/**
+ * Enables the use of split screen aspect ratio. This allows an app to use all the available
+ * space in split mode avoiding letterboxing.
+ * @hide
+ */
+ @ChangeId
+ @Disabled
+ @Overridable
+ @TestApi
+ public static final long OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN = 208648326L;
+
+ /**
* Compares activity window layout min width/height with require space for multi window to
* determine if it can be put into multi window mode.
*/
@@ -1317,8 +1328,8 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
* Returns true if the activity has maximum or minimum aspect ratio.
* @hide
*/
- public boolean hasFixedAspectRatio(@ScreenOrientation int orientation) {
- return getMaxAspectRatio() != 0 || getMinAspectRatio(orientation) != 0;
+ public boolean hasFixedAspectRatio() {
+ return getMaxAspectRatio() != 0 || getMinAspectRatio() != 0;
}
/**
@@ -1460,30 +1471,10 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
}
/**
- * Returns the min aspect ratio of this activity.
- *
- * This takes into account the minimum aspect ratio as defined in the app's manifest and
- * possible overrides as per OVERRIDE_MIN_ASPECT_RATIO.
- *
- * In the rare cases where the manifest minimum aspect ratio is required, use
- * {@code getManifestMinAspectRatio}.
+ * Returns the min aspect ratio of this activity as defined in the manifest file.
* @hide
*/
- public float getMinAspectRatio(@ScreenOrientation int orientation) {
- if (applicationInfo == null || !isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO) || (
- isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY)
- && !isFixedOrientationPortrait(orientation))) {
- return mMinAspectRatio;
- }
-
- if (isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_LARGE)) {
- return Math.max(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE, mMinAspectRatio);
- }
-
- if (isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM)) {
- return Math.max(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE, mMinAspectRatio);
- }
-
+ public float getMinAspectRatio() {
return mMinAspectRatio;
}
@@ -1512,7 +1503,13 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
}
}
- private boolean isChangeEnabled(long changeId) {
+ /**
+ * Checks if a changeId is enabled for the current user
+ * @param changeId The changeId to verify
+ * @return True of the changeId is enabled
+ * @hide
+ */
+ public boolean isChangeEnabled(long changeId) {
return CompatChanges.isChangeEnabled(changeId, applicationInfo.packageName,
UserHandle.getUserHandleForUid(applicationInfo.uid));
}
@@ -1633,12 +1630,9 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
if (getMaxAspectRatio() != 0) {
pw.println(prefix + "maxAspectRatio=" + getMaxAspectRatio());
}
- final float minAspectRatio = getMinAspectRatio(screenOrientation);
+ final float minAspectRatio = getMinAspectRatio();
if (minAspectRatio != 0) {
pw.println(prefix + "minAspectRatio=" + minAspectRatio);
- if (getManifestMinAspectRatio() != minAspectRatio) {
- pw.println(prefix + "getManifestMinAspectRatio=" + getManifestMinAspectRatio());
- }
}
if (supportsSizeChanges) {
pw.println(prefix + "supportsSizeChanges=true");
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 7092e43596ec..7247ef77afb4 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -29,6 +29,7 @@ import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricFaceConstants;
+import android.hardware.biometrics.BiometricStateListener;
import android.hardware.biometrics.CryptoObject;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.os.Binder;
@@ -674,6 +675,45 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
}
/**
+ * Forwards BiometricStateListener to FaceService.
+ *
+ * @param listener new BiometricStateListener being added
+ * @hide
+ */
+ public void registerBiometricStateListener(@NonNull BiometricStateListener listener) {
+ try {
+ mService.registerBiometricStateListener(listener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Adds a callback that gets called when the service registers all of the face
+ * authenticators (HALs).
+ *
+ * If the face authenticators are already registered when the callback is added, the
+ * callback is invoked immediately.
+ *
+ * The callback is automatically removed after it's invoked.
+ *
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public void addAuthenticatorsRegisteredCallback(
+ IFaceAuthenticatorsRegisteredCallback callback) {
+ if (mService != null) {
+ try {
+ mService.addAuthenticatorsRegisteredCallback(callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ Slog.w(TAG, "addAuthenticatorsRegisteredCallback(): Service not connected!");
+ }
+ }
+
+ /**
* @hide
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
diff --git a/core/java/android/hardware/face/IFaceAuthenticatorsRegisteredCallback.aidl b/core/java/android/hardware/face/IFaceAuthenticatorsRegisteredCallback.aidl
new file mode 100644
index 000000000000..78f978d21ed7
--- /dev/null
+++ b/core/java/android/hardware/face/IFaceAuthenticatorsRegisteredCallback.aidl
@@ -0,0 +1,35 @@
+/*
+ * 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.hardware.face;
+
+import android.hardware.face.FaceSensorPropertiesInternal;
+import java.util.List;
+
+/**
+ * Callback to notify FaceManager that FaceService has registered all of the
+ * face authenticators (HALs).
+ * See {@link android.hardware.face.IFaceService#registerAuthenticators}.
+ *
+ * @hide
+ */
+oneway interface IFaceAuthenticatorsRegisteredCallback {
+ /**
+ * Notifies FaceManager that all of the face authenticators have been registered.
+ *
+ * @param sensors A consolidated list of sensor properties for all of the authenticators.
+ */
+ void onAllAuthenticatorsRegistered(in List<FaceSensorPropertiesInternal> sensors);
+}
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index 369248edd580..9b56f43a0f22 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -17,9 +17,11 @@ package android.hardware.face;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.IBiometricStateListener;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
+import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
import android.hardware.face.IFaceServiceReceiver;
import android.hardware.face.Face;
import android.hardware.face.FaceSensorPropertiesInternal;
@@ -163,4 +165,11 @@ interface IFaceService {
// hidlSensors must be non-null and empty. See AuthService.java
@EnforcePermission("USE_BIOMETRIC_INTERNAL")
void registerAuthenticators(in List<FaceSensorPropertiesInternal> hidlSensors);
+
+ // Adds a callback which gets called when the service registers all of the face
+ // authenticators. The callback is automatically removed after it's invoked.
+ void addAuthenticatorsRegisteredCallback(IFaceAuthenticatorsRegisteredCallback callback);
+
+ // Registers BiometricStateListener.
+ void registerBiometricStateListener(IBiometricStateListener listener);
}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index cc7ed183ed64..1ba9a0471c88 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -202,8 +202,10 @@ interface IFingerprintService {
void setSidefpsController(in ISidefpsController controller);
// Registers BiometricStateListener.
+ @EnforcePermission("USE_BIOMETRIC_INTERNAL")
void registerBiometricStateListener(IBiometricStateListener listener);
// Sends a power button pressed event to all listeners.
+ @EnforcePermission("USE_BIOMETRIC_INTERNAL")
oneway void onPowerPressed();
}
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 2483f99a07ec..b7adcb84102d 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -755,6 +755,13 @@ public final class DeviceConfig {
public static final String NAMESPACE_VENDOR_SYSTEM_NATIVE = "vendor_system_native";
/**
+ * Namespace for Vendor System Native Boot related features.
+ *
+ * @hide
+ */
+ public static final String NAMESPACE_VENDOR_SYSTEM_NATIVE_BOOT = "vendor_system_native_boot";
+
+ /**
* Namespace for memory safety related features (e.g. MTE)
*
* @hide
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 05b3925fcb9f..a7c72730a89f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5486,6 +5486,15 @@ public final class Settings {
public static final String MULTI_AUDIO_FOCUS_ENABLED = "multi_audio_focus_enabled";
/**
+ * Whether desktop mode is enabled or not.
+ * 0 = off
+ * 1 = on
+ * @hide
+ */
+ @Readable
+ public static final String DESKTOP_MODE = "desktop_mode";
+
+ /**
* IMPORTANT: If you add a new public settings you also have to add it to
* PUBLIC_SETTINGS below. If the new setting is hidden you have to add
* it to PRIVATE_SETTINGS below. Also add a validator that can validate
@@ -5614,6 +5623,7 @@ public final class Settings {
PRIVATE_SETTINGS.add(SHOW_BATTERY_PERCENT);
PRIVATE_SETTINGS.add(DISPLAY_COLOR_MODE);
PRIVATE_SETTINGS.add(DISPLAY_COLOR_MODE_VENDOR_HINT);
+ PRIVATE_SETTINGS.add(DESKTOP_MODE);
}
/**
@@ -17688,20 +17698,13 @@ public final class Settings {
"wear_activity_auto_resume_timeout_set_by_user";
/**
- * The maximum times that we are allowed to reset the activity auto-resume timeout
- * timer.
- * @hide
- */
- public static final String WEAR_ACTIVITY_AUTO_RESUME_TIMEOUT_MAX_RESET_COUNT =
- "wear_activity_auto_resume_timeout_max_reset_count";
-
- /**
* If burn in protection is enabled.
* @hide
*/
public static final String BURN_IN_PROTECTION_ENABLED = "burn_in_protection";
/**
+
* Whether the device has combined location setting enabled.
* @hide
*/
diff --git a/core/java/android/service/voice/HotwordDetectedResult.java b/core/java/android/service/voice/HotwordDetectedResult.java
index 72341453a1f4..ab71459ed51e 100644
--- a/core/java/android/service/voice/HotwordDetectedResult.java
+++ b/core/java/android/service/voice/HotwordDetectedResult.java
@@ -95,6 +95,16 @@ public final class HotwordDetectedResult implements Parcelable {
/** Limits the max value for the triggered audio channel. */
private static final int LIMIT_AUDIO_CHANNEL_MAX_VALUE = 63;
+ /**
+ * The bundle key for proximity value
+ *
+ * TODO(b/238896013): Move the proximity logic out of bundle to proper API.
+ *
+ * @hide
+ */
+ public static final String EXTRA_PROXIMITY_METERS =
+ "android.service.voice.extra.PROXIMITY_METERS";
+
/** Confidence level in the trigger outcome. */
@HotwordConfidenceLevelValue
private final int mConfidenceLevel;
@@ -197,6 +207,14 @@ public final class HotwordDetectedResult implements Parcelable {
* <p>The use of this method is discouraged, and support for it will be removed in future
* versions of Android.
*
+ * <p>After the trigger happens, a special case of proximity-related extra, with the key of
+ * 'android.service.voice.extra.PROXIMITY_METERS' and the value of distance in meters (double),
+ * will be stored to enable proximity logic. The proximity meters is provided by the system,
+ * on devices that support detecting proximity of nearby users, to help disambiguate which
+ * nearby device should respond. When the proximity is unknown, the proximity value will not
+ * be stored. This mapping will be excluded from the max bundle size calculation because this
+ * mapping is included after the result is returned from the hotword detector service.
+ *
* <p>This is a PersistableBundle so it doesn't allow any remotable objects or other contents
* that can be used to communicate with other processes.
*/
@@ -315,8 +333,23 @@ public final class HotwordDetectedResult implements Parcelable {
"audioChannel");
}
if (!mExtras.isEmpty()) {
- Preconditions.checkArgumentInRange(getParcelableSize(mExtras), 0, getMaxBundleSize(),
- "extras");
+ // Remove the proximity key from the bundle before checking the bundle size. The
+ // proximity value is added after the privileged module and can avoid the
+ // maxBundleSize limitation.
+ if (mExtras.containsKey(EXTRA_PROXIMITY_METERS)) {
+ double proximityMeters = mExtras.getDouble(EXTRA_PROXIMITY_METERS);
+ mExtras.remove(EXTRA_PROXIMITY_METERS);
+ // Skip checking parcelable size if the new bundle size is 0. Newly empty bundle
+ // has parcelable size of 4, but the default bundle has parcelable size of 0.
+ if (mExtras.size() > 0) {
+ Preconditions.checkArgumentInRange(getParcelableSize(mExtras), 0,
+ getMaxBundleSize(), "extras");
+ }
+ mExtras.putDouble(EXTRA_PROXIMITY_METERS, proximityMeters);
+ } else {
+ Preconditions.checkArgumentInRange(getParcelableSize(mExtras), 0,
+ getMaxBundleSize(), "extras");
+ }
}
}
@@ -513,6 +546,14 @@ public final class HotwordDetectedResult implements Parcelable {
* <p>The use of this method is discouraged, and support for it will be removed in future
* versions of Android.
*
+ * <p>After the trigger happens, a special case of proximity-related extra, with the key of
+ * 'android.service.voice.extra.PROXIMITY_METERS' and the value of distance in meters (double),
+ * will be stored to enable proximity logic. The proximity meters is provided by the system,
+ * on devices that support detecting proximity of nearby users, to help disambiguate which
+ * nearby device should respond. When the proximity is unknown, the proximity value will not
+ * be stored. This mapping will be excluded from the max bundle size calculation because this
+ * mapping is included after the result is returned from the hotword detector service.
+ *
* <p>This is a PersistableBundle so it doesn't allow any remotable objects or other contents
* that can be used to communicate with other processes.
*/
@@ -813,6 +854,14 @@ public final class HotwordDetectedResult implements Parcelable {
* <p>The use of this method is discouraged, and support for it will be removed in future
* versions of Android.
*
+ * <p>After the trigger happens, a special case of proximity-related extra, with the key of
+ * 'android.service.voice.extra.PROXIMITY_METERS' and the value of distance in meters (double),
+ * will be stored to enable proximity logic. The proximity meters is provided by the system,
+ * on devices that support detecting proximity of nearby users, to help disambiguate which
+ * nearby device should respond. When the proximity is unknown, the proximity value will not
+ * be stored. This mapping will be excluded from the max bundle size calculation because this
+ * mapping is included after the result is returned from the hotword detector service.
+ *
* <p>This is a PersistableBundle so it doesn't allow any remotable objects or other contents
* that can be used to communicate with other processes.
*/
@@ -882,10 +931,10 @@ public final class HotwordDetectedResult implements Parcelable {
}
@DataClass.Generated(
- time = 1625541522353L,
+ time = 1658357814396L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/service/voice/HotwordDetectedResult.java",
- inputSignatures = "public static final int CONFIDENCE_LEVEL_NONE\npublic static final int CONFIDENCE_LEVEL_LOW\npublic static final int CONFIDENCE_LEVEL_LOW_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM_HIGH\npublic static final int CONFIDENCE_LEVEL_HIGH\npublic static final int CONFIDENCE_LEVEL_VERY_HIGH\npublic static final int HOTWORD_OFFSET_UNSET\npublic static final int AUDIO_CHANNEL_UNSET\nprivate static final int LIMIT_HOTWORD_OFFSET_MAX_VALUE\nprivate static final int LIMIT_AUDIO_CHANNEL_MAX_VALUE\nprivate final @android.service.voice.HotwordDetectedResult.HotwordConfidenceLevelValue int mConfidenceLevel\nprivate @android.annotation.Nullable android.media.MediaSyncEvent mMediaSyncEvent\nprivate int mHotwordOffsetMillis\nprivate int mHotwordDurationMillis\nprivate int mAudioChannel\nprivate boolean mHotwordDetectionPersonalized\nprivate final int mScore\nprivate final int mPersonalizedScore\nprivate final int mHotwordPhraseId\nprivate final @android.annotation.NonNull android.os.PersistableBundle mExtras\nprivate static int sMaxBundleSize\nprivate static int defaultConfidenceLevel()\nprivate static int defaultScore()\nprivate static int defaultPersonalizedScore()\npublic static int getMaxScore()\nprivate static int defaultHotwordPhraseId()\npublic static int getMaxHotwordPhraseId()\nprivate static android.os.PersistableBundle defaultExtras()\npublic static int getMaxBundleSize()\npublic @android.annotation.Nullable android.media.MediaSyncEvent getMediaSyncEvent()\npublic static int getParcelableSize(android.os.Parcelable)\npublic static int getUsageSize(android.service.voice.HotwordDetectedResult)\nprivate static int bitCount(long)\nprivate void onConstructed()\nclass HotwordDetectedResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
+ inputSignatures = "public static final int CONFIDENCE_LEVEL_NONE\npublic static final int CONFIDENCE_LEVEL_LOW\npublic static final int CONFIDENCE_LEVEL_LOW_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM_HIGH\npublic static final int CONFIDENCE_LEVEL_HIGH\npublic static final int CONFIDENCE_LEVEL_VERY_HIGH\npublic static final int HOTWORD_OFFSET_UNSET\npublic static final int AUDIO_CHANNEL_UNSET\nprivate static final int LIMIT_HOTWORD_OFFSET_MAX_VALUE\nprivate static final int LIMIT_AUDIO_CHANNEL_MAX_VALUE\npublic static final java.lang.String EXTRA_PROXIMITY_METERS\nprivate final @android.service.voice.HotwordDetectedResult.HotwordConfidenceLevelValue int mConfidenceLevel\nprivate @android.annotation.Nullable android.media.MediaSyncEvent mMediaSyncEvent\nprivate int mHotwordOffsetMillis\nprivate int mHotwordDurationMillis\nprivate int mAudioChannel\nprivate boolean mHotwordDetectionPersonalized\nprivate final int mScore\nprivate final int mPersonalizedScore\nprivate final int mHotwordPhraseId\nprivate final @android.annotation.NonNull android.os.PersistableBundle mExtras\nprivate static int sMaxBundleSize\nprivate static int defaultConfidenceLevel()\nprivate static int defaultScore()\nprivate static int defaultPersonalizedScore()\npublic static int getMaxScore()\nprivate static int defaultHotwordPhraseId()\npublic static int getMaxHotwordPhraseId()\nprivate static android.os.PersistableBundle defaultExtras()\npublic static int getMaxBundleSize()\npublic @android.annotation.Nullable android.media.MediaSyncEvent getMediaSyncEvent()\npublic static int getParcelableSize(android.os.Parcelable)\npublic static int getUsageSize(android.service.voice.HotwordDetectedResult)\nprivate static int bitCount(long)\nprivate void onConstructed()\nclass HotwordDetectedResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 4fbf4b528fe9..bc8822cfb3e9 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -1153,8 +1153,8 @@ public abstract class WallpaperService extends Service {
mLayout.surfaceInsets.set(0, 0, 0, 0);
}
final int relayoutResult = mSession.relayout(mWindow, mLayout, mWidth, mHeight,
- View.VISIBLE, 0, mWinFrames, mMergedConfiguration, mSurfaceControl,
- mInsetsState, mTempControls, mSyncSeqIdBundle);
+ View.VISIBLE, 0, 0, 0, mWinFrames, mMergedConfiguration,
+ mSurfaceControl, mInsetsState, mTempControls, mSyncSeqIdBundle);
final int transformHint = SurfaceControl.rotationToBufferTransform(
(mDisplayInstallOrientation + mDisplay.getRotation()) % 4);
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 3016473fa040..afcec6639dcf 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -75,41 +75,42 @@ interface IWindowSession {
* @param requestedWidth The width the window wants to be.
* @param requestedHeight The height the window wants to be.
* @param viewVisibility Window root view's visibility.
- * @param flags Request flags: {@link WindowManagerGlobal#RELAYOUT_INSETS_PENDING},
- * {@link WindowManagerGlobal#RELAYOUT_DEFER_SURFACE_DESTROY}.
- * @param outFrame Rect in which is placed the new position/size on
- * screen.
- * @param outContentInsets Rect in which is placed the offsets from
- * <var>outFrame</var> in which the content of the window should be
- * placed. This can be used to modify the window layout to ensure its
- * contents are visible to the user, taking into account system windows
- * like the status bar or a soft keyboard.
- * @param outVisibleInsets Rect in which is placed the offsets from
- * <var>outFrame</var> in which the window is actually completely visible
- * to the user. This can be used to temporarily scroll the window's
- * contents to make sure the user can see it. This is different than
- * <var>outContentInsets</var> in that these insets change transiently,
- * so complex relayout of the window should not happen based on them.
- * @param outOutsets Rect in which is placed the dead area of the screen that we would like to
- * treat as real display. Example of such area is a chin in some models of wearable devices.
- * @param outBackdropFrame Rect which is used draw the resizing background during a resize
- * operation.
+ * @param flags Request flags: {@link WindowManagerGlobal#RELAYOUT_INSETS_PENDING}.
+ * @param seq The calling sequence of {@link #relayout} and {@link #relayoutAsync}.
+ * @param lastSyncSeqId The last SyncSeqId that the client applied.
+ * @param outFrames The window frames used by the client side for layout.
* @param outMergedConfiguration New config container that holds global, override and merged
- * config for window, if it is now becoming visible and the merged configuration has changed
- * since it was last displayed.
- * @param outSurface Object in which is placed the new display surface.
+ * config for window, if it is now becoming visible and the merged
+ * config has changed since it was last displayed.
+ * @param outSurfaceControl Object in which is placed the new display surface.
* @param insetsState The current insets state in the system.
- *
- * @return int Result flags: {@link WindowManagerGlobal#RELAYOUT_SHOW_FOCUS},
- * {@link WindowManagerGlobal#RELAYOUT_FIRST_TIME}.
+ * @param activeControls Objects which allow controlling {@link InsetsSource}s.
+ * @param bundle A temporary object to obtain the latest SyncSeqId.
+ * @return int Result flags, defined in {@link WindowManagerGlobal}.
*/
int relayout(IWindow window, in WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewVisibility,
- int flags, out ClientWindowFrames outFrames,
+ int flags, int seq, int lastSyncSeqId, out ClientWindowFrames outFrames,
out MergedConfiguration outMergedConfiguration, out SurfaceControl outSurfaceControl,
out InsetsState insetsState, out InsetsSourceControl[] activeControls,
out Bundle bundle);
+ /**
+ * Similar to {@link #relayout} but this is an oneway method which doesn't return anything.
+ *
+ * @param window The window being modified.
+ * @param attrs If non-null, new attributes to apply to the window.
+ * @param requestedWidth The width the window wants to be.
+ * @param requestedHeight The height the window wants to be.
+ * @param viewVisibility Window root view's visibility.
+ * @param flags Request flags: {@link WindowManagerGlobal#RELAYOUT_INSETS_PENDING}.
+ * @param seq The calling sequence of {@link #relayout} and {@link #relayoutAsync}.
+ * @param lastSyncSeqId The last SyncSeqId that the client applied.
+ */
+ oneway void relayoutAsync(IWindow window, in WindowManager.LayoutParams attrs,
+ int requestedWidth, int requestedHeight, int viewVisibility, int flags, int seq,
+ int lastSyncSeqId);
+
/*
* Notify the window manager that an application is relaunching and
* windows should be prepared for replacement.
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index d63c25a09382..5236fe772a7b 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -176,7 +176,9 @@ public class InsetsSourceConsumer {
// If we have a new leash, make sure visibility is up-to-date, even though we
// didn't want to run an animation above.
- applyRequestedVisibilityToControl();
+ if (mController.getAnimationType(control.getType()) == ANIMATION_TYPE_NONE) {
+ applyRequestedVisibilityToControl();
+ }
// Remove the surface that owned by last control when it lost.
if (!requestedVisible && lastControl == null) {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 70a5eda1d03e..8f9c5fe2b87f 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -119,10 +119,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
final int[] mLocation = new int[2];
@UnsupportedAppUsage
- // Used to ensure the Surface remains valid between SurfaceHolder#lockCanvas and
- // SurfaceHolder#unlockCanvasAndPost calls. This prevents SurfaceView from destroying or
- // invalidating the Surface. This means this lock should be acquired when destroying the
- // BlastBufferQueue.
final ReentrantLock mSurfaceLock = new ReentrantLock();
@UnsupportedAppUsage
final Surface mSurface = new Surface(); // Current surface in use
@@ -727,18 +723,14 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
private void releaseSurfaces(boolean releaseSurfacePackage) {
mSurfaceAlpha = 1f;
- try {
- mSurfaceLock.lock();
- mSurface.destroy();
+ mSurface.destroy();
+
+ synchronized (mSurfaceControlLock) {
if (mBlastBufferQueue != null) {
mBlastBufferQueue.destroy();
mBlastBufferQueue = null;
}
- } finally {
- mSurfaceLock.unlock();
- }
- synchronized (mSurfaceControlLock) {
final Transaction transaction = new Transaction();
if (mSurfaceControl != null) {
transaction.remove(mSurfaceControl);
@@ -1240,21 +1232,16 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
.build();
}
- mTransformHint = viewRoot.getBufferTransformHint();
- mBlastSurfaceControl.setTransformHint(mTransformHint);
-
// Always recreate the IGBP for compatibility. This can be optimized in the future but
// the behavior change will need to be gated by SDK version.
- try {
- mSurfaceLock.lock();
- if (mBlastBufferQueue != null) {
- mBlastBufferQueue.destroy();
- }
- mBlastBufferQueue = new BLASTBufferQueue(name, false /* updateDestinationFrame */);
- mBlastBufferQueue.update(mBlastSurfaceControl, mSurfaceWidth, mSurfaceHeight, mFormat);
- } finally {
- mSurfaceLock.unlock();
+ if (mBlastBufferQueue != null) {
+ mBlastBufferQueue.destroy();
}
+ mTransformHint = viewRoot.getBufferTransformHint();
+ mBlastSurfaceControl.setTransformHint(mTransformHint);
+
+ mBlastBufferQueue = new BLASTBufferQueue(name, false /* updateDestinationFrame */);
+ mBlastBufferQueue.update(mBlastSurfaceControl, mSurfaceWidth, mSurfaceHeight, mFormat);
mBlastBufferQueue.setTransactionHangCallback(ViewRootImpl.sTransactionHangCallback);
}
@@ -1827,14 +1814,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
// so the next connect will always work if we end up reusing
// the surface.
if (mSurface.isValid()) {
- // We need to grab this lock since mSurface.forceScopedDisconnect
- // will free buffers from the queue.
- try {
- mSurfaceLock.lock();
- mSurface.forceScopedDisconnect();
- } finally {
- mSurfaceLock.unlock();
- }
+ mSurface.forceScopedDisconnect();
}
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f79f0d49959d..12bc169153a4 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -75,6 +75,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
@@ -707,6 +708,8 @@ public final class ViewRootImpl implements ViewParent,
final Rect mPendingBackDropFrame = new Rect();
boolean mPendingAlwaysConsumeSystemBars;
+ private int mRelayoutSeq;
+ private final Rect mWinFrameInScreen = new Rect();
private final InsetsState mTempInsets = new InsetsState();
private final InsetsSourceControl[] mTempControls = new InsetsSourceControl[SIZE];
private final WindowConfiguration mTempWinConfig = new WindowConfiguration();
@@ -3364,20 +3367,6 @@ public final class ViewRootImpl implements ViewParent,
}
}
} else {
- // If a relayout isn't going to happen, we still need to check if this window can draw
- // when mCheckIfCanDraw is set. This is because it means we had a sync in the past, but
- // have not been told by WMS that the sync is complete and that we can continue to draw
- if (mCheckIfCanDraw) {
- try {
- cancelDraw = mWindowSession.cancelDraw(mWindow);
- cancelReason = "wm_sync";
- if (DEBUG_BLAST) {
- Log.d(mTag, "cancelDraw returned " + cancelDraw);
- }
- } catch (RemoteException e) {
- }
- }
-
// Not the first pass and no window/insets/visibility change but the window
// may have moved and we need check that and if so to update the left and right
// in the attach info. We translate only the window frame since on window move
@@ -3386,6 +3375,20 @@ public final class ViewRootImpl implements ViewParent,
maybeHandleWindowMove(frame);
}
+ if (!mRelayoutRequested && mCheckIfCanDraw) {
+ // We had a sync previously, but we didn't call IWindowSession#relayout in this
+ // traversal. So we don't know if the sync is complete that we can continue to draw.
+ // Here invokes cancelDraw to obtain the information.
+ try {
+ cancelDraw = mWindowSession.cancelDraw(mWindow);
+ cancelReason = "wm_sync";
+ if (DEBUG_BLAST) {
+ Log.d(mTag, "cancelDraw returned " + cancelDraw);
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
if (surfaceSizeChanged || surfaceReplaced || surfaceCreated || windowAttributesChanged) {
// If the surface has been replaced, there's a chance the bounds layer is not parented
// to the new layer. When updating bounds layer, also reparent to the main VRI
@@ -6403,6 +6406,12 @@ public final class ViewRootImpl implements ViewParent,
// Make sure the fallback event policy sees all keys that will be
// delivered to the view hierarchy.
mFallbackEventHandler.preDispatchKeyEvent(event);
+
+ // Reset last tracked MotionEvent click toolType.
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ mLastClickToolType = MotionEvent.TOOL_TYPE_UNKNOWN;
+ }
+
return FORWARD;
}
@@ -8151,7 +8160,43 @@ public final class ViewRootImpl implements ViewParent,
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
- mRelayoutRequested = true;
+ final WindowConfiguration winConfigFromAm = getConfiguration().windowConfiguration;
+ final WindowConfiguration winConfigFromWm =
+ mLastReportedMergedConfiguration.getGlobalConfiguration().windowConfiguration;
+ final WindowConfiguration winConfig = getCompatWindowConfiguration();
+ final int measuredWidth = mView.getMeasuredWidth();
+ final int measuredHeight = mView.getMeasuredHeight();
+ final boolean relayoutAsync;
+ if (LOCAL_LAYOUT && !mFirst && viewVisibility == mViewVisibility
+ && mWindowAttributes.type != TYPE_APPLICATION_STARTING
+ && mSyncSeqId <= mLastSyncSeqId
+ && winConfigFromAm.diff(winConfigFromWm, false /* compareUndefined */) == 0) {
+ final InsetsState state = mInsetsController.getState();
+ final Rect displayCutoutSafe = mTempRect;
+ state.getDisplayCutoutSafe(displayCutoutSafe);
+ mWindowLayout.computeFrames(mWindowAttributes.forRotation(winConfig.getRotation()),
+ state, displayCutoutSafe, winConfig.getBounds(), winConfig.getWindowingMode(),
+ measuredWidth, measuredHeight, mInsetsController.getRequestedVisibilities(),
+ 1f /* compatScale */, mTmpFrames);
+ mWinFrameInScreen.set(mTmpFrames.frame);
+ if (mTranslator != null) {
+ mTranslator.translateRectInAppWindowToScreen(mWinFrameInScreen);
+ }
+
+ // If the position and the size of the frame are both changed, it will trigger a BLAST
+ // sync, and we still need to call relayout to obtain the syncSeqId. Otherwise, we just
+ // need to send attributes via relayoutAsync.
+ final Rect oldFrame = mWinFrame;
+ final Rect newFrame = mTmpFrames.frame;
+ final boolean positionChanged =
+ newFrame.top != oldFrame.top || newFrame.left != oldFrame.left;
+ final boolean sizeChanged =
+ newFrame.width() != oldFrame.width() || newFrame.height() != oldFrame.height();
+ relayoutAsync = !positionChanged || !sizeChanged;
+ } else {
+ relayoutAsync = false;
+ }
+
float appScale = mAttachInfo.mApplicationScale;
boolean restore = false;
if (params != null && mTranslator != null) {
@@ -8173,26 +8218,47 @@ public final class ViewRootImpl implements ViewParent,
}
}
- final int requestedWidth = (int) (mView.getMeasuredWidth() * appScale + 0.5f);
- final int requestedHeight = (int) (mView.getMeasuredHeight() * appScale + 0.5f);
+ final int requestedWidth = (int) (measuredWidth * appScale + 0.5f);
+ final int requestedHeight = (int) (measuredHeight * appScale + 0.5f);
+ int relayoutResult = 0;
+ mRelayoutSeq++;
+ if (relayoutAsync) {
+ mWindowSession.relayoutAsync(mWindow, params,
+ requestedWidth, requestedHeight, viewVisibility,
+ insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, mRelayoutSeq,
+ mLastSyncSeqId);
+ } else {
+ relayoutResult = mWindowSession.relayout(mWindow, params,
+ requestedWidth, requestedHeight, viewVisibility,
+ insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, mRelayoutSeq,
+ mLastSyncSeqId, mTmpFrames, mPendingMergedConfiguration, mSurfaceControl,
+ mTempInsets, mTempControls, mRelayoutBundle);
+ mRelayoutRequested = true;
+ final int maybeSyncSeqId = mRelayoutBundle.getInt("seqid");
+ if (maybeSyncSeqId > 0) {
+ mSyncSeqId = maybeSyncSeqId;
+ }
+ mWinFrameInScreen.set(mTmpFrames.frame);
+ if (mTranslator != null) {
+ mTranslator.translateRectInScreenToAppWindow(mTmpFrames.frame);
+ mTranslator.translateRectInScreenToAppWindow(mTmpFrames.displayFrame);
+ mTranslator.translateRectInScreenToAppWindow(mTmpFrames.attachedFrame);
+ mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets);
+ mTranslator.translateSourceControlsInScreenToAppWindow(mTempControls);
+ }
+ mInvSizeCompatScale = 1f / mTmpFrames.sizeCompatScale;
+ mInsetsController.onStateChanged(mTempInsets);
+ mInsetsController.onControlsChanged(mTempControls);
- int relayoutResult = mWindowSession.relayout(mWindow, params,
- requestedWidth, requestedHeight, viewVisibility,
- insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
- mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
- mTempControls, mRelayoutBundle);
- final int maybeSyncSeqId = mRelayoutBundle.getInt("seqid");
- if (maybeSyncSeqId > 0) {
- mSyncSeqId = maybeSyncSeqId;
+ mPendingAlwaysConsumeSystemBars =
+ (relayoutResult & RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS) != 0;
}
- mInvSizeCompatScale = 1f / mTmpFrames.sizeCompatScale;
final int transformHint = SurfaceControl.rotationToBufferTransform(
(mDisplayInstallOrientation + mDisplay.getRotation()) % 4);
- final WindowConfiguration winConfig = getCompatWindowConfiguration();
WindowLayout.computeSurfaceSize(mWindowAttributes, winConfig.getMaxBounds(), requestedWidth,
- requestedHeight, mTmpFrames.frame, mPendingDragResizing, mSurfaceSize);
+ requestedHeight, mWinFrameInScreen, mPendingDragResizing, mSurfaceSize);
final boolean transformHintChanged = transformHint != mLastTransformHint;
final boolean sizeChanged = !mLastSurfaceSize.equals(mSurfaceSize);
@@ -8239,23 +8305,11 @@ public final class ViewRootImpl implements ViewParent,
destroySurface();
}
- mPendingAlwaysConsumeSystemBars =
- (relayoutResult & RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS) != 0;
-
if (restore) {
params.restore();
}
- if (mTranslator != null) {
- mTranslator.translateRectInScreenToAppWindow(mTmpFrames.frame);
- mTranslator.translateRectInScreenToAppWindow(mTmpFrames.displayFrame);
- mTranslator.translateRectInScreenToAppWindow(mTmpFrames.attachedFrame);
- mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets);
- mTranslator.translateSourceControlsInScreenToAppWindow(mTempControls);
- }
setFrame(mTmpFrames.frame);
- mInsetsController.onStateChanged(mTempInsets);
- mInsetsController.onControlsChanged(mTempControls);
return relayoutResult;
}
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index d55c838c3e53..1ec17d00e99a 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -286,10 +286,11 @@ public class WindowlessWindowManager implements IWindowSession {
@Override
public int relayout(IWindow window, WindowManager.LayoutParams inAttrs,
- int requestedWidth, int requestedHeight, int viewFlags, int flags,
- ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
- SurfaceControl outSurfaceControl, InsetsState outInsetsState,
- InsetsSourceControl[] outActiveControls, Bundle outSyncSeqIdBundle) {
+ int requestedWidth, int requestedHeight, int viewFlags, int flags, int seq,
+ int lastSyncSeqId, ClientWindowFrames outFrames,
+ MergedConfiguration outMergedConfiguration, SurfaceControl outSurfaceControl,
+ InsetsState outInsetsState, InsetsSourceControl[] outActiveControls,
+ Bundle outSyncSeqIdBundle) {
final State state;
synchronized (this) {
state = mStateForWindow.get(window.asBinder());
@@ -309,15 +310,23 @@ public class WindowlessWindowManager implements IWindowSession {
if (viewFlags == View.VISIBLE) {
t.setOpaque(sc, isOpaque(attrs)).show(sc).apply();
- outSurfaceControl.copyFrom(sc, "WindowlessWindowManager.relayout");
+ if (outSurfaceControl != null) {
+ outSurfaceControl.copyFrom(sc, "WindowlessWindowManager.relayout");
+ }
} else {
t.hide(sc).apply();
- outSurfaceControl.release();
+ if (outSurfaceControl != null) {
+ outSurfaceControl.release();
+ }
+ }
+ if (outFrames != null) {
+ outFrames.frame.set(0, 0, attrs.width, attrs.height);
+ outFrames.displayFrame.set(outFrames.frame);
}
- outFrames.frame.set(0, 0, attrs.width, attrs.height);
- outFrames.displayFrame.set(outFrames.frame);
- mergedConfiguration.setConfiguration(mConfiguration, mConfiguration);
+ if (outMergedConfiguration != null) {
+ outMergedConfiguration.setConfiguration(mConfiguration, mConfiguration);
+ }
if ((attrChanges & WindowManager.LayoutParams.FLAGS_CHANGED) != 0
&& state.mInputChannelToken != null) {
@@ -335,7 +344,7 @@ public class WindowlessWindowManager implements IWindowSession {
}
}
- if (mInsetsState != null) {
+ if (outInsetsState != null && mInsetsState != null) {
outInsetsState.set(mInsetsState);
}
@@ -343,6 +352,16 @@ public class WindowlessWindowManager implements IWindowSession {
}
@Override
+ public void relayoutAsync(IWindow window, WindowManager.LayoutParams inAttrs,
+ int requestedWidth, int requestedHeight, int viewFlags, int flags, int seq,
+ int lastSyncSeqId) {
+ relayout(window, inAttrs, requestedWidth, requestedHeight, viewFlags, flags, seq,
+ lastSyncSeqId, null /* outFrames */, null /* outMergedConfiguration */,
+ null /* outSurfaceControl */, null /* outInsetsState */,
+ null /* outActiveControls */, null /* outSyncSeqIdBundle */);
+ }
+
+ @Override
public void prepareToReplaceWindows(android.os.IBinder appToken, boolean childrenOnly) {
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 0c03d109f649..8d3cf6d890d2 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -18,7 +18,6 @@ package android.view.inputmethod;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
-import static android.view.inputmethod.InputConnection.CURSOR_UPDATE_FILTER_EDITOR_BOUNDS;
import static android.view.inputmethod.InputConnection.CURSOR_UPDATE_IMMEDIATE;
import static android.view.inputmethod.InputConnection.CURSOR_UPDATE_MONITOR;
import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.DISPLAY_ID;
@@ -2086,15 +2085,6 @@ public final class InputMethodManager {
Log.w(TAG, "Ignoring startStylusHandwriting: View's window does not have focus.");
return;
}
- if (mServedInputConnection != null && getDelegate().hasActiveConnection(view)) {
- // TODO (b/210039666): optimize CURSOR_UPDATE_IMMEDIATE.
- // TODO (b/210039666): Pipe IME displayId from InputBindResult and use it here.
- // instead of mDisplayId.
- mServedInputConnection.requestCursorUpdatesFromImm(
- CURSOR_UPDATE_IMMEDIATE | CURSOR_UPDATE_MONITOR,
- CURSOR_UPDATE_FILTER_EDITOR_BOUNDS,
- mDisplayId);
- }
mServiceInvoker.startStylusHandwriting(mClient);
// TODO(b/210039666): do we need any extra work for supporting non-native
@@ -2341,9 +2331,6 @@ public final class InputMethodManager {
editorInfo.packageName = view.getContext().getOpPackageName();
editorInfo.autofillId = view.getAutofillId();
editorInfo.fieldId = view.getId();
- synchronized (mH) {
- editorInfo.setInitialToolType(mCurRootView.getLastClickToolType());
- }
InputConnection ic = view.onCreateInputConnection(editorInfo);
if (DEBUG) Log.v(TAG, "Starting input: editorInfo=" + editorInfo + " ic=" + ic);
@@ -2383,6 +2370,8 @@ public final class InputMethodManager {
startInputFlags |= StartInputFlags.INITIAL_CONNECTION;
}
+ editorInfo.setInitialToolType(mCurRootView.getLastClickToolType());
+
// Hook 'em up and let 'er rip.
mCurrentEditorInfo = editorInfo.createCopyInternal();
// Store the previously served connection so that we can determine whether it is safe
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index b233e5453c05..b21c5b35e24b 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -226,6 +226,8 @@ public class Editor {
final UndoInputFilter mUndoInputFilter = new UndoInputFilter(this);
boolean mAllowUndo = true;
+ private int mLastToolType = MotionEvent.TOOL_TYPE_UNKNOWN;
+
private final MetricsLogger mMetricsLogger = new MetricsLogger();
// Cursor Controllers.
@@ -1732,6 +1734,9 @@ public class Editor {
@VisibleForTesting
public void onTouchEvent(MotionEvent event) {
final boolean filterOutEvent = shouldFilterOutTouchEvent(event);
+
+ mLastToolType = event.getToolType(event.getActionIndex());
+
mLastButtonState = event.getButtonState();
if (filterOutEvent) {
if (event.getActionMasked() == MotionEvent.ACTION_UP) {
@@ -1784,7 +1789,7 @@ public class Editor {
}
private void showFloatingToolbar() {
- if (mTextActionMode != null) {
+ if (mTextActionMode != null && showUIForFingerInput()) {
// Delay "show" so it doesn't interfere with click confirmations
// or double-clicks that could "dismiss" the floating toolbar.
int delay = ViewConfiguration.getDoubleTapTimeout();
@@ -1864,7 +1869,8 @@ public class Editor {
final CursorController cursorController = mTextView.hasSelection()
? getSelectionController() : getInsertionController();
if (cursorController != null && !cursorController.isActive()
- && !cursorController.isCursorBeingModified()) {
+ && !cursorController.isCursorBeingModified()
+ && showUIForFingerInput()) {
cursorController.show();
}
}
@@ -2515,6 +2521,10 @@ public class Editor {
return false;
}
+ if (!showUIForFingerInput()) {
+ return false;
+ }
+
ActionMode.Callback actionModeCallback = new TextActionModeCallback(actionMode);
mTextActionMode = mTextView.startActionMode(actionModeCallback, ActionMode.TYPE_FLOATING);
registerOnBackInvokedCallback();
@@ -2667,7 +2677,7 @@ public class Editor {
mTextView.postDelayed(mShowSuggestionRunnable,
ViewConfiguration.getDoubleTapTimeout());
} else if (hasInsertionController()) {
- if (shouldInsertCursor) {
+ if (shouldInsertCursor && showUIForFingerInput()) {
getInsertionController().show();
} else {
getInsertionController().hide();
@@ -5397,7 +5407,8 @@ public class Editor {
final PointF showPosInView = new PointF();
final boolean shouldShow = checkForTransforms() /*check not rotated and compute scale*/
&& !tooLargeTextForMagnifier()
- && obtainMagnifierShowCoordinates(event, showPosInView);
+ && obtainMagnifierShowCoordinates(event, showPosInView)
+ && showUIForFingerInput();
if (shouldShow) {
// Make the cursor visible and stop blinking.
mRenderCursorRegardlessTiming = true;
@@ -6343,6 +6354,15 @@ public class Editor {
}
}
+ /**
+ * Returns true when need to show UIs, e.g. floating toolbar, etc, for finger based interaction.
+ *
+ * @return true if UIs need to show for finger interaciton. false if UIs are not necessary.
+ */
+ public boolean showUIForFingerInput() {
+ return mLastToolType != MotionEvent.TOOL_TYPE_MOUSE;
+ }
+
/** Controller for the insertion cursor. */
@VisibleForTesting
public class InsertionPointCursorController implements CursorController {
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index a0ec48bc6beb..54a415cfa01b 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -301,7 +301,11 @@ public final class SelectionActionModeHelper {
final SelectionModifierCursorController controller = mEditor.getSelectionController();
if (controller != null
&& (mTextView.isTextSelectable() || mTextView.isTextEditable())) {
- controller.show();
+ if (mEditor.showUIForFingerInput()) {
+ controller.show();
+ } else {
+ controller.hide();
+ }
}
if (result != null) {
switch (actionMode) {
diff --git a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
index 2bef10f1aee5..b63ce1b22cdb 100644
--- a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
@@ -968,27 +968,6 @@ public final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub
});
}
- /**
- * Dispatches {@link InputConnection#requestCursorUpdates(int)}.
- *
- * <p>This method is intended to be called only from {@link InputMethodManager}.</p>
- * @param cursorUpdateMode the mode for {@link InputConnection#requestCursorUpdates(int, int)}
- * @param cursorUpdateFilter the filter for
- * {@link InputConnection#requestCursorUpdates(int, int)}
- * @param imeDisplayId displayId on which IME is displayed.
- */
- @Dispatching(cancellable = true)
- public void requestCursorUpdatesFromImm(int cursorUpdateMode, int cursorUpdateFilter,
- int imeDisplayId) {
- final int currentSessionId = mCurrentSessionId.get();
- dispatchWithTracing("requestCursorUpdatesFromImm", () -> {
- if (currentSessionId != mCurrentSessionId.get()) {
- return; // cancelled
- }
- requestCursorUpdatesInternal(cursorUpdateMode, cursorUpdateFilter, imeDisplayId);
- });
- }
-
@Dispatching(cancellable = true)
@Override
public void requestCursorUpdates(InputConnectionCommandHeader header, int cursorUpdateMode,
diff --git a/core/java/com/android/internal/policy/DecorContext.java b/core/java/com/android/internal/policy/DecorContext.java
index 5e34c15c42e2..134a91710c0b 100644
--- a/core/java/com/android/internal/policy/DecorContext.java
+++ b/core/java/com/android/internal/policy/DecorContext.java
@@ -137,4 +137,13 @@ public class DecorContext extends ContextThemeWrapper {
}
return false;
}
+
+ @Override
+ public boolean isConfigurationContext() {
+ Context context = mContext.get();
+ if (context != null) {
+ return context.isConfigurationContext();
+ }
+ return false;
+ }
}
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index b9243ece9158..9474f6fc3252 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -1,6 +1,7 @@
package com.android.internal.util;
import static android.content.Intent.ACTION_USER_SWITCHED;
+import static android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -27,7 +28,6 @@ import android.os.Parcelable;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
-import android.view.WindowManager;
import android.view.WindowManager.ScreenshotSource;
import android.view.WindowManager.ScreenshotType;
@@ -42,10 +42,15 @@ public class ScreenshotHelper {
public static final int SCREENSHOT_MSG_PROCESS_COMPLETE = 2;
/**
- * Describes a screenshot request (to make it easier to pass data through to the handler).
+ * Describes a screenshot request.
*/
public static class ScreenshotRequest implements Parcelable {
+ @ScreenshotType
+ private final int mType;
+
+ @ScreenshotSource
private final int mSource;
+
private final Bundle mBitmapBundle;
private final Rect mBoundsInScreen;
private final Insets mInsets;
@@ -53,20 +58,27 @@ public class ScreenshotHelper {
private final int mUserId;
private final ComponentName mTopComponent;
- @VisibleForTesting
- public ScreenshotRequest(int source) {
- mSource = source;
- mBitmapBundle = null;
- mBoundsInScreen = null;
- mInsets = null;
- mTaskId = -1;
- mUserId = -1;
- mTopComponent = null;
+
+ public ScreenshotRequest(@ScreenshotType int type, @ScreenshotSource int source) {
+ this(type, source, /* topComponent */ null);
}
- @VisibleForTesting
- public ScreenshotRequest(int source, Bundle bitmapBundle, Rect boundsInScreen,
- Insets insets, int taskId, int userId, ComponentName topComponent) {
+ public ScreenshotRequest(@ScreenshotType int type, @ScreenshotSource int source,
+ ComponentName topComponent) {
+ this(type,
+ source,
+ /* bitmapBundle*/ null,
+ /* boundsInScreen */ null,
+ /* insets */ null,
+ /* taskId */ -1,
+ /* userId */ -1,
+ topComponent);
+ }
+
+ public ScreenshotRequest(@ScreenshotType int type, @ScreenshotSource int source,
+ Bundle bitmapBundle, Rect boundsInScreen, Insets insets, int taskId, int userId,
+ ComponentName topComponent) {
+ mType = type;
mSource = source;
mBitmapBundle = bitmapBundle;
mBoundsInScreen = boundsInScreen;
@@ -77,6 +89,7 @@ public class ScreenshotHelper {
}
ScreenshotRequest(Parcel in) {
+ mType = in.readInt();
mSource = in.readInt();
if (in.readInt() == 1) {
mBitmapBundle = in.readBundle(getClass().getClassLoader());
@@ -96,6 +109,12 @@ public class ScreenshotHelper {
}
}
+ @ScreenshotType
+ public int getType() {
+ return mType;
+ }
+
+ @ScreenshotSource
public int getSource() {
return mSource;
}
@@ -131,6 +150,7 @@ public class ScreenshotHelper {
@Override
public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mType);
dest.writeInt(mSource);
if (mBitmapBundle == null) {
dest.writeInt(0);
@@ -208,8 +228,7 @@ public class ScreenshotHelper {
* Extracts the Bitmap added to a Bundle with {@link #hardwareBitmapToBundle(Bitmap)} .}
*
* <p>This Bitmap contains the HardwareBuffer from the original caller, be careful passing
- * this
- * Bitmap on to any other source.
+ * this Bitmap on to any other source.
*
* @param bundle containing the bitmap
* @return a hardware Bitmap
@@ -261,16 +280,16 @@ public class ScreenshotHelper {
* Added to support reducing unit test duration; the method variant without a timeout argument
* is recommended for general use.
*
- * @param screenshotType The type of screenshot, defined by {@link ScreenshotType}
+ * @param type The type of screenshot, defined by {@link ScreenshotType}
* @param source The source of the screenshot request, defined by {@link ScreenshotSource}
* @param handler used to process messages received from the screenshot service
* @param completionConsumer receives the URI of the captured screenshot, once saved or
* null if no screenshot was saved
*/
- public void takeScreenshot(@ScreenshotType int screenshotType, @ScreenshotSource int source,
+ public void takeScreenshot(@ScreenshotType int type, @ScreenshotSource int source,
@NonNull Handler handler, @Nullable Consumer<Uri> completionConsumer) {
- ScreenshotRequest screenshotRequest = new ScreenshotRequest(source);
- takeScreenshot(screenshotType, handler, screenshotRequest, SCREENSHOT_TIMEOUT_MS,
+ ScreenshotRequest screenshotRequest = new ScreenshotRequest(type, source);
+ takeScreenshot(handler, screenshotRequest, SCREENSHOT_TIMEOUT_MS,
completionConsumer);
}
@@ -280,7 +299,7 @@ public class ScreenshotHelper {
* Added to support reducing unit test duration; the method variant without a timeout argument
* is recommended for general use.
*
- * @param screenshotType The type of screenshot, defined by {@link ScreenshotType}
+ * @param type The type of screenshot, defined by {@link ScreenshotType}
* @param source The source of the screenshot request, defined by {@link ScreenshotSource}
* @param handler used to process messages received from the screenshot service
* @param timeoutMs time limit for processing, intended only for testing
@@ -288,10 +307,10 @@ public class ScreenshotHelper {
* null if no screenshot was saved
*/
@VisibleForTesting
- public void takeScreenshot(@ScreenshotType int screenshotType, @ScreenshotSource int source,
+ public void takeScreenshot(@ScreenshotType int type, @ScreenshotSource int source,
@NonNull Handler handler, long timeoutMs, @Nullable Consumer<Uri> completionConsumer) {
- ScreenshotRequest screenshotRequest = new ScreenshotRequest(source);
- takeScreenshot(screenshotType, handler, screenshotRequest, timeoutMs, completionConsumer);
+ ScreenshotRequest screenshotRequest = new ScreenshotRequest(type, source);
+ takeScreenshot(handler, screenshotRequest, timeoutMs, completionConsumer);
}
/**
@@ -312,14 +331,12 @@ public class ScreenshotHelper {
@NonNull Insets insets, int taskId, int userId, ComponentName topComponent,
@ScreenshotSource int source, @NonNull Handler handler,
@Nullable Consumer<Uri> completionConsumer) {
- ScreenshotRequest screenshotRequest = new ScreenshotRequest(source, screenshotBundle,
- boundsInScreen, insets, taskId, userId, topComponent);
- takeScreenshot(WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE, handler, screenshotRequest,
- SCREENSHOT_TIMEOUT_MS,
- completionConsumer);
+ ScreenshotRequest screenshotRequest = new ScreenshotRequest(TAKE_SCREENSHOT_PROVIDED_IMAGE,
+ source, screenshotBundle, boundsInScreen, insets, taskId, userId, topComponent);
+ takeScreenshot(handler, screenshotRequest, SCREENSHOT_TIMEOUT_MS, completionConsumer);
}
- private void takeScreenshot(@ScreenshotType int screenshotType, @NonNull Handler handler,
+ private void takeScreenshot(@NonNull Handler handler,
ScreenshotRequest screenshotRequest, long timeoutMs,
@Nullable Consumer<Uri> completionConsumer) {
synchronized (mScreenshotLock) {
@@ -337,7 +354,7 @@ public class ScreenshotHelper {
}
};
- Message msg = Message.obtain(null, screenshotType, screenshotRequest);
+ Message msg = Message.obtain(null, 0, screenshotRequest);
Handler h = new Handler(handler.getLooper()) {
@Override
diff --git a/core/res/res/layout/side_fps_toast.xml b/core/res/res/layout/side_fps_toast.xml
index 58b8cc9438ae..96860b050393 100644
--- a/core/res/res/layout/side_fps_toast.xml
+++ b/core/res/res/layout/side_fps_toast.xml
@@ -20,13 +20,14 @@
android:layout_height="wrap_content"
android:minWidth="350dp"
android:layout_gravity="center"
- android:theme="?attr/alertDialogTheme">
+ android:background="@color/side_fps_toast_background">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/fp_power_button_enrollment_title"
android:singleLine="true"
android:ellipsize="end"
+ android:textColor="@color/side_fps_text_color"
android:paddingLeft="20dp"/>
<Space
android:layout_width="wrap_content"
@@ -39,5 +40,6 @@
android:text="@string/fp_power_button_enrollment_button_text"
android:paddingRight="20dp"
style="?android:attr/buttonBarNegativeButtonStyle"
+ android:textColor="@color/side_fps_button_color"
android:maxLines="1"/>
</LinearLayout> \ No newline at end of file
diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml
index e2db49cfa657..88171ce368f9 100644
--- a/core/res/res/values-night/colors.xml
+++ b/core/res/res/values-night/colors.xml
@@ -41,4 +41,9 @@
<!-- Lily Language Picker language item view colors -->
<color name="language_picker_item_text_color">#F1F3F4</color>
<color name="language_picker_item_text_color_secondary">#BDC1C6</color>
+
+ <!-- Color for side fps toast dark theme-->
+ <color name="side_fps_toast_background">#2E3132</color>
+ <color name="side_fps_text_color">#EFF1F2</color>
+ <color name="side_fps_button_color">#33B9DB</color>
</resources>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index faf48f3c3fdc..ac083277b787 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -454,4 +454,9 @@
<!-- Lily Language Picker language item view colors -->
<color name="language_picker_item_text_color">#202124</color>
<color name="language_picker_item_text_color_secondary">#5F6368</color>
+
+ <!-- Color for side fps toast light theme -->
+ <color name="side_fps_toast_background">#F7F9FA</color>
+ <color name="side_fps_text_color">#191C1D</color>
+ <color name="side_fps_button_color">#00677E</color>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 215376aab11c..9faf5e85c31f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2004,6 +2004,9 @@
on grouped devices. -->
<bool name="config_volumeAdjustmentForRemoteGroupSessions">true</bool>
+ <!-- Flag indicating current media Output Switcher version. -->
+ <integer name="config_mediaOutputSwitchDialogVersion">1</integer>
+
<!-- Flag indicating that an outbound call must have a call capable phone account
that has declared it can process the call's handle. -->
<bool name="config_requireCallCapableAccountForHandle">false</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c45db7e7468f..51712ff5e35f 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4709,6 +4709,8 @@
<java-symbol type="bool" name="config_volumeAdjustmentForRemoteGroupSessions" />
+ <java-symbol type="integer" name="config_mediaOutputSwitchDialogVersion" />
+
<!-- List of shared library packages that should be loaded by the classloader after the
code and resources provided by applications. -->
<java-symbol type="array" name="config_sharedLibrariesLoadedAfterApp" />
diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
index 2054b4fe9a35..8cf118c4b79a 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
@@ -18,8 +18,10 @@ package android.view;
import static android.view.InsetsController.ANIMATION_TYPE_NONE;
import static android.view.InsetsController.ANIMATION_TYPE_USER;
+import static android.view.InsetsSourceConsumer.ShowResult.SHOW_IMMEDIATELY;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.statusBars;
import static junit.framework.Assert.assertEquals;
@@ -28,6 +30,7 @@ import static junit.framework.TestCase.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
@@ -75,6 +78,7 @@ public class InsetsSourceConsumerTest {
private boolean mRemoveSurfaceCalled = false;
private InsetsController mController;
private InsetsState mState;
+ private ViewRootImpl mViewRoot;
@Before
public void setup() {
@@ -86,10 +90,9 @@ public class InsetsSourceConsumerTest {
instrumentation.runOnMainSync(() -> {
final Context context = instrumentation.getTargetContext();
// cannot mock ViewRootImpl since it's final.
- final ViewRootImpl viewRootImpl = new ViewRootImpl(context,
- context.getDisplayNoVerify());
+ mViewRoot = new ViewRootImpl(context, context.getDisplayNoVerify());
try {
- viewRootImpl.setView(new TextView(context), new LayoutParams(), null);
+ mViewRoot.setView(new TextView(context), new LayoutParams(), null);
} catch (BadTokenException e) {
// activity isn't running, lets ignore BadTokenException.
}
@@ -97,7 +100,7 @@ public class InsetsSourceConsumerTest {
mSpyInsetsSource = Mockito.spy(new InsetsSource(ITYPE_STATUS_BAR));
mState.addSource(mSpyInsetsSource);
- mController = new InsetsController(new ViewRootInsetsControllerHost(viewRootImpl));
+ mController = new InsetsController(new ViewRootInsetsControllerHost(mViewRoot));
mConsumer = new InsetsSourceConsumer(ITYPE_STATUS_BAR, mState,
() -> mMockTransaction, mController) {
@Override
@@ -207,4 +210,40 @@ public class InsetsSourceConsumerTest {
});
}
+
+ @Test
+ public void testWontUpdateImeLeashVisibility_whenAnimation() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ InsetsState state = new InsetsState();
+ ViewRootInsetsControllerHost host = new ViewRootInsetsControllerHost(mViewRoot);
+ InsetsController insetsController = new InsetsController(host, (controller, type) -> {
+ if (type == ITYPE_IME) {
+ return new InsetsSourceConsumer(ITYPE_IME, state,
+ () -> mMockTransaction, controller) {
+ @Override
+ public int requestShow(boolean fromController) {
+ return SHOW_IMMEDIATELY;
+ }
+ };
+ }
+ return new InsetsSourceConsumer(type, controller.getState(), Transaction::new,
+ controller);
+ }, host.getHandler());
+ InsetsSourceConsumer imeConsumer = insetsController.getSourceConsumer(ITYPE_IME);
+
+ // Initial IME insets source control with its leash.
+ imeConsumer.setControl(new InsetsSourceControl(ITYPE_IME, mLeash,
+ false /* initialVisible */, new Point(), Insets.NONE), new int[1], new int[1]);
+ reset(mMockTransaction);
+
+ // Verify when the app requests controlling show IME animation, the IME leash
+ // visibility won't be updated when the consumer received the same leash in setControl.
+ insetsController.controlWindowInsetsAnimation(ime(), 0L,
+ null /* interpolator */, null /* cancellationSignal */, null /* listener */);
+ assertTrue(insetsController.getAnimationType(ITYPE_IME) == ANIMATION_TYPE_USER);
+ imeConsumer.setControl(new InsetsSourceControl(ITYPE_IME, mLeash,
+ true /* initialVisible */, new Point(), Insets.NONE), new int[1], new int[1]);
+ verify(mMockTransaction, never()).show(mLeash);
+ });
+ }
}
diff --git a/libs/WindowManager/Shell/res/animator/tv_pip_menu_action_button_animator.xml b/libs/WindowManager/Shell/res/animator/tv_window_menu_action_button_animator.xml
index 7475abac4695..7475abac4695 100644
--- a/libs/WindowManager/Shell/res/animator/tv_pip_menu_action_button_animator.xml
+++ b/libs/WindowManager/Shell/res/animator/tv_window_menu_action_button_animator.xml
diff --git a/libs/WindowManager/Shell/res/color/tv_pip_menu_close_icon.xml b/libs/WindowManager/Shell/res/color/tv_window_menu_close_icon.xml
index ce8640df0093..67467bbc72ae 100644
--- a/libs/WindowManager/Shell/res/color/tv_pip_menu_close_icon.xml
+++ b/libs/WindowManager/Shell/res/color/tv_window_menu_close_icon.xml
@@ -15,5 +15,5 @@
~ limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="@color/tv_pip_menu_icon_unfocused" />
+ <item android:color="@color/tv_window_menu_icon_unfocused" />
</selector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/color/tv_pip_menu_icon_bg.xml b/libs/WindowManager/Shell/res/color/tv_window_menu_close_icon_bg.xml
index 4f5e63dac5c0..4182bfeefa1b 100644
--- a/libs/WindowManager/Shell/res/color/tv_pip_menu_icon_bg.xml
+++ b/libs/WindowManager/Shell/res/color/tv_window_menu_close_icon_bg.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2021 The Android Open Source Project
+ ~ 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.
@@ -16,6 +16,6 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true"
- android:color="@color/tv_pip_menu_icon_bg_focused" />
- <item android:color="@color/tv_pip_menu_icon_bg_unfocused" />
+ android:color="@color/tv_window_menu_close_icon_bg_focused" />
+ <item android:color="@color/tv_window_menu_close_icon_bg_unfocused" />
</selector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/color/tv_pip_menu_icon.xml b/libs/WindowManager/Shell/res/color/tv_window_menu_icon.xml
index 275870450493..45205d2a7138 100644
--- a/libs/WindowManager/Shell/res/color/tv_pip_menu_icon.xml
+++ b/libs/WindowManager/Shell/res/color/tv_window_menu_icon.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2021 The Android Open Source Project
+ ~ 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.
@@ -16,8 +16,8 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true"
- android:color="@color/tv_pip_menu_icon_focused" />
+ android:color="@color/tv_window_menu_icon_focused" />
<item android:state_enabled="false"
- android:color="@color/tv_pip_menu_icon_disabled" />
- <item android:color="@color/tv_pip_menu_icon_unfocused" />
+ android:color="@color/tv_window_menu_icon_disabled" />
+ <item android:color="@color/tv_window_menu_icon_unfocused" />
</selector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/color/tv_pip_menu_close_icon_bg.xml b/libs/WindowManager/Shell/res/color/tv_window_menu_icon_bg.xml
index 6cbf66f00df7..1bd26e1d6583 100644
--- a/libs/WindowManager/Shell/res/color/tv_pip_menu_close_icon_bg.xml
+++ b/libs/WindowManager/Shell/res/color/tv_window_menu_icon_bg.xml
@@ -16,6 +16,6 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true"
- android:color="@color/tv_pip_menu_close_icon_bg_focused" />
- <item android:color="@color/tv_pip_menu_close_icon_bg_unfocused" />
+ android:color="@color/tv_window_menu_icon_bg_focused" />
+ <item android:color="@color/tv_window_menu_icon_bg_unfocused" />
</selector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/decor_minimize_button_dark.xml b/libs/WindowManager/Shell/res/drawable/decor_minimize_button_dark.xml
new file mode 100644
index 000000000000..0bcaa530dc80
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/decor_minimize_button_dark.xml
@@ -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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white" android:pathData="M6,21V19H18V21Z"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/tv_pip_button_bg.xml b/libs/WindowManager/Shell/res/drawable/tv_pip_button_bg.xml
deleted file mode 100644
index 1938f4562e97..000000000000
--- a/libs/WindowManager/Shell/res/drawable/tv_pip_button_bg.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2020 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.
--->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <corners android:radius="@dimen/pip_menu_button_radius" />
- <solid android:color="@color/tv_pip_menu_icon_bg" />
-</shape> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/tv_pip_menu_border.xml b/libs/WindowManager/Shell/res/drawable/tv_pip_menu_border.xml
index 846fdb3e8a58..7085a2c72c86 100644
--- a/libs/WindowManager/Shell/res/drawable/tv_pip_menu_border.xml
+++ b/libs/WindowManager/Shell/res/drawable/tv_pip_menu_border.xml
@@ -15,7 +15,7 @@
~ limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android"
- android:exitFadeDuration="@integer/pip_menu_fade_animation_duration">
+ android:exitFadeDuration="@integer/tv_window_menu_fade_animation_duration">
<item android:state_activated="true">
<shape android:shape="rectangle">
<corners android:radius="@dimen/pip_menu_border_corner_radius" />
diff --git a/libs/WindowManager/Shell/res/drawable/tv_window_button_bg.xml b/libs/WindowManager/Shell/res/drawable/tv_window_button_bg.xml
new file mode 100644
index 000000000000..2dba37daf059
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/tv_window_button_bg.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners android:radius="@dimen/tv_window_menu_button_radius" />
+ <solid android:color="@color/tv_window_menu_icon_bg" />
+</shape> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/caption_window_decoration.xml b/libs/WindowManager/Shell/res/layout/caption_window_decoration.xml
index a112f1933dd1..d183e42c173b 100644
--- a/libs/WindowManager/Shell/res/layout/caption_window_decoration.xml
+++ b/libs/WindowManager/Shell/res/layout/caption_window_decoration.xml
@@ -22,6 +22,17 @@
android:gravity="end"
android:background="@drawable/decor_caption_title">
<Button
+ android:id="@+id/minimize_window"
+ android:visibility="gone"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:layout_margin="5dp"
+ android:padding="4dp"
+ android:layout_gravity="top|end"
+ android:contentDescription="@string/maximize_button_text"
+ android:background="@drawable/decor_minimize_button_dark"
+ android:duplicateParentState="true"/>
+ <Button
android:id="@+id/maximize_window"
android:layout_width="32dp"
android:layout_height="32dp"
@@ -42,4 +53,3 @@
android:background="@drawable/decor_close_button_dark"
android:duplicateParentState="true"/>
</com.android.wm.shell.windowdecor.WindowDecorLinearLayout>
-
diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
index 2d50d3f1392d..8533a5994d33 100644
--- a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
@@ -64,14 +64,14 @@
android:layout_width="@dimen/pip_menu_button_wrapper_margin"
android:layout_height="@dimen/pip_menu_button_wrapper_margin"/>
- <com.android.wm.shell.pip.tv.TvPipMenuActionButton
+ <com.android.wm.shell.common.TvWindowMenuActionButton
android:id="@+id/tv_pip_menu_fullscreen_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/pip_ic_fullscreen_white"
android:text="@string/pip_fullscreen" />
- <com.android.wm.shell.pip.tv.TvPipMenuActionButton
+ <com.android.wm.shell.common.TvWindowMenuActionButton
android:id="@+id/tv_pip_menu_close_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -80,14 +80,14 @@
<!-- More TvPipMenuActionButtons may be added here at runtime. -->
- <com.android.wm.shell.pip.tv.TvPipMenuActionButton
+ <com.android.wm.shell.common.TvWindowMenuActionButton
android:id="@+id/tv_pip_menu_move_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/pip_ic_move_white"
android:text="@string/pip_move" />
- <com.android.wm.shell.pip.tv.TvPipMenuActionButton
+ <com.android.wm.shell.common.TvWindowMenuActionButton
android:id="@+id/tv_pip_menu_expand_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -145,7 +145,7 @@
android:layout_margin="@dimen/pip_menu_outer_space_frame"
android:background="@drawable/tv_pip_menu_border"/>
- <com.android.wm.shell.pip.tv.TvPipMenuActionButton
+ <com.android.wm.shell.common.TvWindowMenuActionButton
android:id="@+id/tv_pip_menu_done_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml
deleted file mode 100644
index db96d8de4094..000000000000
--- a/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2020 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.
--->
-<!-- Layout for TvPipMenuActionButton -->
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/button"
- android:layout_width="@dimen/pip_menu_button_size"
- android:layout_height="@dimen/pip_menu_button_size"
- android:padding="@dimen/pip_menu_button_margin"
- android:stateListAnimator="@animator/tv_pip_menu_action_button_animator"
- android:focusable="true">
-
- <View android:id="@+id/background"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:duplicateParentState="true"
- android:background="@drawable/tv_pip_button_bg"/>
-
- <ImageView android:id="@+id/icon"
- android:layout_width="@dimen/pip_menu_icon_size"
- android:layout_height="@dimen/pip_menu_icon_size"
- android:layout_gravity="center"
- android:duplicateParentState="true"
- android:tint="@color/tv_pip_menu_icon" />
-</FrameLayout> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/tv_window_menu_action_button.xml b/libs/WindowManager/Shell/res/layout/tv_window_menu_action_button.xml
new file mode 100644
index 000000000000..c4dbd39c729a
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/tv_window_menu_action_button.xml
@@ -0,0 +1,40 @@
+<?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.
+ -->
+<!-- Layout for TvWindowMenuActionButton -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/button"
+ android:layout_width="@dimen/tv_window_menu_button_size"
+ android:layout_height="@dimen/tv_window_menu_button_size"
+ android:padding="@dimen/tv_window_menu_button_margin"
+ android:stateListAnimator="@animator/tv_window_menu_action_button_animator"
+ android:focusable="true">
+
+ <View android:id="@+id/background"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:duplicateParentState="true"
+ android:background="@drawable/tv_window_button_bg"/>
+
+ <ImageView android:id="@+id/icon"
+ android:layout_width="@dimen/tv_window_menu_icon_size"
+ android:layout_height="@dimen/tv_window_menu_icon_size"
+ android:layout_gravity="center"
+ android:duplicateParentState="true"
+ android:tint="@color/tv_window_menu_icon" />
+</FrameLayout> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values-es/strings_tv.xml b/libs/WindowManager/Shell/res/values-es/strings_tv.xml
index 7993e03b2464..75db421ec405 100644
--- a/libs/WindowManager/Shell/res/values-es/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings_tv.xml
@@ -23,7 +23,7 @@
<string name="pip_fullscreen" msgid="7278047353591302554">"Pantalla completa"</string>
<string name="pip_move" msgid="158770205886688553">"Mover"</string>
<string name="pip_expand" msgid="1051966011679297308">"Mostrar"</string>
- <string name="pip_collapse" msgid="3903295106641385962">"Ocultar"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Contraer"</string>
<string name="pip_edu_text" msgid="3672999496647508701">" Pulsa dos veces "<annotation icon="home_icon">"INICIO"</annotation>" para ver los controles"</string>
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Menú de imagen en imagen."</string>
<string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Mover hacia la izquierda"</string>
diff --git a/libs/WindowManager/Shell/res/values-television/dimen.xml b/libs/WindowManager/Shell/res/values-television/dimen.xml
index 14e89f8b08df..376cc4faca6f 100644
--- a/libs/WindowManager/Shell/res/values-television/dimen.xml
+++ b/libs/WindowManager/Shell/res/values-television/dimen.xml
@@ -21,4 +21,7 @@
<!-- Padding between PIP and keep clear areas that caused it to move. -->
<dimen name="pip_keep_clear_area_padding">16dp</dimen>
+
+ <!-- The corner radius for PiP window. -->
+ <dimen name="pip_corner_radius">0dp</dimen>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml b/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml
index 02e726fbc3bf..b45b9ec0c457 100644
--- a/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml
+++ b/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml
@@ -15,20 +15,20 @@
limitations under the License.
-->
<resources>
- <!-- The dimensions to user for picture-in-picture action buttons. -->
- <dimen name="pip_menu_button_size">48dp</dimen>
- <dimen name="pip_menu_button_radius">20dp</dimen>
- <dimen name="pip_menu_icon_size">20dp</dimen>
- <dimen name="pip_menu_button_margin">4dp</dimen>
- <dimen name="pip_menu_button_wrapper_margin">26dp</dimen>
- <dimen name="pip_menu_border_width">4dp</dimen>
- <integer name="pip_menu_fade_animation_duration">500</integer>
+ <!-- The dimensions to use for tv window menu action buttons. -->
+ <dimen name="tv_window_menu_button_size">48dp</dimen>
+ <dimen name="tv_window_menu_button_radius">20dp</dimen>
+ <dimen name="tv_window_menu_icon_size">20dp</dimen>
+ <dimen name="tv_window_menu_button_margin">4dp</dimen>
+ <integer name="tv_window_menu_fade_animation_duration">500</integer>
<!-- The pip menu front border corner radius is 2dp smaller than
the background corner radius to hide the background from
showing through. -->
<dimen name="pip_menu_border_corner_radius">4dp</dimen>
<dimen name="pip_menu_background_corner_radius">6dp</dimen>
+ <dimen name="pip_menu_border_width">4dp</dimen>
<dimen name="pip_menu_outer_space">24dp</dimen>
+ <dimen name="pip_menu_button_wrapper_margin">26dp</dimen>
<!-- outer space minus border width -->
<dimen name="pip_menu_outer_space_frame">20dp</dimen>
diff --git a/libs/WindowManager/Shell/res/values/colors_tv.xml b/libs/WindowManager/Shell/res/values/colors_tv.xml
index fa90fe36b545..3e71c1010278 100644
--- a/libs/WindowManager/Shell/res/values/colors_tv.xml
+++ b/libs/WindowManager/Shell/res/values/colors_tv.xml
@@ -15,13 +15,15 @@
~ limitations under the License.
-->
<resources>
- <color name="tv_pip_menu_icon_focused">#0E0E0F</color>
- <color name="tv_pip_menu_icon_unfocused">#F8F9FA</color>
- <color name="tv_pip_menu_icon_disabled">#80868B</color>
- <color name="tv_pip_menu_close_icon_bg_focused">#D93025</color>
- <color name="tv_pip_menu_close_icon_bg_unfocused">#D69F261F</color>
- <color name="tv_pip_menu_icon_bg_focused">#E8EAED</color>
- <color name="tv_pip_menu_icon_bg_unfocused">#990E0E0F</color>
+ <color name="tv_window_menu_icon_focused">#0E0E0F</color>
+ <color name="tv_window_menu_icon_unfocused">#F8F9FA</color>
+
+ <color name="tv_window_menu_icon_disabled">#80868B</color>
+ <color name="tv_window_menu_close_icon_bg_focused">#D93025</color>
+ <color name="tv_window_menu_close_icon_bg_unfocused">#D69F261F</color>
+ <color name="tv_window_menu_icon_bg_focused">#E8EAED</color>
+ <color name="tv_window_menu_icon_bg_unfocused">#990E0E0F</color>
+
<color name="tv_pip_menu_focus_border">#E8EAED</color>
<color name="tv_pip_menu_background">#1E232C</color>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 2a38766f409e..679bfb9d5187 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -197,6 +197,8 @@
<!-- Freeform window caption strings -->
<!-- Accessibility text for the maximize window button [CHAR LIMIT=NONE] -->
<string name="maximize_button_text">Maximize</string>
+ <!-- Accessibility text for the minimize window button [CHAR LIMIT=NONE] -->
+ <string name="minimize_button_text">Minimize</string>
<!-- Accessibility text for the close window button [CHAR LIMIT=NONE] -->
<string name="close_button_text">Close</string>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
index d28a68a42b2b..a8764e05c3e2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
@@ -54,7 +54,10 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
/** Callback for listening task state. */
public interface Listener {
- /** Called when the container is ready for launching activities. */
+ /**
+ * Only called once when the surface has been created & the container is ready for
+ * launching activities.
+ */
default void onInitialized() {}
/** Called when the container can no longer launch activities. */
@@ -80,12 +83,13 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
private final SyncTransactionQueue mSyncQueue;
private final TaskViewTransitions mTaskViewTransitions;
- private ActivityManager.RunningTaskInfo mTaskInfo;
+ protected ActivityManager.RunningTaskInfo mTaskInfo;
private WindowContainerToken mTaskToken;
private SurfaceControl mTaskLeash;
private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
private boolean mSurfaceCreated;
private boolean mIsInitialized;
+ private boolean mNotifiedForInitialized;
private Listener mListener;
private Executor mListenerExecutor;
private Region mObscuredTouchRegion;
@@ -110,6 +114,13 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
mGuard.open("release");
}
+ /**
+ * @return {@code True} when the TaskView's surface has been created, {@code False} otherwise.
+ */
+ public boolean isInitialized() {
+ return mIsInitialized;
+ }
+
/** Until all users are converted, we may have mixed-use (eg. Car). */
private boolean isUsingShellTransitions() {
return mTaskViewTransitions != null && Transitions.ENABLE_SHELL_TRANSITIONS;
@@ -269,11 +280,17 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
resetTaskInfo();
});
mGuard.close();
- if (mListener != null && mIsInitialized) {
+ mIsInitialized = false;
+ notifyReleased();
+ }
+
+ /** Called when the {@link TaskView} has been released. */
+ protected void notifyReleased() {
+ if (mListener != null && mNotifiedForInitialized) {
mListenerExecutor.execute(() -> {
mListener.onReleased();
});
- mIsInitialized = false;
+ mNotifiedForInitialized = false;
}
}
@@ -407,12 +424,8 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
@Override
public void surfaceCreated(SurfaceHolder holder) {
mSurfaceCreated = true;
- if (mListener != null && !mIsInitialized) {
- mIsInitialized = true;
- mListenerExecutor.execute(() -> {
- mListener.onInitialized();
- });
- }
+ mIsInitialized = true;
+ notifyInitialized();
mShellExecutor.execute(() -> {
if (mTaskToken == null) {
// Nothing to update, task is not yet available
@@ -430,6 +443,16 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
});
}
+ /** Called when the {@link TaskView} is initialized. */
+ protected void notifyInitialized() {
+ if (mListener != null && !mNotifiedForInitialized) {
+ mNotifiedForInitialized = true;
+ mListenerExecutor.execute(() -> {
+ mListener.onInitialized();
+ });
+ }
+ }
+
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (mTaskToken == null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TvWindowMenuActionButton.java
index a09aab666a31..572e3335eb11 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TvWindowMenuActionButton.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.pip.tv;
+package com.android.wm.shell.common;
import android.content.Context;
import android.content.res.TypedArray;
@@ -28,33 +28,32 @@ import android.widget.RelativeLayout;
import com.android.wm.shell.R;
/**
- * A View that represents Pip Menu action button, such as "Fullscreen" and "Close" as well custom
- * (provided by the application in Pip) and media buttons.
+ * A common action button for TV window menu layouts.
*/
-public class TvPipMenuActionButton extends RelativeLayout implements View.OnClickListener {
+public class TvWindowMenuActionButton extends RelativeLayout implements View.OnClickListener {
private final ImageView mIconImageView;
private final View mButtonBackgroundView;
private final View mButtonView;
private OnClickListener mOnClickListener;
- public TvPipMenuActionButton(Context context) {
+ public TvWindowMenuActionButton(Context context) {
this(context, null, 0, 0);
}
- public TvPipMenuActionButton(Context context, AttributeSet attrs) {
+ public TvWindowMenuActionButton(Context context, AttributeSet attrs) {
this(context, attrs, 0, 0);
}
- public TvPipMenuActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
+ public TvWindowMenuActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
- public TvPipMenuActionButton(
+ public TvWindowMenuActionButton(
Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
final LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- inflater.inflate(R.layout.tv_pip_menu_action_button, this);
+ inflater.inflate(R.layout.tv_window_menu_action_button, this);
mIconImageView = findViewById(R.id.icon);
mButtonView = findViewById(R.id.button);
@@ -129,20 +128,27 @@ public class TvPipMenuActionButton extends RelativeLayout implements View.OnClic
return mButtonView.isEnabled();
}
- void setIsCustomCloseAction(boolean isCustomCloseAction) {
+ /**
+ * Marks this button as a custom close action button.
+ * This changes the style of the action button to highlight that this action finishes the
+ * Picture-in-Picture activity.
+ *
+ * @param isCustomCloseAction sets or unsets this button as a custom close action button.
+ */
+ public void setIsCustomCloseAction(boolean isCustomCloseAction) {
mIconImageView.setImageTintList(
getResources().getColorStateList(
- isCustomCloseAction ? R.color.tv_pip_menu_close_icon
- : R.color.tv_pip_menu_icon));
+ isCustomCloseAction ? R.color.tv_window_menu_close_icon
+ : R.color.tv_window_menu_icon));
mButtonBackgroundView.setBackgroundTintList(getResources()
- .getColorStateList(isCustomCloseAction ? R.color.tv_pip_menu_close_icon_bg
- : R.color.tv_pip_menu_icon_bg));
+ .getColorStateList(isCustomCloseAction ? R.color.tv_window_menu_close_icon_bg
+ : R.color.tv_window_menu_icon_bg));
}
@Override
public String toString() {
if (mButtonView.getContentDescription() == null) {
- return TvPipMenuActionButton.class.getSimpleName();
+ return TvWindowMenuActionButton.class.getSimpleName();
}
return mButtonView.getContentDescription().toString();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
index af205ed051d7..a1e9f938d280 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
@@ -92,6 +92,12 @@ public class FreeformTaskTransitionHandler
}
@Override
+ public void startMinimizedModeTransition(WindowContainerTransaction wct) {
+ final int type = WindowManager.TRANSIT_TO_BACK;
+ mPendingTransitionTokens.add(mTransitions.startTransition(type, wct, this));
+ }
+
+ @Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startT,
@NonNull SurfaceControl.Transaction finishT,
@@ -121,6 +127,8 @@ public class FreeformTaskTransitionHandler
transition, info.getType(), change, startT, finishT);
break;
case WindowManager.TRANSIT_TO_BACK:
+ transitionHandled |= startMinimizeTransition(transition);
+ break;
case WindowManager.TRANSIT_TO_FRONT:
break;
}
@@ -169,6 +177,13 @@ public class FreeformTaskTransitionHandler
return false;
}
+ private boolean startMinimizeTransition(IBinder transition) {
+ if (!mPendingTransitionTokens.contains(transition)) {
+ return false;
+ }
+ return true;
+ }
+
private boolean startChangeTransition(
IBinder transition,
int type,
@@ -243,4 +258,5 @@ public class FreeformTaskTransitionHandler
return null;
}
}
+
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarter.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarter.java
index 25eaa0e05a09..c947cf1b8cd1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarter.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarter.java
@@ -29,6 +29,15 @@ public interface FreeformTaskTransitionStarter {
*
* @param targetWindowingMode the target windowing mode
* @param wct the {@link WindowContainerTransaction} that changes the windowing mode
+ *
*/
void startWindowingModeTransition(int targetWindowingMode, WindowContainerTransaction wct);
-}
+
+ /**
+ * Starts window minimization transition
+ *
+ * @param wct the {@link WindowContainerTransaction} that changes the windowing mode
+ *
+ */
+ void startMinimizedModeTransition(WindowContainerTransaction wct);
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
index 57d3a44ed2af..4d7c8465bcc2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
@@ -56,6 +56,7 @@ import androidx.annotation.Nullable;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
+import com.android.wm.shell.common.TvWindowMenuActionButton;
import com.android.wm.shell.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
@@ -79,7 +80,7 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
private final LinearLayout mActionButtonsContainer;
private final View mMenuFrameView;
- private final List<TvPipMenuActionButton> mAdditionalButtons = new ArrayList<>();
+ private final List<TvWindowMenuActionButton> mAdditionalButtons = new ArrayList<>();
private final View mPipFrameView;
private final View mPipView;
private final TextView mEduTextView;
@@ -94,7 +95,7 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
private final ImageView mArrowRight;
private final ImageView mArrowDown;
private final ImageView mArrowLeft;
- private final TvPipMenuActionButton mA11yDoneButton;
+ private final TvWindowMenuActionButton mA11yDoneButton;
private final ScrollView mScrollView;
private final HorizontalScrollView mHorizontalScrollView;
@@ -104,8 +105,8 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
private boolean mMoveMenuIsVisible;
private boolean mButtonMenuIsVisible;
- private final TvPipMenuActionButton mExpandButton;
- private final TvPipMenuActionButton mCloseButton;
+ private final TvWindowMenuActionButton mExpandButton;
+ private final TvWindowMenuActionButton mCloseButton;
private boolean mSwitchingOrientation;
@@ -166,7 +167,7 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
mResizeAnimationDuration = context.getResources().getInteger(
R.integer.config_pipResizeAnimationDuration);
mPipMenuFadeAnimationDuration = context.getResources()
- .getInteger(R.integer.pip_menu_fade_animation_duration);
+ .getInteger(R.integer.tv_window_menu_fade_animation_duration);
mPipMenuOuterSpace = context.getResources()
.getDimensionPixelSize(R.dimen.pip_menu_outer_space);
@@ -568,7 +569,7 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
if (actionsNumber > buttonsNumber) {
// Add buttons until we have enough to display all the actions.
while (actionsNumber > buttonsNumber) {
- TvPipMenuActionButton button = new TvPipMenuActionButton(mContext);
+ TvWindowMenuActionButton button = new TvWindowMenuActionButton(mContext);
button.setOnClickListener(this);
mActionButtonsContainer.addView(button,
@@ -591,7 +592,7 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
// "Assign" actions to the buttons.
for (int index = 0; index < actionsNumber; index++) {
final RemoteAction action = actions.get(index);
- final TvPipMenuActionButton button = mAdditionalButtons.get(index);
+ final TvWindowMenuActionButton button = mAdditionalButtons.get(index);
// Remove action if it matches the custom close action.
if (PipUtils.remoteActionsMatch(action, closeAction)) {
@@ -607,7 +608,7 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
}
}
- private void setActionForButton(RemoteAction action, TvPipMenuActionButton button,
+ private void setActionForButton(RemoteAction action, TvWindowMenuActionButton button,
Handler mainHandler) {
button.setVisibility(View.VISIBLE); // Ensure the button is visible.
if (action.getContentDescription().length() > 0) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index b70bde3a64ee..7b498e4f54ec 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -246,7 +246,7 @@ public class TaskSnapshotWindow {
window.setOuter(snapshotSurface);
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "TaskSnapshot#relayout");
- session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0,
+ session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0, 0, 0,
tmpFrames, tmpMergedConfiguration, surfaceControl, tmpInsetsState,
tmpControls, new Bundle());
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 279d57a23e6e..9335438cea50 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -119,6 +119,8 @@ public class Transitions implements RemoteCallable<Transitions> {
/** List of possible handlers. Ordered by specificity (eg. tapped back to front). */
private final ArrayList<TransitionHandler> mHandlers = new ArrayList<>();
+ private final ArrayList<TransitionObserver> mObservers = new ArrayList<>();
+
/** List of {@link Runnable} instances to run when the last active transition has finished. */
private final ArrayList<Runnable> mRunWhenIdleQueue = new ArrayList<>();
@@ -242,6 +244,16 @@ public class Transitions implements RemoteCallable<Transitions> {
mRemoteTransitionHandler.removeFiltered(remoteTransition);
}
+ /** Registers an observer on the lifecycle of transitions. */
+ public void registerObserver(@NonNull TransitionObserver observer) {
+ mObservers.add(observer);
+ }
+
+ /** Unregisters the observer. */
+ public void unregisterObserver(@NonNull TransitionObserver observer) {
+ mObservers.remove(observer);
+ }
+
/** Boosts the process priority of remote animation player. */
public static void setRunningRemoteTransitionDelegate(IApplicationThread appThread) {
if (appThread == null) return;
@@ -407,6 +419,11 @@ public class Transitions implements RemoteCallable<Transitions> {
+ Arrays.toString(mActiveTransitions.stream().map(
activeTransition -> activeTransition.mToken).toArray()));
}
+
+ for (int i = 0; i < mObservers.size(); ++i) {
+ mObservers.get(i).onTransitionReady(transitionToken, info, t, finishT);
+ }
+
if (!info.getRootLeash().isValid()) {
// Invalid root-leash implies that the transition is empty/no-op, so just do
// housekeeping and return.
@@ -474,6 +491,10 @@ public class Transitions implements RemoteCallable<Transitions> {
}
private void playTransition(@NonNull ActiveTransition active) {
+ for (int i = 0; i < mObservers.size(); ++i) {
+ mObservers.get(i).onTransitionStarting(active.mToken);
+ }
+
setupAnimHierarchy(active.mInfo, active.mStartT, active.mFinishT);
// If a handler already chose to run this animation, try delegating to it first.
@@ -546,6 +567,10 @@ public class Transitions implements RemoteCallable<Transitions> {
active.mHandler.onTransitionConsumed(
active.mToken, abort, abort ? null : active.mFinishT);
}
+ for (int i = 0; i < mObservers.size(); ++i) {
+ mObservers.get(i).onTransitionMerged(
+ active.mToken, mActiveTransitions.get(0).mToken);
+ }
return;
}
final ActiveTransition active = mActiveTransitions.get(activeIdx);
@@ -555,6 +580,9 @@ public class Transitions implements RemoteCallable<Transitions> {
active.mHandler.onTransitionConsumed(
transition, true /* aborted */, null /* finishTransaction */);
}
+ for (int i = 0; i < mObservers.size(); ++i) {
+ mObservers.get(i).onTransitionFinished(active.mToken, active.mAborted);
+ }
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
"Transition animation finished (abort=%b), notifying core %s", abort, transition);
// Merge all relevant transactions together
@@ -593,6 +621,9 @@ public class Transitions implements RemoteCallable<Transitions> {
transition, true /* aborted */, null /* finishTransaction */);
}
mOrganizer.finishTransition(aborted.mToken, null /* wct */, null /* wctCB */);
+ for (int i = 0; i < mObservers.size(); ++i) {
+ mObservers.get(i).onTransitionFinished(active.mToken, true);
+ }
}
if (mActiveTransitions.size() <= activeIdx) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "All active transition animations "
@@ -792,6 +823,52 @@ public class Transitions implements RemoteCallable<Transitions> {
default void setAnimScaleSetting(float scale) {}
}
+ /**
+ * Interface for something that needs to know the lifecycle of some transitions, but never
+ * handles any transition by itself.
+ */
+ public interface TransitionObserver {
+ /**
+ * Called when the transition is ready to play. It may later be merged into other
+ * transitions. Note this doesn't mean this transition will be played anytime soon.
+ *
+ * @param transition the unique token of this transition
+ * @param startTransaction the transaction given to the handler to be applied before the
+ * transition animation. This will be applied when the transition
+ * handler that handles this transition starts the transition.
+ * @param finishTransaction the transaction given to the handler to be applied after the
+ * transition animation. The Transition system will apply it when
+ * finishCallback is called by the transition handler.
+ */
+ void onTransitionReady(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction);
+
+ /**
+ * Called when the transition is starting to play. It isn't called for merged transitions.
+ *
+ * @param transition the unique token of this transition
+ */
+ void onTransitionStarting(@NonNull IBinder transition);
+
+ /**
+ * Called when a transition is merged into another transition. There won't be any following
+ * lifecycle calls for the merged transition.
+ *
+ * @param merged the unique token of the transition that's merged to another one
+ * @param playing the unique token of the transition that accepts the merge
+ */
+ void onTransitionMerged(@NonNull IBinder merged, @NonNull IBinder playing);
+
+ /**
+ * Called when the transition is finished. This isn't called for merged transitions.
+ *
+ * @param transition the unique token of this transition
+ * @param aborted {@code true} if this transition is aborted; {@code false} otherwise.
+ */
+ void onTransitionFinished(@NonNull IBinder transition, boolean aborted);
+ }
+
@BinderThread
private class TransitionPlayerImpl extends ITransitionPlayer.Stub {
@Override
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 08d6c50f94b4..e7695926d244 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
@@ -51,7 +51,6 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel<Caption
private final Choreographer mMainChoreographer;
private final DisplayController mDisplayController;
private final SyncTransactionQueue mSyncQueue;
-
private FreeformTaskTransitionStarter mTransitionStarter;
public CaptionWindowDecorViewModel(
@@ -168,6 +167,14 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel<Caption
} else {
mSyncQueue.queue(wct);
}
+ } else if (id == R.id.minimize_window) {
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.reorder(mTaskToken, false);
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ mTransitionStarter.startMinimizedModeTransition(wct);
+ } else {
+ mSyncQueue.queue(wct);
+ }
}
}
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 dc212fc2ab4d..98b5ee9f0cfb 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
@@ -161,12 +161,13 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
*/
private void setupRootView() {
View caption = mResult.mRootView.findViewById(R.id.caption);
-
caption.setOnTouchListener(mOnCaptionTouchListener);
View maximize = caption.findViewById(R.id.maximize_window);
maximize.setOnClickListener(mOnCaptionButtonClickListener);
View close = caption.findViewById(R.id.close_window);
close.setOnClickListener(mOnCaptionButtonClickListener);
+ View minimize = caption.findViewById(R.id.minimize_window);
+ minimize.setOnClickListener(mOnCaptionButtonClickListener);
}
void setCaptionColor(int captionColor) {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
index 1c587a2078cf..0a54b8c016fb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
@@ -49,7 +49,10 @@ abstract class BaseTest @JvmOverloads constructor(
) {
init {
testSpec.setIsTablet(
- WindowManagerStateHelper(instrumentation).currentState.wmState.isTablet
+ WindowManagerStateHelper(
+ instrumentation,
+ clearCacheAfterParsing = false
+ ).currentState.wmState.isTablet
)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index 1e4d23c924e7..330c9c95e484 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -20,6 +20,7 @@ package com.android.wm.shell.flicker
import android.view.Surface
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.traces.layers.LayersTraceSubject
import com.android.server.wm.traces.common.IComponentMatcher
import com.android.server.wm.traces.common.region.Region
@@ -83,23 +84,26 @@ fun FlickerTestParameter.layerIsVisibleAtEnd(
}
}
+fun FlickerTestParameter.layerKeepVisible(
+ component: IComponentMatcher
+) {
+ assertLayers {
+ this.isVisible(component)
+ }
+}
+
fun FlickerTestParameter.splitAppLayerBoundsBecomesVisible(
component: IComponentMatcher,
splitLeftTop: Boolean
) {
assertLayers {
- val dividerRegion = this.last().layer(SPLIT_SCREEN_DIVIDER_COMPONENT).visibleRegion.region
this.isInvisible(component)
.then()
- .invoke("splitAppLayerBoundsBecomesVisible") {
- it.visibleRegion(component).coversAtMost(
- if (splitLeftTop) {
- getSplitLeftTopRegion(dividerRegion, endRotation)
- } else {
- getSplitRightBottomRegion(dividerRegion, endRotation)
- }
- )
- }
+ .isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
+ .isVisible(component)
+ .then()
+ .isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
+ .splitAppLayerBoundsSnapToDivider(component, splitLeftTop, endRotation)
}
}
@@ -108,16 +112,7 @@ fun FlickerTestParameter.splitAppLayerBoundsBecomesInvisible(
splitLeftTop: Boolean
) {
assertLayers {
- val dividerRegion = this.first().layer(SPLIT_SCREEN_DIVIDER_COMPONENT).visibleRegion.region
- this.invoke("splitAppLayerBoundsBecomesVisible") {
- it.visibleRegion(component).coversAtMost(
- if (splitLeftTop) {
- getSplitLeftTopRegion(dividerRegion, endRotation)
- } else {
- getSplitRightBottomRegion(dividerRegion, endRotation)
- }
- )
- }
+ this.splitAppLayerBoundsSnapToDivider(component, splitLeftTop, endRotation)
.then()
.isVisible(component, true)
.then()
@@ -141,6 +136,49 @@ fun FlickerTestParameter.splitAppLayerBoundsIsVisibleAtEnd(
}
}
+fun FlickerTestParameter.splitAppLayerBoundsKeepVisible(
+ component: IComponentMatcher,
+ splitLeftTop: Boolean
+) {
+ assertLayers {
+ this.splitAppLayerBoundsSnapToDivider(component, splitLeftTop, endRotation)
+ }
+}
+
+fun FlickerTestParameter.splitAppLayerBoundsChanges(
+ component: IComponentMatcher,
+ splitLeftTop: Boolean
+) {
+ assertLayers {
+ if (splitLeftTop) {
+ this.splitAppLayerBoundsSnapToDivider(component, splitLeftTop, endRotation)
+ .then()
+ .isInvisible(component)
+ .then()
+ .splitAppLayerBoundsSnapToDivider(component, splitLeftTop, endRotation)
+ } else {
+ this.splitAppLayerBoundsSnapToDivider(component, splitLeftTop, endRotation)
+ }
+ }
+}
+
+fun LayersTraceSubject.splitAppLayerBoundsSnapToDivider(
+ component: IComponentMatcher,
+ splitLeftTop: Boolean,
+ rotation: Int
+): LayersTraceSubject {
+ return invoke("splitAppLayerBoundsSnapToDivider") {
+ val dividerRegion = it.layer(SPLIT_SCREEN_DIVIDER_COMPONENT).visibleRegion.region
+ it.visibleRegion(component).coversAtMost(
+ if (splitLeftTop) {
+ getSplitLeftTopRegion(dividerRegion, rotation)
+ } else {
+ getSplitRightBottomRegion(dividerRegion, rotation)
+ }
+ )
+ }
+}
+
fun FlickerTestParameter.appWindowBecomesVisible(
component: IComponentMatcher
) {
@@ -169,6 +207,14 @@ fun FlickerTestParameter.appWindowIsVisibleAtEnd(
}
}
+fun FlickerTestParameter.appWindowKeepVisible(
+ component: IComponentMatcher
+) {
+ assertWm {
+ this.isAppWindowVisible(component)
+ }
+}
+
fun FlickerTestParameter.dockedStackDividerIsVisibleAtEnd() {
assertLayersEnd {
this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
index 3708e5f65485..8b717a0cb75e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
@@ -23,3 +23,4 @@ const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui"
val APP_PAIR_SPLIT_DIVIDER_COMPONENT = ComponentMatcher("", "AppPairSplitDivider#")
val DOCKED_STACK_DIVIDER_COMPONENT = ComponentMatcher("", "DockedStackDivider#")
val SPLIT_SCREEN_DIVIDER_COMPONENT = ComponentMatcher("", "StageCoordinatorSplitDivider#")
+val SPLIT_DECOR_MANAGER = ComponentMatcher("", "SplitDecorManager#")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
index 4877442bacf7..a1226e682e05 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
@@ -30,6 +30,7 @@ import com.android.launcher3.tapl.LauncherInstrumentation
import com.android.server.wm.traces.common.IComponentMatcher
import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.wm.shell.flicker.SPLIT_DECOR_MANAGER
import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME
import com.android.wm.shell.flicker.testapp.Components
@@ -46,6 +47,7 @@ class SplitScreenHelper(
const val NOTIFICATION_SCROLLER = "notification_stack_scroller"
const val DIVIDER_BAR = "docked_divider_handle"
const val GESTURE_STEP_MS = 16L
+ const val LONG_PRESS_TIME_MS = 100L
private val notificationScrollerSelector: BySelector
get() = By.res(SYSTEM_UI_PACKAGE_NAME, NOTIFICATION_SCROLLER)
@@ -82,6 +84,13 @@ class SplitScreenHelper(
Components.SendNotificationActivity.COMPONENT.toFlickerComponent()
)
+ fun getIme(instrumentation: Instrumentation): SplitScreenHelper =
+ SplitScreenHelper(
+ instrumentation,
+ Components.ImeActivity.LABEL,
+ Components.ImeActivity.COMPONENT.toFlickerComponent()
+ )
+
fun waitForSplitComplete(
wmHelper: WindowManagerStateHelper,
primaryApp: IComponentMatcher,
@@ -206,6 +215,16 @@ class SplitScreenHelper(
}
}
+ fun longPress(
+ instrumentation: Instrumentation,
+ point: Point
+ ) {
+ val downTime = SystemClock.uptimeMillis()
+ touch(instrumentation, MotionEvent.ACTION_DOWN, downTime, downTime, TIMEOUT_MS, point)
+ SystemClock.sleep(LONG_PRESS_TIME_MS)
+ touch(instrumentation, MotionEvent.ACTION_UP, downTime, downTime, TIMEOUT_MS, point)
+ }
+
fun createShortcutOnHotseatIfNotExist(
tapl: LauncherInstrumentation,
appName: String
@@ -220,6 +239,23 @@ class SplitScreenHelper(
}
}
+ fun dragDividerToResizeAndWait(
+ device: UiDevice,
+ wmHelper: WindowManagerStateHelper
+ ) {
+ val displayBounds = wmHelper.currentState.layerState
+ .displays.firstOrNull { !it.isVirtual }
+ ?.layerStackSpace
+ ?: error("Display not found")
+ val dividerBar = device.wait(Until.findObject(dividerBarSelector), TIMEOUT_MS)
+ dividerBar.drag(Point(displayBounds.width * 2 / 3, displayBounds.height * 2 / 3))
+
+ wmHelper.StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withWindowSurfaceDisappeared(SPLIT_DECOR_MANAGER)
+ .waitForAndVerify()
+ }
+
fun dragDividerToDismissSplit(
device: UiDevice,
wmHelper: WindowManagerStateHelper
@@ -240,5 +276,33 @@ class SplitScreenHelper(
SystemClock.sleep(interval.toLong())
dividerBar.click()
}
+
+ fun copyContentFromLeftToRight(
+ instrumentation: Instrumentation,
+ device: UiDevice,
+ sourceApp: IComponentMatcher,
+ destinationApp: IComponentMatcher,
+ ) {
+ // Copy text from sourceApp
+ val textView = device.wait(Until.findObject(
+ By.res(sourceApp.packageNames.firstOrNull(), "SplitScreenTest")), TIMEOUT_MS)
+ longPress(instrumentation, textView.getVisibleCenter())
+
+ val copyBtn = device.wait(Until.findObject(By.text("Copy")), TIMEOUT_MS)
+ copyBtn.click()
+
+ // Paste text to destinationApp
+ val editText = device.wait(Until.findObject(
+ By.res(destinationApp.packageNames.firstOrNull(), "plain_text_input")), TIMEOUT_MS)
+ longPress(instrumentation, editText.getVisibleCenter())
+
+ val pasteBtn = device.wait(Until.findObject(By.text("Paste")), TIMEOUT_MS)
+ pasteBtn.click()
+
+ // Verify text
+ if (!textView.getText().contentEquals(editText.getText())) {
+ error("Fail to copy content in split")
+ }
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
new file mode 100644
index 000000000000..f69107eae638
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.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.wm.shell.flicker.splitscreen
+
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
+import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.appWindowKeepVisible
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import com.android.wm.shell.flicker.layerKeepVisible
+import com.android.wm.shell.flicker.splitAppLayerBoundsKeepVisible
+import org.junit.Assume
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test copy content from the left to the right side of the split-screen.
+ *
+ * To run this test: `atest WMShellFlickerTests:CopyContentInSplit`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+class CopyContentInSplit(testSpec: FlickerTestParameter) : SplitScreenBase(testSpec) {
+ protected val textEditApp = SplitScreenHelper.getIme(instrumentation)
+
+ // TODO(b/231399940): Remove this once we can use recent shortcut to enter split.
+ @Before
+ open fun before() {
+ Assume.assumeTrue(tapl.isTablet)
+ }
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
+ setup {
+ eachRun {
+ textEditApp.launchViaIntent(wmHelper)
+ // TODO(b/231399940): Use recent shortcut to enter split.
+ tapl.launchedAppState.taskbar
+ .openAllApps()
+ .getAppIcon(primaryApp.appName)
+ .dragToSplitscreen(primaryApp.`package`, textEditApp.`package`)
+ SplitScreenHelper.waitForSplitComplete(wmHelper, textEditApp, primaryApp)
+ }
+ }
+ transitions {
+ SplitScreenHelper.copyContentFromLeftToRight(
+ instrumentation, device, primaryApp, textEditApp)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun splitScreenDividerKeepVisible() = testSpec.layerKeepVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
+
+ @Presubmit
+ @Test
+ fun primaryAppLayerKeepVisible() = testSpec.layerKeepVisible(primaryApp)
+
+ @Presubmit
+ @Test
+ fun textEditAppLayerKeepVisible() = testSpec.layerKeepVisible(textEditApp)
+
+ @Presubmit
+ @Test
+ fun primaryAppBoundsKeepVisible() = testSpec.splitAppLayerBoundsKeepVisible(
+ primaryApp, splitLeftTop = true)
+
+ @Presubmit
+ @Test
+ fun textEditAppBoundsKeepVisible() = testSpec.splitAppLayerBoundsKeepVisible(
+ textEditApp, splitLeftTop = false)
+
+ @Presubmit
+ @Test
+ fun primaryAppWindowKeepVisible() = testSpec.appWindowKeepVisible(primaryApp)
+
+ @Presubmit
+ @Test
+ fun textEditAppWindowKeepVisible() = testSpec.appWindowKeepVisible(textEditApp)
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun entireScreenCovered() =
+ super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() =
+ super.navBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() =
+ super.navBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarWindowIsAlwaysVisible() =
+ super.navBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() =
+ super.statusBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() =
+ super.statusBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() =
+ super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() =
+ super.taskBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
+ repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
+ supportedNavigationModes =
+ listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY))
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
new file mode 100644
index 000000000000..0f4d98d69c00
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
@@ -0,0 +1,196 @@
+/*
+ * 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.flicker.splitscreen
+
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
+import android.view.Surface
+import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.appWindowKeepVisible
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import com.android.wm.shell.flicker.layerKeepVisible
+import com.android.wm.shell.flicker.splitAppLayerBoundsChanges
+import org.junit.Assume
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test resize split by dragging the divider bar.
+ *
+ * To run this test: `atest WMShellFlickerTests:DragDividerToResize`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+class DragDividerToResize (testSpec: FlickerTestParameter) : SplitScreenBase(testSpec) {
+
+ // TODO(b/231399940): Remove this once we can use recent shortcut to enter split.
+ @Before
+ open fun before() {
+ Assume.assumeTrue(tapl.isTablet)
+ }
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
+ setup {
+ eachRun {
+ tapl.goHome()
+ primaryApp.launchViaIntent(wmHelper)
+ // TODO(b/231399940): Use recent shortcut to enter split.
+ tapl.launchedAppState.taskbar
+ .openAllApps()
+ .getAppIcon(secondaryApp.appName)
+ .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+ SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+ }
+ transitions {
+ SplitScreenHelper.dragDividerToResizeAndWait(device, wmHelper)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun splitScreenDividerKeepVisible() = testSpec.layerKeepVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
+
+ @Presubmit
+ @Test
+ fun primaryAppLayerKeepVisible() = testSpec.layerKeepVisible(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppLayerVisibilityChanges() {
+ testSpec.assertLayers {
+ this.isVisible(secondaryApp)
+ .then()
+ .isInvisible(secondaryApp)
+ .then()
+ .isVisible(secondaryApp)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun primaryAppWindowKeepVisible() = testSpec.appWindowKeepVisible(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppWindowKeepVisible() = testSpec.appWindowKeepVisible(secondaryApp)
+
+ @Presubmit
+ @Test
+ fun primaryAppBoundsChanges() = testSpec.splitAppLayerBoundsChanges(
+ primaryApp, splitLeftTop = false)
+
+ @Presubmit
+ @Test
+ fun secondaryAppBoundsChanges() = testSpec.splitAppLayerBoundsChanges(
+ secondaryApp, splitLeftTop = true)
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun entireScreenCovered() =
+ super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() =
+ super.navBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() =
+ super.navBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarWindowIsAlwaysVisible() =
+ super.navBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() =
+ super.statusBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() =
+ super.statusBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() =
+ super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() =
+ super.taskBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
+ repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ supportedRotations = listOf(Surface.ROTATION_0),
+ // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
+ supportedNavigationModes =
+ listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY))
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
index 38279a3dfd17..bdfd9c7de32f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
@@ -29,6 +29,7 @@ import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import com.android.wm.shell.flicker.layerIsVisibleAtEnd
+import com.android.wm.shell.flicker.layerKeepVisible
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import org.junit.Assume
import org.junit.Before
@@ -80,11 +81,7 @@ class SwitchAppByDoubleTapDivider (testSpec: FlickerTestParameter) : SplitScreen
@Presubmit
@Test
- fun splitScreenDividerKeepVisible() {
- testSpec.assertLayers {
- this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
- }
- }
+ fun splitScreenDividerKeepVisible() = testSpec.layerKeepVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
new file mode 100644
index 000000000000..da954d97aec2
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
@@ -0,0 +1,193 @@
+/*
+ * 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.flicker.splitscreen
+
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
+import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.wm.shell.flicker.appWindowBecomesVisible
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import com.android.wm.shell.flicker.layerBecomesVisible
+import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
+import org.junit.Assume
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test quick switch to split pair from another app.
+ *
+ * To run this test: `atest WMShellFlickerTests:SwitchBackToSplitFromAnotherApp`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+class SwitchBackToSplitFromAnotherApp(testSpec: FlickerTestParameter) : SplitScreenBase(testSpec) {
+ val thirdApp = SplitScreenHelper.getNonResizeable(instrumentation)
+
+ // TODO(b/231399940): Remove this once we can use recent shortcut to enter split.
+ @Before
+ open fun before() {
+ Assume.assumeTrue(tapl.isTablet)
+ }
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
+ setup {
+ eachRun {
+ primaryApp.launchViaIntent(wmHelper)
+ // TODO(b/231399940): Use recent shortcut to enter split.
+ tapl.launchedAppState.taskbar
+ .openAllApps()
+ .getAppIcon(secondaryApp.appName)
+ .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+ SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+
+ thirdApp.launchViaIntent(wmHelper)
+ wmHelper.StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withWindowSurfaceAppeared(thirdApp)
+ .waitForAndVerify()
+ }
+ }
+ transitions {
+ tapl.launchedAppState.quickSwitchToPreviousApp()
+ SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun splitScreenDividerBecomesVisible() = testSpec.splitScreenDividerBecomesVisible()
+
+ @Presubmit
+ @Test
+ fun primaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(secondaryApp)
+
+ @Presubmit
+ @Test
+ fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
+ primaryApp, splitLeftTop = false)
+
+ @Presubmit
+ @Test
+ fun secondaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
+ secondaryApp, splitLeftTop = true)
+
+ @Presubmit
+ @Test
+ fun primaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp)
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun entireScreenCovered() =
+ super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() =
+ super.navBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() =
+ super.navBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarWindowIsAlwaysVisible() =
+ super.navBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() =
+ super.statusBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() =
+ super.statusBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() =
+ super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() =
+ super.taskBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
+ repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
+ supportedNavigationModes =
+ listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY))
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
index c48f3f77858b..db89ff52178b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
@@ -26,11 +26,8 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.wm.shell.flicker.appWindowBecomesVisible
-import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import com.android.wm.shell.flicker.layerBecomesVisible
-import com.android.wm.shell.flicker.layerIsVisibleAtEnd
-import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisible
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
import org.junit.Assume
@@ -42,7 +39,7 @@ import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
- * Test switch back to split pair after go home
+ * Test quick switch to split pair from home.
*
* To run this test: `atest WMShellFlickerTests:SwitchBackToSplitFromHome`
*/
@@ -60,30 +57,30 @@ class SwitchBackToSplitFromHome(testSpec: FlickerTestParameter) : SplitScreenBas
}
override val transition: FlickerBuilder.() -> Unit
- get() = {
- super.transition(this)
- setup {
- eachRun {
- primaryApp.launchViaIntent(wmHelper)
- // TODO(b/231399940): Use recent shortcut to enter split.
- tapl.launchedAppState.taskbar
- .openAllApps()
- .getAppIcon(secondaryApp.appName)
- .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+ get() = {
+ super.transition(this)
+ setup {
+ eachRun {
+ primaryApp.launchViaIntent(wmHelper)
+ // TODO(b/231399940): Use recent shortcut to enter split.
+ tapl.launchedAppState.taskbar
+ .openAllApps()
+ .getAppIcon(secondaryApp.appName)
+ .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+ SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+
+ tapl.goHome()
+ wmHelper.StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withHomeActivityVisible()
+ .waitForAndVerify()
+ }
+ }
+ transitions {
+ tapl.workspace.quickSwitchToPreviousApp()
SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
-
- tapl.goHome()
- wmHelper.StateSyncBuilder()
- .withAppTransitionIdle()
- .withHomeActivityVisible()
- .waitForAndVerify()
}
}
- transitions {
- tapl.workspace.quickSwitchToPreviousApp()
- SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
- }
- }
@Presubmit
@Test
@@ -91,7 +88,7 @@ class SwitchBackToSplitFromHome(testSpec: FlickerTestParameter) : SplitScreenBas
@Presubmit
@Test
- fun primaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(primaryApp)
+ fun primaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(primaryApp)
@Presubmit
@Test
@@ -104,12 +101,12 @@ class SwitchBackToSplitFromHome(testSpec: FlickerTestParameter) : SplitScreenBas
@Presubmit
@Test
- fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisible(
+ fun secondaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
secondaryApp, splitLeftTop = true)
@Presubmit
@Test
- fun primaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(primaryApp)
+ fun primaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(primaryApp)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
new file mode 100644
index 000000000000..c23cdb610671
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
@@ -0,0 +1,194 @@
+/*
+ * 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.flicker.splitscreen
+
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
+import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.wm.shell.flicker.appWindowBecomesVisible
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import com.android.wm.shell.flicker.layerBecomesVisible
+import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
+import org.junit.Assume
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test switch back to split pair from recent.
+ *
+ * To run this test: `atest WMShellFlickerTests:SwitchBackToSplitFromRecent`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+class SwitchBackToSplitFromRecent(testSpec: FlickerTestParameter) : SplitScreenBase(testSpec) {
+
+ // TODO(b/231399940): Remove this once we can use recent shortcut to enter split.
+ @Before
+ open fun before() {
+ Assume.assumeTrue(tapl.isTablet)
+ }
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
+ setup {
+ eachRun {
+ primaryApp.launchViaIntent(wmHelper)
+ // TODO(b/231399940): Use recent shortcut to enter split.
+ tapl.launchedAppState.taskbar
+ .openAllApps()
+ .getAppIcon(secondaryApp.appName)
+ .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+ SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+
+ tapl.goHome()
+ wmHelper.StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withHomeActivityVisible()
+ .waitForAndVerify()
+ }
+ }
+ transitions {
+ tapl.workspace.switchToOverview()
+ .currentTask
+ .open()
+ SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun splitScreenDividerBecomesVisible() = testSpec.splitScreenDividerBecomesVisible()
+
+ @Presubmit
+ @Test
+ fun primaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(secondaryApp)
+
+ @Presubmit
+ @Test
+ fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
+ primaryApp, splitLeftTop = false)
+
+ @Presubmit
+ @Test
+ fun secondaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
+ secondaryApp, splitLeftTop = true)
+
+ @Presubmit
+ @Test
+ fun primaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp)
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun entireScreenCovered() =
+ super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() =
+ super.navBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() =
+ super.navBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarWindowIsAlwaysVisible() =
+ super.navBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() =
+ super.statusBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() =
+ super.statusBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() =
+ super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() =
+ super.taskBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
+ repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
+ supportedNavigationModes =
+ listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY))
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_splitscreen.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_splitscreen.xml
index 84789f5a6c02..642a08b5bbe0 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_splitscreen.xml
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_splitscreen.xml
@@ -26,6 +26,7 @@
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_vertical|center_horizontal"
+ android:textIsSelectable="true"
android:text="PrimaryActivity"
android:textAppearance="?android:attr/textAppearanceLarge"/>
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
index 32f1587752cb..ff1d2990a82a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
@@ -169,6 +169,7 @@ public class TaskViewTest extends ShellTestCase {
mTaskView.onTaskAppeared(mTaskInfo, mLeash);
verify(mViewListener).onTaskCreated(eq(mTaskInfo.taskId), any());
+ assertThat(mTaskView.isInitialized()).isTrue();
verify(mViewListener, never()).onTaskVisibilityChanged(anyInt(), anyBoolean());
}
@@ -178,6 +179,7 @@ public class TaskViewTest extends ShellTestCase {
mTaskView.surfaceCreated(mock(SurfaceHolder.class));
verify(mViewListener).onInitialized();
+ assertThat(mTaskView.isInitialized()).isTrue();
// No task, no visibility change
verify(mViewListener, never()).onTaskVisibilityChanged(anyInt(), anyBoolean());
}
@@ -189,6 +191,7 @@ public class TaskViewTest extends ShellTestCase {
mTaskView.surfaceCreated(mock(SurfaceHolder.class));
verify(mViewListener).onInitialized();
+ assertThat(mTaskView.isInitialized()).isTrue();
verify(mViewListener).onTaskVisibilityChanged(eq(mTaskInfo.taskId), eq(true));
}
@@ -223,6 +226,7 @@ public class TaskViewTest extends ShellTestCase {
verify(mOrganizer).removeListener(eq(mTaskView));
verify(mViewListener).onReleased();
+ assertThat(mTaskView.isInitialized()).isFalse();
}
@Test
@@ -270,6 +274,7 @@ public class TaskViewTest extends ShellTestCase {
verify(mViewListener).onTaskCreated(eq(mTaskInfo.taskId), any());
verify(mViewListener, never()).onInitialized();
+ assertThat(mTaskView.isInitialized()).isFalse();
// If there's no surface the task should be made invisible
verify(mViewListener).onTaskVisibilityChanged(eq(mTaskInfo.taskId), eq(false));
}
@@ -281,6 +286,7 @@ public class TaskViewTest extends ShellTestCase {
verify(mTaskViewTransitions, never()).setTaskViewVisible(any(), anyBoolean());
verify(mViewListener).onInitialized();
+ assertThat(mTaskView.isInitialized()).isTrue();
// No task, no visibility change
verify(mViewListener, never()).onTaskVisibilityChanged(anyInt(), anyBoolean());
}
@@ -353,6 +359,7 @@ public class TaskViewTest extends ShellTestCase {
verify(mOrganizer).removeListener(eq(mTaskView));
verify(mViewListener).onReleased();
+ assertThat(mTaskView.isInitialized()).isFalse();
verify(mTaskViewTransitions).removeTaskView(eq(mTaskView));
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 388792b63db3..b142039e6aa9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -43,11 +43,13 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
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.ArgumentMatchers.isNull;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -59,8 +61,6 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
-import android.view.IDisplayWindowListener;
-import android.view.IWindowManager;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.WindowManager;
@@ -82,6 +82,7 @@ import androidx.test.platform.app.InstrumentationRegistry;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.sysui.ShellInit;
@@ -89,6 +90,7 @@ import com.android.wm.shell.sysui.ShellInit;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.InOrder;
import java.util.ArrayList;
@@ -688,6 +690,204 @@ public class ShellTransitionTests extends ShellTestCase {
verify(runnable4, times(1)).run();
}
+ @Test
+ public void testObserverLifecycle_basicTransitionFlow() {
+ Transitions transitions = createTestTransitions();
+ Transitions.TransitionObserver observer = mock(Transitions.TransitionObserver.class);
+ transitions.registerObserver(observer);
+ transitions.replaceDefaultHandlerForTest(mDefaultHandler);
+
+ IBinder transitToken = new Binder();
+ transitions.requestStartTransition(transitToken,
+ new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
+ TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
+ SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
+ SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
+ transitions.onTransitionReady(transitToken, info, startT, finishT);
+
+ InOrder observerOrder = inOrder(observer);
+ observerOrder.verify(observer).onTransitionReady(transitToken, info, startT, finishT);
+ observerOrder.verify(observer).onTransitionStarting(transitToken);
+ verify(observer, times(0)).onTransitionFinished(eq(transitToken), anyBoolean());
+ mDefaultHandler.finishAll();
+ mMainExecutor.flushAll();
+ verify(observer).onTransitionFinished(transitToken, false);
+ }
+
+ @Test
+ public void testObserverLifecycle_queueing() {
+ Transitions transitions = createTestTransitions();
+ Transitions.TransitionObserver observer = mock(Transitions.TransitionObserver.class);
+ transitions.registerObserver(observer);
+ transitions.replaceDefaultHandlerForTest(mDefaultHandler);
+
+ IBinder transitToken1 = new Binder();
+ transitions.requestStartTransition(transitToken1,
+ new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
+ TransitionInfo info1 = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
+ SurfaceControl.Transaction startT1 = mock(SurfaceControl.Transaction.class);
+ SurfaceControl.Transaction finishT1 = mock(SurfaceControl.Transaction.class);
+ transitions.onTransitionReady(transitToken1, info1, startT1, finishT1);
+ verify(observer).onTransitionReady(transitToken1, info1, startT1, finishT1);
+
+ IBinder transitToken2 = new Binder();
+ transitions.requestStartTransition(transitToken2,
+ new TransitionRequestInfo(TRANSIT_CLOSE, null /* trigger */, null /* remote */));
+ TransitionInfo info2 = new TransitionInfoBuilder(TRANSIT_CLOSE)
+ .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
+ SurfaceControl.Transaction startT2 = mock(SurfaceControl.Transaction.class);
+ SurfaceControl.Transaction finishT2 = mock(SurfaceControl.Transaction.class);
+ transitions.onTransitionReady(transitToken2, info2, startT2, finishT2);
+ verify(observer, times(1)).onTransitionReady(transitToken2, info2, startT2, finishT2);
+ verify(observer, times(0)).onTransitionStarting(transitToken2);
+ verify(observer, times(0)).onTransitionFinished(eq(transitToken1), anyBoolean());
+ verify(observer, times(0)).onTransitionFinished(eq(transitToken2), anyBoolean());
+
+ mDefaultHandler.finishAll();
+ mMainExecutor.flushAll();
+ // first transition finished
+ verify(observer, times(1)).onTransitionFinished(transitToken1, false);
+ verify(observer, times(1)).onTransitionStarting(transitToken2);
+ verify(observer, times(0)).onTransitionFinished(eq(transitToken2), anyBoolean());
+
+ mDefaultHandler.finishAll();
+ mMainExecutor.flushAll();
+ verify(observer, times(1)).onTransitionFinished(transitToken2, false);
+ }
+
+
+ @Test
+ public void testObserverLifecycle_merging() {
+ Transitions transitions = createTestTransitions();
+ Transitions.TransitionObserver observer = mock(Transitions.TransitionObserver.class);
+ transitions.registerObserver(observer);
+ mDefaultHandler.setSimulateMerge(true);
+ transitions.replaceDefaultHandlerForTest(mDefaultHandler);
+
+ IBinder transitToken1 = new Binder();
+ transitions.requestStartTransition(transitToken1,
+ new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
+ TransitionInfo info1 = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
+ SurfaceControl.Transaction startT1 = mock(SurfaceControl.Transaction.class);
+ SurfaceControl.Transaction finishT1 = mock(SurfaceControl.Transaction.class);
+ transitions.onTransitionReady(transitToken1, info1, startT1, finishT1);
+
+ IBinder transitToken2 = new Binder();
+ transitions.requestStartTransition(transitToken2,
+ new TransitionRequestInfo(TRANSIT_CLOSE, null /* trigger */, null /* remote */));
+ TransitionInfo info2 = new TransitionInfoBuilder(TRANSIT_CLOSE)
+ .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
+ SurfaceControl.Transaction startT2 = mock(SurfaceControl.Transaction.class);
+ SurfaceControl.Transaction finishT2 = mock(SurfaceControl.Transaction.class);
+ transitions.onTransitionReady(transitToken2, info2, startT2, finishT2);
+
+ InOrder observerOrder = inOrder(observer);
+ observerOrder.verify(observer).onTransitionReady(transitToken2, info2, startT2, finishT2);
+ observerOrder.verify(observer).onTransitionMerged(transitToken2, transitToken1);
+ verify(observer, times(0)).onTransitionFinished(eq(transitToken1), anyBoolean());
+
+ mDefaultHandler.finishAll();
+ mMainExecutor.flushAll();
+ // transition + merged all finished.
+ verify(observer, times(1)).onTransitionFinished(transitToken1, false);
+ // Merged transition won't receive any lifecycle calls beyond ready
+ verify(observer, times(0)).onTransitionStarting(transitToken2);
+ verify(observer, times(0)).onTransitionFinished(eq(transitToken2), anyBoolean());
+ }
+
+ @Test
+ public void testObserverLifecycle_mergingAfterQueueing() {
+ Transitions transitions = createTestTransitions();
+ Transitions.TransitionObserver observer = mock(Transitions.TransitionObserver.class);
+ transitions.registerObserver(observer);
+ mDefaultHandler.setSimulateMerge(true);
+ transitions.replaceDefaultHandlerForTest(mDefaultHandler);
+
+ // Make a test handler that only responds to multi-window triggers AND only animates
+ // Change transitions.
+ final WindowContainerTransaction handlerWCT = new WindowContainerTransaction();
+ TestTransitionHandler testHandler = new TestTransitionHandler() {
+ @Override
+ public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ for (TransitionInfo.Change chg : info.getChanges()) {
+ if (chg.getMode() == TRANSIT_CHANGE) {
+ return super.startAnimation(transition, info, startTransaction,
+ finishTransaction, finishCallback);
+ }
+ }
+ return false;
+ }
+
+ @Nullable
+ @Override
+ public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+ @NonNull TransitionRequestInfo request) {
+ final RunningTaskInfo task = request.getTriggerTask();
+ return (task != null && task.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW)
+ ? handlerWCT : null;
+ }
+ };
+ transitions.addHandler(testHandler);
+
+ // Use test handler to play an animation
+ IBinder transitToken1 = new Binder();
+ RunningTaskInfo mwTaskInfo =
+ createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
+ transitions.requestStartTransition(transitToken1,
+ new TransitionRequestInfo(TRANSIT_CHANGE, mwTaskInfo, null /* remote */));
+ TransitionInfo change = new TransitionInfoBuilder(TRANSIT_CHANGE)
+ .addChange(TRANSIT_CHANGE).build();
+ SurfaceControl.Transaction startT1 = mock(SurfaceControl.Transaction.class);
+ SurfaceControl.Transaction finishT1 = mock(SurfaceControl.Transaction.class);
+ transitions.onTransitionReady(transitToken1, change, startT1, finishT1);
+
+ // Request the second transition that should be handled by the default handler
+ IBinder transitToken2 = new Binder();
+ TransitionInfo open = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
+ transitions.requestStartTransition(transitToken2,
+ new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
+ SurfaceControl.Transaction startT2 = mock(SurfaceControl.Transaction.class);
+ SurfaceControl.Transaction finishT2 = mock(SurfaceControl.Transaction.class);
+ transitions.onTransitionReady(transitToken2, open, startT2, finishT2);
+ verify(observer).onTransitionReady(transitToken2, open, startT2, finishT2);
+ verify(observer, times(0)).onTransitionStarting(transitToken2);
+
+ // Request the third transition that should be merged into the second one
+ IBinder transitToken3 = new Binder();
+ transitions.requestStartTransition(transitToken3,
+ new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
+ SurfaceControl.Transaction startT3 = mock(SurfaceControl.Transaction.class);
+ SurfaceControl.Transaction finishT3 = mock(SurfaceControl.Transaction.class);
+ transitions.onTransitionReady(transitToken3, open, startT3, finishT3);
+ verify(observer, times(0)).onTransitionStarting(transitToken2);
+ verify(observer).onTransitionReady(transitToken3, open, startT3, finishT3);
+ verify(observer, times(0)).onTransitionStarting(transitToken3);
+
+ testHandler.finishAll();
+ mMainExecutor.flushAll();
+
+ verify(observer).onTransitionFinished(transitToken1, false);
+
+ mDefaultHandler.finishAll();
+ mMainExecutor.flushAll();
+
+ InOrder observerOrder = inOrder(observer);
+ observerOrder.verify(observer).onTransitionStarting(transitToken2);
+ observerOrder.verify(observer).onTransitionMerged(transitToken3, transitToken2);
+ observerOrder.verify(observer).onTransitionFinished(transitToken2, false);
+
+ // Merged transition won't receive any lifecycle calls beyond ready
+ verify(observer, times(0)).onTransitionStarting(transitToken3);
+ verify(observer, times(0)).onTransitionFinished(eq(transitToken3), anyBoolean());
+ }
+
class TransitionInfoBuilder {
final TransitionInfo mInfo;
@@ -834,16 +1034,13 @@ public class ShellTransitionTests extends ShellTestCase {
}
private DisplayController createTestDisplayController() {
- IWindowManager mockWM = mock(IWindowManager.class);
- final IDisplayWindowListener[] displayListener = new IDisplayWindowListener[1];
- try {
- doReturn(new int[]{DEFAULT_DISPLAY}).when(mockWM).registerDisplayWindowListener(any());
- } catch (RemoteException e) {
- // No remote stuff happening, so this can't be hit
- }
- ShellInit shellInit = new ShellInit(mMainExecutor);
- DisplayController out = new DisplayController(mContext, mockWM, shellInit, mMainExecutor);
- shellInit.init();
+ DisplayLayout displayLayout = mock(DisplayLayout.class);
+ doReturn(Surface.ROTATION_180).when(displayLayout).getUpsideDownRotation();
+ // By default we ignore nav bar in deciding if a seamless rotation is allowed.
+ doReturn(true).when(displayLayout).allowSeamlessRotationDespiteNavBarMoving();
+
+ DisplayController out = mock(DisplayController.class);
+ doReturn(displayLayout).when(out).getDisplayLayout(DEFAULT_DISPLAY);
return out;
}
@@ -854,17 +1051,4 @@ public class ShellTransitionTests extends ShellTestCase {
shellInit.init();
return t;
}
-//
-// private class TestDisplayController extends DisplayController {
-// private final DisplayLayout mTestDisplayLayout;
-// TestDisplayController() {
-// super(mContext, mock(IWindowManager.class), mMainExecutor);
-// mTestDisplayLayout = new DisplayLayout();
-// mTestDisplayLayout.
-// }
-//
-// @Override
-// DisplayLayout
-// }
-
}
diff --git a/libs/hwui/FrameInfoVisualizer.cpp b/libs/hwui/FrameInfoVisualizer.cpp
index 3a8e559f6d7e..687e4dd324d3 100644
--- a/libs/hwui/FrameInfoVisualizer.cpp
+++ b/libs/hwui/FrameInfoVisualizer.cpp
@@ -179,7 +179,7 @@ void FrameInfoVisualizer::initializeRects(const int baseline, const int width) {
void FrameInfoVisualizer::nextBarSegment(FrameInfoIndex start, FrameInfoIndex end) {
int fast_i = (mNumFastRects - 1) * 4;
int janky_i = (mNumJankyRects - 1) * 4;
- ;
+
for (size_t fi = 0; fi < mFrameSource.size(); fi++) {
if (mFrameSource[fi][FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame) {
continue;
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 2aca41e41905..62e42b8e1863 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -112,7 +112,7 @@ IRenderPipeline::DrawResult SkiaOpenGLPipeline::draw(
if (CC_UNLIKELY(Properties::showDirtyRegions ||
ProfileType::None != Properties::getProfileType())) {
SkCanvas* profileCanvas = surface->getCanvas();
- SkiaProfileRenderer profileRenderer(profileCanvas);
+ SkiaProfileRenderer profileRenderer(profileCanvas, frame.width(), frame.height());
profiler->draw(profileRenderer);
}
diff --git a/libs/hwui/pipeline/skia/SkiaProfileRenderer.cpp b/libs/hwui/pipeline/skia/SkiaProfileRenderer.cpp
index 492c39f1288c..81cfc5d93419 100644
--- a/libs/hwui/pipeline/skia/SkiaProfileRenderer.cpp
+++ b/libs/hwui/pipeline/skia/SkiaProfileRenderer.cpp
@@ -33,13 +33,5 @@ void SkiaProfileRenderer::drawRects(const float* rects, int count, const SkPaint
}
}
-uint32_t SkiaProfileRenderer::getViewportWidth() {
- return mCanvas->imageInfo().width();
-}
-
-uint32_t SkiaProfileRenderer::getViewportHeight() {
- return mCanvas->imageInfo().height();
-}
-
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaProfileRenderer.h b/libs/hwui/pipeline/skia/SkiaProfileRenderer.h
index dc8420f4e01b..96d2a5e58139 100644
--- a/libs/hwui/pipeline/skia/SkiaProfileRenderer.h
+++ b/libs/hwui/pipeline/skia/SkiaProfileRenderer.h
@@ -23,18 +23,21 @@ namespace uirenderer {
class SkiaProfileRenderer : public IProfileRenderer {
public:
- explicit SkiaProfileRenderer(SkCanvas* canvas) : mCanvas(canvas) {}
+ explicit SkiaProfileRenderer(SkCanvas* canvas, uint32_t width, uint32_t height)
+ : mCanvas(canvas), mWidth(width), mHeight(height) {}
void drawRect(float left, float top, float right, float bottom, const SkPaint& paint) override;
void drawRects(const float* rects, int count, const SkPaint& paint) override;
- uint32_t getViewportWidth() override;
- uint32_t getViewportHeight() override;
+ uint32_t getViewportWidth() override { return mWidth; }
+ uint32_t getViewportHeight() override { return mHeight; }
virtual ~SkiaProfileRenderer() {}
private:
// Does not have ownership.
SkCanvas* mCanvas;
+ uint32_t mWidth;
+ uint32_t mHeight;
};
} /* namespace uirenderer */
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 905d46e58014..53a4c60cf8a8 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -88,7 +88,9 @@ IRenderPipeline::DrawResult SkiaVulkanPipeline::draw(
if (CC_UNLIKELY(Properties::showDirtyRegions ||
ProfileType::None != Properties::getProfileType())) {
SkCanvas* profileCanvas = backBuffer->getCanvas();
- SkiaProfileRenderer profileRenderer(profileCanvas);
+ SkAutoCanvasRestore saver(profileCanvas, true);
+ profileCanvas->concat(mVkSurface->getCurrentPreTransform());
+ SkiaProfileRenderer profileRenderer(profileCanvas, frame.width(), frame.height());
profiler->draw(profileRenderer);
}
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 976117b9bbd4..75d3ff7753cb 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -512,9 +512,19 @@ nsecs_t CanvasContext::draw() {
ATRACE_FORMAT("Drawing " RECT_STRING, SK_RECT_ARGS(dirty));
- const auto drawResult = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry,
- &mLayerUpdateQueue, mContentDrawBounds, mOpaque,
- mLightInfo, mRenderNodes, &(profiler()));
+ IRenderPipeline::DrawResult drawResult;
+ {
+ // FrameInfoVisualizer accesses the frame events, which cannot be mutated mid-draw
+ // or it can lead to memory corruption.
+ // This lock is overly broad, but it's the quickest fix since this mutex is otherwise
+ // not visible to IRenderPipeline much less FrameInfoVisualizer. And since this is
+ // the thread we're primarily concerned about being responsive, this being too broad
+ // shouldn't pose a performance issue.
+ std::scoped_lock lock(mFrameMetricsReporterMutex);
+ drawResult = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry,
+ &mLayerUpdateQueue, mContentDrawBounds, mOpaque,
+ mLightInfo, mRenderNodes, &(profiler()));
+ }
uint64_t frameCompleteNr = getFrameNumber();
@@ -754,11 +764,11 @@ void CanvasContext::onSurfaceStatsAvailable(void* context, int32_t surfaceContro
FrameInfo* frameInfo = instance->getFrameInfoFromLast4(frameNumber, surfaceControlId);
if (frameInfo != nullptr) {
+ std::scoped_lock lock(instance->mFrameMetricsReporterMutex);
frameInfo->set(FrameInfoIndex::FrameCompleted) = std::max(gpuCompleteTime,
frameInfo->get(FrameInfoIndex::SwapBuffersCompleted));
frameInfo->set(FrameInfoIndex::GpuCompleted) = std::max(
gpuCompleteTime, frameInfo->get(FrameInfoIndex::CommandSubmissionCompleted));
- std::scoped_lock lock(instance->mFrameMetricsReporterMutex);
instance->mJankTracker.finishFrame(*frameInfo, instance->mFrameMetricsReporter, frameNumber,
surfaceControlId);
}
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index 55ee3aaaaf77..f5a9850b31dd 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -47,6 +47,15 @@ import java.util.StringTokenizer;
* <p>All locations generated through {@link LocationManager} are guaranteed to have a valid
* latitude, longitude, timestamp (both Unix epoch time and elapsed realtime since boot), and
* accuracy. All other parameters are optional.
+ *
+ * <p class="note">Note that Android provides the ability for applications to submit "mock" or faked
+ * locations through {@link LocationManager}, and that these locations can then be received by
+ * applications using LocationManager to obtain location information. These locations can be
+ * identified via the {@link #isMock()} API. Applications that wish to determine if a given location
+ * represents the best estimate of the real position of the device as opposed to a fake location
+ * coming from another application or the user should use this API. Keep in mind that the user may
+ * have a good reason for mocking their location, and thus apps should generally reject mock
+ * locations only when it is essential to their use case that only real locations are accepted.
*/
public class Location implements Parcelable {
diff --git a/media/java/android/media/IMediaRouter2.aidl b/media/java/android/media/IMediaRouter2.aidl
index fe15f0e67b1d..29bfd1acae17 100644
--- a/media/java/android/media/IMediaRouter2.aidl
+++ b/media/java/android/media/IMediaRouter2.aidl
@@ -26,9 +26,7 @@ import android.os.Bundle;
oneway interface IMediaRouter2 {
void notifyRouterRegistered(in List<MediaRoute2Info> currentRoutes,
in RoutingSessionInfo currentSystemSessionInfo);
- void notifyRoutesAdded(in List<MediaRoute2Info> routes);
- void notifyRoutesRemoved(in List<MediaRoute2Info> routes);
- void notifyRoutesChanged(in List<MediaRoute2Info> routes);
+ void notifyRoutesUpdated(in List<MediaRoute2Info> routes);
void notifySessionCreated(int requestId, in @nullable RoutingSessionInfo sessionInfo);
void notifySessionInfoChanged(in RoutingSessionInfo sessionInfo);
void notifySessionReleased(in RoutingSessionInfo sessionInfo);
diff --git a/media/java/android/media/IMediaRouter2Manager.aidl b/media/java/android/media/IMediaRouter2Manager.aidl
index 71dc2a781ba9..9f3c3ff89032 100644
--- a/media/java/android/media/IMediaRouter2Manager.aidl
+++ b/media/java/android/media/IMediaRouter2Manager.aidl
@@ -30,8 +30,6 @@ oneway interface IMediaRouter2Manager {
void notifySessionReleased(in RoutingSessionInfo session);
void notifyDiscoveryPreferenceChanged(String packageName,
in RouteDiscoveryPreference discoveryPreference);
- void notifyRoutesAdded(in List<MediaRoute2Info> routes);
- void notifyRoutesRemoved(in List<MediaRoute2Info> routes);
- void notifyRoutesChanged(in List<MediaRoute2Info> routes);
+ void notifyRoutesUpdated(in List<MediaRoute2Info> routes);
void notifyRequestFailed(int requestId, int reason);
}
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index a7a21e7a2013..26cb9f8e9ee1 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -132,7 +132,7 @@ public final class MediaRouter2 {
/**
* Stores an auxiliary copy of {@link #mFilteredRoutes} at the time of the last route callback
* dispatch. This is only used to determine what callback a route should be assigned to (added,
- * removed, changed) in {@link #dispatchFilteredRoutesChangedLocked(List)}.
+ * removed, changed) in {@link #dispatchFilteredRoutesUpdatedOnHandler(List)}.
*/
private volatile ArrayMap<String, MediaRoute2Info> mPreviousRoutes = new ArrayMap<>();
@@ -820,7 +820,7 @@ public final class MediaRouter2 {
}
}
- void dispatchFilteredRoutesChangedLocked(List<MediaRoute2Info> newRoutes) {
+ void dispatchFilteredRoutesUpdatedOnHandler(List<MediaRoute2Info> newRoutes) {
List<MediaRoute2Info> addedRoutes = new ArrayList<>();
List<MediaRoute2Info> removedRoutes = new ArrayList<>();
List<MediaRoute2Info> changedRoutes = new ArrayList<>();
@@ -863,29 +863,16 @@ public final class MediaRouter2 {
if (!changedRoutes.isEmpty()) {
notifyRoutesChanged(changedRoutes);
}
- }
- void addRoutesOnHandler(List<MediaRoute2Info> routes) {
- synchronized (mLock) {
- for (MediaRoute2Info route : routes) {
- mRoutes.put(route.getId(), route);
- }
- updateFilteredRoutesLocked();
+ // Note: We don't notify clients of changes in route ordering.
+ if (!addedRoutes.isEmpty() || !removedRoutes.isEmpty() || !changedRoutes.isEmpty()) {
+ notifyRoutesUpdated(newRoutes);
}
}
- void removeRoutesOnHandler(List<MediaRoute2Info> routes) {
- synchronized (mLock) {
- for (MediaRoute2Info route : routes) {
- mRoutes.remove(route.getId());
- }
- updateFilteredRoutesLocked();
- }
- }
-
- void changeRoutesOnHandler(List<MediaRoute2Info> routes) {
- List<MediaRoute2Info> changedRoutes = new ArrayList<>();
+ void updateRoutesOnHandler(List<MediaRoute2Info> routes) {
synchronized (mLock) {
+ mRoutes.clear();
for (MediaRoute2Info route : routes) {
mRoutes.put(route.getId(), route);
}
@@ -900,8 +887,10 @@ public final class MediaRouter2 {
Collections.unmodifiableList(
filterRoutesWithCompositePreferenceLocked(List.copyOf(mRoutes.values())));
mHandler.sendMessage(
- obtainMessage(MediaRouter2::dispatchFilteredRoutesChangedLocked,
- this, mFilteredRoutes));
+ obtainMessage(
+ MediaRouter2::dispatchFilteredRoutesUpdatedOnHandler,
+ this,
+ mFilteredRoutes));
}
/**
@@ -1211,6 +1200,14 @@ public final class MediaRouter2 {
}
}
+ private void notifyRoutesUpdated(List<MediaRoute2Info> routes) {
+ for (RouteCallbackRecord record : mRouteCallbackRecords) {
+ List<MediaRoute2Info> filteredRoutes =
+ filterRoutesWithIndividualPreference(routes, record.mPreference);
+ record.mExecutor.execute(() -> record.mRouteCallback.onRoutesUpdated(filteredRoutes));
+ }
+ }
+
private void notifyPreferredFeaturesChanged(List<String> features) {
for (RouteCallbackRecord record : mRouteCallbackRecords) {
record.mExecutor.execute(
@@ -1246,29 +1243,44 @@ public final class MediaRouter2 {
/** Callback for receiving events about media route discovery. */
public abstract static class RouteCallback {
/**
- * Called when routes are added. Whenever you registers a callback, this will be invoked
- * with known routes.
+ * Called when routes are added. Whenever you register a callback, this will be invoked with
+ * known routes.
*
* @param routes the list of routes that have been added. It's never empty.
+ * @deprecated Use {@link #onRoutesUpdated(List)} instead.
*/
+ @Deprecated
public void onRoutesAdded(@NonNull List<MediaRoute2Info> routes) {}
/**
* Called when routes are removed.
*
* @param routes the list of routes that have been removed. It's never empty.
+ * @deprecated Use {@link #onRoutesUpdated(List)} instead.
*/
+ @Deprecated
public void onRoutesRemoved(@NonNull List<MediaRoute2Info> routes) {}
/**
- * Called when routes are changed. For example, it is called when the route's name or volume
- * have been changed.
+ * Called when the properties of one or more existing routes are changed. For example, it is
+ * called when a route's name or volume have changed.
*
* @param routes the list of routes that have been changed. It's never empty.
+ * @deprecated Use {@link #onRoutesUpdated(List)} instead.
*/
+ @Deprecated
public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {}
/**
+ * Called when the route list is updated, which can happen when routes are added, removed,
+ * or modified. It will also be called when a route callback is registered.
+ *
+ * @param routes the updated list of routes filtered by the callback's individual discovery
+ * preferences.
+ */
+ public void onRoutesUpdated(@NonNull List<MediaRoute2Info> routes) {}
+
+ /**
* Called when the client app's preferred features are changed. When this is called, it is
* recommended to {@link #getRoutes()} to get the routes that are currently available to the
* app.
@@ -1985,21 +1997,9 @@ public final class MediaRouter2 {
}
@Override
- public void notifyRoutesAdded(List<MediaRoute2Info> routes) {
- mHandler.sendMessage(
- obtainMessage(MediaRouter2::addRoutesOnHandler, MediaRouter2.this, routes));
- }
-
- @Override
- public void notifyRoutesRemoved(List<MediaRoute2Info> routes) {
- mHandler.sendMessage(
- obtainMessage(MediaRouter2::removeRoutesOnHandler, MediaRouter2.this, routes));
- }
-
- @Override
- public void notifyRoutesChanged(List<MediaRoute2Info> routes) {
+ public void notifyRoutesUpdated(List<MediaRoute2Info> routes) {
mHandler.sendMessage(
- obtainMessage(MediaRouter2::changeRoutesOnHandler, MediaRouter2.this, routes));
+ obtainMessage(MediaRouter2::updateRoutesOnHandler, MediaRouter2.this, routes));
}
@Override
@@ -2047,17 +2047,7 @@ public final class MediaRouter2 {
class ManagerCallback implements MediaRouter2Manager.Callback {
@Override
- public void onRoutesAdded(@NonNull List<MediaRoute2Info> routes) {
- updateAllRoutesFromManager();
- }
-
- @Override
- public void onRoutesRemoved(@NonNull List<MediaRoute2Info> routes) {
- updateAllRoutesFromManager();
- }
-
- @Override
- public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {
+ public void onRoutesUpdated() {
updateAllRoutesFromManager();
}
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 44c0b54546be..8afc7d999d2e 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -546,37 +546,15 @@ public final class MediaRouter2Manager {
}
}
- void addRoutesOnHandler(List<MediaRoute2Info> routes) {
+ void updateRoutesOnHandler(@NonNull List<MediaRoute2Info> routes) {
synchronized (mRoutesLock) {
+ mRoutes.clear();
for (MediaRoute2Info route : routes) {
mRoutes.put(route.getId(), route);
}
}
- if (routes.size() > 0) {
- notifyRoutesAdded(routes);
- }
- }
- void removeRoutesOnHandler(List<MediaRoute2Info> routes) {
- synchronized (mRoutesLock) {
- for (MediaRoute2Info route : routes) {
- mRoutes.remove(route.getId());
- }
- }
- if (routes.size() > 0) {
- notifyRoutesRemoved(routes);
- }
- }
-
- void changeRoutesOnHandler(List<MediaRoute2Info> routes) {
- synchronized (mRoutesLock) {
- for (MediaRoute2Info route : routes) {
- mRoutes.put(route.getId(), route);
- }
- }
- if (routes.size() > 0) {
- notifyRoutesChanged(routes);
- }
+ notifyRoutesUpdated();
}
void createSessionOnHandler(int requestId, RoutingSessionInfo sessionInfo) {
@@ -650,24 +628,9 @@ public final class MediaRouter2Manager {
notifySessionUpdated(sessionInfo);
}
- private void notifyRoutesAdded(List<MediaRoute2Info> routes) {
- for (CallbackRecord record: mCallbackRecords) {
- record.mExecutor.execute(
- () -> record.mCallback.onRoutesAdded(routes));
- }
- }
-
- private void notifyRoutesRemoved(List<MediaRoute2Info> routes) {
+ private void notifyRoutesUpdated() {
for (CallbackRecord record: mCallbackRecords) {
- record.mExecutor.execute(
- () -> record.mCallback.onRoutesRemoved(routes));
- }
- }
-
- private void notifyRoutesChanged(List<MediaRoute2Info> routes) {
- for (CallbackRecord record: mCallbackRecords) {
- record.mExecutor.execute(
- () -> record.mCallback.onRoutesChanged(routes));
+ record.mExecutor.execute(() -> record.mCallback.onRoutesUpdated());
}
}
@@ -963,23 +926,12 @@ public final class MediaRouter2Manager {
* Interface for receiving events about media routing changes.
*/
public interface Callback {
- /**
- * Called when routes are added.
- * @param routes the list of routes that have been added. It's never empty.
- */
- default void onRoutesAdded(@NonNull List<MediaRoute2Info> routes) {}
/**
- * Called when routes are removed.
- * @param routes the list of routes that have been removed. It's never empty.
+ * Called when the routes list changes. This includes adding, modifying, or removing
+ * individual routes.
*/
- default void onRoutesRemoved(@NonNull List<MediaRoute2Info> routes) {}
-
- /**
- * Called when routes are changed.
- * @param routes the list of routes that have been changed. It's never empty.
- */
- default void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {}
+ default void onRoutesUpdated() {}
/**
* Called when a session is changed.
@@ -1115,21 +1067,12 @@ public final class MediaRouter2Manager {
}
@Override
- public void notifyRoutesAdded(List<MediaRoute2Info> routes) {
- mHandler.sendMessage(obtainMessage(MediaRouter2Manager::addRoutesOnHandler,
- MediaRouter2Manager.this, routes));
- }
-
- @Override
- public void notifyRoutesRemoved(List<MediaRoute2Info> routes) {
- mHandler.sendMessage(obtainMessage(MediaRouter2Manager::removeRoutesOnHandler,
- MediaRouter2Manager.this, routes));
- }
-
- @Override
- public void notifyRoutesChanged(List<MediaRoute2Info> routes) {
- mHandler.sendMessage(obtainMessage(MediaRouter2Manager::changeRoutesOnHandler,
- MediaRouter2Manager.this, routes));
+ public void notifyRoutesUpdated(List<MediaRoute2Info> routes) {
+ mHandler.sendMessage(
+ obtainMessage(
+ MediaRouter2Manager::updateRoutesOnHandler,
+ MediaRouter2Manager.this,
+ routes));
}
}
}
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
index 4086dec99218..37c836762da0 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
@@ -32,7 +32,6 @@ import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_I
import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID_FIXED_VOLUME;
import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID_SPECIAL_FEATURE;
import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID_VARIABLE_VOLUME;
-import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_NAME2;
import static com.android.mediaroutertest.StubMediaRoute2ProviderService.VOLUME_MAX;
import static org.junit.Assert.assertEquals;
@@ -56,10 +55,10 @@ import android.media.RoutingSessionInfo;
import android.os.Bundle;
import android.text.TextUtils;
-import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.compatibility.common.util.PollingCheck;
@@ -69,6 +68,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -115,7 +115,7 @@ public class MediaRouter2ManagerTest {
@Before
public void setUp() throws Exception {
- mContext = InstrumentationRegistry.getTargetContext();
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
mUiAutomation.adoptShellPermissionIdentity(Manifest.permission.MEDIA_CONTENT_CONTROL,
Manifest.permission.MODIFY_AUDIO_ROUTING);
@@ -170,51 +170,95 @@ public class MediaRouter2ManagerTest {
}
@Test
- public void testOnRoutesRemovedAndAdded() throws Exception {
- RouteCallback routeCallback = new RouteCallback() {};
- mRouteCallbacks.add(routeCallback);
- mRouter2.registerRouteCallback(mExecutor, routeCallback,
- new RouteDiscoveryPreference.Builder(FEATURES_ALL, true).build());
+ public void testOnRoutesUpdated() throws Exception {
+ final String routeId0 = "routeId0";
+ final String routeName0 = "routeName0";
+ final String routeId1 = "routeId1";
+ final String routeName1 = "routeName1";
+ final List<String> features = Collections.singletonList("customFeature");
- Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
+ final int newConnectionState = MediaRoute2Info.CONNECTION_STATE_CONNECTED;
+
+ final List<MediaRoute2Info> routes = new ArrayList<>();
+ routes.add(new MediaRoute2Info.Builder(routeId0, routeName0).addFeatures(features).build());
+ routes.add(new MediaRoute2Info.Builder(routeId1, routeName1).addFeatures(features).build());
- CountDownLatch removedLatch = new CountDownLatch(1);
CountDownLatch addedLatch = new CountDownLatch(1);
+ CountDownLatch changedLatch = new CountDownLatch(1);
+ CountDownLatch removedLatch = new CountDownLatch(1);
- addManagerCallback(new MediaRouter2Manager.Callback() {
- @Override
- public void onRoutesRemoved(List<MediaRoute2Info> routes) {
- assertTrue(routes.size() > 0);
- for (MediaRoute2Info route : routes) {
- if (route.getOriginalId().equals(ROUTE_ID2)
- && route.getName().equals(ROUTE_NAME2)) {
- removedLatch.countDown();
+ addManagerCallback(
+ new MediaRouter2Manager.Callback() {
+ @Override
+ public void onRoutesUpdated() {
+ if (addedLatch.getCount() == 1
+ && checkRoutesMatch(mManager.getAllRoutes(), routes)) {
+ addedLatch.countDown();
+ } else if (changedLatch.getCount() == 1
+ && checkRoutesMatch(
+ mManager.getAllRoutes(), routes.subList(1, 2))) {
+ changedLatch.countDown();
+ } else if (removedLatch.getCount() == 1
+ && checkRoutesRemoved(mManager.getAllRoutes(), routes)) {
+ removedLatch.countDown();
+ }
}
- }
- }
- @Override
- public void onRoutesAdded(List<MediaRoute2Info> routes) {
- assertTrue(routes.size() > 0);
- if (removedLatch.getCount() > 0) {
- return;
- }
- for (MediaRoute2Info route : routes) {
- if (route.getOriginalId().equals(ROUTE_ID2)
- && route.getName().equals(ROUTE_NAME2)) {
- addedLatch.countDown();
- }
- }
- }
- });
+ });
- MediaRoute2Info routeToRemove = routes.get(ROUTE_ID2);
- assertNotNull(routeToRemove);
+ mService.addRoutes(routes);
+ assertTrue(
+ "Added routes not found or onRoutesUpdated() never called.",
+ addedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
- mService.removeRoute(ROUTE_ID2);
- assertTrue(removedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ MediaRoute2Info newRoute2 =
+ new MediaRoute2Info.Builder(routes.get(1))
+ .setConnectionState(newConnectionState)
+ .build();
+ routes.set(1, newRoute2);
+ mService.addRoute(routes.get(1));
+ assertTrue(
+ "Modified route not found or onRoutesUpdated() never called.",
+ changedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+ List<String> routeIds = new ArrayList<>();
+ routeIds.add(routeId0);
+ routeIds.add(routeId1);
+
+ mService.removeRoutes(routeIds);
+ assertTrue(
+ "Removed routes not found or onRoutesUpdated() never called.",
+ removedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
- mService.addRoute(routeToRemove);
- assertTrue(addedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ private static boolean checkRoutesMatch(
+ List<MediaRoute2Info> routesReceived, List<MediaRoute2Info> expectedRoutes) {
+ for (MediaRoute2Info expectedRoute : expectedRoutes) {
+ MediaRoute2Info matchingRoute =
+ routesReceived.stream()
+ .filter(r -> r.getOriginalId().equals(expectedRoute.getOriginalId()))
+ .findFirst()
+ .orElse(null);
+
+ if (matchingRoute == null) {
+ return false;
+ }
+ assertTrue(TextUtils.equals(expectedRoute.getName(), matchingRoute.getName()));
+ assertEquals(expectedRoute.getFeatures(), matchingRoute.getFeatures());
+ assertEquals(expectedRoute.getConnectionState(), matchingRoute.getConnectionState());
+ }
+
+ return true;
+ }
+
+ private static boolean checkRoutesRemoved(
+ List<MediaRoute2Info> routesReceived, List<MediaRoute2Info> routesRemoved) {
+ for (MediaRoute2Info removedRoute : routesRemoved) {
+ if (routesReceived.stream()
+ .anyMatch(r -> r.getOriginalId().equals(removedRoute.getOriginalId()))) {
+ return false;
+ }
+ }
+ return true;
}
@Test
@@ -874,28 +918,31 @@ public class MediaRouter2ManagerTest {
// A dummy callback is required to send route feature info.
RouteCallback routeCallback = new RouteCallback() {};
- MediaRouter2Manager.Callback managerCallback = new MediaRouter2Manager.Callback() {
- @Override
- public void onRoutesAdded(List<MediaRoute2Info> routes) {
- for (MediaRoute2Info route : routes) {
- if (!route.isSystemRoute()
- && hasMatchingFeature(route.getFeatures(), preference
- .getPreferredFeatures())) {
- addedLatch.countDown();
- break;
+ MediaRouter2Manager.Callback managerCallback =
+ new MediaRouter2Manager.Callback() {
+ @Override
+ public void onRoutesUpdated() {
+ List<MediaRoute2Info> routes = mManager.getAllRoutes();
+ for (MediaRoute2Info route : routes) {
+ if (!route.isSystemRoute()
+ && hasMatchingFeature(
+ route.getFeatures(),
+ preference.getPreferredFeatures())) {
+ addedLatch.countDown();
+ break;
+ }
+ }
}
- }
- }
- @Override
- public void onDiscoveryPreferenceChanged(String packageName,
- RouteDiscoveryPreference discoveryPreference) {
- if (TextUtils.equals(mPackageName, packageName)
- && Objects.equals(preference, discoveryPreference)) {
- preferenceLatch.countDown();
- }
- }
- };
+ @Override
+ public void onDiscoveryPreferenceChanged(
+ String packageName, RouteDiscoveryPreference discoveryPreference) {
+ if (TextUtils.equals(mPackageName, packageName)
+ && Objects.equals(preference, discoveryPreference)) {
+ preferenceLatch.countDown();
+ }
+ }
+ };
mManager.registerCallback(mExecutor, managerCallback);
mRouter2.registerRouteCallback(mExecutor, routeCallback, preference);
@@ -923,15 +970,17 @@ public class MediaRouter2ManagerTest {
void awaitOnRouteChangedManager(Runnable task, String routeId,
Predicate<MediaRoute2Info> predicate) throws Exception {
CountDownLatch latch = new CountDownLatch(1);
- MediaRouter2Manager.Callback callback = new MediaRouter2Manager.Callback() {
- @Override
- public void onRoutesChanged(List<MediaRoute2Info> changed) {
- MediaRoute2Info route = createRouteMap(changed).get(routeId);
- if (route != null && predicate.test(route)) {
- latch.countDown();
- }
- }
- };
+ MediaRouter2Manager.Callback callback =
+ new MediaRouter2Manager.Callback() {
+ @Override
+ public void onRoutesUpdated() {
+ MediaRoute2Info route =
+ createRouteMap(mManager.getAllRoutes()).get(routeId);
+ if (route != null && predicate.test(route)) {
+ latch.countDown();
+ }
+ }
+ };
mManager.registerCallback(mExecutor, callback);
try {
task.run();
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/StubMediaRoute2ProviderService.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/StubMediaRoute2ProviderService.java
index a51e3714b6f7..a7ae5f45b795 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/StubMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/StubMediaRoute2ProviderService.java
@@ -30,7 +30,9 @@ import android.os.Bundle;
import android.os.IBinder;
import android.text.TextUtils;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -146,19 +148,44 @@ public class StubMediaRoute2ProviderService extends MediaRoute2ProviderService {
* they have the same route id.
*/
public void addRoute(@NonNull MediaRoute2Info route) {
- Objects.requireNonNull(route, "route must not be null");
- mRoutes.put(route.getOriginalId(), route);
- publishRoutes();
+ addRoutes(Collections.singletonList(route));
}
/**
- * Removes a route and publishes it.
+ * Adds a list of routes and publishes it. It will replace existing routes with matching ids.
+ *
+ * @param routes list of routes to be added.
*/
+ public void addRoutes(@NonNull List<MediaRoute2Info> routes) {
+ Objects.requireNonNull(routes, "Routes must not be null.");
+ for (MediaRoute2Info route : routes) {
+ Objects.requireNonNull(route, "Route must not be null");
+ mRoutes.put(route.getOriginalId(), route);
+ }
+ publishRoutes();
+ }
+
+ /** Removes a route and publishes it. */
public void removeRoute(@NonNull String routeId) {
- Objects.requireNonNull(routeId, "routeId must not be null");
- MediaRoute2Info route = mRoutes.get(routeId);
- if (route != null) {
- mRoutes.remove(routeId);
+ removeRoutes(Collections.singletonList(routeId));
+ }
+
+ /**
+ * Removes a list of routes and publishes the changes.
+ *
+ * @param routes list of route ids to be removed.
+ */
+ public void removeRoutes(@NonNull List<String> routes) {
+ Objects.requireNonNull(routes, "Routes must not be null");
+ boolean hasRemovedRoutes = false;
+ for (String routeId : routes) {
+ MediaRoute2Info route = mRoutes.get(routeId);
+ if (route != null) {
+ mRoutes.remove(routeId);
+ hasRemovedRoutes = true;
+ }
+ }
+ if (hasRemovedRoutes) {
publishRoutes();
}
}
diff --git a/packages/SettingsLib/Spa/OWNERS b/packages/SettingsLib/Spa/OWNERS
index b352b045c352..288787241caa 100644
--- a/packages/SettingsLib/Spa/OWNERS
+++ b/packages/SettingsLib/Spa/OWNERS
@@ -1,6 +1,6 @@
+set noparent
+
chaohuiw@google.com
hanxu@google.com
kellyz@google.com
pierreqian@google.com
-
-per-file *.xml = set noparent
diff --git a/packages/SettingsLib/Spa/build.gradle b/packages/SettingsLib/Spa/build.gradle
index 8c97eca548b5..d38013679ad4 100644
--- a/packages/SettingsLib/Spa/build.gradle
+++ b/packages/SettingsLib/Spa/build.gradle
@@ -16,9 +16,9 @@
buildscript {
ext {
- minSdk_version = 31
- compose_version = '1.2.0-alpha04'
- compose_material3_version = '1.0.0-alpha06'
+ spa_min_sdk = 31
+ jetpack_compose_version = '1.2.0-alpha04'
+ jetpack_compose_material3_version = '1.0.0-alpha06'
}
}
plugins {
diff --git a/packages/SettingsLib/Spa/codelab/AndroidManifest.xml b/packages/SettingsLib/Spa/codelab/AndroidManifest.xml
index 9a89e5efdddb..36b93134bdcb 100644
--- a/packages/SettingsLib/Spa/codelab/AndroidManifest.xml
+++ b/packages/SettingsLib/Spa/codelab/AndroidManifest.xml
@@ -13,14 +13,14 @@
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.settingslib.spa.codelab">
<application
android:label="@string/app_name"
android:supportsRtl="true"
- android:theme="@style/Theme.SettingsLib.Compose.DayNight">
+ android:theme="@style/Theme.SpaLib.DayNight">
<activity
android:name="com.android.settingslib.spa.codelab.MainActivity"
android:exported="true">
diff --git a/packages/SettingsLib/Spa/codelab/build.gradle b/packages/SettingsLib/Spa/codelab/build.gradle
index 5251ddd8c01d..169ecf08aeac 100644
--- a/packages/SettingsLib/Spa/codelab/build.gradle
+++ b/packages/SettingsLib/Spa/codelab/build.gradle
@@ -25,7 +25,7 @@ android {
defaultConfig {
applicationId "com.android.settingslib.spa.codelab"
- minSdk minSdk_version
+ minSdk spa_min_sdk
targetSdk 33
versionCode 1
versionName "1.0"
@@ -52,7 +52,7 @@ android {
compose true
}
composeOptions {
- kotlinCompilerExtensionVersion compose_version
+ kotlinCompilerExtensionVersion jetpack_compose_version
}
packagingOptions {
resources {
diff --git a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/ArgumentPage.kt b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/ArgumentPage.kt
index 484b6045f443..5c6b609b25a4 100644
--- a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/ArgumentPage.kt
+++ b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/ArgumentPage.kt
@@ -22,10 +22,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import androidx.navigation.NavType
import androidx.navigation.navArgument
-import com.android.settingslib.spa.api.SettingsPageProvider
-import com.android.settingslib.spa.framework.navigator
-import com.android.settingslib.spa.framework.toState
-import com.android.settingslib.spa.theme.SettingsTheme
+import com.android.settingslib.spa.framework.api.SettingsPageProvider
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.framework.compose.toState
+import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
diff --git a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/FooterPage.kt b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/FooterPage.kt
new file mode 100644
index 000000000000..9fcbff843203
--- /dev/null
+++ b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/FooterPage.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.settingslib.spa.codelab.page
+
+import android.os.Bundle
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.api.SettingsPageProvider
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.framework.compose.stateOf
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.ui.Footer
+
+object FooterPageProvider : SettingsPageProvider {
+ override val name = "Footer"
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ FooterPage()
+ }
+
+ @Composable
+ fun EntryItem() {
+ Preference(object : PreferenceModel {
+ override val title = "Sample Footer"
+ override val onClick = navigator(name)
+ })
+ }
+}
+
+@Composable
+private fun FooterPage() {
+ Column(Modifier.verticalScroll(rememberScrollState())) {
+ Preference(remember {
+ object : PreferenceModel {
+ override val title = "Some Preference"
+ override val summary = stateOf("Some summary")
+ }
+ })
+ Footer(footerText = "Footer text always at the end of page.")
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun FooterPagePreview() {
+ SettingsTheme {
+ FooterPage()
+ }
+}
diff --git a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/HomePage.kt b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/HomePage.kt
index 27c70e450555..57a69c4be22f 100644
--- a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/HomePage.kt
+++ b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/HomePage.kt
@@ -25,10 +25,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
-import com.android.settingslib.spa.api.SettingsPageProvider
import com.android.settingslib.spa.codelab.R
-import com.android.settingslib.spa.theme.SettingsDimension
-import com.android.settingslib.spa.theme.SettingsTheme
+import com.android.settingslib.spa.framework.api.SettingsPageProvider
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsTheme
object HomePageProvider : SettingsPageProvider {
override val name = Destinations.Home
@@ -50,8 +50,11 @@ private fun HomePage() {
)
PreferencePageProvider.EntryItem()
-
+ SwitchPreferencePageProvider.EntryItem()
ArgumentPageProvider.EntryItem(stringParam = "foo", intParam = 0)
+
+ SliderPageProvider.EntryItem()
+ FooterPageProvider.EntryItem()
}
}
diff --git a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PageRepository.kt b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PageRepository.kt
index 862fc1ee3151..54c588ab18f0 100644
--- a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PageRepository.kt
+++ b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PageRepository.kt
@@ -16,15 +16,24 @@
package com.android.settingslib.spa.codelab.page
-import com.android.settingslib.spa.api.SettingsPageRepository
+import com.android.settingslib.spa.framework.api.SettingsPageRepository
object Destinations {
const val Home = "Home"
const val Preference = "Preference"
+ const val SwitchPreference = "SwitchPreference"
const val Argument = "Argument"
+ const val Slider = "Slider"
}
val codelabPageRepository = SettingsPageRepository(
- allPages = listOf(HomePageProvider, PreferencePageProvider, ArgumentPageProvider),
+ allPages = listOf(
+ HomePageProvider,
+ PreferencePageProvider,
+ SwitchPreferencePageProvider,
+ ArgumentPageProvider,
+ SliderPageProvider,
+ FooterPageProvider,
+ ),
startDestination = Destinations.Home,
)
diff --git a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PreferencePage.kt b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PreferencePage.kt
index 81627f60168e..d53562d31566 100644
--- a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PreferencePage.kt
+++ b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PreferencePage.kt
@@ -30,10 +30,10 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
-import com.android.settingslib.spa.api.SettingsPageProvider
-import com.android.settingslib.spa.framework.navigator
-import com.android.settingslib.spa.framework.toState
-import com.android.settingslib.spa.theme.SettingsTheme
+import com.android.settingslib.spa.framework.api.SettingsPageProvider
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.framework.compose.toState
+import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import kotlinx.coroutines.delay
diff --git a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/SliderPage.kt b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/SliderPage.kt
new file mode 100644
index 000000000000..6e965813afc4
--- /dev/null
+++ b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/SliderPage.kt
@@ -0,0 +1,107 @@
+/*
+ * 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.settingslib.spa.codelab.page
+
+import android.os.Bundle
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.AccessAlarm
+import androidx.compose.material.icons.outlined.MusicNote
+import androidx.compose.material.icons.outlined.MusicOff
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.api.SettingsPageProvider
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.ui.SettingsSlider
+import com.android.settingslib.spa.widget.ui.SettingsSliderModel
+
+object SliderPageProvider : SettingsPageProvider {
+ override val name = Destinations.Slider
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ SliderPage()
+ }
+
+ @Composable
+ fun EntryItem() {
+ Preference(object : PreferenceModel {
+ override val title = "Sample Slider"
+ override val onClick = navigator(Destinations.Slider)
+ })
+ }
+}
+
+@Composable
+private fun SliderPage() {
+ Column(Modifier.verticalScroll(rememberScrollState())) {
+ SettingsSlider(object : SettingsSliderModel {
+ override val title = "Slider"
+ override val initValue = 40
+ })
+
+ SettingsSlider(object : SettingsSliderModel {
+ override val title = "Slider with icon"
+ override val initValue = 30
+ override val onValueChangeFinished = {
+ println("onValueChangeFinished")
+ }
+ override val icon = Icons.Outlined.AccessAlarm
+ })
+
+ val initValue = 0
+ var icon by remember { mutableStateOf(Icons.Outlined.MusicOff) }
+ var sliderPosition by remember { mutableStateOf(initValue) }
+ SettingsSlider(object : SettingsSliderModel {
+ override val title = "Slider with changeable icon"
+ override val initValue = initValue
+ override val onValueChange = { it: Int ->
+ sliderPosition = it
+ icon = if (it > 0) Icons.Outlined.MusicNote else Icons.Outlined.MusicOff
+ }
+ override val onValueChangeFinished = {
+ println("onValueChangeFinished: the value is $sliderPosition")
+ }
+ override val icon = icon
+ })
+
+ SettingsSlider(object : SettingsSliderModel {
+ override val title = "Slider with steps"
+ override val initValue = 2
+ override val valueRange = 1..5
+ override val showSteps = true
+ })
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun SliderPagePreview() {
+ SettingsTheme {
+ SliderPage()
+ }
+}
diff --git a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/SwitchPreferencePage.kt b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/SwitchPreferencePage.kt
new file mode 100644
index 000000000000..b566afa84d42
--- /dev/null
+++ b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/SwitchPreferencePage.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.settingslib.spa.codelab.page
+
+import android.os.Bundle
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.produceState
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.api.SettingsPageProvider
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.framework.compose.stateOf
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.preference.SwitchPreference
+import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
+import kotlinx.coroutines.delay
+
+object SwitchPreferencePageProvider : SettingsPageProvider {
+ override val name = Destinations.SwitchPreference
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ SwitchPreferencePage()
+ }
+
+ @Composable
+ fun EntryItem() {
+ Preference(object : PreferenceModel {
+ override val title = "Sample SwitchPreference"
+ override val onClick = navigator(Destinations.SwitchPreference)
+ })
+ }
+}
+
+@Composable
+private fun SwitchPreferencePage() {
+ Column(Modifier.verticalScroll(rememberScrollState())) {
+ SampleSwitchPreference()
+ SampleSwitchPreferenceWithSummary()
+ SampleSwitchPreferenceWithAsyncSummary()
+ SampleNotChangeableSwitchPreference()
+ }
+}
+
+@Composable
+private fun SampleSwitchPreference() {
+ val checked = rememberSaveable { mutableStateOf(false) }
+ SwitchPreference(remember {
+ object : SwitchPreferenceModel {
+ override val title = "SwitchPreference"
+ override val checked = checked
+ override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
+ }
+ })
+}
+
+@Composable
+private fun SampleSwitchPreferenceWithSummary() {
+ val checked = rememberSaveable { mutableStateOf(true) }
+ SwitchPreference(remember {
+ object : SwitchPreferenceModel {
+ override val title = "SwitchPreference"
+ override val summary = stateOf("With summary")
+ override val checked = checked
+ override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
+ }
+ })
+}
+
+@Composable
+private fun SampleSwitchPreferenceWithAsyncSummary() {
+ val checked = rememberSaveable { mutableStateOf(true) }
+ val summary = produceState(initialValue = " ") {
+ delay(1000L)
+ value = "Async summary"
+ }
+ SwitchPreference(remember {
+ object : SwitchPreferenceModel {
+ override val title = "SwitchPreference"
+ override val summary = summary
+ override val checked = checked
+ override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
+ }
+ })
+}
+
+@Composable
+private fun SampleNotChangeableSwitchPreference() {
+ val checked = rememberSaveable { mutableStateOf(true) }
+ SwitchPreference(remember {
+ object : SwitchPreferenceModel {
+ override val title = "SwitchPreference"
+ override val summary = stateOf("Not changeable")
+ override val changeable = stateOf(false)
+ override val checked = checked
+ override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
+ }
+ })
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun SwitchPreferencePagePreview() {
+ SettingsTheme {
+ SwitchPreferencePage()
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/Android.bp b/packages/SettingsLib/Spa/spa/Android.bp
index 463c0765943f..3c8d91edc05b 100644
--- a/packages/SettingsLib/Spa/spa/Android.bp
+++ b/packages/SettingsLib/Spa/spa/Android.bp
@@ -31,6 +31,9 @@ android_library {
"androidx.navigation_navigation-compose",
"com.google.android.material_material",
],
- kotlincflags: ["-Xjvm-default=all"],
+ kotlincflags: [
+ "-Xjvm-default=all",
+ "-Xopt-in=kotlin.RequiresOptIn",
+ ],
min_sdk_version: "31",
}
diff --git a/packages/SettingsLib/Spa/spa/build.gradle b/packages/SettingsLib/Spa/spa/build.gradle
index 60794c88d00d..ad69da314735 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle
+++ b/packages/SettingsLib/Spa/spa/build.gradle
@@ -24,7 +24,7 @@ android {
compileSdk 33
defaultConfig {
- minSdk minSdk_version
+ minSdk spa_min_sdk
targetSdk 33
}
@@ -43,13 +43,13 @@ android {
}
kotlinOptions {
jvmTarget = '1.8'
- freeCompilerArgs = ["-Xjvm-default=all"]
+ freeCompilerArgs = ["-Xjvm-default=all", "-Xopt-in=kotlin.RequiresOptIn"]
}
buildFeatures {
compose true
}
composeOptions {
- kotlinCompilerExtensionVersion compose_version
+ kotlinCompilerExtensionVersion jetpack_compose_version
}
packagingOptions {
resources {
@@ -59,11 +59,11 @@ android {
}
dependencies {
- api "androidx.compose.material3:material3:$compose_material3_version"
- api "androidx.compose.material:material-icons-extended:$compose_version"
- api "androidx.compose.runtime:runtime-livedata:$compose_version"
- api "androidx.compose.ui:ui-tooling-preview:$compose_version"
+ api "androidx.compose.material3:material3:$jetpack_compose_material3_version"
+ api "androidx.compose.material:material-icons-extended:$jetpack_compose_version"
+ api "androidx.compose.runtime:runtime-livedata:$jetpack_compose_version"
+ api "androidx.compose.ui:ui-tooling-preview:$jetpack_compose_version"
api 'androidx.navigation:navigation-compose:2.5.0'
api 'com.google.android.material:material:1.6.1'
- debugApi "androidx.compose.ui:ui-tooling:$compose_version"
+ debugApi "androidx.compose.ui:ui-tooling:$jetpack_compose_version"
}
diff --git a/packages/SettingsLib/Spa/spa/res/values-night/themes.xml b/packages/SettingsLib/Spa/spa/res/values-night/themes.xml
index 8b52b507bdd9..67dd2b0cc5e0 100644
--- a/packages/SettingsLib/Spa/spa/res/values-night/themes.xml
+++ b/packages/SettingsLib/Spa/spa/res/values-night/themes.xml
@@ -13,8 +13,8 @@
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>
- <style name="Theme.SettingsLib.Compose.DayNight" />
+ <style name="Theme.SpaLib.DayNight" />
</resources>
diff --git a/packages/SettingsLib/Spa/spa/res/values/themes.xml b/packages/SettingsLib/Spa/spa/res/values/themes.xml
index 01f9ea592f6d..e0e5fc211ec6 100644
--- a/packages/SettingsLib/Spa/spa/res/values/themes.xml
+++ b/packages/SettingsLib/Spa/spa/res/values/themes.xml
@@ -13,15 +13,15 @@
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>
- <style name="Theme.SettingsLib.Compose" parent="Theme.Material3.DayNight.NoActionBar">
+ <style name="Theme.SpaLib" parent="Theme.Material3.DayNight.NoActionBar">
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
- <style name="Theme.SettingsLib.Compose.DayNight">
+ <style name="Theme.SpaLib.DayNight">
<item name="android:windowLightStatusBar">true</item>
</style>
</resources>
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/SpaActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/SpaActivity.kt
index 28a5899e0141..5b39b6e08e6a 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/SpaActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/SpaActivity.kt
@@ -24,9 +24,10 @@ import androidx.compose.runtime.CompositionLocalProvider
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
-import com.android.settingslib.spa.api.SettingsPageProvider
-import com.android.settingslib.spa.api.SettingsPageRepository
-import com.android.settingslib.spa.theme.SettingsTheme
+import com.android.settingslib.spa.framework.api.SettingsPageProvider
+import com.android.settingslib.spa.framework.api.SettingsPageRepository
+import com.android.settingslib.spa.framework.compose.localNavController
+import com.android.settingslib.spa.framework.theme.SettingsTheme
open class SpaActivity(
private val settingsPageRepository: SettingsPageRepository,
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/api/SettingsPageProvider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/api/SettingsPageProvider.kt
index 0ad0003cca64..84daf224fa10 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/api/SettingsPageProvider.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/api/SettingsPageProvider.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.api
+package com.android.settingslib.spa.framework.api
import android.os.Bundle
import androidx.compose.runtime.Composable
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/api/SettingsPageRepository.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/api/SettingsPageRepository.kt
index ce39f4fbfa2e..4a270b128eb7 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/api/SettingsPageRepository.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/api/SettingsPageRepository.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.api
+package com.android.settingslib.spa.framework.api
data class SettingsPageRepository(
val allPages: List<SettingsPageProvider>,
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/DrawablePainter.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/DrawablePainter.kt
new file mode 100644
index 000000000000..ae325f8862eb
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/DrawablePainter.kt
@@ -0,0 +1,180 @@
+/*
+ * 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.settingslib.spa.framework.compose
+
+import android.graphics.drawable.Animatable
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.ColorDrawable
+import android.graphics.drawable.Drawable
+import android.os.Handler
+import android.os.Looper
+import android.view.View
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.RememberObserver
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.ColorFilter
+import androidx.compose.ui.graphics.asAndroidColorFilter
+import androidx.compose.ui.graphics.asImageBitmap
+import androidx.compose.ui.graphics.drawscope.DrawScope
+import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
+import androidx.compose.ui.graphics.nativeCanvas
+import androidx.compose.ui.graphics.painter.BitmapPainter
+import androidx.compose.ui.graphics.painter.ColorPainter
+import androidx.compose.ui.graphics.painter.Painter
+import androidx.compose.ui.graphics.withSave
+import androidx.compose.ui.unit.LayoutDirection
+import kotlin.math.roundToInt
+
+/**
+ * *************************************************************************************************
+ * This file was forked from
+ * https://github.com/google/accompanist/blob/main/drawablepainter/src/main/java/com/google/accompanist/drawablepainter/DrawablePainter.kt
+ * and will be removed once it lands in AndroidX.
+ */
+
+private val MAIN_HANDLER by lazy(LazyThreadSafetyMode.NONE) {
+ Handler(Looper.getMainLooper())
+}
+
+/**
+ * A [Painter] which draws an Android [Drawable] and supports [Animatable] drawables. Instances
+ * should be remembered to be able to start and stop [Animatable] animations.
+ *
+ * Instances are usually retrieved from [rememberDrawablePainter].
+ */
+class DrawablePainter(
+ val drawable: Drawable
+) : Painter(), RememberObserver {
+ private var drawInvalidateTick by mutableStateOf(0)
+ private var drawableIntrinsicSize by mutableStateOf(drawable.intrinsicSize)
+
+ private val callback: Drawable.Callback by lazy {
+ object : Drawable.Callback {
+ override fun invalidateDrawable(d: Drawable) {
+ // Update the tick so that we get re-drawn
+ drawInvalidateTick++
+ // Update our intrinsic size too
+ drawableIntrinsicSize = drawable.intrinsicSize
+ }
+
+ override fun scheduleDrawable(d: Drawable, what: Runnable, time: Long) {
+ MAIN_HANDLER.postAtTime(what, time)
+ }
+
+ override fun unscheduleDrawable(d: Drawable, what: Runnable) {
+ MAIN_HANDLER.removeCallbacks(what)
+ }
+ }
+ }
+
+ init {
+ if (drawable.intrinsicWidth >= 0 && drawable.intrinsicHeight >= 0) {
+ // Update the drawable's bounds to match the intrinsic size
+ drawable.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight)
+ }
+ }
+
+ override fun onRemembered() {
+ drawable.callback = callback
+ drawable.setVisible(true, true)
+ if (drawable is Animatable) drawable.start()
+ }
+
+ override fun onAbandoned() = onForgotten()
+
+ override fun onForgotten() {
+ if (drawable is Animatable) drawable.stop()
+ drawable.setVisible(false, false)
+ drawable.callback = null
+ }
+
+ override fun applyAlpha(alpha: Float): Boolean {
+ drawable.alpha = (alpha * 255).roundToInt().coerceIn(0, 255)
+ return true
+ }
+
+ override fun applyColorFilter(colorFilter: ColorFilter?): Boolean {
+ drawable.colorFilter = colorFilter?.asAndroidColorFilter()
+ return true
+ }
+
+ override fun applyLayoutDirection(layoutDirection: LayoutDirection): Boolean =
+ drawable.setLayoutDirection(
+ when (layoutDirection) {
+ LayoutDirection.Ltr -> View.LAYOUT_DIRECTION_LTR
+ LayoutDirection.Rtl -> View.LAYOUT_DIRECTION_RTL
+ }
+ )
+
+ override val intrinsicSize: Size get() = drawableIntrinsicSize
+
+ override fun DrawScope.onDraw() {
+ drawIntoCanvas { canvas ->
+ // Reading this ensures that we invalidate when invalidateDrawable() is called
+ drawInvalidateTick
+
+ // Update the Drawable's bounds
+ drawable.setBounds(0, 0, size.width.roundToInt(), size.height.roundToInt())
+
+ canvas.withSave {
+ drawable.draw(canvas.nativeCanvas)
+ }
+ }
+ }
+}
+
+/**
+ * Remembers [Drawable] wrapped up as a [Painter]. This function attempts to un-wrap the
+ * drawable contents and use Compose primitives where possible.
+ *
+ * If the provided [drawable] is `null`, an empty no-op painter is returned.
+ *
+ * This function tries to dispatch lifecycle events to [drawable] as much as possible from
+ * within Compose.
+ *
+ * @sample com.google.accompanist.sample.drawablepainter.BasicSample
+ */
+@Composable
+fun rememberDrawablePainter(drawable: Drawable?): Painter = remember(drawable) {
+ when (drawable) {
+ null -> EmptyPainter
+ is BitmapDrawable -> BitmapPainter(drawable.bitmap.asImageBitmap())
+ is ColorDrawable -> ColorPainter(Color(drawable.color))
+ // Since the DrawablePainter will be remembered and it implements RememberObserver, it
+ // will receive the necessary events
+ else -> DrawablePainter(drawable.mutate())
+ }
+}
+
+private val Drawable.intrinsicSize: Size
+ get() = when {
+ // Only return a finite size if the drawable has an intrinsic size
+ intrinsicWidth >= 0 && intrinsicHeight >= 0 -> {
+ Size(width = intrinsicWidth.toFloat(), height = intrinsicHeight.toFloat())
+ }
+ else -> Size.Unspecified
+ }
+
+internal object EmptyPainter : Painter() {
+ override val intrinsicSize: Size get() = Size.Unspecified
+ override fun DrawScope.onDraw() {}
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/NavControllerWrapper.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavControllerWrapper.kt
index 35112adf322c..c68d5de0d7c7 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/NavControllerWrapper.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavControllerWrapper.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.framework
+package com.android.settingslib.spa.framework.compose
import androidx.compose.runtime.Composable
import androidx.compose.runtime.compositionLocalOf
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/RuntimeUtils.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/RuntimeUtils.kt
index 22d85e0cff88..ba8854653b0b 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/RuntimeUtils.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/RuntimeUtils.kt
@@ -14,11 +14,19 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.framework
+package com.android.settingslib.spa.framework.compose
+import android.content.Context
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalContext
+
+@Composable
+fun <T> rememberContext(constructor: (Context) -> T): T {
+ val context = LocalContext.current
+ return remember(context) { constructor(context) }
+}
/**
* Remember the [State] initialized with the [this].
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/MaterialColors.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/MaterialColors.kt
index 8cd9184317ca..4626f0bbed49 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/MaterialColors.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/MaterialColors.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.theme
+package com.android.settingslib.spa.framework.theme
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsColors.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsColors.kt
new file mode 100644
index 000000000000..27fdc916a434
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsColors.kt
@@ -0,0 +1,91 @@
+/*
+ * 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.settingslib.spa.framework.theme
+
+import android.content.Context
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.staticCompositionLocalOf
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
+
+data class SettingsColorScheme(
+ val background: Color = Color.Unspecified,
+ val categoryTitle: Color = Color.Unspecified,
+ val surface: Color = Color.Unspecified,
+ val surfaceHeader: Color = Color.Unspecified,
+ val secondaryText: Color = Color.Unspecified,
+ val primaryContainer: Color = Color.Unspecified,
+ val onPrimaryContainer: Color = Color.Unspecified,
+)
+
+internal val LocalColorScheme = staticCompositionLocalOf { SettingsColorScheme() }
+
+@Composable
+internal fun settingsColorScheme(isDarkTheme: Boolean): SettingsColorScheme {
+ val context = LocalContext.current
+ return remember(isDarkTheme) {
+ when {
+ isDarkTheme -> dynamicDarkColorScheme(context)
+ else -> dynamicLightColorScheme(context)
+ }
+ }
+}
+
+/**
+ * Creates a light dynamic color scheme.
+ *
+ * Use this function to create a color scheme based off the system wallpaper. If the developer
+ * changes the wallpaper this color scheme will change accordingly. This dynamic scheme is a
+ * light theme variant.
+ *
+ * @param context The context required to get system resource data.
+ */
+private fun dynamicLightColorScheme(context: Context): SettingsColorScheme {
+ val tonalPalette = dynamicTonalPalette(context)
+ return SettingsColorScheme(
+ background = tonalPalette.neutral95,
+ categoryTitle = tonalPalette.primary40,
+ surface = tonalPalette.neutral99,
+ surfaceHeader = tonalPalette.neutral90,
+ secondaryText = tonalPalette.neutralVariant30,
+ primaryContainer = tonalPalette.primary90,
+ onPrimaryContainer = tonalPalette.neutral10,
+ )
+}
+
+/**
+ * Creates a dark dynamic color scheme.
+ *
+ * Use this function to create a color scheme based off the system wallpaper. If the developer
+ * changes the wallpaper this color scheme will change accordingly. This dynamic scheme is a dark
+ * theme variant.
+ *
+ * @param context The context required to get system resource data.
+ */
+private fun dynamicDarkColorScheme(context: Context): SettingsColorScheme {
+ val tonalPalette = dynamicTonalPalette(context)
+ return SettingsColorScheme(
+ background = tonalPalette.neutral10,
+ categoryTitle = tonalPalette.primary90,
+ surface = tonalPalette.neutral20,
+ surfaceHeader = tonalPalette.neutral30,
+ secondaryText = tonalPalette.neutralVariant80,
+ primaryContainer = tonalPalette.secondary90,
+ onPrimaryContainer = tonalPalette.neutral10,
+ )
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsDimension.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
index 51e75c53e046..e1ca69bd7940 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsDimension.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
@@ -14,12 +14,13 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.theme
+package com.android.settingslib.spa.framework.theme
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.ui.unit.dp
object SettingsDimension {
+ val itemIconSize = 24.dp
val itemIconContainerSize = 72.dp
val itemPaddingStart = 24.dp
val itemPaddingEnd = 16.dp
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsOpacity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsOpacity.kt
index 5f4da8af5cb4..04ee3c3be402 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsOpacity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsOpacity.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.theme
+package com.android.settingslib.spa.framework.theme
object SettingsOpacity {
const val Full = 1f
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt
new file mode 100644
index 000000000000..e6fa74e34cc8
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.settingslib.spa.framework.theme
+
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.ReadOnlyComposable
+
+/**
+ * The Material 3 Theme for Settings.
+ */
+@Composable
+fun SettingsTheme(content: @Composable () -> Unit) {
+ val isDarkTheme = isSystemInDarkTheme()
+ val settingsColorScheme = settingsColorScheme(isDarkTheme)
+ val colorScheme = materialColorScheme(isDarkTheme).copy(
+ background = settingsColorScheme.background,
+ )
+
+ CompositionLocalProvider(LocalColorScheme provides settingsColorScheme(isDarkTheme)) {
+ MaterialTheme(colorScheme = colorScheme, typography = rememberSettingsTypography()) {
+ content()
+ }
+ }
+}
+
+object SettingsTheme {
+ val colorScheme: SettingsColorScheme
+ @Composable
+ @ReadOnlyComposable
+ get() = LocalColorScheme.current
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTonalPalette.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTonalPalette.kt
new file mode 100644
index 000000000000..f81f5e734fb4
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTonalPalette.kt
@@ -0,0 +1,205 @@
+/*
+ * 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.settingslib.spa.framework.theme
+
+import android.R
+import android.content.Context
+import androidx.annotation.ColorRes
+import androidx.annotation.DoNotInline
+import androidx.compose.ui.graphics.Color
+
+/**
+ * Tonal Palette structure in Material.
+ *
+ * A tonal palette is comprised of 5 tonal ranges. Each tonal range includes the 13 stops, or
+ * tonal swatches.
+ *
+ * Tonal range names are:
+ * - Neutral (N)
+ * - Neutral variant (NV)
+ * - Primary (P)
+ * - Secondary (S)
+ * - Tertiary (T)
+ */
+internal class SettingsTonalPalette(
+ // The neutral tonal range from the generated dynamic color palette.
+ // Ordered from the lightest shade [neutral100] to the darkest shade [neutral0].
+ val neutral100: Color,
+ val neutral99: Color,
+ val neutral95: Color,
+ val neutral90: Color,
+ val neutral80: Color,
+ val neutral70: Color,
+ val neutral60: Color,
+ val neutral50: Color,
+ val neutral40: Color,
+ val neutral30: Color,
+ val neutral20: Color,
+ val neutral10: Color,
+ val neutral0: Color,
+
+ // The neutral variant tonal range, sometimes called "neutral 2", from the
+ // generated dynamic color palette.
+ // Ordered from the lightest shade [neutralVariant100] to the darkest shade [neutralVariant0].
+ val neutralVariant100: Color,
+ val neutralVariant99: Color,
+ val neutralVariant95: Color,
+ val neutralVariant90: Color,
+ val neutralVariant80: Color,
+ val neutralVariant70: Color,
+ val neutralVariant60: Color,
+ val neutralVariant50: Color,
+ val neutralVariant40: Color,
+ val neutralVariant30: Color,
+ val neutralVariant20: Color,
+ val neutralVariant10: Color,
+ val neutralVariant0: Color,
+
+ // The primary tonal range from the generated dynamic color palette.
+ // Ordered from the lightest shade [primary100] to the darkest shade [primary0].
+ val primary100: Color,
+ val primary99: Color,
+ val primary95: Color,
+ val primary90: Color,
+ val primary80: Color,
+ val primary70: Color,
+ val primary60: Color,
+ val primary50: Color,
+ val primary40: Color,
+ val primary30: Color,
+ val primary20: Color,
+ val primary10: Color,
+ val primary0: Color,
+
+ // The secondary tonal range from the generated dynamic color palette.
+ // Ordered from the lightest shade [secondary100] to the darkest shade [secondary0].
+ val secondary100: Color,
+ val secondary99: Color,
+ val secondary95: Color,
+ val secondary90: Color,
+ val secondary80: Color,
+ val secondary70: Color,
+ val secondary60: Color,
+ val secondary50: Color,
+ val secondary40: Color,
+ val secondary30: Color,
+ val secondary20: Color,
+ val secondary10: Color,
+ val secondary0: Color,
+
+ // The tertiary tonal range from the generated dynamic color palette.
+ // Ordered from the lightest shade [tertiary100] to the darkest shade [tertiary0].
+ val tertiary100: Color,
+ val tertiary99: Color,
+ val tertiary95: Color,
+ val tertiary90: Color,
+ val tertiary80: Color,
+ val tertiary70: Color,
+ val tertiary60: Color,
+ val tertiary50: Color,
+ val tertiary40: Color,
+ val tertiary30: Color,
+ val tertiary20: Color,
+ val tertiary10: Color,
+ val tertiary0: Color,
+)
+
+/** Dynamic colors in Material. */
+internal fun dynamicTonalPalette(context: Context) = SettingsTonalPalette(
+ // The neutral tonal range from the generated dynamic color palette.
+ neutral100 = ColorResourceHelper.getColor(context, R.color.system_neutral1_0),
+ neutral99 = ColorResourceHelper.getColor(context, R.color.system_neutral1_10),
+ neutral95 = ColorResourceHelper.getColor(context, R.color.system_neutral1_50),
+ neutral90 = ColorResourceHelper.getColor(context, R.color.system_neutral1_100),
+ neutral80 = ColorResourceHelper.getColor(context, R.color.system_neutral1_200),
+ neutral70 = ColorResourceHelper.getColor(context, R.color.system_neutral1_300),
+ neutral60 = ColorResourceHelper.getColor(context, R.color.system_neutral1_400),
+ neutral50 = ColorResourceHelper.getColor(context, R.color.system_neutral1_500),
+ neutral40 = ColorResourceHelper.getColor(context, R.color.system_neutral1_600),
+ neutral30 = ColorResourceHelper.getColor(context, R.color.system_neutral1_700),
+ neutral20 = ColorResourceHelper.getColor(context, R.color.system_neutral1_800),
+ neutral10 = ColorResourceHelper.getColor(context, R.color.system_neutral1_900),
+ neutral0 = ColorResourceHelper.getColor(context, R.color.system_neutral1_1000),
+
+ // The neutral variant tonal range, sometimes called "neutral 2", from the
+ // generated dynamic color palette.
+ neutralVariant100 = ColorResourceHelper.getColor(context, R.color.system_neutral2_0),
+ neutralVariant99 = ColorResourceHelper.getColor(context, R.color.system_neutral2_10),
+ neutralVariant95 = ColorResourceHelper.getColor(context, R.color.system_neutral2_50),
+ neutralVariant90 = ColorResourceHelper.getColor(context, R.color.system_neutral2_100),
+ neutralVariant80 = ColorResourceHelper.getColor(context, R.color.system_neutral2_200),
+ neutralVariant70 = ColorResourceHelper.getColor(context, R.color.system_neutral2_300),
+ neutralVariant60 = ColorResourceHelper.getColor(context, R.color.system_neutral2_400),
+ neutralVariant50 = ColorResourceHelper.getColor(context, R.color.system_neutral2_500),
+ neutralVariant40 = ColorResourceHelper.getColor(context, R.color.system_neutral2_600),
+ neutralVariant30 = ColorResourceHelper.getColor(context, R.color.system_neutral2_700),
+ neutralVariant20 = ColorResourceHelper.getColor(context, R.color.system_neutral2_800),
+ neutralVariant10 = ColorResourceHelper.getColor(context, R.color.system_neutral2_900),
+ neutralVariant0 = ColorResourceHelper.getColor(context, R.color.system_neutral2_1000),
+
+ // The primary tonal range from the generated dynamic color palette.
+ primary100 = ColorResourceHelper.getColor(context, R.color.system_accent1_0),
+ primary99 = ColorResourceHelper.getColor(context, R.color.system_accent1_10),
+ primary95 = ColorResourceHelper.getColor(context, R.color.system_accent1_50),
+ primary90 = ColorResourceHelper.getColor(context, R.color.system_accent1_100),
+ primary80 = ColorResourceHelper.getColor(context, R.color.system_accent1_200),
+ primary70 = ColorResourceHelper.getColor(context, R.color.system_accent1_300),
+ primary60 = ColorResourceHelper.getColor(context, R.color.system_accent1_400),
+ primary50 = ColorResourceHelper.getColor(context, R.color.system_accent1_500),
+ primary40 = ColorResourceHelper.getColor(context, R.color.system_accent1_600),
+ primary30 = ColorResourceHelper.getColor(context, R.color.system_accent1_700),
+ primary20 = ColorResourceHelper.getColor(context, R.color.system_accent1_800),
+ primary10 = ColorResourceHelper.getColor(context, R.color.system_accent1_900),
+ primary0 = ColorResourceHelper.getColor(context, R.color.system_accent1_1000),
+
+ // The secondary tonal range from the generated dynamic color palette.
+ secondary100 = ColorResourceHelper.getColor(context, R.color.system_accent2_0),
+ secondary99 = ColorResourceHelper.getColor(context, R.color.system_accent2_10),
+ secondary95 = ColorResourceHelper.getColor(context, R.color.system_accent2_50),
+ secondary90 = ColorResourceHelper.getColor(context, R.color.system_accent2_100),
+ secondary80 = ColorResourceHelper.getColor(context, R.color.system_accent2_200),
+ secondary70 = ColorResourceHelper.getColor(context, R.color.system_accent2_300),
+ secondary60 = ColorResourceHelper.getColor(context, R.color.system_accent2_400),
+ secondary50 = ColorResourceHelper.getColor(context, R.color.system_accent2_500),
+ secondary40 = ColorResourceHelper.getColor(context, R.color.system_accent2_600),
+ secondary30 = ColorResourceHelper.getColor(context, R.color.system_accent2_700),
+ secondary20 = ColorResourceHelper.getColor(context, R.color.system_accent2_800),
+ secondary10 = ColorResourceHelper.getColor(context, R.color.system_accent2_900),
+ secondary0 = ColorResourceHelper.getColor(context, R.color.system_accent2_1000),
+
+ // The tertiary tonal range from the generated dynamic color palette.
+ tertiary100 = ColorResourceHelper.getColor(context, R.color.system_accent3_0),
+ tertiary99 = ColorResourceHelper.getColor(context, R.color.system_accent3_10),
+ tertiary95 = ColorResourceHelper.getColor(context, R.color.system_accent3_50),
+ tertiary90 = ColorResourceHelper.getColor(context, R.color.system_accent3_100),
+ tertiary80 = ColorResourceHelper.getColor(context, R.color.system_accent3_200),
+ tertiary70 = ColorResourceHelper.getColor(context, R.color.system_accent3_300),
+ tertiary60 = ColorResourceHelper.getColor(context, R.color.system_accent3_400),
+ tertiary50 = ColorResourceHelper.getColor(context, R.color.system_accent3_500),
+ tertiary40 = ColorResourceHelper.getColor(context, R.color.system_accent3_600),
+ tertiary30 = ColorResourceHelper.getColor(context, R.color.system_accent3_700),
+ tertiary20 = ColorResourceHelper.getColor(context, R.color.system_accent3_800),
+ tertiary10 = ColorResourceHelper.getColor(context, R.color.system_accent3_900),
+ tertiary0 = ColorResourceHelper.getColor(context, R.color.system_accent3_1000),
+)
+
+private object ColorResourceHelper {
+ @DoNotInline
+ fun getColor(context: Context, @ColorRes id: Int): Color {
+ return Color(context.resources.getColor(id, context.theme))
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTypography.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTypography.kt
new file mode 100644
index 000000000000..07f09ba95ca3
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTypography.kt
@@ -0,0 +1,144 @@
+/*
+ * 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.settingslib.spa.framework.theme
+
+import androidx.compose.material3.Typography
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.em
+import androidx.compose.ui.unit.sp
+
+private class SettingsTypography {
+ private val brand = FontFamily.Default
+ private val plain = FontFamily.Default
+
+ val typography = Typography(
+ displayLarge = TextStyle(
+ fontFamily = brand,
+ fontWeight = FontWeight.Normal,
+ fontSize = 57.sp,
+ lineHeight = 64.sp,
+ letterSpacing = (-0.2).sp
+ ),
+ displayMedium = TextStyle(
+ fontFamily = brand,
+ fontWeight = FontWeight.Normal,
+ fontSize = 45.sp,
+ lineHeight = 52.sp,
+ letterSpacing = 0.0.sp
+ ),
+ displaySmall = TextStyle(
+ fontFamily = brand,
+ fontWeight = FontWeight.Normal,
+ fontSize = 36.sp,
+ lineHeight = 44.sp,
+ letterSpacing = 0.0.sp
+ ),
+ headlineLarge = TextStyle(
+ fontFamily = brand,
+ fontWeight = FontWeight.Normal,
+ fontSize = 32.sp,
+ lineHeight = 40.sp,
+ letterSpacing = 0.0.sp
+ ),
+ headlineMedium = TextStyle(
+ fontFamily = brand,
+ fontWeight = FontWeight.Normal,
+ fontSize = 28.sp,
+ lineHeight = 36.sp,
+ letterSpacing = 0.0.sp
+ ),
+ headlineSmall = TextStyle(
+ fontFamily = brand,
+ fontWeight = FontWeight.Normal,
+ fontSize = 24.sp,
+ lineHeight = 32.sp,
+ letterSpacing = 0.0.sp
+ ),
+ titleLarge = TextStyle(
+ fontFamily = brand,
+ fontWeight = FontWeight.Normal,
+ fontSize = 22.sp,
+ lineHeight = 28.sp,
+ letterSpacing = 0.02.em,
+ ),
+ titleMedium = TextStyle(
+ fontFamily = brand,
+ fontWeight = FontWeight.Normal,
+ fontSize = 20.sp,
+ lineHeight = 24.sp,
+ letterSpacing = 0.02.em,
+ ),
+ titleSmall = TextStyle(
+ fontFamily = brand,
+ fontWeight = FontWeight.Normal,
+ fontSize = 18.sp,
+ lineHeight = 20.sp,
+ letterSpacing = 0.02.em,
+ ),
+ bodyLarge = TextStyle(
+ fontFamily = plain,
+ fontWeight = FontWeight.Normal,
+ fontSize = 16.sp,
+ lineHeight = 24.sp,
+ letterSpacing = 0.01.em,
+ ),
+ bodyMedium = TextStyle(
+ fontFamily = plain,
+ fontWeight = FontWeight.Normal,
+ fontSize = 14.sp,
+ lineHeight = 20.sp,
+ letterSpacing = 0.01.em,
+ ),
+ bodySmall = TextStyle(
+ fontFamily = plain,
+ fontWeight = FontWeight.Normal,
+ fontSize = 12.sp,
+ lineHeight = 16.sp,
+ letterSpacing = 0.01.em,
+ ),
+ labelLarge = TextStyle(
+ fontFamily = plain,
+ fontWeight = FontWeight.Medium,
+ fontSize = 16.sp,
+ lineHeight = 24.sp,
+ letterSpacing = 0.01.em,
+ ),
+ labelMedium = TextStyle(
+ fontFamily = plain,
+ fontWeight = FontWeight.Medium,
+ fontSize = 14.sp,
+ lineHeight = 20.sp,
+ letterSpacing = 0.01.em,
+ ),
+ labelSmall = TextStyle(
+ fontFamily = plain,
+ fontWeight = FontWeight.Medium,
+ fontSize = 12.sp,
+ lineHeight = 16.sp,
+ letterSpacing = 0.01.em,
+ ),
+ )
+}
+
+@Composable
+internal fun rememberSettingsTypography(): Typography {
+ return remember { SettingsTypography().typography }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt
index d415e9b262e3..9a34dbf36735 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt
@@ -25,8 +25,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Divider
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.Alignment
@@ -35,10 +33,11 @@ import androidx.compose.ui.draw.alpha
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
-import com.android.settingslib.spa.framework.toState
-import com.android.settingslib.spa.theme.SettingsDimension
-import com.android.settingslib.spa.theme.SettingsOpacity
-import com.android.settingslib.spa.theme.SettingsTheme
+import com.android.settingslib.spa.framework.compose.toState
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsOpacity
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.ui.SettingsTitle
@Composable
internal fun BaseLayout(
@@ -94,11 +93,7 @@ private fun BaseIcon(
@Composable
private fun Titles(title: String, subTitle: @Composable () -> Unit, modifier: Modifier) {
Column(modifier) {
- Text(
- text = title,
- color = MaterialTheme.colorScheme.onSurface,
- style = MaterialTheme.typography.titleMedium,
- )
+ SettingsTitle(title)
subTitle()
}
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BasePreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BasePreference.kt
index 563a47a9a940..4b2c8e41a388 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BasePreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BasePreference.kt
@@ -19,16 +19,15 @@ package com.android.settingslib.spa.widget.preference
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.BatteryChargingFull
import androidx.compose.material3.Icon
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
-import com.android.settingslib.spa.framework.toState
-import com.android.settingslib.spa.theme.SettingsDimension
-import com.android.settingslib.spa.theme.SettingsTheme
+import com.android.settingslib.spa.framework.compose.toState
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.ui.SettingsBody
@Composable
internal fun BasePreference(
@@ -44,15 +43,7 @@ internal fun BasePreference(
) {
BaseLayout(
title = title,
- subTitle = {
- if (summary.value.isNotEmpty()) {
- Text(
- text = summary.value,
- color = MaterialTheme.colorScheme.onSurfaceVariant,
- style = MaterialTheme.typography.bodyMedium,
- )
- }
- },
+ subTitle = { SettingsBody(summary) },
modifier = modifier,
icon = icon,
enabled = enabled,
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt
index ee20280fb0cc..1a80ed20b031 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt
@@ -20,7 +20,7 @@ import androidx.compose.foundation.clickable
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.Modifier
-import com.android.settingslib.spa.framework.stateOf
+import com.android.settingslib.spa.framework.compose.stateOf
/**
* The widget model for [Preference] widget.
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SwitchPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SwitchPreference.kt
new file mode 100644
index 000000000000..0dab0dfe1d6d
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SwitchPreference.kt
@@ -0,0 +1,146 @@
+/*
+ * 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.settingslib.spa.widget.preference
+
+import androidx.compose.foundation.LocalIndication
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.selection.toggleable
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.Dp
+import com.android.settingslib.spa.framework.compose.stateOf
+import com.android.settingslib.spa.framework.compose.toState
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.ui.SettingsSwitch
+
+/**
+ * The widget model for [SwitchPreference] widget.
+ */
+interface SwitchPreferenceModel {
+ /**
+ * The title of this [SwitchPreference].
+ */
+ val title: String
+
+ /**
+ * The summary of this [SwitchPreference].
+ */
+ val summary: State<String>
+ get() = stateOf("")
+
+ /**
+ * Indicates whether this [SwitchPreference] is checked.
+ *
+ * This can be `null` during the data loading before the data is available.
+ */
+ val checked: State<Boolean?>
+
+ /**
+ * Indicates whether this [SwitchPreference] is changeable.
+ *
+ * Not changeable [SwitchPreference] will be displayed in disabled style.
+ */
+ val changeable: State<Boolean>
+ get() = stateOf(true)
+
+ /**
+ * The switch change handler of this [SwitchPreference].
+ *
+ * If `null`, this [SwitchPreference] is not [toggleable].
+ */
+ val onCheckedChange: ((newChecked: Boolean) -> Unit)?
+}
+
+/**
+ * SwitchPreference widget.
+ *
+ * Data is provided through [SwitchPreferenceModel].
+ */
+@Composable
+fun SwitchPreference(model: SwitchPreferenceModel) {
+ InternalSwitchPreference(
+ title = model.title,
+ summary = model.summary,
+ checked = model.checked,
+ changeable = model.changeable,
+ onCheckedChange = model.onCheckedChange,
+ )
+}
+
+@Composable
+internal fun InternalSwitchPreference(
+ title: String,
+ summary: State<String> = "".toState(),
+ checked: State<Boolean?>,
+ changeable: State<Boolean> = true.toState(),
+ paddingStart: Dp = SettingsDimension.itemPaddingStart,
+ paddingEnd: Dp = SettingsDimension.itemPaddingEnd,
+ paddingVertical: Dp = SettingsDimension.itemPaddingVertical,
+ onCheckedChange: ((newChecked: Boolean) -> Unit)?,
+) {
+ val checkedValue = checked.value
+ val indication = LocalIndication.current
+ val modifier = remember(checkedValue) {
+ if (checkedValue != null && onCheckedChange != null) {
+ Modifier.toggleable(
+ value = checkedValue,
+ interactionSource = MutableInteractionSource(),
+ indication = indication,
+ enabled = changeable.value,
+ role = Role.Switch,
+ onValueChange = onCheckedChange,
+ )
+ } else Modifier
+ }
+ BasePreference(
+ title = title,
+ summary = summary,
+ modifier = modifier,
+ enabled = changeable,
+ paddingStart = paddingStart,
+ paddingEnd = paddingEnd,
+ paddingVertical = paddingVertical,
+ ) {
+ SettingsSwitch(checked = checked, changeable = changeable)
+ }
+}
+
+@Preview
+@Composable
+private fun SwitchPreferencePreview() {
+ SettingsTheme {
+ Column {
+ InternalSwitchPreference(
+ title = "Use Dark theme",
+ checked = true.toState(),
+ onCheckedChange = {},
+ )
+ InternalSwitchPreference(
+ title = "Use Dark theme",
+ summary = "Summary".toState(),
+ checked = false.toState(),
+ onCheckedChange = {},
+ )
+ }
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Footer.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Footer.kt
new file mode 100644
index 000000000000..41fd03b08563
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Footer.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.settingslib.spa.widget.ui
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Info
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+
+@Composable
+fun Footer(footerText: String) {
+ if (footerText.isEmpty()) return
+ Column(Modifier.padding(SettingsDimension.itemPadding)) {
+ SettingsIcon(imageVector = Icons.Outlined.Info, contentDescription = null)
+ Spacer(modifier = Modifier.height(SettingsDimension.itemPaddingVertical))
+ SettingsBody(footerText)
+ }
+}
+
+@Preview
+@Composable
+private fun FooterPreview() {
+ SettingsTheme {
+ Footer("Footer text always at the end of page.")
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Icon.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Icon.kt
new file mode 100644
index 000000000000..cb08cdb20885
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Icon.kt
@@ -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 com.android.settingslib.spa.widget.ui
+
+import androidx.compose.foundation.layout.size
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.vector.ImageVector
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+
+@Composable
+fun SettingsIcon(
+ imageVector: ImageVector,
+ contentDescription: String?,
+) {
+ Icon(
+ imageVector = imageVector,
+ contentDescription = contentDescription,
+ modifier = Modifier.size(SettingsDimension.itemIconSize),
+ tint = MaterialTheme.colorScheme.onSurface,
+ )
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/SettingsSlider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/SettingsSlider.kt
new file mode 100644
index 000000000000..0454ac37cfb7
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/SettingsSlider.kt
@@ -0,0 +1,185 @@
+/*
+ * 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.settingslib.spa.widget.ui
+
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.AccessAlarm
+import androidx.compose.material.icons.outlined.MusicNote
+import androidx.compose.material.icons.outlined.MusicOff
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Slider
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.preference.BaseLayout
+import kotlin.math.roundToInt
+
+/**
+ * The widget model for [SettingsSlider] widget.
+ */
+interface SettingsSliderModel {
+ /**
+ * The title of this [SettingsSlider].
+ */
+ val title: String
+
+ /**
+ * The initial position of the [SettingsSlider].
+ */
+ val initValue: Int
+
+ /**
+ * The value range for this [SettingsSlider].
+ */
+ val valueRange: IntRange
+ get() = 0..100
+
+ /**
+ * The lambda to be invoked during the value change by dragging or a click. This callback is
+ * used to get the real time value of the [SettingsSlider].
+ */
+ val onValueChange: ((value: Int) -> Unit)?
+ get() = null
+
+ /**
+ * The lambda to be invoked when value change has ended. This callback is used to get when the
+ * user has completed selecting a new value by ending a drag or a click.
+ */
+ val onValueChangeFinished: (() -> Unit)?
+ get() = null
+
+ /**
+ * The icon image for [SettingsSlider]. If not specified, the slider hides the icon by default.
+ */
+ val icon: ImageVector?
+ get() = null
+
+ /**
+ * Indicates whether to show step marks. If show step marks, when user finish sliding,
+ * the slider will automatically jump to the nearest step mark. Otherwise, the slider hides
+ * the step marks by default.
+ *
+ * The step is fixed to 1.
+ */
+ val showSteps: Boolean
+ get() = false
+}
+
+/**
+ * Settings slider widget.
+ *
+ * Data is provided through [SettingsSliderModel].
+ */
+@Composable
+fun SettingsSlider(model: SettingsSliderModel) {
+ SettingsSlider(
+ title = model.title,
+ initValue = model.initValue,
+ valueRange = model.valueRange,
+ onValueChange = model.onValueChange,
+ onValueChangeFinished = model.onValueChangeFinished,
+ icon = model.icon,
+ showSteps = model.showSteps,
+ )
+}
+
+@Composable
+internal fun SettingsSlider(
+ title: String,
+ initValue: Int,
+ valueRange: IntRange = 0..100,
+ onValueChange: ((value: Int) -> Unit)? = null,
+ onValueChangeFinished: (() -> Unit)? = null,
+ icon: ImageVector? = null,
+ showSteps: Boolean = false,
+ modifier: Modifier = Modifier,
+) {
+ var sliderPosition by rememberSaveable { mutableStateOf(initValue.toFloat()) }
+ BaseLayout(
+ title = title,
+ subTitle = {
+ Slider(
+ value = sliderPosition,
+ onValueChange = {
+ sliderPosition = it
+ onValueChange?.invoke(sliderPosition.roundToInt())
+ },
+ modifier = modifier,
+ valueRange = valueRange.first.toFloat()..valueRange.last.toFloat(),
+ steps = if (showSteps) (valueRange.count() - 2) else 0,
+ onValueChangeFinished = onValueChangeFinished,
+ )
+ },
+ icon = if (icon != null) ({
+ Icon(imageVector = icon, contentDescription = null)
+ }) else null,
+ )
+}
+
+@Preview
+@Composable
+private fun SettingsSliderPreview() {
+ SettingsTheme {
+ val initValue = 30
+ var sliderPosition by rememberSaveable { mutableStateOf(initValue) }
+ SettingsSlider(
+ title = "Alarm Volume",
+ initValue = 30,
+ onValueChange = { sliderPosition = it },
+ onValueChangeFinished = {
+ println("onValueChangeFinished: the value is $sliderPosition")
+ },
+ icon = Icons.Outlined.AccessAlarm,
+ )
+ }
+}
+
+@Preview
+@Composable
+private fun SettingsSliderIconChangePreview() {
+ SettingsTheme {
+ var icon by remember { mutableStateOf(Icons.Outlined.MusicNote) }
+ SettingsSlider(
+ title = "Media Volume",
+ initValue = 40,
+ onValueChange = { it: Int ->
+ icon = if (it > 0) Icons.Outlined.MusicNote else Icons.Outlined.MusicOff
+ },
+ icon = icon,
+ )
+ }
+}
+
+@Preview
+@Composable
+private fun SettingsSliderStepsPreview() {
+ SettingsTheme {
+ SettingsSlider(
+ title = "Display Text",
+ initValue = 2,
+ valueRange = 1..5,
+ showSteps = true,
+ )
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Switch.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Switch.kt
new file mode 100644
index 000000000000..45d5f6baa9cb
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Switch.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.settingslib.spa.widget.ui
+
+import androidx.compose.material3.Checkbox
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun SettingsSwitch(
+ checked: State<Boolean?>,
+ changeable: State<Boolean>,
+ onCheckedChange: ((newChecked: Boolean) -> Unit)? = null,
+) {
+ // TODO: Replace Checkbox with Switch when the androidx.compose.material3_material3 library is
+ // updated to date.
+ val checkedValue = checked.value
+ if (checkedValue != null) {
+ Checkbox(
+ checked = checkedValue,
+ onCheckedChange = onCheckedChange,
+ enabled = changeable.value,
+ )
+ } else {
+ Checkbox(
+ checked = false,
+ onCheckedChange = null,
+ enabled = false,
+ )
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
new file mode 100644
index 000000000000..a414c89dc24c
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
@@ -0,0 +1,52 @@
+/*
+ * 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.settingslib.spa.widget.ui
+
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+
+@Composable
+fun SettingsTitle(title: State<String>) {
+ SettingsTitle(title.value)
+}
+
+@Composable
+fun SettingsTitle(title: String) {
+ Text(
+ text = title,
+ color = MaterialTheme.colorScheme.onSurface,
+ style = MaterialTheme.typography.titleMedium,
+ )
+}
+
+@Composable
+fun SettingsBody(body: State<String>) {
+ SettingsBody(body.value)
+}
+
+@Composable
+fun SettingsBody(body: String) {
+ if (body.isNotEmpty()) {
+ Text(
+ text = body,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ style = MaterialTheme.typography.bodyMedium,
+ )
+ }
+}
diff --git a/packages/SettingsLib/Spa/tests/build.gradle b/packages/SettingsLib/Spa/tests/build.gradle
index 707017e7e17f..be5a5ec40c4f 100644
--- a/packages/SettingsLib/Spa/tests/build.gradle
+++ b/packages/SettingsLib/Spa/tests/build.gradle
@@ -24,7 +24,7 @@ android {
compileSdk 33
defaultConfig {
- minSdk minSdk_version
+ minSdk spa_min_sdk
targetSdk 33
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -50,7 +50,7 @@ android {
compose true
}
composeOptions {
- kotlinCompilerExtensionVersion compose_version
+ kotlinCompilerExtensionVersion jetpack_compose_version
}
packagingOptions {
resources {
@@ -62,6 +62,6 @@ android {
dependencies {
androidTestImplementation(project(":spa"))
androidTestImplementation 'androidx.test.ext:junit-ktx:1.1.3'
- androidTestImplementation("androidx.compose.ui:ui-test-junit4:$compose_version")
- androidTestDebugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version"
+ androidTestImplementation("androidx.compose.ui:ui-test-junit4:$jetpack_compose_version")
+ androidTestDebugImplementation "androidx.compose.ui:ui-test-manifest:$jetpack_compose_version"
}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/PreferenceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/PreferenceTest.kt
index 4097946abc79..a92f8713308f 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/PreferenceTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/PreferenceTest.kt
@@ -26,7 +26,7 @@ import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.settingslib.spa.framework.toState
+import com.android.settingslib.spa.framework.compose.toState
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/SwitchPreferenceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/SwitchPreferenceTest.kt
new file mode 100644
index 000000000000..d6c8fbc9dc9e
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/SwitchPreferenceTest.kt
@@ -0,0 +1,91 @@
+/*
+ * 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.settingslib.spa.widget.preference
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsOff
+import androidx.compose.ui.test.assertIsOn
+import androidx.compose.ui.test.isToggleable
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.framework.compose.stateOf
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SwitchPreferenceTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Test
+ fun title_displayed() {
+ composeTestRule.setContent {
+ TestSwitchPreference(changeable = true)
+ }
+
+ composeTestRule.onNodeWithText("SwitchPreference").assertIsDisplayed()
+ }
+
+ @Test
+ fun toggleable_initialStateIsCorrect() {
+ composeTestRule.setContent {
+ TestSwitchPreference(changeable = true)
+ }
+
+ composeTestRule.onNode(isToggleable()).assertIsOff()
+ }
+
+ @Test
+ fun click_changeable_withEffect() {
+ composeTestRule.setContent {
+ TestSwitchPreference(changeable = true)
+ }
+
+ composeTestRule.onNodeWithText("SwitchPreference").performClick()
+ composeTestRule.onNode(isToggleable()).assertIsOn()
+ }
+
+ @Test
+ fun click_notChangeable_noEffect() {
+ composeTestRule.setContent {
+ TestSwitchPreference(changeable = false)
+ }
+
+ composeTestRule.onNodeWithText("SwitchPreference").performClick()
+ composeTestRule.onNode(isToggleable()).assertIsOff()
+ }
+}
+
+@Composable
+private fun TestSwitchPreference(changeable: Boolean) {
+ val checked = rememberSaveable { mutableStateOf(false) }
+ SwitchPreference(remember {
+ object : SwitchPreferenceModel {
+ override val title = "SwitchPreference"
+ override val checked = checked
+ override val changeable = stateOf(changeable)
+ override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
+ }
+ })
+}
diff --git a/packages/SettingsLib/SpaPrivileged/Android.bp b/packages/SettingsLib/SpaPrivileged/Android.bp
new file mode 100644
index 000000000000..48f7ff270ac7
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/Android.bp
@@ -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 {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_library {
+ name: "SpaPrivilegedLib",
+
+ srcs: ["src/**/*.kt"],
+
+ static_libs: [
+ "SpaLib",
+ "SettingsLib",
+ "androidx.compose.runtime_runtime",
+ ],
+ kotlincflags: ["-Xjvm-default=all"],
+ min_sdk_version: "31",
+}
diff --git a/packages/SettingsLib/SpaPrivileged/AndroidManifest.xml b/packages/SettingsLib/SpaPrivileged/AndroidManifest.xml
new file mode 100644
index 000000000000..2efa10744bb3
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<?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 package="com.android.settingslib.spaprivileged" />
diff --git a/packages/SettingsLib/SpaPrivileged/OWNERS b/packages/SettingsLib/SpaPrivileged/OWNERS
new file mode 100644
index 000000000000..9256ca5cc2b0
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/packages/SettingsLib/Spa/OWNERS
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/AppRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/AppRepository.kt
new file mode 100644
index 000000000000..a6378ef53437
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/AppRepository.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.settingslib.spaprivileged.framework.app
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.graphics.drawable.Drawable
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.produceState
+import com.android.settingslib.Utils
+import com.android.settingslib.spa.framework.compose.rememberContext
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+
+@Composable
+fun rememberAppRepository(): AppRepository = rememberContext(::AppRepositoryImpl)
+
+interface AppRepository {
+ @Composable
+ fun produceLabel(app: ApplicationInfo): State<String>
+
+ @Composable
+ fun produceIcon(app: ApplicationInfo): State<Drawable?>
+}
+
+private class AppRepositoryImpl(private val context: Context) : AppRepository {
+ private val packageManager = context.packageManager
+
+ @Composable
+ override fun produceLabel(app: ApplicationInfo) = produceState(initialValue = "", app) {
+ withContext(Dispatchers.Default) {
+ value = app.loadLabel(packageManager).toString()
+ }
+ }
+
+ @Composable
+ override fun produceIcon(app: ApplicationInfo) =
+ produceState<Drawable?>(initialValue = null, app) {
+ withContext(Dispatchers.Default) {
+ value = Utils.getBadgedIcon(context, app)
+ }
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/PackageManagers.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/PackageManagers.kt
new file mode 100644
index 000000000000..5a3e66619c39
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/PackageManagers.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.settingslib.spaprivileged.framework.app
+
+import android.content.pm.PackageInfo
+import android.content.pm.PackageManager
+
+object PackageManagers {
+ fun getPackageInfoAsUser(packageName: String, userId: Int): PackageInfo =
+ PackageManager.getPackageInfoAsUserCached(packageName, 0, userId)
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
new file mode 100644
index 000000000000..5ae514cfb524
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
@@ -0,0 +1,70 @@
+/*
+ * 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.settingslib.spaprivileged.template.app
+
+import android.content.pm.ApplicationInfo
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.android.settingslib.spa.framework.compose.rememberDrawablePainter
+import com.android.settingslib.spa.widget.ui.SettingsBody
+import com.android.settingslib.spa.widget.ui.SettingsTitle
+import com.android.settingslib.spaprivileged.framework.app.PackageManagers
+import com.android.settingslib.spaprivileged.framework.app.rememberAppRepository
+
+@Composable
+fun AppInfo(packageName: String, userId: Int) {
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(16.dp),
+ horizontalAlignment = Alignment.CenterHorizontally) {
+ val packageInfo = remember { PackageManagers.getPackageInfoAsUser(packageName, userId) }
+ Box(modifier = Modifier.padding(8.dp)) {
+ AppIcon(app = packageInfo.applicationInfo, size = 48)
+ }
+ AppLabel(packageInfo.applicationInfo)
+ Spacer(modifier = Modifier.height(4.dp))
+ SettingsBody(packageInfo.versionName)
+ }
+}
+
+@Composable
+fun AppIcon(app: ApplicationInfo, size: Int) {
+ val appRepository = rememberAppRepository()
+ Image(
+ painter = rememberDrawablePainter(appRepository.produceIcon(app).value),
+ contentDescription = null,
+ modifier = Modifier.size(size.dp)
+ )
+}
+
+@Composable
+fun AppLabel(app: ApplicationInfo) {
+ val appRepository = rememberAppRepository()
+ SettingsTitle(appRepository.produceLabel(app))
+}
diff --git a/packages/SettingsLib/res/values-af/arrays.xml b/packages/SettingsLib/res/values-af/arrays.xml
index 1de76684d860..a7e44d3f33f5 100644
--- a/packages/SettingsLib/res/values-af/arrays.xml
+++ b/packages/SettingsLib/res/values-af/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Gebruik stelselkeuse (verstek)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-oudio"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-oudio"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Gebruik stelselkeuse (verstek)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-oudio"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-oudio"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Gebruik stelselkeuse (verstek)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index fe8030f9a68a..0b23dd130634 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -661,6 +661,7 @@
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Luggehalte"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Uitsaai-inligting"</string>
<string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Huiskontroles"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Kies \'n profielprent"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Verstekgebruikerikoon"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fisieke sleutelbord"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index d3c034fa170c..2bf9cb2894e0 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -660,7 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"የአየር ሁኔታ"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"የአየር ጥራት"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"የCast መረጃ"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"የቤት ውስጥ ቁጥጥሮች"</string>
+ <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) -->
<skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"የመገለጫ ሥዕል ይምረጡ"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"ነባሪ የተጠቃሚ አዶ"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 6783654c32e5..6c71d9c232c4 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -660,7 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"الطقس"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"جودة الهواء"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"معلومات البث"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"إدارة آلية للمنزل"</string>
+ <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) -->
<skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"اختيار صورة الملف الشخصي"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"رمز المستخدم التلقائي"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index 543c703e9003..299df7703e9e 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -121,7 +121,7 @@
<string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"সম্পৰ্কসূচী আৰু কলৰ ইতিহাস শ্বেয়াৰ কৰাৰ বাবে ব্যৱহাৰ কৰক"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ইণ্টাৰনেট সংযোগ শ্বেয়াৰ"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"পাঠ বাৰ্তা"</string>
- <string name="bluetooth_profile_sap" msgid="8304170950447934386">"ছিম প্ৰৱেশ"</string>
+ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"ছিমৰ এক্সেছ"</string>
<string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"এইচ্ছডি অডি\'অ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
<string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"এইচ্ছডি অডিঅ’"</string>
<string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"শ্ৰৱণ যন্ত্ৰ"</string>
@@ -660,7 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"বতৰ"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"বায়ুৰ গুণগত মান"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"কাষ্টৰ তথ্য"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"গৃহ নিয়ন্ত্ৰণ"</string>
+ <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) -->
<skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"এখন প্ৰ’ফাইল চিত্ৰ বাছনি কৰক"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"ডিফ’ল্ট ব্যৱহাৰকাৰীৰ চিহ্ন"</string>
diff --git a/packages/SettingsLib/res/values-az/arrays.xml b/packages/SettingsLib/res/values-az/arrays.xml
index 48974a7bd40f..d1f157af8194 100644
--- a/packages/SettingsLib/res/values-az/arrays.xml
+++ b/packages/SettingsLib/res/values-az/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Sistem Seçimini istifadə edin (Defolt)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Sistem Seçimini istifadə edin (Defolt)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Sistem Seçimini istifadə edin (Defolt)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index f3e5d95219fa..fd791924ed32 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Hava"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Havanın keyfiyyəti"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Yayım məlumatı"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Əsas səhifə kontrolları"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Profil şəkli seçin"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Defolt istifadəçi ikonası"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fiziki klaviatura"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml b/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml
index 337da265711c..63b08fa92963 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Koristi izbor sistema (podrazumevano)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Koristi izbor sistema (podrazumevano)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Koristi izbor sistema (podrazumevano)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 6a880aac349f..f7d9be91c874 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Vreme"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kvalitet vazduha"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Podaci o prebacivanju"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Upravljanje domom"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"SmartSpace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Odaberite sliku profila"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Podrazumevana ikona korisnika"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fizička tastatura"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index a90242667b56..431bcd5bd91a 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -660,7 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Надвор\'е"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Якасць паветра"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Даныя пра трансляцыю"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Кіраванне домам"</string>
+ <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) -->
<skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Выберыце відарыс профілю"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Стандартны карыстальніцкі значок"</string>
diff --git a/packages/SettingsLib/res/values-bg/arrays.xml b/packages/SettingsLib/res/values-bg/arrays.xml
index 1aad6ca714c4..849e6942100a 100644
--- a/packages/SettingsLib/res/values-bg/arrays.xml
+++ b/packages/SettingsLib/res/values-bg/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Използване на сист. избор (стандартно)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"Разширено аудиокодиране (AAC)"</item>
+ <item msgid="1049450003868150455">"Аудио: <xliff:g id="APTX">aptX™</xliff:g> от <xliff:g id="QUALCOMM">Qualcomm®</xliff:g>"</item>
+ <item msgid="2908219194098827570">"Аудио: <xliff:g id="APTX_HD">aptX™ HD</xliff:g> от <xliff:g id="QUALCOMM">Qualcomm®</xliff:g>"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Използване на сист. избор (стандартно)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"Разширено аудиокодиране (AAC)"</item>
+ <item msgid="8627333814413492563">"Аудио: <xliff:g id="APTX">aptX™</xliff:g> от <xliff:g id="QUALCOMM">Qualcomm®</xliff:g>"</item>
+ <item msgid="3517061573669307965">"Аудио: <xliff:g id="APTX_HD">aptX™ HD</xliff:g> от <xliff:g id="QUALCOMM">Qualcomm®</xliff:g>"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Използване на сист. избор (стандартно)"</item>
<item msgid="8003118270854840095">"44,1 кХц"</item>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index c5123667f2b2..cbc432207941 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Времето"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Качество на въздуха"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Предаване: Инф."</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Контроли за дома"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Изберете снимка на потребителския профил"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Икона за основния потребител"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Физическа клавиатура"</string>
diff --git a/packages/SettingsLib/res/values-bn/arrays.xml b/packages/SettingsLib/res/values-bn/arrays.xml
index 5e6bb9527c29..a3bc4fd61465 100644
--- a/packages/SettingsLib/res/values-bn/arrays.xml
+++ b/packages/SettingsLib/res/values-bn/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"সিস্টেমের নির্বাচন ব্যবহার করুন (ডিফল্ট)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> অডিও"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> অডিও"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"সিস্টেমের নির্বাচন ব্যবহার করুন (ডিফল্ট)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> অডিও"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> অডিও"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"সিস্টেমের নির্বাচন ব্যবহার করুন (ডিফল্ট)"</item>
<item msgid="8003118270854840095">"৪৪.১ kHz"</item>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 23eed04b4fb6..3394eea7e80d 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -661,6 +661,7 @@
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"এয়ার কোয়ালিটি"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"কাস্ট সম্পর্কিত তথ্য"</string>
<string name="dream_complication_title_home_controls" msgid="9153381632476738811">"হোম কন্ট্রোল"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"একটি প্রোফাইল ছবি বেছে নিন"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"ডিফল্ট ব্যবহারকারীর আইকন"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"ফিজিক্যাল কীবোর্ড"</string>
diff --git a/packages/SettingsLib/res/values-bs/arrays.xml b/packages/SettingsLib/res/values-bs/arrays.xml
index 262a35feab65..926ad8464ccf 100644
--- a/packages/SettingsLib/res/values-bs/arrays.xml
+++ b/packages/SettingsLib/res/values-bs/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Korištenje odabira sistema (zadano)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Korištenje odabira sistema (zadano)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Korištenje odabira sistema (zadano)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 78a484f28e56..1547e8352abf 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -265,7 +265,7 @@
<string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, otklanjanje grešaka, programer"</string>
<string name="bugreport_in_power" msgid="8664089072534638709">"Prečica za izvještaj o greškama"</string>
<string name="bugreport_in_power_summary" msgid="1885529649381831775">"Vidite dugme za prijavu grešaka u meniju napajanja"</string>
- <string name="keep_screen_on" msgid="1187161672348797558">"Ne zaključavaj ekran"</string>
+ <string name="keep_screen_on" msgid="1187161672348797558">"Ne zaključavaj"</string>
<string name="keep_screen_on_summary" msgid="1510731514101925829">"Ekran neće prelaziti u stanje mirovanja tokom punjenja"</string>
<string name="bt_hci_snoop_log" msgid="7291287955649081448">"Omogući Bluetooth HCI snoop zapis"</string>
<string name="bt_hci_snoop_log_summary" msgid="6808538971394092284">"Snimite Bluetooth pakete. (Uključite/isključite Bluetooth nakon što promijenite ovu postavku)"</string>
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Vremenska prognoza"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kvalitet zraka"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Podaci o emitiranju"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Kontrole doma"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Odaberite sliku profila"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Zadana ikona korisnika"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fizička tastatura"</string>
diff --git a/packages/SettingsLib/res/values-ca/arrays.xml b/packages/SettingsLib/res/values-ca/arrays.xml
index 8c34a1f0f72a..b6f15903ecbd 100644
--- a/packages/SettingsLib/res/values-ca/arrays.xml
+++ b/packages/SettingsLib/res/values-ca/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Utilitza la selecció del sistema (predeterminada)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"Àudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="2908219194098827570">"Àudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Utilitza la selecció del sistema (predeterminada)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"Àudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="3517061573669307965">"Àudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Utilitza la selecció del sistema (predeterminada)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 606a379b1651..c97a6c1bc157 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Temps"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Qualitat de l\'aire"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Informació d\'emissió"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Domòtica"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"D\'una ullada"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Tria una foto de perfil"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Icona d\'usuari predeterminat"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Teclat físic"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 37c0bb4ced79..dfd6d612fb32 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -660,7 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Počasí"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kvalita vzduchu"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info o odesílání"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Ovládání domácnosti"</string>
+ <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) -->
<skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Vyberte profilový obrázek"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Výchozí uživatelská ikona"</string>
diff --git a/packages/SettingsLib/res/values-da/arrays.xml b/packages/SettingsLib/res/values-da/arrays.xml
index 155104ae81dc..48a33f61a427 100644
--- a/packages/SettingsLib/res/values-da/arrays.xml
+++ b/packages/SettingsLib/res/values-da/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Brug systemvalg (standard)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-lyd"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-lyd"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Brug systemvalg (standard)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-lyd"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-lyd"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Brug systemvalg (standard)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index d80e1ec4cbba..790819ce369f 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Vejr"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Luftkvalitet"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Cast-oplysninger"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Styring af hjem"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Overblik"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Vælg et profilbillede"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Ikon for standardbruger"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fysisk tastatur"</string>
diff --git a/packages/SettingsLib/res/values-de/arrays.xml b/packages/SettingsLib/res/values-de/arrays.xml
index 31126a803f8f..ca999db843b5 100644
--- a/packages/SettingsLib/res/values-de/arrays.xml
+++ b/packages/SettingsLib/res/values-de/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Systemauswahl verwenden (Standard)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-Audio"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-Audio"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Systemauswahl verwenden (Standard)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-Audio"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-Audio"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Systemauswahl verwenden (Standard)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index d4fafe12e10c..bd919f4d2e37 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Wetter"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Luftqualität"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Streaming-Info"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Smart-Home-Steuerung"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Profilbild auswählen"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Standardmäßiges Nutzersymbol"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Physische Tastatur"</string>
diff --git a/packages/SettingsLib/res/values-el/arrays.xml b/packages/SettingsLib/res/values-el/arrays.xml
index 70000e1917a7..b95f6fc903a7 100644
--- a/packages/SettingsLib/res/values-el/arrays.xml
+++ b/packages/SettingsLib/res/values-el/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Χρήση επιλογής συστήματος (Προεπιλογή)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"Ήχος <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="2908219194098827570">"Ήχος <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Χρήση επιλογής συστήματος (Προεπιλογή)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"Ήχος <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="3517061573669307965">"Ήχος <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Χρήση επιλογής συστήματος (Προεπιλογή)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index aa613a5eeb17..9326dace365a 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Καιρός"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Ποιότητα αέρα"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Πληροφορίες ηθοποιών"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Οικιακοί έλεγχοι"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Επιλογή φωτογραφίας προφίλ"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Προεπιλεγμένο εικονίδιο χρήστη"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Φυσικό πληκτρολόγιο"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/arrays.xml b/packages/SettingsLib/res/values-en-rAU/arrays.xml
index fc6f791bf6ea..327e4e9990d1 100644
--- a/packages/SettingsLib/res/values-en-rAU/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rAU/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Use system selection (default)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Use system selection (default)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Use system selection (default)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 62c8e21bf489..667e06a8f832 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -661,6 +661,7 @@
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Air quality"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Cast info"</string>
<string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Home Controls"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Choose a profile picture"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Default user icon"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Physical keyboard"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/arrays.xml b/packages/SettingsLib/res/values-en-rCA/arrays.xml
index fc6f791bf6ea..327e4e9990d1 100644
--- a/packages/SettingsLib/res/values-en-rCA/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rCA/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Use system selection (default)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Use system selection (default)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Use system selection (default)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index 8650b77dec89..bf20fccb96ce 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -661,6 +661,7 @@
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Air quality"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Cast info"</string>
<string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Home Controls"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Choose a profile picture"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Default user icon"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Physical keyboard"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/arrays.xml b/packages/SettingsLib/res/values-en-rGB/arrays.xml
index fc6f791bf6ea..327e4e9990d1 100644
--- a/packages/SettingsLib/res/values-en-rGB/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rGB/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Use system selection (default)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Use system selection (default)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Use system selection (default)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 62c8e21bf489..667e06a8f832 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -661,6 +661,7 @@
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Air quality"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Cast info"</string>
<string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Home Controls"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Choose a profile picture"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Default user icon"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Physical keyboard"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/arrays.xml b/packages/SettingsLib/res/values-en-rIN/arrays.xml
index fc6f791bf6ea..327e4e9990d1 100644
--- a/packages/SettingsLib/res/values-en-rIN/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rIN/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Use system selection (default)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Use system selection (default)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Use system selection (default)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 62c8e21bf489..667e06a8f832 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -661,6 +661,7 @@
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Air quality"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Cast info"</string>
<string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Home Controls"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Choose a profile picture"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Default user icon"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Physical keyboard"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/arrays.xml b/packages/SettingsLib/res/values-en-rXC/arrays.xml
index 34db380b0cf6..8af0a4ab5903 100644
--- a/packages/SettingsLib/res/values-en-rXC/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rXC/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‏‎‏‎‎‎‎‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‏‎‎‎‏‎‏‏‏‏‎‏‏‏‏‎‏‎‎‎map13‎‏‎‎‏‎"</item>
<item msgid="8147982633566548515">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‏‎‎‏‏‎‏‏‏‎‎‏‏‎‎‎‎‏‎‎‏‎‎‎‎‏‏‎‏‏‏‎‎‏‎‎‏‎‎‏‏‎‎‏‎‎‎‏‎‎‎‏‏‎map14‎‏‎‎‏‎"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‏‎‏‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‎‏‏‏‎‏‎‎‏‎‎‏‏‏‎‎‎‏‎‏‏‏‎‎‎‎‏‏‎‏‎‏‏‏‎‏‏‎Use System Selection (Default)‎‏‎‎‏‎"</item>
+ <item msgid="4055460186095649420">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‎‏‏‏‏‏‏‎‎‎‏‏‏‎‎‎‏‏‎‏‎‏‏‏‎‎‎‎‏‏‏‏‏‎‎‎‏‎‎‎‎‎‏‎‏‎‎‎‏‏‎‎‎SBC‎‏‎‎‏‎"</item>
+ <item msgid="720249083677397051">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‏‎‏‏‏‎‎‏‏‎‏‏‏‏‏‎‎‏‏‎‎‏‎‎‎‏‎‏‎‎‎‎‎‎‏‏‏‎‏‏‎AAC‎‏‎‎‏‎"</item>
+ <item msgid="1049450003868150455">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‏‎‏‎‎‏‎‎‎‎‎‏‏‎‎‏‎‏‎‎‏‏‎‏‏‏‏‏‏‎‎‎‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‎‏‎‏‎‏‏‎‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="QUALCOMM">Qualcomm®</xliff:g>‎‏‎‎‏‏‏‎ ‎‏‎‎‏‏‎<xliff:g id="APTX">aptX™</xliff:g>‎‏‎‎‏‏‏‎ audio‎‏‎‎‏‎"</item>
+ <item msgid="2908219194098827570">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‏‏‎‎‏‎‎‏‏‏‎‏‏‏‏‎‎‎‎‎‏‎‎‏‏‎‎‏‎‎‎‏‎‎‏‏‎<xliff:g id="QUALCOMM">Qualcomm®</xliff:g>‎‏‎‎‏‏‏‎ ‎‏‎‎‏‏‎<xliff:g id="APTX_HD">aptX™ HD</xliff:g>‎‏‎‎‏‏‏‎ audio‎‏‎‎‏‎"</item>
+ <item msgid="3825367753087348007">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‏‎‏‎‎‎‏‎‏‏‎‎‏‏‎‏‏‏‏‏‎‏‏‎‏‎‎‎‎‏‎‏‎‏‎‎‎‎‎‎‎‎‏‎‏‏‎‏‏‎‏‎‎‏‎‎‏‏‏‎LDAC‎‏‎‎‏‎"</item>
+ <item msgid="328951785723550863">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‏‏‎‎‏‎‎‏‎‎‎‎‏‎‏‎‏‎‏‏‏‏‏‎‏‎‎‎‎‎‎‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‎‏‏‏‎‎‏‎‎‎‏‏‏‏‎LC3‎‏‎‎‏‎"</item>
+ <item msgid="506175145534048710">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‏‏‏‏‎‎‎‎‎‏‏‎‎‏‎‎‏‎‏‏‏‎‎‏‏‏‎‎‏‏‏‎‏‏‏‏‏‎‏‏‎‎‎‎‎‏‎‎‏‎‎‏‏‏‎‎‎‏‏‎‎Opus‎‏‎‎‏‎"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‎‏‏‏‎‏‏‎‏‎‏‎‏‎‎‏‏‏‏‎‎‏‎‏‎‎‏‎‏‎‎‎‏‏‎‎‏‎‎‏‏‏‎‏‏‏‎‎‎‎‎Use System Selection (Default)‎‏‎‎‏‎"</item>
+ <item msgid="9024885861221697796">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‎‏‏‎‏‎‏‎‏‏‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‎‎‎‎‏‎‎‎‏‎‎‏‎‎‎‎‎‏‎‎‎SBC‎‏‎‎‏‎"</item>
+ <item msgid="4688890470703790013">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‎‎‏‎‎‎‏‎‎‏‎‎‏‎‎‏‎‎‏‎‎‎‎‏‎‏‎‎‏‎‏‎‎‎‏‎‎‎‎‎‎‏‏‎‏‏‏‎‏‏‏‏‎‏‏‏‏‎‏‎AAC‎‏‎‎‏‎"</item>
+ <item msgid="8627333814413492563">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎‎‏‎‏‎‎‎‎‎‏‏‎‏‏‎‏‎‏‎‏‎‎‎‎‎‎‎‏‎‏‎‏‎‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="QUALCOMM">Qualcomm®</xliff:g>‎‏‎‎‏‏‏‎ ‎‏‎‎‏‏‎<xliff:g id="APTX">aptX™</xliff:g>‎‏‎‎‏‏‏‎ audio‎‏‎‎‏‎"</item>
+ <item msgid="3517061573669307965">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‎‏‏‏‏‎‎‎‏‏‏‎‎‏‏‎‏‏‏‎‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‎‎‎‏‎‎‎‏‏‎‎‎‏‏‏‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="QUALCOMM">Qualcomm®</xliff:g>‎‏‎‎‏‏‏‎ ‎‏‎‎‏‏‎<xliff:g id="APTX_HD">aptX™ HD</xliff:g>‎‏‎‎‏‏‏‎ audio‎‏‎‎‏‎"</item>
+ <item msgid="2553206901068987657">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‏‏‎‏‏‎‏‏‏‎‏‏‎‏‎‎‎‎‎‎‏‎‎‏‏‎‎‎‏‎‏‎‎‎‎‏‎‎‏‎‏‎‏‏‏‏‏‏‎‏‎‎‎‎‏‎‎‏‎LDAC‎‏‎‎‏‎"</item>
+ <item msgid="3940992993241040716">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‏‏‎‏‎‏‏‎‎‎‏‎‎‏‏‏‎‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‎‎‏‏‎‏‏‏‏‏‎‎‎‎‏‏‏‎‏‎‎‏‏‎‎‎LC3‎‏‎‎‏‎"</item>
+ <item msgid="7940970833006181407">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‏‏‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‏‎‎‏‎‎‏‎‎‎‏‎‎‏‎‎‎‏‏‏‏‎‎‎‎‎‏‏‏‏‏‎Opus‎‏‎‎‏‎"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‎‎‏‏‎‏‏‏‎‎‏‎‏‏‎‎‎‎‎‎‎‏‏‏‎‎‏‏‎‏‏‎‎‏‏‏‏‎‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‎‏‏‏‏‎Use System Selection (Default)‎‏‎‎‏‎"</item>
<item msgid="8003118270854840095">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‎‏‎‎‎‎‏‏‎‎‏‎‎‏‏‎‏‎‏‎‏‎‎‎‏‏‏‏‎‏‏‎‏‏‏‏‎‏‏‎‏‏‏‏‏‏‎‎‎‏‏‏‏‏‎44.1 kHz‎‏‎‎‏‎"</item>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 21697b9a4fb5..21c1bb72501e 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -661,6 +661,7 @@
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‎‎‎‎‏‎‏‏‎‎‏‎‏‎‏‎‏‏‏‏‎‏‎‏‎‏‏‎‎‏‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‎Air Quality‎‏‎‎‏‎"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‎‎‎‎‎‎‏‏‎‎‏‎‎‏‏‏‎‏‏‏‏‏‎‏‏‏‏‎‏‎‎‏‏‏‏‏‎‎‏‎‎‎‎‎‎‏‏‏‎‏‏‎‎‏‏‏‎‎‎Cast Info‎‏‎‎‏‎"</string>
<string name="dream_complication_title_home_controls" msgid="9153381632476738811">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‎‏‎‏‏‎‎‎‎‎‎‏‏‎‎‏‎‎‎‏‏‎‎‏‎‏‎‏‏‏‎‎‎‏‏‏‎‏‎‎‏‏‏‏‏‎‏‏‎Home Controls‎‏‎‎‏‎"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‏‎‎‏‎‎‎‎‎‏‏‎‏‏‎‎‎‎‎‎‎‏‏‎‎‎‎‎‏‏‎‎‎‏‎‎‏‎‎‎‏‎‎‏‎‏‏‎‎‎‏‎‏‏‎‎‎‎‎Smartspace‎‏‎‎‏‎"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‏‎‎‏‏‎‎‏‎‎‏‎‎‏‎‏‎‎‎‎‏‏‎‏‎‎‏‎‎‎‏‏‎‏‏‎‎‎‏‎‏‏‎‏‏‎‏‏‏‎‎‎Choose a profile picture‎‏‎‎‏‎"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‏‎‏‎‎‏‎‏‎‎‏‏‏‎‎‏‎‏‏‏‎‏‏‎‏‎‏‎‎‎‎‎‎‏‏‏‎‏‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎Default user icon‎‏‎‎‏‎"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‎‏‎‏‏‎‎‎‏‏‏‎‏‏‎‏‏‎‏‏‏‎‎‏‎‎‎‏‎‏‎‎‎‎‎‎‎‎‏‏‏‎‏‎‏‏‏‎‎‎‏‎‏‎‏‎‏‎‎‎Physical keyboard‎‏‎‎‏‎"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 27cd0ab200f5..8821f424dece 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -661,6 +661,8 @@
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Calidad del aire"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info de reparto"</string>
<string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Control de la casa"</string>
+ <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Elige una foto de perfil"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Ícono de usuario predeterminado"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Teclado físico"</string>
diff --git a/packages/SettingsLib/res/values-es/arrays.xml b/packages/SettingsLib/res/values-es/arrays.xml
index 0677864d1f46..49244078105e 100644
--- a/packages/SettingsLib/res/values-es/arrays.xml
+++ b/packages/SettingsLib/res/values-es/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Usar preferencia del sistema (predeterminado)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Usar preferencia del sistema (predeterminado)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Usar preferencia del sistema (predeterminado)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index d221c8dbe250..cea4835df66f 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Tiempo"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Calidad del aire"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info. de emisión"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Domótica"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"De un vistazo"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Elige una imagen de perfil"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Icono de usuario predeterminado"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Teclado físico"</string>
diff --git a/packages/SettingsLib/res/values-et/arrays.xml b/packages/SettingsLib/res/values-et/arrays.xml
index d986ecf47260..0402ac2f5719 100644
--- a/packages/SettingsLib/res/values-et/arrays.xml
+++ b/packages/SettingsLib/res/values-et/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Süsteemi valiku kasutamine (vaikeseade)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"Heli: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="2908219194098827570">"Heli: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Süsteemi valiku kasutamine (vaikeseade)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"Heli: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="3517061573669307965">"Heli: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Süsteemi valiku kasutamine (vaikeseade)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index f32ae2347ef6..13fd3190aac8 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Ilm"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Õhukvaliteet"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Osatäitjate teave"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Kodu juhtimine"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Ülevaade"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Valige profiilipilt"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Vaikekasutajaikoon"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Füüsiline klaviatuur"</string>
diff --git a/packages/SettingsLib/res/values-eu/arrays.xml b/packages/SettingsLib/res/values-eu/arrays.xml
index d166e1b97a38..9c12e95cf5c8 100644
--- a/packages/SettingsLib/res/values-eu/arrays.xml
+++ b/packages/SettingsLib/res/values-eu/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Erabili sistema-hautapena (lehenetsia)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audioa"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audioa"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Erabili sistema-hautapena (lehenetsia)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audioa"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audioa"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Erabili sistema-hautapena (lehenetsia)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index bcb57e89b213..3c96b6f71c5c 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Eguraldia"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Airearen kalitatea"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Igorpenari buruzko informazioa"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Etxeko gailuak kontrolatzeko aukerak"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Aukeratu profileko argazki bat"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Erabiltzaile lehenetsiaren ikonoa"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Teklatu fisikoa"</string>
diff --git a/packages/SettingsLib/res/values-fa/arrays.xml b/packages/SettingsLib/res/values-fa/arrays.xml
index b7761dd6ebcb..41410cbc3288 100644
--- a/packages/SettingsLib/res/values-fa/arrays.xml
+++ b/packages/SettingsLib/res/values-fa/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"استفاده از انتخاب سیستم (پیش‌فرض)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"صوت <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="2908219194098827570">"صوت <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"استفاده از انتخاب سیستم (پیش‌فرض)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"صوت <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="3517061573669307965">"صوت <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"استفاده از انتخاب سیستم (پیش‌فرض)"</item>
<item msgid="8003118270854840095">"۴۴٫۱ کیلوهرتز"</item>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 6abe873d27eb..5190e0fbd4ca 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"آب‌وهوا"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"کیفیت هوا"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"اطلاعات پخش محتوا"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"کنترل لوازم خانگی"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"انتخاب عکس نمایه"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"نماد کاربر پیش‌فرض"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"صفحه‌کلید فیزیکی"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 2f3436e1e7c6..c50a2e0139f2 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -660,7 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Sää"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Ilmanlaatu"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Striimaustiedot"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Kodin ohjaus"</string>
+ <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) -->
<skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Valitse profiilikuva"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Oletuskäyttäjäkuvake"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/arrays.xml b/packages/SettingsLib/res/values-fr-rCA/arrays.xml
index 12acbb6736b4..808c3f2c9a73 100644
--- a/packages/SettingsLib/res/values-fr-rCA/arrays.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Utiliser sélect. du système (par défaut)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Utiliser sélect. du système (par défaut)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Utiliser sélect. du système (par défaut)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 28b3cd96aed6..b8b465948905 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Météo"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Qualité de l\'air"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info diffusion"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Domotique"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Aperçu"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Choisir une photo de profil"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Icône d\'utilisateur par défaut"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Clavier physique"</string>
diff --git a/packages/SettingsLib/res/values-fr/arrays.xml b/packages/SettingsLib/res/values-fr/arrays.xml
index 80ac7e4e4752..92546da3f56d 100644
--- a/packages/SettingsLib/res/values-fr/arrays.xml
+++ b/packages/SettingsLib/res/values-fr/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Utiliser la sélection du système (par défaut)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Utiliser la sélection du système (par défaut)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Utiliser la sélection du système (par défaut)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index da1497aa980b..4a2d01eabb50 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Météo"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Qualité de l\'air"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Infos distribution"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Domotique"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Choisissez une photo de profil"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Icône de l\'utilisateur par défaut"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Clavier physique"</string>
diff --git a/packages/SettingsLib/res/values-gl/arrays.xml b/packages/SettingsLib/res/values-gl/arrays.xml
index b6cf48e54f72..f66312084e4b 100644
--- a/packages/SettingsLib/res/values-gl/arrays.xml
+++ b/packages/SettingsLib/res/values-gl/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Usar selección do sistema (predeterminado)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Usa a selección do sistema (predeterminado)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Usar selección do sistema (predeterminado)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 7822749eea0f..93f9fcf9a296 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -662,6 +662,7 @@
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Datos da emisión"</string>
<!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
<skip />
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Espazo intelixente"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Escolle unha imaxe do perfil"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Icona do usuario predeterminado"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Teclado físico"</string>
diff --git a/packages/SettingsLib/res/values-gu/arrays.xml b/packages/SettingsLib/res/values-gu/arrays.xml
index 7e668e71f91d..e527d81fbd12 100644
--- a/packages/SettingsLib/res/values-gu/arrays.xml
+++ b/packages/SettingsLib/res/values-gu/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"સિસ્ટમ પસંદગીનો ઉપયોગ કરો (ડિફૉલ્ટ)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ઑડિયો"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ઑડિયો"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"સિસ્ટમ પસંદગીનો ઉપયોગ કરો (ડિફૉલ્ટ)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ઑડિયો"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ઑડિયો"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"સિસ્ટમ પસંદગીનો ઉપયોગ કરો (ડિફૉલ્ટ)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index cebb1febc001..0e1912558f89 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"હવામાન"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"હવાની ક્વૉલિટી"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"કાસ્ટ વિશેની માહિતી"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"ઘરેલુ સાધન નિયંત્રણો"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"પ્રોફાઇલ ફોટો પસંદ કરો"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"ડિફૉલ્ટ વપરાશકર્તાનું આઇકન"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"ભૌતિક કીબોર્ડ"</string>
diff --git a/packages/SettingsLib/res/values-hi/arrays.xml b/packages/SettingsLib/res/values-hi/arrays.xml
index 13da75b9bdba..9b8d83e67e2e 100644
--- a/packages/SettingsLib/res/values-hi/arrays.xml
+++ b/packages/SettingsLib/res/values-hi/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"सिस्टम से चुने जाने का उपयोग करें (डिफ़ॉल्ट)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ऑडियो"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ऑडियो"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"सिस्टम से चुने जाने का इस्तेमाल करें (डिफ़ॉल्ट)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ऑडियो"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ऑडियो"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"सिस्टम से चुने जाने का उपयोग करें (डिफ़ॉल्ट)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 417d2b282821..ed0a7716987e 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"मौसम"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"हवा की क्वालिटी"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"कास्टिंग की जानकारी"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"होम कंट्रोल"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"स्मार्टस्पेस"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"प्रोफ़ाइल फ़ोटो चुनें"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"उपयोगकर्ता के लिए डिफ़ॉल्ट आइकॉन"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"फ़िज़िकल कीबोर्ड"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 267000ff9d4b..af26040bdc52 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -660,7 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Vremenska prognoza"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kvaliteta zraka"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Inform. o emitiranju"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Upravlj. kuć. uređ."</string>
+ <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) -->
<skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Odabir profilne slike"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Ikona zadanog korisnika"</string>
diff --git a/packages/SettingsLib/res/values-hu/arrays.xml b/packages/SettingsLib/res/values-hu/arrays.xml
index a5f37ea4cab9..409d600562f5 100644
--- a/packages/SettingsLib/res/values-hu/arrays.xml
+++ b/packages/SettingsLib/res/values-hu/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Rendszerérték (alapértelmezett)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"Hang: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="2908219194098827570">"Hang: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Rendszerérték (alapértelmezett)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"Hang: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="3517061573669307965">"Hang: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Rendszerérték (alapértelmezett)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 69ddad82c712..3db43e739252 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Időjárás"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Levegőminőség"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Átküldési információ"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Otthonvezérlés"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Profilkép választása"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Alapértelmezett felhasználó ikonja"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fizikai billentyűzet"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index d8efbd36bbeb..8971fceb2cae 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -660,7 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Եղանակ"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Օդի որակը"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Հեռարձակման տվյալներ"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Տան կարգավորումներ"</string>
+ <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) -->
<skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Պրոֆիլի նկար ընտրեք"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Օգտատիրոջ կանխադրված պատկերակ"</string>
diff --git a/packages/SettingsLib/res/values-in/arrays.xml b/packages/SettingsLib/res/values-in/arrays.xml
index 5b0ad98aa2d5..95274177e888 100644
--- a/packages/SettingsLib/res/values-in/arrays.xml
+++ b/packages/SettingsLib/res/values-in/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Gunakan Pilihan Sistem (Default)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Gunakan Pilihan Sistem (Default)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Gunakan Pilihan Sistem (Default)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 029ad8bcb196..6185fd320b81 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Cuaca"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kualitas Udara"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info Transmisi"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Kontrol Rumah"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Pilih foto profil"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Ikon pengguna default"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Keyboard fisik"</string>
diff --git a/packages/SettingsLib/res/values-is/arrays.xml b/packages/SettingsLib/res/values-is/arrays.xml
index 9d481f8993d8..0b5b9788f96c 100644
--- a/packages/SettingsLib/res/values-is/arrays.xml
+++ b/packages/SettingsLib/res/values-is/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Nota val kerfisins (sjálfgefið)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> hljóð"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> hljóð"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Nota val kerfisins (sjálfgefið)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> hljóð"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> hljóð"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Nota val kerfisins (sjálfgefið)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index ccf06b027213..e71878654b5f 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Veður"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Loftgæði"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Útsendingaruppl."</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Heimastýringar"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Veldu prófílmynd"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Tákn sjálfgefins notanda"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Vélbúnaðarlyklaborð"</string>
diff --git a/packages/SettingsLib/res/values-it/arrays.xml b/packages/SettingsLib/res/values-it/arrays.xml
index 62450da37ff7..ae1e515ed573 100644
--- a/packages/SettingsLib/res/values-it/arrays.xml
+++ b/packages/SettingsLib/res/values-it/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Usa selezione di sistema (predefinita)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Usa selezione di sistema (predefinita)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Usa selezione di sistema (predefinita)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 3811152467f3..2e9b20205c9b 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -117,7 +117,7 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Trasferimento file"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo di input"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Accesso a Internet"</string>
- <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Condivis. contatti e cronologia chiamate"</string>
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Condivisione contatti e cronologia chiamate"</string>
<string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Usa per condivisione di contatti e cronologia chiamate"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Condivisione connessione Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS"</string>
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Meteo"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Qualità dell\'aria"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info sul cast"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Controlli della casa"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Scegli un\'immagine del profilo"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Icona dell\'utente predefinito"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Tastiera fisica"</string>
diff --git a/packages/SettingsLib/res/values-iw/arrays.xml b/packages/SettingsLib/res/values-iw/arrays.xml
index 49f3fcf3cce6..5d72aff0c04c 100644
--- a/packages/SettingsLib/res/values-iw/arrays.xml
+++ b/packages/SettingsLib/res/values-iw/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"שימוש בבחירת המערכת (ברירת המחדל)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"אודיו <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="2908219194098827570">"אודיו <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"שימוש בבחירת המערכת (ברירת המחדל)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"אודיו <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="3517061573669307965">"אודיו <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"שימוש בבחירת המערכת (ברירת המחדל)"</item>
<item msgid="8003118270854840095">"44.1 קילו-הרץ"</item>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 3421fb5e7b83..7e9fd893442c 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -661,6 +661,7 @@
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"איכות האוויר"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"פרטי ההעברה"</string>
<string name="dream_complication_title_home_controls" msgid="9153381632476738811">"בית חכם"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"בחירה של תמונת פרופיל"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"סמל המשתמש שמוגדר כברירת מחדל"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"מקלדת פיזית"</string>
diff --git a/packages/SettingsLib/res/values-ja/arrays.xml b/packages/SettingsLib/res/values-ja/arrays.xml
index d73cc4362a56..775e31c11278 100644
--- a/packages/SettingsLib/res/values-ja/arrays.xml
+++ b/packages/SettingsLib/res/values-ja/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"システムの選択(デフォルト)を使用"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> オーディオ"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> オーディオ"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"システムの選択(デフォルト)を使用"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> オーディオ"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> オーディオ"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"システムの選択(デフォルト)を使用"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index e7d2cf8cccff..a3311f54d15a 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"天気"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"大気質"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"キャスト情報"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"スマートホーム"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"プロフィール写真の選択"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"デフォルト ユーザー アイコン"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"物理キーボード"</string>
diff --git a/packages/SettingsLib/res/values-ka/arrays.xml b/packages/SettingsLib/res/values-ka/arrays.xml
index c0d6f97eb552..f3545b6f95d8 100644
--- a/packages/SettingsLib/res/values-ka/arrays.xml
+++ b/packages/SettingsLib/res/values-ka/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"სისტემის არჩეულის გამოყენება (ნაგულისხმევი)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> აუდიო"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> აუდიო"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"სისტემის არჩეულის გამოყენება (ნაგულისხმევი)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> აუდიო"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> აუდიო"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"სისტემის არჩეულის გამოყენება (ნაგულისხმევი)"</item>
<item msgid="8003118270854840095">"44,1 კჰც"</item>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 09f547132420..b3e4402df582 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -661,6 +661,7 @@
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"ჰაერის ხარისხი"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"ტრანსლირების ინფო"</string>
<string name="dream_complication_title_home_controls" msgid="9153381632476738811">"სახლის მართვა"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"ჭკვიანი სივრცე"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"აირჩიეთ პროფილის სურათი"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"მომხმარებლის ნაგულისხმევი ხატულა"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"ფიზიკური კლავიატურა"</string>
diff --git a/packages/SettingsLib/res/values-kk/arrays.xml b/packages/SettingsLib/res/values-kk/arrays.xml
index fc998e73a16c..9971f8651c12 100644
--- a/packages/SettingsLib/res/values-kk/arrays.xml
+++ b/packages/SettingsLib/res/values-kk/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Жүйенің таңдағанын алу (әдепкі)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудиокодегі"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудиокодегі"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"L34C"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Жүйенің таңдағанын алу (әдепкі)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудиокодегі"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудиокодегі"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"L34C"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Жүйенің таңдағанын алу (әдепкі)"</item>
<item msgid="8003118270854840095">"44,1 кГц"</item>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 3281303a6d33..5ecba9bad4bd 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Ауа райы"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Ауа сапасы"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Трансляция ақпараты"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Үйді басқару элементтері"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Профиль суретін таңдау"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Әдепкі пайдаланушы белгішесі"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Пернетақта"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 87c1aa22ff92..275bcc17a8ee 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -660,7 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"អាកាសធាតុ"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"គុណភាព​ខ្យល់"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"ព័ត៌មានអំពីការបញ្ជូន"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"ការគ្រប់គ្រង​ផ្ទះ"</string>
+ <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) -->
<skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"ជ្រើសរើស​រូបភាព​កម្រង​ព័ត៌មាន"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"រូបអ្នកប្រើប្រាស់លំនាំដើម"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index ab437e8a2d3a..7e9ff120d80d 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -660,7 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"ಹವಾಮಾನ"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"ವಾಯು ಗುಣಮಟ್ಟ"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"ಬಿತ್ತರಿಸಿದ ಮಾಹಿತಿ"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"ಹೋಮ್ ನಿಯಂತ್ರಣಗಳು"</string>
+ <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) -->
<skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"ಪ್ರೊಫೈಲ್ ಚಿತ್ರವನ್ನು ಆಯ್ಕೆ ಮಾಡಿ"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"ಡೀಫಾಲ್ಟ್ ಬಳಕೆದಾರರ ಐಕಾನ್"</string>
diff --git a/packages/SettingsLib/res/values-ko/arrays.xml b/packages/SettingsLib/res/values-ko/arrays.xml
index 7138113c8b74..16b840bd9f5f 100644
--- a/packages/SettingsLib/res/values-ko/arrays.xml
+++ b/packages/SettingsLib/res/values-ko/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"시스템 설정 사용(기본)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> 오디오"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> 오디오"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"시스템 설정 사용(기본)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> 오디오"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> 오디오"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"시스템 설정 사용(기본)"</item>
<item msgid="8003118270854840095">"44.1kHz"</item>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 2b8ab7558266..fa96ad785773 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -385,7 +385,7 @@
<string name="force_msaa" msgid="4081288296137775550">"4x MSAA 강제 사용"</string>
<string name="force_msaa_summary" msgid="9070437493586769500">"OpenGL ES 2.0 앱에서 4x MSAA 사용"</string>
<string name="show_non_rect_clip" msgid="7499758654867881817">"사각형이 아닌 클립 작업 디버그"</string>
- <string name="track_frame_time" msgid="522674651937771106">"프로필 HWUI 렌더링"</string>
+ <string name="track_frame_time" msgid="522674651937771106">"HWUI 렌더링 프로파일"</string>
<string name="enable_gpu_debug_layers" msgid="4986675516188740397">"GPU 디버그 레이어 사용 설정"</string>
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"디버그 앱에 GPU 디버그 레이어 로드 허용"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"상세 공급업체 로깅 사용 설정"</string>
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"날씨"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"대기 상태"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"전송 정보"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"홈 컨트롤"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"프로필 사진 선택하기"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"기본 사용자 아이콘"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"물리적 키보드"</string>
diff --git a/packages/SettingsLib/res/values-ky/arrays.xml b/packages/SettingsLib/res/values-ky/arrays.xml
index 40271f71afdb..700aae13822e 100644
--- a/packages/SettingsLib/res/values-ky/arrays.xml
+++ b/packages/SettingsLib/res/values-ky/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"карта13"</item>
<item msgid="8147982633566548515">"карта14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Система тандаганды колдонуу (демейки)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудио"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Система тандаганды колдонуу (демейки)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудио"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Система тандаганды колдонуу (демейки)"</item>
<item msgid="8003118270854840095">"44,1 кГц"</item>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index e993e3f3049e..9ebd47be01db 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Аба ырайы"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Абанын сапаты"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Тышкы экранга чыгаруу маалыматы"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Үйдү көзөмөлдөө"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Профилдин сүрөтүн тандоо"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Демейки колдонуучунун сүрөтчөсү"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Аппараттык баскычтоп"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index d295932053e2..11d6d43749df 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -660,7 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"ສະພາບອາກາດ"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"ຄຸນນະພາບ​ອາກາດ"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"ຂໍ້ມູນການສົ່ງສັນຍານ"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"ການຄວບຄຸມເຮືອນ"</string>
+ <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) -->
<skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"ເລືອກຮູບໂປຣໄຟລ໌"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"ໄອຄອນຜູ້ໃຊ້ເລີ່ມຕົ້ນ"</string>
diff --git a/packages/SettingsLib/res/values-lt/arrays.xml b/packages/SettingsLib/res/values-lt/arrays.xml
index 946f69c3030b..c0aafdcd4da3 100644
--- a/packages/SettingsLib/res/values-lt/arrays.xml
+++ b/packages/SettingsLib/res/values-lt/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Naudoti sistemos pasirink. (numatytasis)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> garsas"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> garsas"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Naudoti sistemos pasirink. (numatytasis)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> garsas"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> garsas"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Naudoti sistemos pasirink. (numatytasis)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index e38889bbf5ad..e016956b96e3 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -661,6 +661,7 @@
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Oro kokybė"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Perdav. informacija"</string>
<string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Namų sist. valdikl."</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Pasirinkite profilio nuotrauką"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Numatytojo naudotojo piktograma"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fizinė klaviatūra"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index d899adaa1f0e..ffd416353b2a 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -660,7 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Laikapstākļi"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Gaisa kvalitāte"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Apraides informācija"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Mājas kontrolierīces"</string>
+ <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) -->
<skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Profila attēla izvēle"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Noklusējuma lietotāja ikona"</string>
diff --git a/packages/SettingsLib/res/values-mk/arrays.xml b/packages/SettingsLib/res/values-mk/arrays.xml
index 9c46f216522c..41427c1fc871 100644
--- a/packages/SettingsLib/res/values-mk/arrays.xml
+++ b/packages/SettingsLib/res/values-mk/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Користи избор на системот (стандардно)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудио"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Користи избор на системот (стандардно)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудио"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Користи избор на системот (стандардно)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index c8033e3e9ce9..26a87e3cbcd5 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -661,6 +661,7 @@
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Квалитет на воздух"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Инфо за улогите"</string>
<string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Контроли за домот"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Изберете профилна слика"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Икона за стандарден корисник"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Физичка тастатура"</string>
diff --git a/packages/SettingsLib/res/values-ml/arrays.xml b/packages/SettingsLib/res/values-ml/arrays.xml
index 4715e2a32643..98e3bd6ed75b 100644
--- a/packages/SettingsLib/res/values-ml/arrays.xml
+++ b/packages/SettingsLib/res/values-ml/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"സിസ്റ്റം സെലക്ഷൻ ഉപയോഗിക്കൂ ‌(ഡിഫോൾട്ട്)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ഓഡിയോ"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ഓഡിയോ"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"സിസ്റ്റം സെലക്ഷൻ ഉപയോഗിക്കൂ ‌(ഡിഫോൾട്ട്)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ഓഡിയോ"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ഓഡിയോ"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"സിസ്റ്റം സെലക്ഷൻ ഉപയോഗിക്കൂ ‌(ഡിഫോൾട്ട്)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 089f6afd6271..5d9f799b8918 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -661,6 +661,7 @@
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"വായു നിലവാരം"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"കാസ്റ്റ് വിവരങ്ങൾ"</string>
<string name="dream_complication_title_home_controls" msgid="9153381632476738811">"ഹോം കൺട്രോളുകൾ"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"പ്രൊഫൈൽ ചിത്രം തിരഞ്ഞെടുക്കുക"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"ഡിഫോൾട്ട് ഉപയോക്തൃ ഐക്കൺ"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"ഫിസിക്കൽ കീബോർഡ്"</string>
diff --git a/packages/SettingsLib/res/values-mn/arrays.xml b/packages/SettingsLib/res/values-mn/arrays.xml
index 63fa53b799d0..f3c10d7af4a4 100644
--- a/packages/SettingsLib/res/values-mn/arrays.xml
+++ b/packages/SettingsLib/res/values-mn/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Системийн сонголтыг ашиглах (Өгөгдмөл)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудио"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Системийн сонголтыг ашиглах (Өгөгдмөл)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудио"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Системийн сонголтыг ашиглах (Өгөгдмөл)"</item>
<item msgid="8003118270854840095">"44.1 кГц"</item>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index cc350536e8d9..df6fa89b3d54 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -447,8 +447,8 @@
<string name="daltonizer_mode_deuteranomaly" msgid="3507284319584683963">"Дьютераномаль (улаан-ногоон)"</string>
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Протаномаль (улаан-ногоон)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Тританомаль (цэнхэр-шар)"</string>
- <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Өнгө тохируулах"</string>
- <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Өнгөний засвар нь таныг дараахыг хийхийг хүсэх үед хэрэгтэй байж болно:&lt;br/&gt; &lt;ol&gt; &lt;li&gt;&amp;nbsp;Өнгөнүүдийг илүү нарийвчилж харах&lt;/li&gt; &lt;li&gt;&amp;nbsp;Төвлөрөхийн тулд өнгөнүүдийг хасах&lt;/li&gt; &lt;/ol&gt;"</string>
+ <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Өнгө тохируулга"</string>
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Өнгө тохируулга нь таныг дараахыг хийхийг хүсэх үед хэрэгтэй байж болно:&lt;br/&gt; &lt;ol&gt; &lt;li&gt;&amp;nbsp;Өнгөнүүдийг илүү нарийвчилж харах&lt;/li&gt; &lt;li&gt;&amp;nbsp;Төвлөрөхийн тулд өнгөнүүдийг хасах&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Давхарласан <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Ойролцоогоор <xliff:g id="TIME_REMAINING">%1$s</xliff:g> үлдсэн"</string>
@@ -661,6 +661,7 @@
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Агаарын чанар"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Дамжуулах мэдээлэл"</string>
<string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Гэрийн удирдлага"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Профайл зураг сонгох"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Өгөгдмөл хэрэглэгчийн дүрс тэмдэг"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Биет гар"</string>
diff --git a/packages/SettingsLib/res/values-mr/arrays.xml b/packages/SettingsLib/res/values-mr/arrays.xml
index a54f99023a42..c37baaa2d0a7 100644
--- a/packages/SettingsLib/res/values-mr/arrays.xml
+++ b/packages/SettingsLib/res/values-mr/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"सिस्टीम निवड वापरा (डीफॉल्ट)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ऑडिओ"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ऑडिओ"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"सिस्टम निवड वापरा (डीफॉल्ट)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ऑडिओ"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ऑडिओ"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"सिस्टम निवड वापरा (डीफॉल्ट)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 1907d009b896..45975497c49b 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -661,6 +661,7 @@
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"हवेची गुणवत्ता"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"कास्टसंबंधित माहिती"</string>
<string name="dream_complication_title_home_controls" msgid="9153381632476738811">"होम कंट्रोल"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"स्मार्टस्पेस"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"प्रोफाइल फोटो निवडा"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"डीफॉल्ट वापरकर्ता आयकन"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"वास्तविक कीबोर्ड"</string>
diff --git a/packages/SettingsLib/res/values-ms/arrays.xml b/packages/SettingsLib/res/values-ms/arrays.xml
index b26ed2317b5f..b19f0380a618 100644
--- a/packages/SettingsLib/res/values-ms/arrays.xml
+++ b/packages/SettingsLib/res/values-ms/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Gunakan Pilihan Sistem (Lalai)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Gunakan Pilihan Sistem (Lalai)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Gunakan Pilihan Sistem (Lalai)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index b3929c912e23..14710400bf3f 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Cuaca"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kualiti Udara"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Maklumat Pelakon"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Kawalan Rumah"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Pilih gambar profil"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Ikon pengguna lalai"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Papan kekunci fizikal"</string>
diff --git a/packages/SettingsLib/res/values-my/arrays.xml b/packages/SettingsLib/res/values-my/arrays.xml
index ed95dfe2e008..3398c5bc0e75 100644
--- a/packages/SettingsLib/res/values-my/arrays.xml
+++ b/packages/SettingsLib/res/values-my/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"စနစ်ရွေးချယ်မှုကို အသုံးပြုပါ (မူရင်း)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> အသံ"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> အသံ"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"စနစ်ရွေးချယ်မှုကို အသုံးပြုပါ (မူရင်း)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> အသံ"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> အသံ"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"စနစ်ရွေးချယ်မှုကို အသုံးပြုပါ (မူရင်း)"</item>
<item msgid="8003118270854840095">"၄၄.၁ kHz"</item>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index f7a33a95abf7..ee5601f2368a 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"မိုးလေဝသ"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"လေထုအရည်အသွေး"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"ကာစ် အချက်အလက်"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"အိမ်သတ်မှတ်ချက်များ"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"ပရိုဖိုင်ပုံ ရွေးပါ"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"မူရင်းအသုံးပြုသူ သင်္ကေတ"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"ပကတိ ကီးဘုတ်"</string>
diff --git a/packages/SettingsLib/res/values-nb/arrays.xml b/packages/SettingsLib/res/values-nb/arrays.xml
index 317c2dbbce04..7e65fa0d42df 100644
--- a/packages/SettingsLib/res/values-nb/arrays.xml
+++ b/packages/SettingsLib/res/values-nb/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Bruk systemvalg (standard)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-lyd"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-lyd"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Bruk systemvalg (standard)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-lyd"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-lyd"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Bruk systemvalg (standard)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 9b99631aa738..a8fd256899a8 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Vær"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Luftkvalitet"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Castinformasjon"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Hjemkontroller"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Velg et profilbilde"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Standard brukerikon"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fysisk tastatur"</string>
diff --git a/packages/SettingsLib/res/values-ne/arrays.xml b/packages/SettingsLib/res/values-ne/arrays.xml
index b3c3ee2fc86f..ac1f187f12fd 100644
--- a/packages/SettingsLib/res/values-ne/arrays.xml
+++ b/packages/SettingsLib/res/values-ne/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"सिस्टमको छनौट प्रयोग गरियोस् (डिफल्ट)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> अडियो"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> अडियो"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"सिस्टमको छनौट प्रयोग गरियोस् (डिफल्ट)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> अडियो"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> अडियो"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"सिस्टमको छनौट प्रयोग गरियोस् (डिफल्ट)"</item>
<item msgid="8003118270854840095">"४४.१ kHz"</item>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 1534cba6e1bd..17b81b26606f 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"मौसम"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"वायुको गुणस्तर"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"कास्टसम्बन्धी जानकारी"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"घरायसी उपकरणका नियन्त्रण"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"प्रोफाइल फोटो छान्नुहोस्"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"प्रयोगकर्ताको डिफल्ट आइकन"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"भौतिक किबोर्ड"</string>
diff --git a/packages/SettingsLib/res/values-nl/arrays.xml b/packages/SettingsLib/res/values-nl/arrays.xml
index e8094521fcf7..7c90eabb1123 100644
--- a/packages/SettingsLib/res/values-nl/arrays.xml
+++ b/packages/SettingsLib/res/values-nl/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Systeemselectie gebruiken (standaard)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Gebruik systeemselectie (standaard)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Systeemselectie gebruiken (standaard)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index ba11b4653b1c..1251ed959059 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -661,6 +661,7 @@
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Luchtkwaliteit"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Castinformatie"</string>
<string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Bediening voor in huis"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Kies een profielfoto"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Standaard gebruikersicoon"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fysiek toetsenbord"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 09fa59d60ba8..715f1ebca258 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -116,12 +116,12 @@
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"ଫୋନ୍‌ କଲ୍‌‌ଗୁଡ଼ିକ"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ଫାଇଲ୍‌ ଟ୍ରାନ୍ସଫର୍‌"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ଇନ୍‌ପୁଟ୍‌ ଡିଭାଇସ୍"</string>
- <string name="bluetooth_profile_pan" msgid="1006235139308318188">"ଇଣ୍ଟର୍‌ନେଟ୍‌ ଆକ୍ସେସ୍"</string>
+ <string name="bluetooth_profile_pan" msgid="1006235139308318188">"ଇଣ୍ଟରନେଟ ଆକ୍ସେସ"</string>
<string name="bluetooth_profile_pbap" msgid="4262303387989406171">"କଣ୍ଟାକ୍ଟ ଏବଂ କଲ ଇତିହାସ ସେୟାରିଂ"</string>
<string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"କଣ୍ଟାକ୍ଟ ଏବଂ କଲ ଇତିହାସ ସେୟାରିଂ ପାଇଁ ବ୍ୟବହାର କରନ୍ତୁ"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ଇଣ୍ଟର୍‌ନେଟ୍‌ ସଂଯୋଗ ଶେୟାରିଙ୍ଗ"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"ଟେକ୍ସଟ୍ ମେସେଜ୍"</string>
- <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM ଆକ୍ସେସ୍‌"</string>
+ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM ଆକ୍ସେସ"</string>
<string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD ଅଡିଓ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
<string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD ଅଡିଓ"</string>
<string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"ଶ୍ରବଣ ଯନ୍ତ୍ର"</string>
@@ -156,7 +156,7 @@
<string name="bluetooth_pairing_rejected_error_message" msgid="5943444352777314442">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ଦ୍ୱାରା ପେୟାରିଙ୍ଗ ପାଇଁ ପ୍ରତ୍ୟାଖ୍ୟାନ କରିଦିଆଗଲା।"</string>
<string name="bluetooth_talkback_computer" msgid="3736623135703893773">"କମ୍ପ୍ୟୁଟର୍"</string>
<string name="bluetooth_talkback_headset" msgid="3406852564400882682">"ହେଡ୍‌ସେଟ୍‌"</string>
- <string name="bluetooth_talkback_phone" msgid="868393783858123880">"ଫୋନ୍‌"</string>
+ <string name="bluetooth_talkback_phone" msgid="868393783858123880">"ଫୋନ"</string>
<string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"ଇମେଜିଙ୍ଗ"</string>
<string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"ହେଡ୍‌ଫୋନ୍‌"</string>
<string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"ଇନ୍‌ପୁଟ୍‌ ଉପକରଣ"</string>
@@ -246,7 +246,7 @@
<string name="adb_pair_method_code_summary" msgid="6370414511333685185">"ଛଅ ଡିଜିଟ୍ କୋଡ୍ ବ୍ୟବହାର କରି ନୂଆ ଡିଭାଇସଗୁଡ଼ିକୁ ପେୟାର୍ କରନ୍ତୁ"</string>
<string name="adb_paired_devices_title" msgid="5268997341526217362">"ପେୟାର୍ ହୋଇଥିବା ଡିଭାଇସଗୁଡ଼ିକ"</string>
<string name="adb_wireless_device_connected_summary" msgid="3039660790249148713">"ବର୍ତ୍ତମାନ ସଂଯୁକ୍ତ ଅଛି"</string>
- <string name="adb_wireless_device_details_title" msgid="7129369670526565786">"ଡିଭାଇସ୍ ବିବରଣୀ"</string>
+ <string name="adb_wireless_device_details_title" msgid="7129369670526565786">"ଡିଭାଇସର ବିବରଣୀ"</string>
<string name="adb_device_forget" msgid="193072400783068417">"ଭୁଲିଯାଆନ୍ତୁ"</string>
<string name="adb_device_fingerprint_title_format" msgid="291504822917843701">"ଡିଭାଇସ୍ ଫିଙ୍ଗରପ୍ରିଣ୍ଟ: <xliff:g id="FINGERPRINT_PARAM">%1$s</xliff:g>"</string>
<string name="adb_wireless_connection_failed_title" msgid="664211177427438438">"ସଂଯୋଗ ବିଫଳ ହେଲା"</string>
@@ -660,7 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"ପାଣିପାଗ"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"ବାୟୁର ଗୁଣବତ୍ତା"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"କାଷ୍ଟ ସୂଚନା"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"ହୋମ କଣ୍ଟ୍ରୋଲ"</string>
+ <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) -->
<skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"ଏକ ପ୍ରୋଫାଇଲ ଛବି ବାଛନ୍ତୁ"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"ଡିଫଲ୍ଟ ଉପଯୋଗକର୍ତ୍ତା ଆଇକନ"</string>
diff --git a/packages/SettingsLib/res/values-pa/arrays.xml b/packages/SettingsLib/res/values-pa/arrays.xml
index a3fae22d9439..0fd5c56a4e7d 100644
--- a/packages/SettingsLib/res/values-pa/arrays.xml
+++ b/packages/SettingsLib/res/values-pa/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"ਸਿਸਟਮ ਚੋਣ ਦੀ ਵਰਤੋਂ ਕਰੋ (ਪੂਰਵ-ਨਿਰਧਾਰਿਤ)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ਆਡੀਓ"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ਆਡੀਓ"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"ਸਿਸਟਮ ਚੋਣ ਦੀ ਵਰਤੋਂ ਕਰੋ (ਪੂਰਵ-ਨਿਰਧਾਰਿਤ)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ਆਡੀਓ"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ਆਡੀਓ"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"ਸਿਸਟਮ ਚੋਣ ਦੀ ਵਰਤੋਂ ਕਰੋ (ਪੂਰਵ-ਨਿਰਧਾਰਿਤ)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 9df0fefd7ff8..51bf22d5acbb 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"ਮੌਸਮ"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"ਹਵਾ ਦੀ ਕੁਆਲਿਟੀ"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"ਕਾਸਟ ਸੰਬੰਧੀ ਜਾਣਕਾਰੀ"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"ਹੋਮ ਕੰਟਰੋਲ"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"ਕੋਈ ਪ੍ਰੋਫਾਈਲ ਤਸਵੀਰ ਚੁਣੋ"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਵਰਤੋਂਕਾਰ ਪ੍ਰਤੀਕ"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"ਭੌਤਿਕ ਕੀ-ਬੋਰਡ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index f4599fdf806a..64cbe78d7518 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -660,7 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Pogoda"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Jakość powietrza"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Obsada"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Sterowanie domem"</string>
+ <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) -->
<skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Wybierz zdjęcie profilowe"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Ikona domyślnego użytkownika"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/arrays.xml b/packages/SettingsLib/res/values-pt-rBR/arrays.xml
index 1883ef355776..f218fab6508b 100644
--- a/packages/SettingsLib/res/values-pt-rBR/arrays.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Usar seleção do sistema (padrão)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="2908219194098827570">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Usar seleção do sistema (padrão)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="3517061573669307965">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Usar seleção do sistema (padrão)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 12507c5449df..531bc6308abe 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -208,7 +208,7 @@
<string name="tts_engine_settings_title" msgid="7849477533103566291">"Configurações para <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
<string name="tts_engine_settings_button" msgid="477155276199968948">"Iniciar configurações do mecanismo"</string>
<string name="tts_engine_preference_section_title" msgid="3861562305498624904">"Mecanismo preferencial"</string>
- <string name="tts_general_section_title" msgid="8919671529502364567">"Gerais"</string>
+ <string name="tts_general_section_title" msgid="8919671529502364567">"Geral"</string>
<string name="tts_reset_speech_pitch_title" msgid="7149398585468413246">"Redefinir o tom de voz"</string>
<string name="tts_reset_speech_pitch_summary" msgid="6822904157021406449">"Redefinir o tom de voz para o padrão."</string>
<string-array name="tts_rate_entries">
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Clima"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Qualidade ­do ar"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info. de transmissão"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Automação residencial"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Escolher a foto do perfil"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Ícone de usuário padrão"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Teclado físico"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/arrays.xml b/packages/SettingsLib/res/values-pt-rPT/arrays.xml
index 985bd515acd0..e323455b9ce2 100644
--- a/packages/SettingsLib/res/values-pt-rPT/arrays.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Utilizar seleção do sistema (predefinido)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="2908219194098827570">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Utilizar seleção do sistema (predefinido)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="3517061573669307965">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Utilizar seleção do sistema (predefinido)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 4988136ba43a..ed3f642e2cc3 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -661,6 +661,7 @@
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Qualidade do ar"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info. de transmissão"</string>
<string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Ctr. domésticos"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Espaço inteligente"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Escolha uma imagem do perfil"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Ícone do utilizador predefinido"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Teclado físico"</string>
diff --git a/packages/SettingsLib/res/values-pt/arrays.xml b/packages/SettingsLib/res/values-pt/arrays.xml
index 1883ef355776..f218fab6508b 100644
--- a/packages/SettingsLib/res/values-pt/arrays.xml
+++ b/packages/SettingsLib/res/values-pt/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Usar seleção do sistema (padrão)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="2908219194098827570">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Usar seleção do sistema (padrão)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="3517061573669307965">"Áudio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Usar seleção do sistema (padrão)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 12507c5449df..531bc6308abe 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -208,7 +208,7 @@
<string name="tts_engine_settings_title" msgid="7849477533103566291">"Configurações para <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
<string name="tts_engine_settings_button" msgid="477155276199968948">"Iniciar configurações do mecanismo"</string>
<string name="tts_engine_preference_section_title" msgid="3861562305498624904">"Mecanismo preferencial"</string>
- <string name="tts_general_section_title" msgid="8919671529502364567">"Gerais"</string>
+ <string name="tts_general_section_title" msgid="8919671529502364567">"Geral"</string>
<string name="tts_reset_speech_pitch_title" msgid="7149398585468413246">"Redefinir o tom de voz"</string>
<string name="tts_reset_speech_pitch_summary" msgid="6822904157021406449">"Redefinir o tom de voz para o padrão."</string>
<string-array name="tts_rate_entries">
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Clima"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Qualidade ­do ar"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info. de transmissão"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Automação residencial"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Escolher a foto do perfil"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Ícone de usuário padrão"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Teclado físico"</string>
diff --git a/packages/SettingsLib/res/values-ro/arrays.xml b/packages/SettingsLib/res/values-ro/arrays.xml
index f1e99865c9a6..34b0ac9e2500 100644
--- a/packages/SettingsLib/res/values-ro/arrays.xml
+++ b/packages/SettingsLib/res/values-ro/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Folosiți selectarea sistemului (prestabilit)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="2908219194098827570">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Folosiți selectarea sistemului (prestabilit)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="3517061573669307965">"Audio <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Folosiți selectarea sistemului (prestabilit)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index f49fcdd96a05..8f87a7c10f97 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Meteo"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Calitatea aerului"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Informații artiști"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Controlul locuinței"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Alegeți o fotografie de profil"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Pictograma prestabilită a utilizatorului"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Tastatură fizică"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 38930a5117ca..e2f05dc6e349 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -660,7 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Погода"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Качество воздуха"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Данные о трансляции"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Автоматизация дома"</string>
+ <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) -->
<skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Выберите фото профиля"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Значок пользователя по умолчанию"</string>
diff --git a/packages/SettingsLib/res/values-si/arrays.xml b/packages/SettingsLib/res/values-si/arrays.xml
index 8386c1aa5559..eaacfb835de5 100644
--- a/packages/SettingsLib/res/values-si/arrays.xml
+++ b/packages/SettingsLib/res/values-si/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"පද්ධති තේරීම භාවිත කරන්න (පෙරනිමි)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ශ්‍රව්‍යය"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ශ්‍රව්‍යය"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"පද්ධති තේරීම භාවිත කරන්න (පෙරනිමි)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ශ්‍රව්‍යය"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ශ්‍රව්‍යය"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"පද්ධති තේරීම භාවිත කරන්න (පෙරනිමි)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index fa0296f80ff0..1d52e0b5b9f7 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"කාලගුණය"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"වායු ගුණත්වය"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"විකාශ තතු"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"නිවෙස් පාලන"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"ස්මාර්ට් අවකාශය"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"පැතිකඩ පින්තූරයක් තේරීම"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"පෙරනිමි පරිශීලක නිරූපකය"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"භෞතික යතුරු පුවරුව"</string>
diff --git a/packages/SettingsLib/res/values-sk/arrays.xml b/packages/SettingsLib/res/values-sk/arrays.xml
index 370b23f6cd58..bbfe9696e698 100644
--- a/packages/SettingsLib/res/values-sk/arrays.xml
+++ b/packages/SettingsLib/res/values-sk/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Použiť voľbu systému (predvolené)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"Zvuk: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="2908219194098827570">"Zvuk: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Použiť voľbu systému (predvolené)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"Zvuk: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="3517061573669307965">"Zvuk: <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Použiť voľbu systému (predvolené)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index a7c69c8247d8..cf501cd62d17 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -661,6 +661,7 @@
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kvalita vzduchu"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Informácie o prenose"</string>
<string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Ovládanie domácnosti"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Výber profilovej fotky"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Predvolená ikona používateľa"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fyzická klávesnica"</string>
diff --git a/packages/SettingsLib/res/values-sl/arrays.xml b/packages/SettingsLib/res/values-sl/arrays.xml
index 6e33e386d897..b2003e5efbc6 100644
--- a/packages/SettingsLib/res/values-sl/arrays.xml
+++ b/packages/SettingsLib/res/values-sl/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Uporabi sistemsko izbiro (privzeto)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"Zvok <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="2908219194098827570">"Zvok <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Uporabi sistemsko izbiro (privzeto)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"Zvok <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="3517061573669307965">"Zvok <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Uporabi sistemsko izbiro (privzeto)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 7334ceafc954..bc89f5af3570 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -661,6 +661,7 @@
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kakovost zraka"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"O zasedbi"</string>
<string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Nadzor doma"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Hitri pregled"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Izbira profilne slike"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Privzeta ikona uporabnika"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fizična tipkovnica"</string>
diff --git a/packages/SettingsLib/res/values-sq/arrays.xml b/packages/SettingsLib/res/values-sq/arrays.xml
index 8a6d853e7818..ed8638016c34 100644
--- a/packages/SettingsLib/res/values-sq/arrays.xml
+++ b/packages/SettingsLib/res/values-sq/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Përdor përzgjedhjen e sistemit (e parazgjedhur)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"Audioja e <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="2908219194098827570">"Audioja e <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Përdor përzgjedhjen e sistemit (e parazgjedhur)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"Audioja e <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="3517061573669307965">"Audioja e <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Përdor përzgjedhjen e sistemit (e parazgjedhur)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 59359cdf74fd..e7af25d7142f 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Moti"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Cilësia e ajrit"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Të dhënat e aktorëve"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Kontrollet e shtëpisë"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Zgjidh një fotografi profili"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Ikona e parazgjedhur e përdoruesit"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Tastiera fizike"</string>
diff --git a/packages/SettingsLib/res/values-sr/arrays.xml b/packages/SettingsLib/res/values-sr/arrays.xml
index 7e198bfe3f6d..a95e47b6b2b0 100644
--- a/packages/SettingsLib/res/values-sr/arrays.xml
+++ b/packages/SettingsLib/res/values-sr/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Користи избор система (подразумевано)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудио"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Користи избор система (подразумевано)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> аудио"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> аудио"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Користи избор система (подразумевано)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index eee3f8917c50..a02d674b92a0 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Време"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Квалитет ваздуха"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Подаци о пребацивању"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Управљање домом"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"SmartSpace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Одаберите слику профила"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Подразумевана икона корисника"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Физичка тастатура"</string>
diff --git a/packages/SettingsLib/res/values-sv/arrays.xml b/packages/SettingsLib/res/values-sv/arrays.xml
index f99a85b84533..c63465c65d83 100644
--- a/packages/SettingsLib/res/values-sv/arrays.xml
+++ b/packages/SettingsLib/res/values-sv/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Använd systemval (standardinställning)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-ljud"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-ljud"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Använd systemval (standardinställning)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>-ljud"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>-ljud"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Använd systemval (standardinställning)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 1a8815c1164b..e135d1b47859 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Väder"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Luftkvalitet"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Info om rollistan"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Hemstyrning"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Välj en profilbild"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Ikon för standardanvändare"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fysiskt tangentbord"</string>
diff --git a/packages/SettingsLib/res/values-sw/arrays.xml b/packages/SettingsLib/res/values-sw/arrays.xml
index dab4279f88dc..53dc6e5440ad 100644
--- a/packages/SettingsLib/res/values-sw/arrays.xml
+++ b/packages/SettingsLib/res/values-sw/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"ramani ya 13"</item>
<item msgid="8147982633566548515">"ramani ya 14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Tumia Uteuzi wa Mfumo (Chaguomsingi)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"Sauti ya <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="2908219194098827570">"Sauti ya <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Tumia Uteuzi wa Mfumo (Chaguomsingi)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"Sauti ya <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="3517061573669307965">"Sauti ya <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Tumia Uteuzi wa Mfumo (Chaguomsingi)"</item>
<item msgid="8003118270854840095">"kHz 44.1"</item>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 5f1d141a0256..a900064efc29 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Hali ya Hewa"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Ubora wa Hewa"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Maelezo ya Wahusika"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Udhibiti wa Vifaa Nyumbani"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Chagua picha ya wasifu"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Aikoni chaguomsingi ya mtumiaji"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Kibodi halisi"</string>
diff --git a/packages/SettingsLib/res/values-ta/arrays.xml b/packages/SettingsLib/res/values-ta/arrays.xml
index a0f1fa6447c8..957bd61130f3 100644
--- a/packages/SettingsLib/res/values-ta/arrays.xml
+++ b/packages/SettingsLib/res/values-ta/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"சாதனத் தேர்வைப் பயன்படுத்து (இயல்பு)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ஆடியோ"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ஆடியோ"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"சாதனத் தேர்வைப் பயன்படுத்து (இயல்பு)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ஆடியோ"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ஆடியோ"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"சாதனத் தேர்வைப் பயன்படுத்து (இயல்பு)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index b8c37b037d02..645278c8fd87 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"வானிலை"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"காற்றின் தரம்"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"அலைபரப்புத் தகவல்"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"ஹோம் கன்ட்ரோல்கள்"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"சுயவிவரப் படத்தைத் தேர்வுசெய்யுங்கள்"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"இயல்புநிலைப் பயனர் ஐகான்"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"கீபோர்டு"</string>
diff --git a/packages/SettingsLib/res/values-te/arrays.xml b/packages/SettingsLib/res/values-te/arrays.xml
index 18a27585cc8c..d4361e52232f 100644
--- a/packages/SettingsLib/res/values-te/arrays.xml
+++ b/packages/SettingsLib/res/values-te/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"సిస్టమ్ ఎంపికను ఉపయోగించండి (ఆటోమేటిక్)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ఆడియో"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ఆడియో"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"సిస్టమ్ ఎంపికను ఉపయోగించండి (ఆటోమేటిక్)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ఆడియో"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ఆడియో"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"సిస్టమ్ ఎంపికను ఉపయోగించండి (ఆటోమేటిక్)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 504c827e9a9e..de3f390cda19 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -661,6 +661,7 @@
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"గాలి క్వాలిటీ"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"కాస్ట్ సమాచారం"</string>
<string name="dream_complication_title_home_controls" msgid="9153381632476738811">"హోమ్ కంట్రోల్స్"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"స్మార్ట్‌స్పేస్"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"ప్రొఫైల్ ఫోటోను ఎంచుకోండి"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"ఆటోమేటిక్ సెట్టింగ్ యూజర్ చిహ్నం"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"భౌతిక కీబోర్డ్"</string>
diff --git a/packages/SettingsLib/res/values-th/arrays.xml b/packages/SettingsLib/res/values-th/arrays.xml
index 04a5f4dd86b9..782e95e91cfe 100644
--- a/packages/SettingsLib/res/values-th/arrays.xml
+++ b/packages/SettingsLib/res/values-th/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"ใช้การเลือกของระบบ (ค่าเริ่มต้น)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"เสียง <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="2908219194098827570">"เสียง <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"ใช้การเลือกของระบบ (ค่าเริ่มต้น)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"เสียง <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="3517061573669307965">"เสียง <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"ใช้การเลือกของระบบ (ค่าเริ่มต้น)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index f33630dd0ea4..dfb87a8d19af 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"สภาพอากาศ"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"คุณภาพอากาศ"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"ข้อมูลแคสต์"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"ระบบควบคุมอุปกรณ์ในบ้าน"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"เลือกรูปโปรไฟล์"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"ไอคอนผู้ใช้เริ่มต้น"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"แป้นพิมพ์จริง"</string>
diff --git a/packages/SettingsLib/res/values-tl/arrays.xml b/packages/SettingsLib/res/values-tl/arrays.xml
index 59cb1f370338..19d3423a7b48 100644
--- a/packages/SettingsLib/res/values-tl/arrays.xml
+++ b/packages/SettingsLib/res/values-tl/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Gamitin ang Pagpili ng System (Default)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> na audio"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> na audio"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Gamitin ang Pagpili ng System (Default)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> na audio"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> na audio"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Gamitin ang Pagpili ng System (Default)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index f95f0e50f1be..fdaece106798 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Lagay ng Panahon"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Kalidad ng Hangin"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Impormasyon ng Cast"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Home Controls"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Pumili ng larawan sa profile"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Icon ng default na user"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Pisikal na keyboard"</string>
diff --git a/packages/SettingsLib/res/values-tr/arrays.xml b/packages/SettingsLib/res/values-tr/arrays.xml
index 5ed35faee347..37891ae5cf3b 100644
--- a/packages/SettingsLib/res/values-tr/arrays.xml
+++ b/packages/SettingsLib/res/values-tr/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Sistem Seçimini Kullan (Varsayılan)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ses"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ses"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Sistem Seçimini Kullan (Varsayılan)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ses"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ses"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Sistem Seçimini Kullan (Varsayılan)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 2fa2bd18c5f3..a2eb0e2bd030 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Hava durumu"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Hava Kalitesi"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Yayın Bilgisi"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Ev Kontrolleri"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Profil fotoğrafı seçin"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Varsayılan kullanıcı simgesi"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fiziksel klavye"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 9be0855b1d48..63d711808748 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -660,7 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Погода"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Якість повітря"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Акторський склад"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Автоматизація дому"</string>
+ <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) -->
<skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Виберіть зображення профілю"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Значок користувача за умовчанням"</string>
diff --git a/packages/SettingsLib/res/values-ur/arrays.xml b/packages/SettingsLib/res/values-ur/arrays.xml
index d0974580b5ee..db9941e7d436 100644
--- a/packages/SettingsLib/res/values-ur/arrays.xml
+++ b/packages/SettingsLib/res/values-ur/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"سسٹم انتخاب کا استعمال کریں (ڈیفالٹ)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> آڈیو"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> آڈیو"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"سسٹم انتخاب کا استعمال کریں (ڈیفالٹ)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> آڈیو"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> آڈیو"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"سسٹم انتخاب کا استعمال کریں (ڈیفالٹ)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 8eb9a117e1a3..2de47ecb0cf2 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -661,6 +661,7 @@
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"ہوا کا معیار"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"کاسٹ کرنے کی معلومات"</string>
<string name="dream_complication_title_home_controls" msgid="9153381632476738811">"ہوم کنٹرولز"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"پروفائل کی تصویر منتخب کریں"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"ڈیفالٹ صارف کا آئیکن"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"فزیکل کی بورڈ"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index a5c868360eae..707a54edb57f 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -660,7 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Ob-havo"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Havo sifati"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Translatsiya axboroti"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Uy boshqaruvi"</string>
+ <!-- no translation found for dream_complication_title_smartspace (4197829945636051120) -->
<skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Profil rasmini tanlash"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Foydalanuvchining standart belgisi"</string>
diff --git a/packages/SettingsLib/res/values-vi/arrays.xml b/packages/SettingsLib/res/values-vi/arrays.xml
index 31867e2f18f7..ee599d64bd39 100644
--- a/packages/SettingsLib/res/values-vi/arrays.xml
+++ b/packages/SettingsLib/res/values-vi/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Sử dụng lựa chọn của hệ thống (Mặc định)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"Âm thanh <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="2908219194098827570">"Âm thanh <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Sử dụng lựa chọn của hệ thống (Mặc định)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"Âm thanh <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
+ <item msgid="3517061573669307965">"Âm thanh <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Sử dụng lựa chọn của hệ thống (Mặc định)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 45a0465719ed..82eb63d6ca23 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Thời tiết"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Chất lượng không khí"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Thông tin về dàn nghệ sĩ"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Điều khiển nhà"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Chọn một ảnh hồ sơ"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Biểu tượng người dùng mặc định"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Bàn phím thực"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/arrays.xml b/packages/SettingsLib/res/values-zh-rCN/arrays.xml
index 973d7d01fcc9..2a85d311a5c4 100644
--- a/packages/SettingsLib/res/values-zh-rCN/arrays.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"使用系统选择(默认)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> 音频"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> 音频"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"使用系统选择(默认)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> 音频"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> 音频"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"使用系统选择(默认)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 2bc8a305d0fc..9f8007b9cb40 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"天气"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"空气质量"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"投放信息"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"家居控制"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"SmartSpace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"选择个人资料照片"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"默认用户图标"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"实体键盘"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/arrays.xml b/packages/SettingsLib/res/values-zh-rHK/arrays.xml
index 87f382524a5c..a84f0e2fa88c 100644
--- a/packages/SettingsLib/res/values-zh-rHK/arrays.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"使用系統選擇 (預設)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> 音訊"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> 音訊"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"使用系統選擇 (預設)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> 音訊"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> 音訊"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"使用系統選擇 (預設)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 114bc6424db5..cc80ebcf9e02 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"天氣"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"空氣質素"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"投放資料"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"智能家居"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"選擇個人檔案相片"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"預設使用者圖示"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"實體鍵盤"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/arrays.xml b/packages/SettingsLib/res/values-zh-rTW/arrays.xml
index 529287f4354f..66aaa56b201a 100644
--- a/packages/SettingsLib/res/values-zh-rTW/arrays.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"系統自動選擇 (預設)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> 音訊"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> 音訊"</item>
+ <item msgid="3825367753087348007">"LDAC"</item>
+ <item msgid="328951785723550863">"LC3"</item>
+ <item msgid="506175145534048710">"Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"系統自動選擇 (預設)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> 音訊"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> 音訊"</item>
+ <item msgid="2553206901068987657">"LDAC"</item>
+ <item msgid="3940992993241040716">"LC3"</item>
+ <item msgid="7940970833006181407">"Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"系統自動選擇 (預設)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index a7ae213c26ba..46aaef88e757 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"天氣"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"空氣品質"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"演出者資訊"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"居家控制系統"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"智慧空間"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"選擇個人資料相片"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"預設使用者圖示"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"實體鍵盤"</string>
diff --git a/packages/SettingsLib/res/values-zu/arrays.xml b/packages/SettingsLib/res/values-zu/arrays.xml
index 59ead86f0f8f..0494f1c0b6df 100644
--- a/packages/SettingsLib/res/values-zu/arrays.xml
+++ b/packages/SettingsLib/res/values-zu/arrays.xml
@@ -85,10 +85,26 @@
<item msgid="7073042887003102964">"Imephu13"</item>
<item msgid="8147982633566548515">"Imephu14"</item>
</string-array>
- <!-- no translation found for bluetooth_a2dp_codec_titles:6 (328951785723550863) -->
- <!-- no translation found for bluetooth_a2dp_codec_titles:7 (506175145534048710) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:6 (3940992993241040716) -->
- <!-- no translation found for bluetooth_a2dp_codec_summaries:7 (7940970833006181407) -->
+ <string-array name="bluetooth_a2dp_codec_titles">
+ <item msgid="2494959071796102843">"Sebenzisa ukukhetha kwesistimu (Okuzenzakalelayo)"</item>
+ <item msgid="4055460186095649420">"SBC"</item>
+ <item msgid="720249083677397051">"I-AAC"</item>
+ <item msgid="1049450003868150455">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> umsindo"</item>
+ <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> umsindo"</item>
+ <item msgid="3825367753087348007">"I-LDAC"</item>
+ <item msgid="328951785723550863">"I-LC3"</item>
+ <item msgid="506175145534048710">"I-Opus"</item>
+ </string-array>
+ <string-array name="bluetooth_a2dp_codec_summaries">
+ <item msgid="8868109554557331312">"Sebenzisa ukukhetha kwesistimu (Okuzenzakalelayo)"</item>
+ <item msgid="9024885861221697796">"SBC"</item>
+ <item msgid="4688890470703790013">"I-AAC"</item>
+ <item msgid="8627333814413492563">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> umsindo"</item>
+ <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> umsindo"</item>
+ <item msgid="2553206901068987657">"I-LDAC"</item>
+ <item msgid="3940992993241040716">"I-LC3"</item>
+ <item msgid="7940970833006181407">"I-Opus"</item>
+ </string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
<item msgid="926809261293414607">"Sebenzisa ukukhetha kwesistimu (Okuzenzakalelayo)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index e037d1275ed1..e78835741bd7 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -660,8 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Isimo sezulu"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Ikhwalithi Yomoya"</string>
<string name="dream_complication_title_cast_info" msgid="4038776652841885084">"Ulwazi Lokusakaza"</string>
- <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
- <skip />
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Izilawuli Zasekhaya"</string>
+ <string name="dream_complication_title_smartspace" msgid="4197829945636051120">"I-Smartspace"</string>
<string name="avatar_picker_title" msgid="8492884172713170652">"Khetha isithombe sephrofayela"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Isithonjana somsebenzisi sokuzenzakalelayo"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Ikhibhodi ephathekayo"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index b8792236d0d9..11cb9c1b54c9 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -962,6 +962,9 @@
<!-- UI debug setting: enable freeform window support summary [CHAR LIMIT=150] -->
<string name="enable_freeform_support_summary">Enable support for experimental freeform windows.</string>
+ <!-- UI debug setting: enable desktop mode [CHAR LIMIT=25] -->
+ <string name="desktop_mode">Desktop mode</string>
+
<!-- Local (desktop) backup password menu title [CHAR LIMIT=25] -->
<string name="local_backup_password_title">Desktop backup password</string>
<!-- Summary text of the "local backup password" setting when the user has not supplied a password -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index d9262cce3cb9..766c036d521c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -528,7 +528,7 @@ public class InfoMediaManager extends MediaManager {
class RouterManagerCallback implements MediaRouter2Manager.Callback {
@Override
- public void onRoutesAdded(List<MediaRoute2Info> routes) {
+ public void onRoutesUpdated() {
refreshDevices();
}
@@ -540,16 +540,6 @@ public class InfoMediaManager extends MediaManager {
}
@Override
- public void onRoutesChanged(List<MediaRoute2Info> routes) {
- refreshDevices();
- }
-
- @Override
- public void onRoutesRemoved(List<MediaRoute2Info> routes) {
- refreshDevices();
- }
-
- @Override
public void onTransferred(RoutingSessionInfo oldSession, RoutingSessionInfo newSession) {
if (DEBUG) {
Log.d(TAG, "onTransferred() oldSession : " + oldSession.getName()
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index ee7b7d6b180f..f4af6e852580 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -112,7 +112,7 @@ public class InfoMediaManagerTest {
final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
assertThat(mediaDevice).isNull();
- mInfoMediaManager.mMediaRouterCallback.onRoutesAdded(routes);
+ mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated();
final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
@@ -135,7 +135,7 @@ public class InfoMediaManagerTest {
assertThat(mediaDevice).isNull();
mInfoMediaManager.mPackageName = "";
- mInfoMediaManager.mMediaRouterCallback.onRoutesAdded(routes);
+ mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated();
final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
@@ -199,7 +199,7 @@ public class InfoMediaManagerTest {
final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
assertThat(mediaDevice).isNull();
- mInfoMediaManager.mMediaRouterCallback.onRoutesChanged(routes);
+ mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated();
final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
@@ -222,7 +222,7 @@ public class InfoMediaManagerTest {
assertThat(mediaDevice).isNull();
mInfoMediaManager.mPackageName = "";
- mInfoMediaManager.mMediaRouterCallback.onRoutesChanged(routes);
+ mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated();
final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
@@ -263,7 +263,7 @@ public class InfoMediaManagerTest {
final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
assertThat(mediaDevice).isNull();
- mInfoMediaManager.mMediaRouterCallback.onRoutesRemoved(routes);
+ mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated();
final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
@@ -286,7 +286,7 @@ public class InfoMediaManagerTest {
assertThat(mediaDevice).isNull();
mInfoMediaManager.mPackageName = "";
- mInfoMediaManager.mMediaRouterCallback.onRoutesRemoved(routes);
+ mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated();
final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 76209da47d55..f0915f8be287 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -337,9 +337,6 @@ public class GlobalSettingsValidators {
VALIDATORS.put(Global.Wearable.CHARGING_SOUNDS_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.Wearable.BEDTIME_MODE, BOOLEAN_VALIDATOR);
VALIDATORS.put(
- Global.Wearable.WEAR_ACTIVITY_AUTO_RESUME_TIMEOUT_MAX_RESET_COUNT,
- NON_NEGATIVE_INTEGER_VALIDATOR);
- VALIDATORS.put(
Global.Wearable.EARLY_UPDATES_STATUS,
new DiscreteValueValidator(
new String[] {
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 874e57069af3..ba7a9bcc7cd7 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -98,6 +98,7 @@ public class SettingsBackupTest {
Settings.System.VOLUME_VOICE, // deprecated since API 2?
Settings.System.WHEN_TO_MAKE_WIFI_CALLS, // bug?
Settings.System.WINDOW_ORIENTATION_LISTENER_LOG, // used for debugging only
+ Settings.System.DESKTOP_MODE, // developer setting for internal prototyping
Settings.System.MIN_REFRESH_RATE, // depends on hardware capabilities
Settings.System.PEAK_REFRESH_RATE, // depends on hardware capabilities
Settings.System.SCREEN_BRIGHTNESS_FLOAT,
@@ -656,7 +657,6 @@ public class SettingsBackupTest {
Settings.Global.Wearable.CHARGING_SOUNDS_ENABLED,
Settings.Global.Wearable.SCREEN_UNLOCK_SOUND_ENABLED,
Settings.Global.Wearable.BEDTIME_MODE,
- Settings.Global.Wearable.WEAR_ACTIVITY_AUTO_RESUME_TIMEOUT_MAX_RESET_COUNT,
Settings.Global.Wearable.EARLY_UPDATES_STATUS);
private static final Set<String> BACKUP_DENY_LIST_SECURE_SETTINGS =
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/BindServiceViaContextDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/BindServiceViaContextDetector.kt
new file mode 100644
index 000000000000..925fae0ebfb4
--- /dev/null
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/BindServiceViaContextDetector.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.internal.systemui.lint
+
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.intellij.psi.PsiMethod
+import org.jetbrains.uast.UCallExpression
+
+@Suppress("UnstableApiUsage")
+class BindServiceViaContextDetector : Detector(), SourceCodeScanner {
+
+ override fun getApplicableMethodNames(): List<String> {
+ return listOf("bindService", "bindServiceAsUser", "unbindService")
+ }
+
+ override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
+ if (context.evaluator.isMemberInSubClassOf(method, "android.content.Context")) {
+ context.report(
+ ISSUE,
+ method,
+ context.getNameLocation(node),
+ "Binding or unbinding services are synchronous calls, please make " +
+ "sure you're on a @Background Executor."
+ )
+ }
+ }
+
+ companion object {
+ @JvmField
+ val ISSUE: Issue =
+ Issue.create(
+ id = "BindServiceViaContextDetector",
+ briefDescription = "Service bound/unbound via Context, please make sure " +
+ "you're on a background thread.",
+ explanation =
+ "Binding or unbinding services are synchronous calls to ActivityManager, " +
+ "they usually take multiple milliseconds to complete and will make" +
+ "the caller drop frames. Make sure you're on a @Background Executor.",
+ category = Category.PERFORMANCE,
+ priority = 8,
+ severity = Severity.WARNING,
+ implementation =
+ Implementation(BindServiceViaContextDetector::class.java, Scope.JAVA_FILE_SCOPE)
+ )
+ }
+}
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
index 397a110f4bc7..226aebbd0464 100644
--- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
@@ -27,7 +27,8 @@ import com.google.auto.service.AutoService
class SystemUIIssueRegistry : IssueRegistry() {
override val issues: List<Issue>
- get() = listOf(BroadcastSentViaContextDetector.ISSUE)
+ get() = listOf(BindServiceViaContextDetector.ISSUE,
+ BroadcastSentViaContextDetector.ISSUE)
override val api: Int
get() = CURRENT_API
diff --git a/packages/SystemUI/checks/tests/com/android/systemui/lint/BindServiceViaContextDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/systemui/lint/BindServiceViaContextDetectorTest.kt
new file mode 100644
index 000000000000..bf685f7c178e
--- /dev/null
+++ b/packages/SystemUI/checks/tests/com/android/systemui/lint/BindServiceViaContextDetectorTest.kt
@@ -0,0 +1,140 @@
+/*
+ * 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.internal.systemui.lint
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestFiles
+import com.android.tools.lint.checks.infrastructure.TestLintTask
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+import org.junit.Test
+
+class BindServiceViaContextDetectorTest : LintDetectorTest() {
+
+ override fun getDetector(): Detector = BindServiceViaContextDetector()
+ override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
+
+ override fun getIssues(): List<Issue> = listOf(
+ BindServiceViaContextDetector.ISSUE)
+
+ private val explanation = "Binding or unbinding services are synchronous calls"
+
+ @Test
+ fun testBindService() {
+ lint().files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import android.content.Context;
+
+ public class TestClass1 {
+ public void bind(Context context) {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ context.bindService(intent, null, 0);
+ }
+ }
+ """
+ ).indented(),
+ *stubs)
+ .issues(BindServiceViaContextDetector.ISSUE)
+ .run()
+ .expectWarningCount(1)
+ .expectContains(explanation)
+ }
+
+ @Test
+ fun testBindServiceAsUser() {
+ lint().files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import android.content.Context;
+ import android.os.UserHandle;
+
+ public class TestClass1 {
+ public void bind(Context context) {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ context.bindServiceAsUser(intent, null, 0, UserHandle.ALL);
+ }
+ }
+ """
+ ).indented(),
+ *stubs)
+ .issues(BindServiceViaContextDetector.ISSUE)
+ .run()
+ .expectWarningCount(1)
+ .expectContains(explanation)
+ }
+
+ @Test
+ fun testUnbindService() {
+ lint().files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import android.content.Context;
+ import android.content.ServiceConnection;
+
+ public class TestClass1 {
+ public void unbind(Context context, ServiceConnection connection) {
+ context.unbindService(connection);
+ }
+ }
+ """
+ ).indented(),
+ *stubs)
+ .issues(BindServiceViaContextDetector.ISSUE)
+ .run()
+ .expectWarningCount(1)
+ .expectContains(explanation)
+ }
+
+ private val contextStub: TestFile = java(
+ """
+ package android.content;
+ import android.os.UserHandle;
+
+ public class Context {
+ public void bindService(Intent intent) {};
+ public void bindServiceAsUser(Intent intent, ServiceConnection connection, int flags,
+ UserHandle userHandle) {};
+ public void unbindService(ServiceConnection connection) {};
+ }
+ """
+ )
+
+ private val serviceConnectionStub: TestFile = java(
+ """
+ package android.content;
+
+ public class ServiceConnection {}
+ """
+ )
+
+ private val userHandleStub: TestFile = java(
+ """
+ package android.os;
+
+ public enum UserHandle {
+ ALL
+ }
+ """
+ )
+
+ private val stubs = arrayOf(contextStub, serviceConnectionStub, userHandleStub)
+}
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 1bf30377d7ff..7f3caeca5a62 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -799,7 +799,7 @@
<!-- Message shown when lock screen is tapped or face authentication fails. [CHAR LIMIT=60] -->
<string name="keyguard_unlock">Swipe up to open</string>
- <!-- Message shown when lock screen is unlocked (ie: by trust agent) and the user taps the empty space on the lock screen and UDFPS is supported. Provides extra instructions for how the user can enter their device [CHAR LIMIT=60] -->
+ <!-- Message shown when lock screen is unlocked (ie: by trust agent or face auth). Provides extra instructions for how the user can enter their device [CHAR LIMIT=60] -->
<string name="keyguard_unlock_press">Press the unlock icon to open</string>
<!-- Message shown when non-bypass face authentication succeeds. Provides extra instructions for how the user can enter their device [CHAR LIMIT=60] -->
@@ -813,6 +813,10 @@
<!-- Message shown when non-bypass face authentication succeeds and UDFPS is supported. Provides extra instructions for how the user can enter their device [CHAR LIMIT=60] -->
<string name="keyguard_face_successful_unlock_press_alt_3">Face recognized. Press the unlock icon to open.</string>
+ <!-- Message shown when non-bypass face authentication succeeds. [CHAR LIMIT=60] -->
+ <string name="keyguard_face_successful_unlock">Unlocked by face</string>
+ <!-- Message shown when non-bypass face authentication succeeds. [CHAR LIMIT=60] -->
+ <string name="keyguard_face_successful_unlock_alt1">Face recognized</string>
<!-- Messages shown when users press outside of udfps region during -->
<string-array name="udfps_accessibility_touch_hints">
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 8b5e3c11b2f6..d27b9ced66a7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -54,6 +54,7 @@ import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.hardware.SensorPrivacyManager;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricSourceType;
@@ -2019,12 +2020,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
// in case authenticators aren't registered yet at this point:
mAuthController.addCallback(new AuthController.Callback() {
@Override
- public void onAllAuthenticatorsRegistered() {
+ public void onAllAuthenticatorsRegistered(
+ @BiometricAuthenticator.Modality int modality) {
mainExecutor.execute(() -> updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE));
}
@Override
- public void onEnrollmentsChanged() {
+ public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) {
mainExecutor.execute(() -> updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE));
}
});
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 99e0ce29a8c2..7a42803859b5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -271,7 +271,7 @@ public class KeyguardUpdateMonitorCallback {
* like fingerprint authentication errors.
*
* @param message Message that indicates an error.
- * @see KeyguardIndicationController.BaseKeyguardCallback#HIDE_DELAY_MS
+ * @see KeyguardIndicationController#DEFAULT_HIDE_DELAY_MS
* @see KeyguardIndicationController#showTransientIndication(CharSequence)
*/
public void onTrustAgentErrorMessage(CharSequence message) { }
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 06e1828ef9f4..d6974dfac570 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -16,6 +16,7 @@
package com.android.keyguard;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
import static com.android.keyguard.LockIconView.ICON_FINGERPRINT;
@@ -29,6 +30,7 @@ import android.content.res.Resources;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.AnimatedStateListDrawable;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricSourceType;
import android.os.Process;
import android.os.VibrationAttributes;
@@ -701,13 +703,17 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() {
@Override
- public void onAllAuthenticatorsRegistered() {
- updateUdfpsConfig();
+ public void onAllAuthenticatorsRegistered(@BiometricAuthenticator.Modality int modality) {
+ if (modality == TYPE_FINGERPRINT) {
+ updateUdfpsConfig();
+ }
}
@Override
- public void onEnrollmentsChanged() {
- updateUdfpsConfig();
+ public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) {
+ if (modality == TYPE_FINGERPRINT) {
+ updateUdfpsConfig();
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 47ff59cfc281..282f25104c44 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -46,6 +46,7 @@ import android.hardware.biometrics.PromptInfo;
import android.hardware.display.DisplayManager;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorPropertiesInternal;
+import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
@@ -156,25 +157,6 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
}
};
- private final IFingerprintAuthenticatorsRegisteredCallback
- mFingerprintAuthenticatorsRegisteredCallback =
- new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
- @Override
- public void onAllAuthenticatorsRegistered(
- List<FingerprintSensorPropertiesInternal> sensors) {
- mHandler.post(() -> handleAllFingerprintAuthenticatorsRegistered(sensors));
- }
- };
-
- private final BiometricStateListener mBiometricStateListener =
- new BiometricStateListener() {
- @Override
- public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {
- mHandler.post(
- () -> handleEnrollmentsChanged(userId, sensorId, hasEnrollments));
- }
- };
-
@VisibleForTesting
final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -249,8 +231,8 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
List<FingerprintSensorPropertiesInternal> sensors) {
mExecution.assertIsMainThread();
if (DEBUG) {
- Log.d(TAG, "handleAllAuthenticatorsRegistered | sensors: " + Arrays.toString(
- sensors.toArray()));
+ Log.d(TAG, "handleAllFingerprintAuthenticatorsRegistered | sensors: "
+ + Arrays.toString(sensors.toArray()));
}
mAllFingerprintAuthenticatorsRegistered = true;
mFpProps = sensors;
@@ -292,15 +274,42 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
mSidefpsController = mSidefpsControllerFactory.get();
}
- mFingerprintManager.registerBiometricStateListener(mBiometricStateListener);
+ mFingerprintManager.registerBiometricStateListener(new BiometricStateListener() {
+ @Override
+ public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {
+ mHandler.post(() -> handleEnrollmentsChanged(
+ TYPE_FINGERPRINT, userId, sensorId, hasEnrollments));
+ }
+ });
updateFingerprintLocation();
for (Callback cb : mCallbacks) {
- cb.onAllAuthenticatorsRegistered();
+ cb.onAllAuthenticatorsRegistered(TYPE_FINGERPRINT);
}
}
- private void handleEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {
+ private void handleAllFaceAuthenticatorsRegistered(List<FaceSensorPropertiesInternal> sensors) {
+ mExecution.assertIsMainThread();
+ if (DEBUG) {
+ Log.d(TAG, "handleAllFaceAuthenticatorsRegistered | sensors: " + Arrays.toString(
+ sensors.toArray()));
+ }
+
+ mFaceManager.registerBiometricStateListener(new BiometricStateListener() {
+ @Override
+ public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {
+ mHandler.post(() -> handleEnrollmentsChanged(
+ TYPE_FACE, userId, sensorId, hasEnrollments));
+ }
+ });
+
+ for (Callback cb : mCallbacks) {
+ cb.onAllAuthenticatorsRegistered(TYPE_FACE);
+ }
+ }
+
+ private void handleEnrollmentsChanged(@Modality int modality, int userId, int sensorId,
+ boolean hasEnrollments) {
mExecution.assertIsMainThread();
Log.d(TAG, "handleEnrollmentsChanged, userId: " + userId + ", sensorId: " + sensorId
+ ", hasEnrollments: " + hasEnrollments);
@@ -314,7 +323,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
}
}
for (Callback cb : mCallbacks) {
- cb.onEnrollmentsChanged();
+ cb.onEnrollmentsChanged(modality);
}
}
@@ -700,7 +709,26 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
if (mFingerprintManager != null) {
mFingerprintManager.addAuthenticatorsRegisteredCallback(
- mFingerprintAuthenticatorsRegisteredCallback);
+ new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
+ @Override
+ public void onAllAuthenticatorsRegistered(
+ List<FingerprintSensorPropertiesInternal> sensors) {
+ mHandler.post(() ->
+ handleAllFingerprintAuthenticatorsRegistered(sensors));
+ }
+ });
+ }
+ if (mFaceManager != null) {
+ mFaceManager.addAuthenticatorsRegisteredCallback(
+ new IFaceAuthenticatorsRegisteredCallback.Stub() {
+ @Override
+ public void onAllAuthenticatorsRegistered(
+ List<FaceSensorPropertiesInternal> sensors) {
+ mHandler.post(() ->
+ handleAllFaceAuthenticatorsRegistered(sensors));
+ }
+ }
+ );
}
mStableDisplaySize = mDisplayManager.getStableDisplaySize();
@@ -1116,13 +1144,13 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
* Called when authenticators are registered. If authenticators are already
* registered before this call, this callback will never be triggered.
*/
- default void onAllAuthenticatorsRegistered() {}
+ default void onAllAuthenticatorsRegistered(@Modality int modality) {}
/**
- * Called when UDFPS enrollments have changed. This is called after boot and on changes to
+ * Called when enrollments have changed. This is called after boot and on changes to
* enrollment.
*/
- default void onEnrollmentsChanged() {}
+ default void onEnrollmentsChanged(@Modality int modality) {}
/**
* Called when the biometric prompt starts showing.
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index 38fab8ffbfad..fd3f6007d8a9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -308,7 +308,7 @@ class AuthRippleController @Inject constructor(
private val authControllerCallback =
object : AuthController.Callback {
- override fun onAllAuthenticatorsRegistered() {
+ override fun onAllAuthenticatorsRegistered(modality: Int) {
updateUdfpsDependentParams()
updateSensorLocation()
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
index a9e310d25f9c..7da2cf150147 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
@@ -16,12 +16,15 @@
package com.android.systemui.doze;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+
import static com.android.systemui.doze.DozeMachine.State.DOZE;
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD;
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED;
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSING;
import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSE_DONE;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.os.Handler;
import android.util.Log;
import android.view.Display;
@@ -232,13 +235,17 @@ public class DozeScreenState implements DozeMachine.Part {
private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() {
@Override
- public void onAllAuthenticatorsRegistered() {
- updateUdfpsController();
+ public void onAllAuthenticatorsRegistered(@BiometricAuthenticator.Modality int modality) {
+ if (modality == TYPE_FINGERPRINT) {
+ updateUdfpsController();
+ }
}
@Override
- public void onEnrollmentsChanged() {
- updateUdfpsController();
+ public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) {
+ if (modality == TYPE_FINGERPRINT) {
+ updateUdfpsController();
+ }
}
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index da6c163b1eea..997a6e554364 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -16,6 +16,8 @@
package com.android.systemui.doze;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+
import static com.android.systemui.doze.DozeLog.REASON_SENSOR_QUICK_PICKUP;
import static com.android.systemui.doze.DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS;
import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_DISPLAY;
@@ -29,6 +31,7 @@ import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.display.AmbientDisplayConfiguration;
import android.net.Uri;
import android.os.Handler;
@@ -835,13 +838,17 @@ public class DozeSensors {
private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() {
@Override
- public void onAllAuthenticatorsRegistered() {
- updateUdfpsEnrolled();
+ public void onAllAuthenticatorsRegistered(@BiometricAuthenticator.Modality int modality) {
+ if (modality == TYPE_FINGERPRINT) {
+ updateUdfpsEnrolled();
+ }
}
@Override
- public void onEnrollmentsChanged() {
- updateUdfpsEnrolled();
+ public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) {
+ if (modality == TYPE_FINGERPRINT) {
+ updateUdfpsEnrolled();
+ }
}
private void updateUdfpsEnrolled() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
index bd00ce61a7e3..1f52fc6e022c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
@@ -27,6 +27,7 @@ import androidx.annotation.IntDef;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -56,8 +57,11 @@ import java.util.Map;
public class KeyguardIndicationRotateTextViewController extends
ViewController<KeyguardIndicationTextView> implements Dumpable {
public static String TAG = "KgIndicationRotatingCtrl";
- private static final long DEFAULT_INDICATION_SHOW_LENGTH = 3500; // milliseconds
- public static final long IMPORTANT_MSG_MIN_DURATION = 2000L + 600L; // 2000ms + [Y in duration]
+ private static final long DEFAULT_INDICATION_SHOW_LENGTH =
+ KeyguardIndicationController.DEFAULT_HIDE_DELAY_MS
+ - KeyguardIndicationTextView.Y_IN_DURATION;
+ public static final long IMPORTANT_MSG_MIN_DURATION =
+ 2000L + KeyguardIndicationTextView.Y_IN_DURATION;
private final StatusBarStateController mStatusBarStateController;
private final float mMaxAlpha;
@@ -375,6 +379,7 @@ public class KeyguardIndicationRotateTextViewController extends
public static final int INDICATION_TYPE_USER_LOCKED = 8;
public static final int INDICATION_TYPE_REVERSE_CHARGING = 10;
public static final int INDICATION_TYPE_BIOMETRIC_MESSAGE = 11;
+ public static final int INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP = 12;
@IntDef({
INDICATION_TYPE_NONE,
@@ -388,7 +393,8 @@ public class KeyguardIndicationRotateTextViewController extends
INDICATION_TYPE_RESTING,
INDICATION_TYPE_USER_LOCKED,
INDICATION_TYPE_REVERSE_CHARGING,
- INDICATION_TYPE_BIOMETRIC_MESSAGE
+ INDICATION_TYPE_BIOMETRIC_MESSAGE,
+ INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP
})
@Retention(RetentionPolicy.SOURCE)
public @interface IndicationType{}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
index beb54c85e021..4397d3d85d62 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
@@ -18,7 +18,6 @@ package com.android.systemui.screenshot
import android.net.Uri
import android.util.Log
-import android.view.WindowManager.ScreenshotType
import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
import android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION
@@ -37,13 +36,12 @@ internal class RequestProcessor @Inject constructor(
private val controller: ScreenshotController,
) {
fun processRequest(
- @ScreenshotType type: Int,
- onSavedListener: Consumer<Uri>,
request: ScreenshotRequest,
+ onSavedListener: Consumer<Uri>,
callback: RequestCallback
) {
- if (type == TAKE_SCREENSHOT_PROVIDED_IMAGE) {
+ if (request.type == TAKE_SCREENSHOT_PROVIDED_IMAGE) {
val image = HardwareBitmapBundler.bundleToHardwareBitmap(request.bitmapBundle)
controller.handleImageAsScreenshot(
@@ -53,12 +51,12 @@ internal class RequestProcessor @Inject constructor(
return
}
- when (type) {
+ when (request.type) {
TAKE_SCREENSHOT_FULLSCREEN ->
controller.takeScreenshotFullscreen(null, onSavedListener, callback)
TAKE_SCREENSHOT_SELECTED_REGION ->
controller.takeScreenshotPartial(null, onSavedListener, callback)
- else -> Log.w(TAG, "Invalid screenshot option: $type")
+ else -> Log.w(TAG, "Invalid screenshot option: ${request.type}")
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index f1f0223632b7..7bf3217e5f15 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -229,11 +229,11 @@ public class TakeScreenshotService extends Service {
if (mFeatureFlags.isEnabled(SCREENSHOT_REQUEST_PROCESSOR)) {
Log.d(TAG, "handleMessage: Using request processor");
- mProcessor.processRequest(msg.what, uriConsumer, screenshotRequest, requestCallback);
+ mProcessor.processRequest(screenshotRequest, uriConsumer, requestCallback);
return true;
}
- switch (msg.what) {
+ switch (screenshotRequest.getType()) {
case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
if (DEBUG_SERVICE) {
Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_FULLSCREEN");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index ca147286a301..c98364473f71 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -27,6 +27,7 @@ import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewCont
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_LOGOUT;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_OWNER_INFO;
@@ -36,7 +37,6 @@ import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewCont
import static com.android.systemui.keyguard.ScreenLifecycle.SCREEN_ON;
import static com.android.systemui.plugins.FalsingManager.LOW_PENALTY;
-import android.app.IActivityManager;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -123,6 +123,8 @@ public class KeyguardIndicationController {
private static final int MSG_SHOW_ACTION_TO_UNLOCK = 2;
private static final int MSG_HIDE_BIOMETRIC_MESSAGE = 3;
private static final long TRANSIENT_BIOMETRIC_ERROR_TIMEOUT = 1300;
+ public static final long DEFAULT_HIDE_DELAY_MS =
+ 3500 + KeyguardIndicationTextView.Y_IN_DURATION;
private final Context mContext;
private final BroadcastDispatcher mBroadcastDispatcher;
@@ -140,7 +142,6 @@ public class KeyguardIndicationController {
protected final @Main DelayableExecutor mExecutor;
protected final @Background DelayableExecutor mBackgroundExecutor;
private final LockPatternUtils mLockPatternUtils;
- private final IActivityManager mIActivityManager;
private final FalsingManager mFalsingManager;
private final KeyguardBypassController mKeyguardBypassController;
private final AccessibilityManager mAccessibilityManager;
@@ -155,6 +156,7 @@ public class KeyguardIndicationController {
private CharSequence mTrustGrantedIndication;
private CharSequence mTransientIndication;
private CharSequence mBiometricMessage;
+ private CharSequence mBiometricMessageFollowUp;
protected ColorStateList mInitialTextColorState;
private boolean mVisible;
private boolean mOrganizationOwnedDevice;
@@ -171,7 +173,7 @@ public class KeyguardIndicationController {
private int mBatteryLevel;
private boolean mBatteryPresent = true;
private long mChargingTimeRemaining;
- private String mMessageToShowOnScreenOn;
+ private String mBiometricErrorMessageToShowOnScreenOn;
private final Set<Integer> mCoExFaceHelpMsgIdsToShow;
private boolean mInited;
@@ -189,11 +191,11 @@ public class KeyguardIndicationController {
private final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
@Override
public void onScreenTurnedOn() {
- if (mMessageToShowOnScreenOn != null) {
- showBiometricMessage(mMessageToShowOnScreenOn);
+ if (mBiometricErrorMessageToShowOnScreenOn != null) {
+ showBiometricMessage(mBiometricErrorMessageToShowOnScreenOn);
// We want to keep this message around in case the screen was off
- hideBiometricMessageDelayed(BaseKeyguardCallback.HIDE_DELAY_MS);
- mMessageToShowOnScreenOn = null;
+ hideBiometricMessageDelayed(DEFAULT_HIDE_DELAY_MS);
+ mBiometricErrorMessageToShowOnScreenOn = null;
}
}
};
@@ -219,7 +221,6 @@ public class KeyguardIndicationController {
FalsingManager falsingManager,
LockPatternUtils lockPatternUtils,
ScreenLifecycle screenLifecycle,
- IActivityManager iActivityManager,
KeyguardBypassController keyguardBypassController,
AccessibilityManager accessibilityManager) {
mContext = context;
@@ -236,7 +237,6 @@ public class KeyguardIndicationController {
mExecutor = executor;
mBackgroundExecutor = bgExecutor;
mLockPatternUtils = lockPatternUtils;
- mIActivityManager = iActivityManager;
mFalsingManager = falsingManager;
mKeyguardBypassController = keyguardBypassController;
mAccessibilityManager = accessibilityManager;
@@ -498,8 +498,23 @@ public class KeyguardIndicationController {
.build(),
true
);
+ if (!TextUtils.isEmpty(mBiometricMessageFollowUp)) {
+ mRotateTextViewController.updateIndication(
+ INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP,
+ new KeyguardIndication.Builder()
+ .setMessage(mBiometricMessageFollowUp)
+ .setMinVisibilityMillis(IMPORTANT_MSG_MIN_DURATION)
+ .setTextColor(mInitialTextColorState)
+ .build(),
+ true
+ );
+ } else {
+ mRotateTextViewController.hideIndication(
+ INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP);
+ }
} else {
mRotateTextViewController.hideIndication(INDICATION_TYPE_BIOMETRIC_MESSAGE);
+ mRotateTextViewController.hideIndication(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP);
}
}
@@ -719,38 +734,45 @@ public class KeyguardIndicationController {
private void showTransientIndication(CharSequence transientIndication) {
mTransientIndication = transientIndication;
mHandler.removeMessages(MSG_HIDE_TRANSIENT);
- hideTransientIndicationDelayed(BaseKeyguardCallback.HIDE_DELAY_MS);
+ hideTransientIndicationDelayed(DEFAULT_HIDE_DELAY_MS);
updateTransient();
}
- /**
- * Shows {@param biometricMessage} until it is hidden by {@link #hideBiometricMessage}.
- */
- public void showBiometricMessage(int biometricMessage) {
- showBiometricMessage(mContext.getResources().getString(biometricMessage));
+ private void showBiometricMessage(CharSequence biometricMessage) {
+ showBiometricMessage(biometricMessage, null);
}
/**
- * Shows {@param biometricMessage} until it is hidden by {@link #hideBiometricMessage}.
+ * Shows {@param biometricMessage} and {@param biometricMessageFollowUp}
+ * until they are hidden by {@link #hideBiometricMessage}. Messages are rotated through
+ * by {@link KeyguardIndicationRotateTextViewController}, see class for rotating message
+ * logic.
*/
- private void showBiometricMessage(CharSequence biometricMessage) {
+ private void showBiometricMessage(CharSequence biometricMessage,
+ CharSequence biometricMessageFollowUp) {
if (TextUtils.equals(biometricMessage, mBiometricMessage)) {
return;
}
mBiometricMessage = biometricMessage;
+ mBiometricMessageFollowUp = biometricMessageFollowUp;
mHandler.removeMessages(MSG_SHOW_ACTION_TO_UNLOCK);
mHandler.removeMessages(MSG_HIDE_BIOMETRIC_MESSAGE);
- hideBiometricMessageDelayed(BaseKeyguardCallback.HIDE_DELAY_MS);
+ hideBiometricMessageDelayed(
+ mBiometricMessageFollowUp != null
+ ? DEFAULT_HIDE_DELAY_MS * 2
+ : DEFAULT_HIDE_DELAY_MS
+ );
updateBiometricMessage();
}
private void hideBiometricMessage() {
- if (mBiometricMessage != null) {
+ if (mBiometricMessage != null || mBiometricMessageFollowUp != null) {
mBiometricMessage = null;
+ mBiometricMessageFollowUp = null;
mHandler.removeMessages(MSG_HIDE_BIOMETRIC_MESSAGE);
updateBiometricMessage();
}
@@ -789,9 +811,9 @@ public class KeyguardIndicationController {
// colors can be hard to read in low brightness.
mTopIndicationView.setTextColor(Color.WHITE);
- CharSequence newIndication = null;
+ CharSequence newIndication;
if (!TextUtils.isEmpty(mBiometricMessage)) {
- newIndication = mBiometricMessage;
+ newIndication = mBiometricMessage; // note: doesn't show mBiometricMessageFollowUp
} else if (!TextUtils.isEmpty(mTransientIndication)) {
newIndication = mTransientIndication;
} else if (!mBatteryPresent) {
@@ -909,15 +931,21 @@ public class KeyguardIndicationController {
|| mAccessibilityManager.isTouchExplorationEnabled();
if (udfpsSupported && faceAuthenticated) { // co-ex
if (a11yEnabled) {
- showBiometricMessage(mContext.getString(
- R.string.keyguard_face_successful_unlock_swipe));
+ showBiometricMessage(
+ mContext.getString(R.string.keyguard_face_successful_unlock),
+ mContext.getString(R.string.keyguard_unlock)
+ );
} else {
- showBiometricMessage(mContext.getString(
- R.string.keyguard_face_successful_unlock_press));
+ showBiometricMessage(
+ mContext.getString(R.string.keyguard_face_successful_unlock),
+ mContext.getString(R.string.keyguard_unlock_press)
+ );
}
} else if (faceAuthenticated) { // face-only
- showBiometricMessage(mContext.getString(
- R.string.keyguard_face_successful_unlock_swipe));
+ showBiometricMessage(
+ mContext.getString(R.string.keyguard_face_successful_unlock),
+ mContext.getString(R.string.keyguard_unlock)
+ );
} else if (udfpsSupported) { // udfps-only
if (a11yEnabled) {
showBiometricMessage(mContext.getString(R.string.keyguard_unlock));
@@ -943,10 +971,11 @@ public class KeyguardIndicationController {
pw.println(" mPowerCharged: " + mPowerCharged);
pw.println(" mChargingSpeed: " + mChargingSpeed);
pw.println(" mChargingWattage: " + mChargingWattage);
- pw.println(" mMessageToShowOnScreenOn: " + mMessageToShowOnScreenOn);
+ pw.println(" mMessageToShowOnScreenOn: " + mBiometricErrorMessageToShowOnScreenOn);
pw.println(" mDozing: " + mDozing);
pw.println(" mTransientIndication: " + mTransientIndication);
pw.println(" mBiometricMessage: " + mBiometricMessage);
+ pw.println(" mBiometricMessageFollowUp: " + mBiometricMessageFollowUp);
pw.println(" mBatteryLevel: " + mBatteryLevel);
pw.println(" mBatteryPresent: " + mBatteryPresent);
pw.println(" AOD text: " + (
@@ -958,8 +987,6 @@ public class KeyguardIndicationController {
}
protected class BaseKeyguardCallback extends KeyguardUpdateMonitorCallback {
- public static final int HIDE_DELAY_MS = 5000;
-
@Override
public void onTimeChanged() {
if (mVisible) {
@@ -1077,7 +1104,7 @@ public class KeyguardIndicationController {
} else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
showBiometricMessage(errString);
} else {
- mMessageToShowOnScreenOn = errString;
+ mBiometricErrorMessageToShowOnScreenOn = errString;
}
}
@@ -1139,7 +1166,7 @@ public class KeyguardIndicationController {
// Let's hide any previous messages when authentication starts, otherwise
// multiple auth attempts would overlap.
hideBiometricMessage();
- mMessageToShowOnScreenOn = null;
+ mBiometricErrorMessageToShowOnScreenOn = null;
}
}
@@ -1179,7 +1206,7 @@ public class KeyguardIndicationController {
@Override
public void onRequireUnlockForNfc() {
showTransientIndication(mContext.getString(R.string.require_unlock_for_nfc));
- hideTransientIndicationDelayed(HIDE_DELAY_MS);
+ hideTransientIndicationDelayed(DEFAULT_HIDE_DELAY_MS);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
index 339f371c0d12..d24469e8421e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
@@ -39,6 +39,8 @@ import com.android.systemui.keyguard.KeyguardIndication;
* A view to show hints on Keyguard ("Swipe up to unlock", "Tap again to open").
*/
public class KeyguardIndicationTextView extends TextView {
+ public static final long Y_IN_DURATION = 600L;
+
@StyleRes
private static int sStyleId = R.style.TextAppearance_Keyguard_BottomArea;
@StyleRes
@@ -259,7 +261,7 @@ public class KeyguardIndicationTextView extends TextView {
private long getYInDuration() {
if (!mAnimationsEnabled) return 0L;
- return 600L;
+ return Y_IN_DURATION;
}
private long getFadeOutDuration() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index c67737136b3b..cde30af447f6 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -16,6 +16,7 @@
package com.android.keyguard;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID;
@@ -1005,7 +1006,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
// WHEN udfps is now enrolled
when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(true);
- callback.onEnrollmentsChanged();
+ callback.onEnrollmentsChanged(TYPE_FINGERPRINT);
// THEN isUdfspEnrolled is TRUE
assertThat(mKeyguardUpdateMonitor.isUdfpsEnrolled()).isTrue();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index d158892e4ec5..e0d1f7a19130 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -60,6 +60,9 @@ import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.SensorProperties;
import android.hardware.display.DisplayManager;
import android.hardware.face.FaceManager;
+import android.hardware.face.FaceSensorProperties;
+import android.hardware.face.FaceSensorPropertiesInternal;
+import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -154,7 +157,9 @@ public class AuthControllerTest extends SysuiTestCase {
@Mock
private InteractionJankMonitor mInteractionJankMonitor;
@Captor
- ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mAuthenticatorsRegisteredCaptor;
+ ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mFpAuthenticatorsRegisteredCaptor;
+ @Captor
+ ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback> mFaceAuthenticatorsRegisteredCaptor;
@Captor
ArgumentCaptor<BiometricStateListener> mBiometricStateCaptor;
@Captor
@@ -193,25 +198,38 @@ public class AuthControllerTest extends SysuiTestCase {
when(mDisplayManager.getStableDisplaySize()).thenReturn(new Point());
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
-
- 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 */));
-
- FingerprintSensorPropertiesInternal prop = new FingerprintSensorPropertiesInternal(
- 1 /* sensorId */,
- SensorProperties.STRENGTH_STRONG,
- 1 /* maxEnrollmentsPerUser */,
- componentInfo,
- FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
- true /* resetLockoutRequireHardwareAuthToken */);
- List<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
- props.add(prop);
- when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(props);
+ when(mFaceManager.isHardwareDetected()).thenReturn(true);
+
+ final List<ComponentInfoInternal> fpComponentInfo = List.of(
+ new ComponentInfoInternal("faceSensor" /* componentId */,
+ "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */,
+ "00000001" /* serialNumber */, "" /* softwareVersion */));
+ final List<ComponentInfoInternal> faceComponentInfo = List.of(
+ new ComponentInfoInternal("matchingAlgorithm" /* componentId */,
+ "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
+ "vendor/version/revision" /* softwareVersion */));
+
+ final List<FingerprintSensorPropertiesInternal> fpProps = List.of(
+ new FingerprintSensorPropertiesInternal(
+ 1 /* sensorId */,
+ SensorProperties.STRENGTH_STRONG,
+ 1 /* maxEnrollmentsPerUser */,
+ fpComponentInfo,
+ FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
+ true /* resetLockoutRequireHardwareAuthToken */));
+ when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(fpProps);
+
+ final List<FaceSensorPropertiesInternal> faceProps = List.of(
+ new FaceSensorPropertiesInternal(
+ 2 /* sensorId */,
+ SensorProperties.STRENGTH_STRONG,
+ 1 /* maxEnrollmentsPerUser */,
+ fpComponentInfo,
+ FaceSensorProperties.TYPE_RGB,
+ true /* supportsFaceDetection */,
+ true /* supportsSelfIllumination */,
+ true /* resetLockoutRequireHardwareAuthToken */));
+ when(mFaceManager.getSensorPropertiesInternal()).thenReturn(faceProps);
mAuthController = new TestableAuthController(mContextSpy, mExecution, mCommandQueue,
mActivityTaskManager, mWindowManager, mFingerprintManager, mFaceManager,
@@ -219,12 +237,15 @@ public class AuthControllerTest extends SysuiTestCase {
mAuthController.start();
verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
- mAuthenticatorsRegisteredCaptor.capture());
+ mFpAuthenticatorsRegisteredCaptor.capture());
+ verify(mFaceManager).addAuthenticatorsRegisteredCallback(
+ mFaceAuthenticatorsRegisteredCaptor.capture());
when(mStatusBarStateController.isDozing()).thenReturn(false);
verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture());
- mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(props);
+ mFpAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(fpProps);
+ mFaceAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(faceProps);
// Ensures that the operations posted on the handler get executed.
mTestableLooper.processAllMessages();
@@ -237,6 +258,7 @@ public class AuthControllerTest extends SysuiTestCase {
throws RemoteException {
// This test is sensitive to prior FingerprintManager interactions.
reset(mFingerprintManager);
+ reset(mFaceManager);
// This test requires an uninitialized AuthController.
AuthController authController = new TestableAuthController(mContextSpy, mExecution,
@@ -246,21 +268,27 @@ public class AuthControllerTest extends SysuiTestCase {
authController.start();
verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
- mAuthenticatorsRegisteredCaptor.capture());
+ mFpAuthenticatorsRegisteredCaptor.capture());
+ verify(mFaceManager).addAuthenticatorsRegisteredCallback(
+ mFaceAuthenticatorsRegisteredCaptor.capture());
mTestableLooper.processAllMessages();
verify(mFingerprintManager, never()).registerBiometricStateListener(any());
+ verify(mFaceManager, never()).registerBiometricStateListener(any());
- mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(new ArrayList<>());
+ mFpAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(List.of());
+ mFaceAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(List.of());
mTestableLooper.processAllMessages();
verify(mFingerprintManager).registerBiometricStateListener(any());
+ verify(mFaceManager).registerBiometricStateListener(any());
}
@Test
public void testDoesNotCrash_afterEnrollmentsChangedForUnknownSensor() throws RemoteException {
// This test is sensitive to prior FingerprintManager interactions.
reset(mFingerprintManager);
+ reset(mFaceManager);
// This test requires an uninitialized AuthController.
AuthController authController = new TestableAuthController(mContextSpy, mExecution,
@@ -270,18 +298,25 @@ public class AuthControllerTest extends SysuiTestCase {
authController.start();
verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
- mAuthenticatorsRegisteredCaptor.capture());
+ mFpAuthenticatorsRegisteredCaptor.capture());
+ verify(mFaceManager).addAuthenticatorsRegisteredCallback(
+ mFaceAuthenticatorsRegisteredCaptor.capture());
// Emulates a device with no authenticators (empty list).
- mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(new ArrayList<>());
+ mFpAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(List.of());
+ mFaceAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(List.of());
mTestableLooper.processAllMessages();
verify(mFingerprintManager).registerBiometricStateListener(
mBiometricStateCaptor.capture());
+ verify(mFaceManager).registerBiometricStateListener(
+ mBiometricStateCaptor.capture());
// Enrollments changed for an unknown sensor.
- mBiometricStateCaptor.getValue().onEnrollmentsChanged(0 /* userId */,
- 0xbeef /* sensorId */, true /* hasEnrollments */);
+ for (BiometricStateListener listener : mBiometricStateCaptor.getAllValues()) {
+ listener.onEnrollmentsChanged(0 /* userId */,
+ 0xbeef /* sensorId */, true /* hasEnrollments */);
+ }
mTestableLooper.processAllMessages();
// Nothing should crash.
@@ -827,4 +862,3 @@ public class AuthControllerTest extends SysuiTestCase {
}
}
}
-
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
index 9ffc5a57cef6..b33f9a7f3933 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.doze;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+
import static com.android.systemui.doze.DozeLog.REASON_SENSOR_TAP;
import static com.android.systemui.doze.DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS;
import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_LOCK_SCREEN;
@@ -412,7 +414,7 @@ public class DozeSensorsTest extends SysuiTestCase {
// WHEN enrollment changes to TRUE
when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(true);
- mAuthControllerCallback.onEnrollmentsChanged();
+ mAuthControllerCallback.onEnrollmentsChanged(TYPE_FINGERPRINT);
// THEN mConfigured = TRUE
assertTrue(triggerSensor.mConfigured);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
index 24d051508fde..5ec6bdf3c00b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.keyguard;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.keyguard.LockIconView.ICON_LOCK;
import static com.android.keyguard.LockIconView.ICON_UNLOCK;
@@ -219,7 +221,7 @@ public class LockIconViewControllerTest extends SysuiTestCase {
Pair<Float, PointF> udfps = setupUdfps();
// WHEN all authenticators are registered
- mAuthControllerCallback.onAllAuthenticatorsRegistered();
+ mAuthControllerCallback.onAllAuthenticatorsRegistered(TYPE_FINGERPRINT);
mDelayableExecutor.runAllReady();
// THEN lock icon view location is updated with the same coordinates as auth controller vals
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
index 002f23a9ed6d..024d3bd8eb0e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
@@ -23,8 +23,12 @@ import android.graphics.Insets
import android.graphics.Rect
import android.hardware.HardwareBuffer
import android.net.Uri
-import android.view.WindowManager
-import android.view.WindowManager.ScreenshotSource
+import android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD
+import android.view.WindowManager.ScreenshotSource.SCREENSHOT_OTHER
+import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
+import android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION
+import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
+
import com.android.internal.util.ScreenshotHelper.HardwareBitmapBundler
import com.android.internal.util.ScreenshotHelper.ScreenshotRequest
import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback
@@ -43,13 +47,12 @@ class RequestProcessorTest {
@Test
fun testFullScreenshot() {
- val request = ScreenshotRequest(ScreenshotSource.SCREENSHOT_KEY_CHORD)
+ val request = ScreenshotRequest(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD)
val onSavedListener = mock<Consumer<Uri>>()
val callback = mock<RequestCallback>()
val processor = RequestProcessor(controller)
- processor.processRequest(WindowManager.TAKE_SCREENSHOT_FULLSCREEN, onSavedListener,
- request, callback)
+ processor.processRequest(request, onSavedListener, callback)
verify(controller).takeScreenshotFullscreen(/* topComponent */ isNull(),
eq(onSavedListener), eq(callback))
@@ -57,13 +60,12 @@ class RequestProcessorTest {
@Test
fun testSelectedRegionScreenshot() {
- val request = ScreenshotRequest(ScreenshotSource.SCREENSHOT_KEY_CHORD)
+ val request = ScreenshotRequest(TAKE_SCREENSHOT_SELECTED_REGION, SCREENSHOT_KEY_CHORD)
val onSavedListener = mock<Consumer<Uri>>()
val callback = mock<RequestCallback>()
val processor = RequestProcessor(controller)
- processor.processRequest(WindowManager.TAKE_SCREENSHOT_SELECTED_REGION, onSavedListener,
- request, callback)
+ processor.processRequest(request, onSavedListener, callback)
verify(controller).takeScreenshotPartial(/* topComponent */ isNull(),
eq(onSavedListener), eq(callback))
@@ -82,14 +84,13 @@ class RequestProcessorTest {
val bitmap = Bitmap.wrapHardwareBuffer(buffer, ColorSpace.get(ColorSpace.Named.SRGB))!!
val bitmapBundle = HardwareBitmapBundler.hardwareBitmapToBundle(bitmap)
- val request = ScreenshotRequest(ScreenshotSource.SCREENSHOT_OTHER, bitmapBundle,
- bounds, Insets.NONE, taskId, userId, topComponent)
+ val request = ScreenshotRequest(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OTHER,
+ bitmapBundle, bounds, Insets.NONE, taskId, userId, topComponent)
val onSavedListener = mock<Consumer<Uri>>()
val callback = mock<RequestCallback>()
- processor.processRequest(WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE, onSavedListener,
- request, callback)
+ processor.processRequest(request, onSavedListener, callback)
verify(controller).handleImageAsScreenshot(
bitmapCaptor.capture(), eq(bounds), eq(Insets.NONE), eq(taskId), eq(userId),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index c5beee892172..22d61a71b93e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -23,6 +23,7 @@ import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_LOGOUT;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_OWNER_INFO;
@@ -50,7 +51,6 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.app.IActivityManager;
import android.app.Instrumentation;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyResourcesManager;
@@ -161,8 +161,6 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
@Mock
private LockPatternUtils mLockPatternUtils;
@Mock
- private IActivityManager mIActivityManager;
- @Mock
private KeyguardBypassController mKeyguardBypassController;
@Mock
private AccessibilityManager mAccessibilityManager;
@@ -256,8 +254,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
mKeyguardStateController, mStatusBarStateController, mKeyguardUpdateMonitor,
mDockManager, mBroadcastDispatcher, mDevicePolicyManager, mIBatteryStats,
mUserManager, mExecutor, mExecutor, mFalsingManager, mLockPatternUtils,
- mScreenLifecycle, mIActivityManager, mKeyguardBypassController,
- mAccessibilityManager);
+ mScreenLifecycle, mKeyguardBypassController, mAccessibilityManager);
mController.init();
mController.setIndicationArea(mIndicationArea);
verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture());
@@ -974,11 +971,11 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
mController.getKeyguardCallback().onBiometricAuthenticated(0,
BiometricSourceType.FACE, false);
- // THEN 'face unlocked. press unlock icon to open' message shows
- String pressToOpen = mContext.getString(R.string.keyguard_face_successful_unlock_press);
- verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, pressToOpen);
-
- assertThat(mTextView.getText()).isNotEqualTo(pressToOpen);
+ // THEN 'face unlocked' then 'press unlock icon to open' message show
+ String unlockedByFace = mContext.getString(R.string.keyguard_face_successful_unlock);
+ verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, unlockedByFace);
+ String pressToOpen = mContext.getString(R.string.keyguard_unlock_press);
+ verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP, pressToOpen);
}
@@ -999,10 +996,11 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
mController.getKeyguardCallback().onBiometricAuthenticated(0,
BiometricSourceType.FACE, false);
- // THEN show 'face unlocked. swipe up to open' message
- String faceUnlockedSwipeToOpen =
- mContext.getString(R.string.keyguard_face_successful_unlock_swipe);
- verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, faceUnlockedSwipeToOpen);
+ // THEN show 'face unlocked' and 'swipe up to open' messages
+ String unlockedByFace = mContext.getString(R.string.keyguard_face_successful_unlock);
+ verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, unlockedByFace);
+ String swipeUpToOpen = mContext.getString(R.string.keyguard_unlock);
+ verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP, swipeUpToOpen);
}
@Test
@@ -1021,10 +1019,11 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
mController.getKeyguardCallback().onBiometricAuthenticated(0,
BiometricSourceType.FACE, false);
- // THEN show 'swipe up to open' message
- String faceUnlockedSwipeToOpen =
- mContext.getString(R.string.keyguard_face_successful_unlock_swipe);
- verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, faceUnlockedSwipeToOpen);
+ // THEN show 'face unlocked' and 'swipe up to open' messages
+ String unlockedByFace = mContext.getString(R.string.keyguard_face_successful_unlock);
+ verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, unlockedByFace);
+ String swipeUpToOpen = mContext.getString(R.string.keyguard_unlock);
+ verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP, swipeUpToOpen);
}
@Test
@@ -1042,10 +1041,11 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
mController.getKeyguardCallback().onBiometricAuthenticated(0,
BiometricSourceType.FACE, false);
- // THEN show 'swipe up to open' message
- String faceUnlockedSwipeToOpen =
- mContext.getString(R.string.keyguard_face_successful_unlock_swipe);
- verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, faceUnlockedSwipeToOpen);
+ // THEN show 'face unlocked' and 'swipe up to open' messages
+ String unlockedByFace = mContext.getString(R.string.keyguard_face_successful_unlock);
+ verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, unlockedByFace);
+ String swipeUpToOpen = mContext.getString(R.string.keyguard_unlock);
+ verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP, swipeUpToOpen);
}
@Test
diff --git a/packages/VpnDialogs/res/values-es/strings.xml b/packages/VpnDialogs/res/values-es/strings.xml
index 0eaf3592a510..9bf86f51f8fe 100644
--- a/packages/VpnDialogs/res/values-es/strings.xml
+++ b/packages/VpnDialogs/res/values-es/strings.xml
@@ -19,7 +19,7 @@
<string name="prompt" msgid="3183836924226407828">"Solicitud de conexión"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> quiere configurar una conexión VPN para controlar el tráfico de red. Solo debes aceptarla si confías en la fuente. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparece en la parte superior de la pantalla cuando se active la conexión VPN."</string>
<string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> quiere configurar una conexión VPN que le permita monitorizar el tráfico de red. Acéptalo solo si confías en la fuente. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparecerá en la pantalla cuando la VPN esté activa."</string>
- <string name="legacy_title" msgid="192936250066580964">"VPN conectada"</string>
+ <string name="legacy_title" msgid="192936250066580964">"La VPN está conectada"</string>
<string name="session" msgid="6470628549473641030">"Sesión:"</string>
<string name="duration" msgid="3584782459928719435">"Duración:"</string>
<string name="data_transmitted" msgid="7988167672982199061">"Enviado:"</string>
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index f903c2005ae3..a94bfe281be1 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -302,6 +302,10 @@ message SystemMessage {
// Package: android
NOTE_WIFI_APM_NOTIFICATION = 73;
+ // Inform the user of bluetooth apm state changes.
+ // Package: android
+ NOTE_BT_APM_NOTIFICATION = 74;
+
// ADD_NEW_IDS_ABOVE_THIS_LINE
// Legacy IDs with arbitrary values appear below
// Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/services/core/java/com/android/server/DropBoxManagerService.java b/services/core/java/com/android/server/DropBoxManagerService.java
index 1d457aa933d7..02c6ca20d34e 100644
--- a/services/core/java/com/android/server/DropBoxManagerService.java
+++ b/services/core/java/com/android/server/DropBoxManagerService.java
@@ -479,6 +479,10 @@ public final class DropBoxManagerService extends SystemService {
if (length > max) {
// Log and fall through to create empty tombstone below
Slog.w(TAG, "Dropping: " + tag + " (" + length + " > " + max + " bytes)");
+ logDropboxDropped(
+ FrameworkStatsLog.DROPBOX_ENTRY_DROPPED__DROP_REASON__ENTRY_TOO_LARGE,
+ tag,
+ 0);
} else {
temp = new File(mDropBoxDir, "drop" + Thread.currentThread().getId() + ".tmp");
try (FileOutputStream out = new FileOutputStream(temp)) {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 83e3b499fc96..4a3f682a592d 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3047,6 +3047,10 @@ class StorageManagerService extends IStorageManager.Stub
try {
mVold.createUserKey(userId, serialNumber, ephemeral);
+ // New keys are always unlocked.
+ synchronized (mLock) {
+ mLocalUnlockedUsers.append(userId);
+ }
} catch (Exception e) {
Slog.wtf(TAG, e);
}
@@ -3058,6 +3062,10 @@ class StorageManagerService extends IStorageManager.Stub
try {
mVold.destroyUserKey(userId);
+ // Destroying a key also locks it.
+ synchronized (mLock) {
+ mLocalUnlockedUsers.remove(userId);
+ }
} catch (Exception e) {
Slog.wtf(TAG, e);
}
diff --git a/services/core/java/com/android/server/am/LmkdStatsReporter.java b/services/core/java/com/android/server/am/LmkdStatsReporter.java
index 91588913c33c..4380b42ee54c 100644
--- a/services/core/java/com/android/server/am/LmkdStatsReporter.java
+++ b/services/core/java/com/android/server/am/LmkdStatsReporter.java
@@ -50,7 +50,8 @@ public final class LmkdStatsReporter {
* Logs the event when LMKD kills a process to reduce memory pressure.
* Code: LMK_KILL_OCCURRED = 51
*/
- public static void logKillOccurred(DataInputStream inputData) {
+ public static void logKillOccurred(DataInputStream inputData, int totalForegroundServices,
+ int procsWithForegroundServices) {
try {
final long pgFault = inputData.readLong();
final long pgMajFault = inputData.readLong();
@@ -67,11 +68,10 @@ public final class LmkdStatsReporter {
final int thrashing = inputData.readInt();
final int maxThrashing = inputData.readInt();
final String procName = inputData.readUTF();
-
FrameworkStatsLog.write(FrameworkStatsLog.LMK_KILL_OCCURRED, uid, procName, oomScore,
pgFault, pgMajFault, rssInBytes, cacheInBytes, swapInBytes, processStartTimeNS,
minOomScore, freeMemKb, freeSwapKb, mapKillReason(killReason), thrashing,
- maxThrashing);
+ maxThrashing, totalForegroundServices, procsWithForegroundServices);
} catch (IOException e) {
Slog.e(TAG, "Invalid buffer data. Failed to log LMK_KILL_OCCURRED");
return;
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 42792bf64f44..ccbca76d1868 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -814,7 +814,12 @@ public final class ProcessList {
< LmkdStatsReporter.KILL_OCCURRED_MSG_SIZE) {
return false;
}
- LmkdStatsReporter.logKillOccurred(inputData);
+ Pair<Integer, Integer> temp = getNumForegroundServices();
+ final int totalForegroundServices = temp.first;
+ final int procsWithForegroundServices = temp.second;
+ LmkdStatsReporter.logKillOccurred(inputData,
+ totalForegroundServices,
+ procsWithForegroundServices);
return true;
case LMK_STATE_CHANGED:
if (receivedLen
@@ -5123,6 +5128,26 @@ public final class ProcessList {
}
}
+ /**
+ * Get the number of foreground services in all processes and number of processes that have
+ * foreground service within.
+ */
+ Pair<Integer, Integer> getNumForegroundServices() {
+ int numForegroundServices = 0;
+ int procs = 0;
+ synchronized (mService) {
+ for (int i = 0, size = mLruProcesses.size(); i < size; i++) {
+ ProcessRecord pr = mLruProcesses.get(i);
+ int numFgs = pr.mServices.getNumForegroundServices();
+ if (numFgs > 0) {
+ numForegroundServices += numFgs;
+ procs++;
+ }
+ }
+ }
+ return new Pair<>(numForegroundServices, procs);
+ }
+
private final class ImperceptibleKillRunner extends IUidObserver.Stub {
private static final String EXTRA_PID = "pid";
private static final String EXTRA_UID = "uid";
diff --git a/services/core/java/com/android/server/am/ProcessServiceRecord.java b/services/core/java/com/android/server/am/ProcessServiceRecord.java
index 9951e983a752..67eb675503ad 100644
--- a/services/core/java/com/android/server/am/ProcessServiceRecord.java
+++ b/services/core/java/com/android/server/am/ProcessServiceRecord.java
@@ -180,6 +180,16 @@ final class ProcessServiceRecord {
mRepFgServiceTypes = foregroundServiceTypes;
}
+ int getNumForegroundServices() {
+ int count = 0;
+ for (int i = 0, serviceCount = mServices.size(); i < serviceCount; i++) {
+ if (mServices.valueAt(i).isForeground) {
+ count++;
+ }
+ }
+ return count;
+ }
+
void updateHasTopStartedAlmostPerceptibleServices() {
mHasTopStartedAlmostPerceptibleServices = false;
mLastTopStartedAlmostPerceptibleBindRequestUptimeMs = 0;
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index d0817b078b61..470de8ce0955 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -99,6 +99,7 @@ public class SettingsToPropertiesMapper {
DeviceConfig.NAMESPACE_SWCODEC_NATIVE,
DeviceConfig.NAMESPACE_TETHERING,
DeviceConfig.NAMESPACE_VENDOR_SYSTEM_NATIVE,
+ DeviceConfig.NAMESPACE_VENDOR_SYSTEM_NATIVE_BOOT,
DeviceConfig.NAMESPACE_VIRTUALIZATION_FRAMEWORK_NATIVE,
DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT,
DeviceConfig.NAMESPACE_MEMORY_SAFETY_NATIVE,
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index d16fe1240d0c..d4ef638d0818 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -343,6 +343,9 @@ public class AttentionManagerService extends SystemService {
*
* Calling this multiple times for duplicate requests will be no-ops, returning true.
*
+ * TODO(b/239130847): Maintain the proximity state in AttentionManagerService and change this
+ * to a polling API.
+ *
* @return {@code true} if the framework was able to dispatch the request
*/
@VisibleForTesting
@@ -853,9 +856,6 @@ public class AttentionManagerService extends SystemService {
@GuardedBy("mLock")
private void cancelAndUnbindLocked() {
synchronized (mLock) {
- if (mCurrentAttentionCheck == null && mCurrentProximityUpdate == null) {
- return;
- }
if (mCurrentAttentionCheck != null) {
cancel();
}
@@ -937,7 +937,7 @@ public class AttentionManagerService extends SystemService {
}
}
- class TestableProximityUpdateCallbackInternal extends ProximityUpdateCallbackInternal {
+ class TestableProximityUpdateCallbackInternal implements ProximityUpdateCallbackInternal {
private double mLastCallbackCode = PROXIMITY_UNKNOWN;
@Override
@@ -1069,6 +1069,7 @@ public class AttentionManagerService extends SystemService {
private void resetStates() {
synchronized (mLock) {
mCurrentProximityUpdate = null;
+ cancelAndUnbindLocked();
}
mComponentName = resolveAttentionService(mContext);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceProvider.java
new file mode 100644
index 000000000000..0f1fe68ad1d7
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceProvider.java
@@ -0,0 +1,61 @@
+/*
+ * 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.biometrics.sensors;
+
+import android.annotation.NonNull;
+import android.hardware.biometrics.SensorPropertiesInternal;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Common attributes for all biometric service providers.
+ *
+ * @param <T> Internal settings type.
+ */
+public interface BiometricServiceProvider<T extends SensorPropertiesInternal> {
+
+ /** Checks if the specified sensor is owned by this provider. */
+ boolean containsSensor(int sensorId);
+
+ /** All sensor properties. */
+ @NonNull
+ List<T> getSensorProperties();
+
+ /** Properties for the given sensor id. */
+ @NonNull
+ T getSensorProperties(int sensorId);
+
+ boolean isHardwareDetected(int sensorId);
+
+ /** If the user has any enrollments for the given sensor. */
+ boolean hasEnrollments(int sensorId, int userId);
+
+ long getAuthenticatorId(int sensorId, int userId);
+
+ @LockoutTracker.LockoutMode
+ int getLockoutModeForUser(int sensorId, int userId);
+
+ void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto,
+ boolean clearSchedulerBuffer);
+
+ void dumpProtoMetrics(int sensorId, @NonNull FileDescriptor fd);
+
+ void dumpInternal(int sensorId, @NonNull PrintWriter pw);
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java
new file mode 100644
index 000000000000..7574523f0662
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java
@@ -0,0 +1,235 @@
+/*
+ * 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.biometrics.sensors;
+
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.biometrics.IBiometricAuthenticator;
+import android.hardware.biometrics.IBiometricService;
+import android.hardware.biometrics.SensorPropertiesInternal;
+import android.os.Handler;
+import android.os.IInterface;
+import android.os.Process;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.util.Pair;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.ServiceThread;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Supplier;
+
+/**
+ * Container for all BiometricServiceProvider implementations.
+ *
+ * @param <T> The service provider type.
+ * @param <P> The internal properties type.
+ * @param <C> The registration callback for {@link #invokeRegisteredCallback(IInterface, List)}.
+ */
+public abstract class BiometricServiceRegistry<T extends BiometricServiceProvider<P>,
+ P extends SensorPropertiesInternal,
+ C extends IInterface> {
+
+ private static final String TAG = "BiometricServiceRegistry";
+
+ // Volatile so they can be read without a lock once all services are registered.
+ // But, ideally remove this and provide immutable copies via the callback instead.
+ @Nullable
+ private volatile List<T> mServiceProviders;
+ @Nullable
+ private volatile List<P> mAllProps;
+
+ @NonNull
+ private final Supplier<IBiometricService> mBiometricServiceSupplier;
+ @NonNull
+ private final RemoteCallbackList<C> mRegisteredCallbacks = new RemoteCallbackList<>();
+
+ public BiometricServiceRegistry(@NonNull Supplier<IBiometricService> biometricSupplier) {
+ mBiometricServiceSupplier = biometricSupplier;
+ }
+
+ /**
+ * Register an implementation by creating a new authenticator and initializing it via
+ * {@link IBiometricService#registerAuthenticator(int, int, int, IBiometricAuthenticator)}
+ * using the given properties.
+ *
+ * @param service service to register with
+ * @param props internal properties to initialize the authenticator
+ */
+ protected abstract void registerService(@NonNull IBiometricService service, @NonNull P props);
+
+ /**
+ * Invoke the callback to notify clients that all authenticators have been registered.
+ *
+ * @param callback callback to invoke
+ * @param allProps properties of all authenticators
+ */
+ protected abstract void invokeRegisteredCallback(@NonNull C callback,
+ @NonNull List<P> allProps) throws RemoteException;
+
+ /**
+ * Register all authenticators in a background thread.
+ *
+ * @param serviceProvider Supplier function that will be invoked on the background thread.
+ */
+ public void registerAll(Supplier<List<T>> serviceProvider) {
+ // Some HAL might not be started before the system service and will cause the code below
+ // to wait, and some of the operations below might take a significant amount of time to
+ // complete (calls to the HALs). To avoid blocking the rest of system server we put
+ // this on a background thread.
+ final ServiceThread thread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND,
+ true /* allowIo */);
+ thread.start();
+ final Handler handler = new Handler(thread.getLooper());
+ handler.post(() -> registerAllInBackground(serviceProvider));
+ thread.quitSafely();
+ }
+
+ /** Register authenticators now, only called by {@link #registerAll(Supplier).} */
+ @VisibleForTesting
+ public void registerAllInBackground(Supplier<List<T>> serviceProvider) {
+ List<T> providers = serviceProvider.get();
+ if (providers == null) {
+ providers = new ArrayList<>();
+ }
+
+ final IBiometricService biometricService = mBiometricServiceSupplier.get();
+ if (biometricService == null) {
+ throw new IllegalStateException("biometric service cannot be null");
+ }
+
+ // Register each sensor individually with BiometricService
+ final List<P> allProps = new ArrayList<>();
+ for (T provider : providers) {
+ final List<P> props = provider.getSensorProperties();
+ for (P prop : props) {
+ registerService(biometricService, prop);
+ }
+ allProps.addAll(props);
+ }
+
+ finishRegistration(providers, allProps);
+ }
+
+ private synchronized void finishRegistration(
+ @NonNull List<T> providers, @NonNull List<P> allProps) {
+ mServiceProviders = Collections.unmodifiableList(providers);
+ mAllProps = Collections.unmodifiableList(allProps);
+ broadcastAllAuthenticatorsRegistered();
+ }
+
+ /**
+ * Add a callback that will be invoked once the work from {@link #registerAll(Supplier)}
+ * has finished registering all providers (executes immediately if already done).
+ *
+ * @param callback registration callback
+ */
+ public synchronized void addAllRegisteredCallback(@Nullable C callback) {
+ if (callback == null) {
+ Slog.e(TAG, "addAllRegisteredCallback, callback is null");
+ return;
+ }
+
+ final boolean registered = mRegisteredCallbacks.register(callback);
+ final boolean allRegistered = mServiceProviders != null;
+ if (registered && allRegistered) {
+ broadcastAllAuthenticatorsRegistered();
+ } else if (!registered) {
+ Slog.e(TAG, "addAllRegisteredCallback failed to register callback");
+ }
+ }
+
+ private synchronized void broadcastAllAuthenticatorsRegistered() {
+ final int n = mRegisteredCallbacks.beginBroadcast();
+ for (int i = 0; i < n; ++i) {
+ final C cb = mRegisteredCallbacks.getBroadcastItem(i);
+ try {
+ invokeRegisteredCallback(cb, mAllProps);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception in broadcastAllAuthenticatorsRegistered", e);
+ } finally {
+ mRegisteredCallbacks.unregister(cb);
+ }
+ }
+ mRegisteredCallbacks.finishBroadcast();
+ }
+
+ /**
+ * Get a list of registered providers.
+ *
+ * Undefined until {@link #registerAll(Supplier)} has fired the completion callback.
+ */
+ @NonNull
+ public List<T> getProviders() {
+ return mServiceProviders != null ? mServiceProviders : Collections.emptyList();
+ }
+
+ /**
+ * Gets the provider for given sensor id or null if not registered.
+ *
+ * Undefined until {@link #registerAll(Supplier)} has fired the completion callback.
+ */
+ @Nullable
+ public T getProviderForSensor(int sensorId) {
+ if (mServiceProviders != null) {
+ for (T provider : mServiceProviders) {
+ if (provider.containsSensor(sensorId)) {
+ return provider;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * For devices with only a single provider, returns that provider.
+ * If no providers, or multiple providers exist, returns null.
+ *
+ * Undefined until {@link #registerAll(Supplier)} has fired the completion callback.
+ */
+ @Nullable
+ public Pair<Integer, T> getSingleProvider() {
+ if (mAllProps == null || mAllProps.size() != 1) {
+ Slog.e(TAG, "Multiple sensors found: " + mAllProps.size());
+ return null;
+ }
+
+ final int sensorId = mAllProps.get(0).sensorId;
+ final T provider = getProviderForSensor(sensorId);
+ if (provider != null) {
+ return new Pair<>(sensorId, provider);
+ }
+
+ Slog.e(TAG, "Single sensor, but provider not found");
+ return null;
+ }
+
+ /**
+ * Get the properties for all providers.
+ *
+ * Undefined until {@link #registerAll(Supplier)} has fired the completion callback.
+ */
+ @NonNull
+ public List<P> getAllProperties() {
+ return mAllProps != null ? mAllProps : Collections.emptyList();
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java b/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java
index 0d789f7a1840..f8543162f95e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricStateCallback.java
@@ -23,32 +23,64 @@ import static android.hardware.biometrics.BiometricStateListener.STATE_IDLE;
import static android.hardware.biometrics.BiometricStateListener.STATE_KEYGUARD_AUTH;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricStateListener;
import android.hardware.biometrics.IBiometricStateListener;
+import android.hardware.biometrics.SensorPropertiesInternal;
import android.os.RemoteException;
+import android.os.UserManager;
import android.util.Slog;
import com.android.server.biometrics.Utils;
+import java.util.Collections;
+import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* A callback for receiving notifications about biometric sensor state changes.
+ *
+ * @param <T> service provider type
+ * @param <P> internal property type
*/
-public class BiometricStateCallback implements ClientMonitorCallback {
+public class BiometricStateCallback<T extends BiometricServiceProvider<P>,
+ P extends SensorPropertiesInternal> implements ClientMonitorCallback {
private static final String TAG = "BiometricStateCallback";
@NonNull
- private final CopyOnWriteArrayList<IBiometricStateListener>
- mBiometricStateListeners = new CopyOnWriteArrayList<>();
-
- private @BiometricStateListener.State int mBiometricState;
+ private final CopyOnWriteArrayList<IBiometricStateListener> mBiometricStateListeners =
+ new CopyOnWriteArrayList<>();
+ @NonNull
+ private final UserManager mUserManager;
+ @BiometricStateListener.State
+ private int mBiometricState;
+ @NonNull
+ private List<T> mProviders = List.of();
- public BiometricStateCallback() {
+ /**
+ * Create a new callback that must be {@link #start(List)}ed.
+ *
+ * @param userManager user manager
+ */
+ public BiometricStateCallback(@NonNull UserManager userManager) {
mBiometricState = STATE_IDLE;
+ mUserManager = userManager;
+ }
+
+ /**
+ * This should be called when the service has been initialized and all providers are ready.
+ *
+ * @param allProviders all registered biometric service providers
+ */
+ public synchronized void start(@NonNull List<T> allProviders) {
+ mProviders = Collections.unmodifiableList(allProviders);
+ broadcastCurrentEnrollmentState(null /* listener */);
}
+ /** Get the current state. */
+ @BiometricStateListener.State
public int getBiometricState() {
return mBiometricState;
}
@@ -120,23 +152,43 @@ public class BiometricStateCallback implements ClientMonitorCallback {
}
/**
- * This should be invoked when:
- * 1) Enrolled --> None-enrolled
- * 2) None-enrolled --> enrolled
- * 3) HAL becomes ready
- * 4) Listener is registered
+ * Enables clients to register a BiometricStateListener. For example, this is used to forward
+ * fingerprint sensor state changes to SideFpsEventHandler.
+ *
+ * @param listener listener to register
*/
- public void notifyAllEnrollmentStateChanged(int userId, int sensorId,
+ public synchronized void registerBiometricStateListener(
+ @NonNull IBiometricStateListener listener) {
+ mBiometricStateListeners.add(listener);
+ broadcastCurrentEnrollmentState(listener);
+ }
+
+ private synchronized void broadcastCurrentEnrollmentState(
+ @Nullable IBiometricStateListener listener) {
+ for (T provider : mProviders) {
+ for (SensorPropertiesInternal prop : provider.getSensorProperties()) {
+ for (UserInfo userInfo : mUserManager.getAliveUsers()) {
+ final boolean enrolled = provider.hasEnrollments(prop.sensorId, userInfo.id);
+ if (listener != null) {
+ notifyEnrollmentStateChanged(
+ listener, userInfo.id, prop.sensorId, enrolled);
+ } else {
+ notifyAllEnrollmentStateChanged(
+ userInfo.id, prop.sensorId, enrolled);
+ }
+ }
+ }
+ }
+ }
+
+ private void notifyAllEnrollmentStateChanged(int userId, int sensorId,
boolean hasEnrollments) {
for (IBiometricStateListener listener : mBiometricStateListeners) {
notifyEnrollmentStateChanged(listener, userId, sensorId, hasEnrollments);
}
}
- /**
- * Notifies the listener of enrollment state changes.
- */
- public void notifyEnrollmentStateChanged(@NonNull IBiometricStateListener listener,
+ private void notifyEnrollmentStateChanged(@NonNull IBiometricStateListener listener,
int userId, int sensorId, boolean hasEnrollments) {
try {
listener.onEnrollmentsChanged(userId, sensorId, hasEnrollments);
@@ -144,14 +196,4 @@ public class BiometricStateCallback implements ClientMonitorCallback {
Slog.e(TAG, "Remote exception", e);
}
}
-
- /**
- * Enables clients to register a BiometricStateListener. For example, this is used to forward
- * fingerprint sensor state changes to SideFpsEventHandler.
- *
- * @param listener
- */
- public void registerBiometricStateListener(@NonNull IBiometricStateListener listener) {
- mBiometricStateListeners.add(listener);
- }
}
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 79e65cc6d2e5..271bce9890c6 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
@@ -17,20 +17,18 @@
package com.android.server.biometrics.sensors.face;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
-import static android.Manifest.permission.MANAGE_BIOMETRIC;
import static android.Manifest.permission.MANAGE_FACE;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
-import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.Context;
-import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.IBiometricStateListener;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
@@ -39,18 +37,18 @@ import android.hardware.biometrics.face.SensorProps;
import android.hardware.face.Face;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.FaceServiceReceiver;
+import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
import android.hardware.face.IFaceService;
import android.hardware.face.IFaceServiceReceiver;
import android.os.Binder;
-import android.os.Handler;
import android.os.IBinder;
import android.os.NativeHandle;
-import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.UserHandle;
+import android.os.UserManager;
import android.util.Pair;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
@@ -58,10 +56,10 @@ import android.view.Surface;
import com.android.internal.util.DumpUtils;
import com.android.internal.widget.LockPatternUtils;
-import com.android.server.ServiceThread;
import com.android.server.SystemService;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.sensors.BiometricStateCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
@@ -88,51 +86,10 @@ public class FaceService extends SystemService {
private final LockoutResetDispatcher mLockoutResetDispatcher;
private final LockPatternUtils mLockPatternUtils;
@NonNull
- private final List<ServiceProvider> mServiceProviders;
-
- @Nullable
- private ServiceProvider getProviderForSensor(int sensorId) {
- for (ServiceProvider provider : mServiceProviders) {
- if (provider.containsSensor(sensorId)) {
- return provider;
- }
- }
- return null;
- }
-
- /**
- * For devices with only a single provider, returns that provider. If no providers, or multiple
- * providers exist, returns null.
- */
- @Nullable
- private Pair<Integer, ServiceProvider> getSingleProvider() {
- final List<FaceSensorPropertiesInternal> properties = getSensorProperties();
- if (properties.size() != 1) {
- Slog.e(TAG, "Multiple sensors found: " + properties.size());
- return null;
- }
-
- // Theoretically we can just return the first provider, but maybe this is easier to
- // understand.
- final int sensorId = properties.get(0).sensorId;
- for (ServiceProvider provider : mServiceProviders) {
- if (provider.containsSensor(sensorId)) {
- return new Pair<>(sensorId, provider);
- }
- }
-
- Slog.e(TAG, "Single sensor, but provider not found");
- return null;
- }
-
+ private final FaceServiceRegistry mRegistry;
@NonNull
- private List<FaceSensorPropertiesInternal> getSensorProperties() {
- final List<FaceSensorPropertiesInternal> properties = new ArrayList<>();
- for (ServiceProvider provider : mServiceProviders) {
- properties.addAll(provider.getSensorProperties());
- }
- return properties;
- }
+ private final BiometricStateCallback<ServiceProvider, FaceSensorPropertiesInternal>
+ mBiometricStateCallback;
/**
* Receives the incoming binder calls from FaceManager.
@@ -142,8 +99,7 @@ public class FaceService extends SystemService {
@Override
public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback,
@NonNull String opPackageName) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for createTestSession, sensorId: " + sensorId);
@@ -156,9 +112,8 @@ public class FaceService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public byte[] dumpSensorServiceStateProto(int sensorId, boolean clearSchedulerBuffer) {
-
final ProtoOutputStream proto = new ProtoOutputStream();
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider != null) {
provider.dumpProtoState(sensorId, proto, clearSchedulerBuffer);
}
@@ -170,16 +125,14 @@ public class FaceService extends SystemService {
@Override // Binder call
public List<FaceSensorPropertiesInternal> getSensorPropertiesInternal(
String opPackageName) {
-
- return FaceService.this.getSensorProperties();
+ return mRegistry.getAllProperties();
}
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override // Binder call
public FaceSensorPropertiesInternal getSensorProperties(int sensorId,
@NonNull String opPackageName) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "No matching sensor for getSensorProperties, sensorId: " + sensorId
+ ", caller: " + opPackageName);
@@ -193,8 +146,7 @@ public class FaceService extends SystemService {
@Override // Binder call
public void generateChallenge(IBinder token, int sensorId, int userId,
IFaceServiceReceiver receiver, String opPackageName) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "No matching sensor for generateChallenge, sensorId: " + sensorId);
return;
@@ -207,8 +159,7 @@ public class FaceService extends SystemService {
@Override // Binder call
public void revokeChallenge(IBinder token, int sensorId, int userId, String opPackageName,
long challenge) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "No matching sensor for revokeChallenge, sensorId: " + sensorId);
return;
@@ -222,8 +173,7 @@ public class FaceService extends SystemService {
public long enroll(int userId, final IBinder token, final byte[] hardwareAuthToken,
final IFaceServiceReceiver receiver, final String opPackageName,
final int[] disabledFeatures, Surface previewSurface, boolean debugConsent) {
-
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for enroll");
return -1;
@@ -245,8 +195,7 @@ public class FaceService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_BIOMETRIC)
@Override // Binder call
public void cancelEnrollment(final IBinder token, long requestId) {
-
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for cancelEnrollment");
return;
@@ -260,7 +209,6 @@ public class FaceService extends SystemService {
public long authenticate(final IBinder token, final long operationId, int userId,
final IFaceServiceReceiver receiver, final String opPackageName,
boolean isKeyguardBypassEnabled) {
-
// TODO(b/152413782): If the sensor supports face detect and the device is encrypted or
// lockdown, something wrong happened. See similar path in FingerprintService.
@@ -273,7 +221,7 @@ public class FaceService extends SystemService {
// permission.
final boolean isKeyguard = Utils.isKeyguard(getContext(), opPackageName);
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for authenticate");
return -1;
@@ -301,7 +249,7 @@ public class FaceService extends SystemService {
return -1;
}
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for detectFace");
return -1;
@@ -318,8 +266,7 @@ public class FaceService extends SystemService {
IBinder token, long operationId, int userId,
IBiometricSensorReceiver sensorReceiver, String opPackageName, long requestId,
int cookie, boolean allowBackgroundAuthentication) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for prepareForAuthentication");
return;
@@ -336,8 +283,7 @@ public class FaceService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override // Binder call
public void startPreparedClient(int sensorId, int cookie) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for startPreparedClient");
return;
@@ -350,8 +296,7 @@ public class FaceService extends SystemService {
@Override // Binder call
public void cancelAuthentication(final IBinder token, final String opPackageName,
final long requestId) {
-
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for cancelAuthentication");
return;
@@ -370,7 +315,7 @@ public class FaceService extends SystemService {
return;
}
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for cancelFaceDetect");
return;
@@ -383,8 +328,7 @@ public class FaceService extends SystemService {
@Override // Binder call
public void cancelAuthenticationFromService(int sensorId, final IBinder token,
final String opPackageName, final long requestId) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for cancelAuthenticationFromService");
return;
@@ -397,8 +341,7 @@ public class FaceService extends SystemService {
@Override // Binder call
public void remove(final IBinder token, final int faceId, final int userId,
final IFaceServiceReceiver receiver, final String opPackageName) {
-
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for remove");
return;
@@ -412,7 +355,6 @@ public class FaceService extends SystemService {
@Override // Binder call
public void removeAll(final IBinder token, final int userId,
final IFaceServiceReceiver receiver, final String opPackageName) {
-
final FaceServiceReceiver internalReceiver = new FaceServiceReceiver() {
int sensorsFinishedRemoving = 0;
final int numSensors = getSensorPropertiesInternal(
@@ -432,7 +374,7 @@ public class FaceService extends SystemService {
// This effectively iterates through all sensors, but has to do so by finding all
// sensors under each provider.
- for (ServiceProvider provider : mServiceProviders) {
+ for (ServiceProvider provider : mRegistry.getProviders()) {
List<FaceSensorPropertiesInternal> props = provider.getSensorProperties();
for (FaceSensorPropertiesInternal prop : props) {
provider.scheduleRemoveAll(prop.sensorId, token, userId, internalReceiver,
@@ -467,27 +409,27 @@ public class FaceService extends SystemService {
try {
if (args.length > 1 && "--proto".equals(args[0]) && "--state".equals(args[1])) {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
- for (ServiceProvider provider : mServiceProviders) {
+ for (ServiceProvider provider : mRegistry.getProviders()) {
for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) {
provider.dumpProtoState(props.sensorId, proto, false);
}
}
proto.flush();
} else if (args.length > 0 && "--proto".equals(args[0])) {
- for (ServiceProvider provider : mServiceProviders) {
+ for (ServiceProvider provider : mRegistry.getProviders()) {
for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) {
provider.dumpProtoMetrics(props.sensorId, fd);
}
}
} else if (args.length > 1 && "--hal".equals(args[0])) {
- for (ServiceProvider provider : mServiceProviders) {
+ for (ServiceProvider provider : mRegistry.getProviders()) {
for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) {
provider.dumpHal(props.sensorId, fd,
Arrays.copyOfRange(args, 1, args.length, args.getClass()));
}
}
} else {
- for (ServiceProvider provider : mServiceProviders) {
+ for (ServiceProvider provider : mRegistry.getProviders()) {
for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) {
pw.println("Dumping for sensorId: " + props.sensorId
+ ", provider: " + provider.getClass().getSimpleName());
@@ -504,10 +446,9 @@ public class FaceService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override // Binder call
public boolean isHardwareDetected(int sensorId, String opPackageName) {
-
final long token = Binder.clearCallingIdentity();
try {
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for isHardwareDetected, caller: " + opPackageName);
return false;
@@ -521,12 +462,11 @@ public class FaceService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override // Binder call
public List<Face> getEnrolledFaces(int sensorId, int userId, String opPackageName) {
-
if (userId != UserHandle.getCallingUserId()) {
Utils.checkPermission(getContext(), INTERACT_ACROSS_USERS);
}
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for getEnrolledFaces, caller: " + opPackageName);
return Collections.emptyList();
@@ -538,12 +478,11 @@ public class FaceService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override // Binder call
public boolean hasEnrolledFaces(int sensorId, int userId, String opPackageName) {
-
if (userId != UserHandle.getCallingUserId()) {
Utils.checkPermission(getContext(), INTERACT_ACROSS_USERS);
}
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for hasEnrolledFaces, caller: " + opPackageName);
return false;
@@ -555,8 +494,7 @@ public class FaceService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override // Binder call
public @LockoutTracker.LockoutMode int getLockoutModeForUser(int sensorId, int userId) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for getLockoutModeForUser");
return LockoutTracker.LOCKOUT_NONE;
@@ -569,8 +507,7 @@ public class FaceService extends SystemService {
@Override
public void invalidateAuthenticatorId(int sensorId, int userId,
IInvalidationCallback callback) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for invalidateAuthenticatorId");
return;
@@ -582,7 +519,7 @@ public class FaceService extends SystemService {
@Override // Binder call
public long getAuthenticatorId(int sensorId, int userId) {
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for getAuthenticatorId");
return 0;
@@ -595,8 +532,7 @@ public class FaceService extends SystemService {
@Override // Binder call
public void resetLockout(IBinder token, int sensorId, int userId, byte[] hardwareAuthToken,
String opPackageName) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for resetLockout, caller: " + opPackageName);
return;
@@ -610,8 +546,7 @@ public class FaceService extends SystemService {
public void setFeature(final IBinder token, int userId, int feature, boolean enabled,
final byte[] hardwareAuthToken, IFaceServiceReceiver receiver,
final String opPackageName) {
-
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for setFeature");
return;
@@ -625,8 +560,7 @@ public class FaceService extends SystemService {
@Override
public void getFeature(final IBinder token, int userId, int feature,
IFaceServiceReceiver receiver, final String opPackageName) {
-
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for getFeature");
return;
@@ -636,18 +570,14 @@ public class FaceService extends SystemService {
new ClientMonitorCallbackConverter(receiver), opPackageName);
}
- private void addHidlProviders(@NonNull List<FaceSensorPropertiesInternal> hidlSensors) {
- for (FaceSensorPropertiesInternal hidlSensor : hidlSensors) {
- mServiceProviders.add(
- Face10.newInstance(getContext(), hidlSensor, mLockoutResetDispatcher));
- }
- }
+ private List<ServiceProvider> getAidlProviders() {
+ final List<ServiceProvider> providers = new ArrayList<>();
- private void addAidlProviders() {
final String[] instances = ServiceManager.getDeclaredInstances(IFace.DESCRIPTOR);
if (instances == null || instances.length == 0) {
- return;
+ return providers;
}
+
for (String instance : instances) {
final String fqName = IFace.DESCRIPTOR + "/" + instance;
final IFace face = IFace.Stub.asInterface(
@@ -660,53 +590,41 @@ public class FaceService extends SystemService {
final SensorProps[] props = face.getSensorProps();
final FaceProvider provider = new FaceProvider(getContext(), props, instance,
mLockoutResetDispatcher, BiometricContext.getInstance(getContext()));
- mServiceProviders.add(provider);
+ providers.add(provider);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception in getSensorProps: " + fqName);
}
}
+
+ return providers;
}
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override // Binder call
public void registerAuthenticators(
@NonNull List<FaceSensorPropertiesInternal> hidlSensors) {
-
- // Some HAL might not be started before the system service and will cause the code below
- // to wait, and some of the operations below might take a significant amount of time to
- // complete (calls to the HALs). To avoid blocking the rest of system server we put
- // this on a background thread.
- final ServiceThread thread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND,
- true /* allowIo */);
- thread.start();
- final Handler handler = new Handler(thread.getLooper());
-
- handler.post(() -> {
- addHidlProviders(hidlSensors);
- addAidlProviders();
-
- final IBiometricService biometricService = IBiometricService.Stub.asInterface(
- ServiceManager.getService(Context.BIOMETRIC_SERVICE));
-
- // Register each sensor individually with BiometricService
- for (ServiceProvider provider : mServiceProviders) {
- final List<FaceSensorPropertiesInternal> props = provider.getSensorProperties();
- for (FaceSensorPropertiesInternal prop : props) {
- final int sensorId = prop.sensorId;
- final @BiometricManager.Authenticators.Types int strength =
- Utils.propertyStrengthToAuthenticatorStrength(prop.sensorStrength);
- final FaceAuthenticator authenticator = new FaceAuthenticator(
- mServiceWrapper, sensorId);
- try {
- biometricService.registerAuthenticator(sensorId, TYPE_FACE, strength,
- authenticator);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when registering sensorId: " + sensorId);
- }
- }
+ mRegistry.registerAll(() -> {
+ final List<ServiceProvider> providers = new ArrayList<>();
+ for (FaceSensorPropertiesInternal hidlSensor : hidlSensors) {
+ providers.add(
+ Face10.newInstance(getContext(), hidlSensor, mLockoutResetDispatcher));
}
+ providers.addAll(getAidlProviders());
+ return providers;
});
}
+
+ @Override
+ public void addAuthenticatorsRegisteredCallback(
+ IFaceAuthenticatorsRegisteredCallback callback) {
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ mRegistry.addAllRegisteredCallback(callback);
+ }
+
+ @Override
+ public void registerBiometricStateListener(@NonNull IBiometricStateListener listener) {
+ mBiometricStateCallback.registerBiometricStateListener(listener);
+ }
}
public FaceService(Context context) {
@@ -714,7 +632,16 @@ public class FaceService extends SystemService {
mServiceWrapper = new FaceServiceWrapper();
mLockoutResetDispatcher = new LockoutResetDispatcher(context);
mLockPatternUtils = new LockPatternUtils(context);
- mServiceProviders = new ArrayList<>();
+ mBiometricStateCallback = new BiometricStateCallback<>(UserManager.get(context));
+ mRegistry = new FaceServiceRegistry(mServiceWrapper,
+ () -> IBiometricService.Stub.asInterface(
+ ServiceManager.getService(Context.BIOMETRIC_SERVICE)));
+ mRegistry.addAllRegisteredCallback(new IFaceAuthenticatorsRegisteredCallback.Stub() {
+ @Override
+ public void onAllAuthenticatorsRegistered(List<FaceSensorPropertiesInternal> sensors) {
+ mBiometricStateCallback.start(mRegistry.getProviders());
+ }
+ });
}
@Override
@@ -752,7 +679,7 @@ public class FaceService extends SystemService {
if (Utils.isVirtualEnabled(getContext())) {
Slog.i(TAG, "Sync virtual enrollments");
final int userId = ActivityManager.getCurrentUser();
- for (ServiceProvider provider : mServiceProviders) {
+ for (ServiceProvider provider : mRegistry.getProviders()) {
for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) {
provider.scheduleInternalCleanup(props.sensorId, userId, null /* callback */,
true /* favorHalEnrollments */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceServiceRegistry.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceServiceRegistry.java
new file mode 100644
index 000000000000..0f0a81d24473
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceServiceRegistry.java
@@ -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.server.biometrics.sensors.face;
+
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.IBiometricService;
+import android.hardware.face.FaceSensorPropertiesInternal;
+import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
+import android.hardware.face.IFaceService;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.sensors.BiometricServiceRegistry;
+
+import java.util.List;
+import java.util.function.Supplier;
+
+/** Registry for {@link IFaceService} providers. */
+public class FaceServiceRegistry extends BiometricServiceRegistry<ServiceProvider,
+ FaceSensorPropertiesInternal, IFaceAuthenticatorsRegisteredCallback> {
+
+ private static final String TAG = "FaceServiceRegistry";
+
+ @NonNull
+ private final IFaceService mService;
+
+ /** Creates a new registry tied to the given service. */
+ public FaceServiceRegistry(@NonNull IFaceService service,
+ @Nullable Supplier<IBiometricService> biometricSupplier) {
+ super(biometricSupplier);
+ mService = service;
+ }
+
+ @Override
+ protected void registerService(@NonNull IBiometricService service,
+ @NonNull FaceSensorPropertiesInternal props) {
+ @BiometricManager.Authenticators.Types final int strength =
+ Utils.propertyStrengthToAuthenticatorStrength(props.sensorStrength);
+ try {
+ service.registerAuthenticator(props.sensorId, TYPE_FACE, strength,
+ new FaceAuthenticator(mService, props.sensorId));
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when registering sensorId: " + props.sensorId);
+ }
+ }
+
+ @Override
+ protected void invokeRegisteredCallback(@NonNull IFaceAuthenticatorsRegisteredCallback callback,
+ @NonNull List<FaceSensorPropertiesInternal> allProps) throws RemoteException {
+ callback.onAllAuthenticatorsRegistered(allProps);
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
index 6f98365332e2..4efaedbd5530 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
@@ -26,15 +26,13 @@ import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.IFaceServiceReceiver;
import android.os.IBinder;
-import android.util.proto.ProtoOutputStream;
import android.view.Surface;
+import com.android.server.biometrics.sensors.BiometricServiceProvider;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
-import com.android.server.biometrics.sensors.LockoutTracker;
import java.io.FileDescriptor;
-import java.io.PrintWriter;
import java.util.List;
/**
@@ -56,24 +54,11 @@ import java.util.List;
* to check (e.g. via {@link FaceManager#getSensorPropertiesInternal()}) that the code path isn't
* taken. ServiceProviders will provide a no-op for unsupported operations to fail safely.
*/
-public interface ServiceProvider {
- /**
- * Checks if the specified sensor is owned by this provider.
- */
- boolean containsSensor(int sensorId);
-
- @NonNull
- List<FaceSensorPropertiesInternal> getSensorProperties();
-
- @NonNull
- FaceSensorPropertiesInternal getSensorProperties(int sensorId);
+public interface ServiceProvider extends BiometricServiceProvider<FaceSensorPropertiesInternal> {
@NonNull
List<Face> getEnrolledFaces(int sensorId, int userId);
- @LockoutTracker.LockoutMode
- int getLockoutModeForUser(int sensorId, int userId);
-
/**
* Requests for the authenticatorId (whose source of truth is in the TEE or equivalent) to be
* invalidated. See {@link com.android.server.biometrics.sensors.InvalidationRequesterClient}
@@ -84,10 +69,6 @@ public interface ServiceProvider {
+ " this method");
}
- long getAuthenticatorId(int sensorId, int userId);
-
- boolean isHardwareDetected(int sensorId);
-
void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token,
@NonNull IFaceServiceReceiver receiver, String opPackageName);
@@ -142,13 +123,6 @@ public interface ServiceProvider {
void scheduleInternalCleanup(int sensorId, int userId,
@Nullable ClientMonitorCallback callback, boolean favorHalEnrollments);
- void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto,
- boolean clearSchedulerBuffer);
-
- void dumpProtoMetrics(int sensorId, @NonNull FileDescriptor fd);
-
- void dumpInternal(int sensorId, @NonNull PrintWriter pw);
-
@NonNull
ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback,
@NonNull String opPackageName);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 19d54c84c706..6bff179e8eb7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -285,6 +285,11 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
}
@Override
+ public boolean hasEnrollments(int sensorId, int userId) {
+ return !getEnrolledFaces(sensorId, userId).isEmpty();
+ }
+
+ @Override
public void scheduleInvalidateAuthenticatorId(int sensorId, int userId,
@NonNull IInvalidationCallback callback) {
mHandler.post(() -> {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index 65289122747c..c0a119ff5f1e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -484,6 +484,11 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
}
@Override
+ public boolean hasEnrollments(int sensorId, int userId) {
+ return !getEnrolledFaces(sensorId, userId).isEmpty();
+ }
+
+ @Override
@LockoutTracker.LockoutMode
public int getLockoutModeForUser(int sensorId, int userId) {
return mLockoutTracker.getLockoutModeForUser(userId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 2ba449ae089e..7e2742edd47a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -17,14 +17,11 @@
package com.android.server.biometrics.sensors.fingerprint;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
-import static android.Manifest.permission.MANAGE_BIOMETRIC;
import static android.Manifest.permission.MANAGE_FINGERPRINT;
-import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
import static android.Manifest.permission.TEST_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.Manifest.permission.USE_FINGERPRINT;
-import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_USER_CANCELED;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_VENDOR;
@@ -36,8 +33,6 @@ import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IBiometricSensorReceiver;
@@ -65,7 +60,6 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Process;
-import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
@@ -79,11 +73,9 @@ import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
import com.android.internal.widget.LockPatternUtils;
-import com.android.server.ServiceThread;
import com.android.server.SystemService;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
@@ -115,74 +107,32 @@ public class FingerprintService extends SystemService {
protected static final String TAG = "FingerprintService";
- private final Object mLock = new Object();
private final AppOpsManager mAppOps;
private final LockoutResetDispatcher mLockoutResetDispatcher;
private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
private final LockPatternUtils mLockPatternUtils;
- @NonNull private final List<ServiceProvider> mServiceProviders;
- @NonNull private final BiometricStateCallback mBiometricStateCallback;
- @NonNull private final Handler mHandler;
- @NonNull private final BiometricContext mBiometricContext;
- @NonNull private final Supplier<IBiometricService> mBiometricServiceSupplier;
- @NonNull private final Function<String, IFingerprint> mIFingerprintProvider;
-
- @GuardedBy("mLock")
- @NonNull private final RemoteCallbackList<IFingerprintAuthenticatorsRegisteredCallback>
- mAuthenticatorsRegisteredCallbacks;
-
- @GuardedBy("mLock")
- @NonNull private final List<FingerprintSensorPropertiesInternal> mSensorProps;
-
- /**
- * Registers BiometricStateListener in list stored by FingerprintService
- * @param listener new BiometricStateListener being added
- */
- public void registerBiometricStateListener(@NonNull IBiometricStateListener listener) {
- mBiometricStateCallback.registerBiometricStateListener(listener);
- broadcastCurrentEnrollmentState(listener);
- }
-
- /**
- * @param listener if non-null, notifies only this listener. if null, notifies all listeners
- * in {@link BiometricStateCallback}. This is slightly ugly, but reduces
- * redundant code.
- */
- private void broadcastCurrentEnrollmentState(@Nullable IBiometricStateListener listener) {
- final UserManager um = UserManager.get(getContext());
- synchronized (mLock) {
- // Update the new listener with current state of all sensors
- for (FingerprintSensorPropertiesInternal prop : mSensorProps) {
- final ServiceProvider provider = getProviderForSensor(prop.sensorId);
- for (UserInfo userInfo : um.getAliveUsers()) {
- final boolean enrolled = !provider
- .getEnrolledFingerprints(prop.sensorId, userInfo.id).isEmpty();
-
- // Defer this work and allow the loop to release the lock sooner
- mHandler.post(() -> {
- if (listener != null) {
- mBiometricStateCallback.notifyEnrollmentStateChanged(
- listener, userInfo.id, prop.sensorId, enrolled);
- } else {
- mBiometricStateCallback.notifyAllEnrollmentStateChanged(
- userInfo.id, prop.sensorId, enrolled);
- }
- });
- }
- }
- }
- }
+ @NonNull
+ private final BiometricContext mBiometricContext;
+ @NonNull
+ private final Supplier<String[]> mAidlInstanceNameSupplier;
+ @NonNull
+ private final Function<String, IFingerprint> mIFingerprintProvider;
+ @NonNull
+ private final BiometricStateCallback<ServiceProvider, FingerprintSensorPropertiesInternal>
+ mBiometricStateCallback;
+ @NonNull
+ private final Handler mHandler;
+ @NonNull
+ private final FingerprintServiceRegistry mRegistry;
- /**
- * Receives the incoming binder calls from FingerprintManager.
- */
- private final IFingerprintService.Stub mServiceWrapper = new IFingerprintService.Stub() {
+ /** Receives the incoming binder calls from FingerprintManager. */
+ @VisibleForTesting
+ final IFingerprintService.Stub mServiceWrapper = new IFingerprintService.Stub() {
@android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
@Override
public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback,
@NonNull String opPackageName) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for createTestSession, sensorId: " + sensorId);
@@ -195,9 +145,8 @@ public class FingerprintService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public byte[] dumpSensorServiceStateProto(int sensorId, boolean clearSchedulerBuffer) {
-
final ProtoOutputStream proto = new ProtoOutputStream();
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider != null) {
provider.dumpProtoState(sensorId, proto, clearSchedulerBuffer);
}
@@ -212,16 +161,14 @@ public class FingerprintService extends SystemService {
!= PackageManager.PERMISSION_GRANTED) {
Utils.checkPermission(getContext(), TEST_BIOMETRIC);
}
-
- return FingerprintService.this.getSensorProperties();
+ return mRegistry.getAllProperties();
}
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public FingerprintSensorPropertiesInternal getSensorProperties(int sensorId,
@NonNull String opPackageName) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "No matching sensor for getSensorProperties, sensorId: " + sensorId
+ ", caller: " + opPackageName);
@@ -234,8 +181,7 @@ public class FingerprintService extends SystemService {
@Override // Binder call
public void generateChallenge(IBinder token, int sensorId, int userId,
IFingerprintServiceReceiver receiver, String opPackageName) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "No matching sensor for generateChallenge, sensorId: " + sensorId);
return;
@@ -248,8 +194,7 @@ public class FingerprintService extends SystemService {
@Override // Binder call
public void revokeChallenge(IBinder token, int sensorId, int userId, String opPackageName,
long challenge) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "No matching sensor for revokeChallenge, sensorId: " + sensorId);
return;
@@ -264,8 +209,7 @@ public class FingerprintService extends SystemService {
public long enroll(final IBinder token, @NonNull final byte[] hardwareAuthToken,
final int userId, final IFingerprintServiceReceiver receiver,
final String opPackageName, @FingerprintManager.EnrollReason int enrollReason) {
-
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for enroll");
return -1;
@@ -278,8 +222,7 @@ public class FingerprintService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_FINGERPRINT)
@Override // Binder call
public void cancelEnrollment(final IBinder token, long requestId) {
-
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for cancelEnrollment");
return;
@@ -339,10 +282,10 @@ public class FingerprintService extends SystemService {
final Pair<Integer, ServiceProvider> provider;
if (sensorId == FingerprintManager.SENSOR_ID_ANY) {
- provider = getSingleProvider();
+ provider = mRegistry.getSingleProvider();
} else {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
- provider = new Pair<>(sensorId, getProviderForSensor(sensorId));
+ provider = new Pair<>(sensorId, mRegistry.getProviderForSensor(sensorId));
}
if (provider == null) {
Slog.w(TAG, "Null provider for authenticate");
@@ -374,7 +317,6 @@ public class FingerprintService extends SystemService {
final IFingerprintServiceReceiver receiver,
final String opPackageName,
boolean ignoreEnrollmentState) throws PackageManager.NameNotFoundException {
-
final Context context = getUiContext();
final Context promptContext = context.createPackageContextAsUser(
opPackageName, 0 /* flags */, UserHandle.getUserHandleForUid(uId));
@@ -468,7 +410,7 @@ public class FingerprintService extends SystemService {
return -1;
}
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for detectFingerprint");
return -1;
@@ -484,8 +426,7 @@ public class FingerprintService extends SystemService {
public void prepareForAuthentication(int sensorId, IBinder token, long operationId,
int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName,
long requestId, int cookie, boolean allowBackgroundAuthentication) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for prepareForAuthentication");
return;
@@ -501,8 +442,7 @@ public class FingerprintService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_BIOMETRIC)
@Override // Binder call
public void startPreparedClient(int sensorId, int cookie) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for startPreparedClient");
return;
@@ -532,7 +472,7 @@ public class FingerprintService extends SystemService {
return;
}
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for cancelAuthentication");
return;
@@ -553,7 +493,7 @@ public class FingerprintService extends SystemService {
// For IBiometricsFingerprint2.1, cancelling fingerprint detect is the same as
// cancelling authentication.
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for cancelFingerprintDetect");
return;
@@ -566,11 +506,9 @@ public class FingerprintService extends SystemService {
@Override // Binder call
public void cancelAuthenticationFromService(final int sensorId, final IBinder token,
final String opPackageName, final long requestId) {
-
-
Slog.d(TAG, "cancelAuthenticationFromService, sensorId: " + sensorId);
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for cancelAuthenticationFromService");
return;
@@ -583,8 +521,7 @@ public class FingerprintService extends SystemService {
@Override // Binder call
public void remove(final IBinder token, final int fingerId, final int userId,
final IFingerprintServiceReceiver receiver, final String opPackageName) {
-
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for remove");
return;
@@ -617,7 +554,7 @@ public class FingerprintService extends SystemService {
// This effectively iterates through all sensors, but has to do so by finding all
// sensors under each provider.
- for (ServiceProvider provider : mServiceProviders) {
+ for (ServiceProvider provider : mRegistry.getProviders()) {
List<FingerprintSensorPropertiesInternal> props = provider.getSensorProperties();
for (FingerprintSensorPropertiesInternal prop : props) {
provider.scheduleRemoveAll(prop.sensorId, token, internalReceiver, userId,
@@ -652,7 +589,7 @@ public class FingerprintService extends SystemService {
try {
if (args.length > 1 && "--proto".equals(args[0]) && "--state".equals(args[1])) {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
- for (ServiceProvider provider : mServiceProviders) {
+ for (ServiceProvider provider : mRegistry.getProviders()) {
for (FingerprintSensorPropertiesInternal props
: provider.getSensorProperties()) {
provider.dumpProtoState(props.sensorId, proto, false);
@@ -660,14 +597,14 @@ public class FingerprintService extends SystemService {
}
proto.flush();
} else if (args.length > 0 && "--proto".equals(args[0])) {
- for (ServiceProvider provider : mServiceProviders) {
+ for (ServiceProvider provider : mRegistry.getProviders()) {
for (FingerprintSensorPropertiesInternal props
: provider.getSensorProperties()) {
provider.dumpProtoMetrics(props.sensorId, fd);
}
}
} else {
- for (ServiceProvider provider : mServiceProviders) {
+ for (ServiceProvider provider : mRegistry.getProviders()) {
for (FingerprintSensorPropertiesInternal props
: provider.getSensorProperties()) {
pw.println("Dumping for sensorId: " + props.sensorId
@@ -698,7 +635,7 @@ public class FingerprintService extends SystemService {
final long token = Binder.clearCallingIdentity();
try {
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for isHardwareDetectedDeprecated, caller: "
+ opPackageName);
@@ -713,8 +650,7 @@ public class FingerprintService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override // Binder call
public boolean isHardwareDetected(int sensorId, String opPackageName) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for isHardwareDetected, caller: " + opPackageName);
return false;
@@ -730,7 +666,7 @@ public class FingerprintService extends SystemService {
return;
}
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for rename");
return;
@@ -781,8 +717,7 @@ public class FingerprintService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
public boolean hasEnrolledFingerprints(int sensorId, int userId, String opPackageName) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for hasEnrolledFingerprints, caller: " + opPackageName);
return false;
@@ -794,8 +729,7 @@ public class FingerprintService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override // Binder call
public @LockoutTracker.LockoutMode int getLockoutModeForUser(int sensorId, int userId) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for getLockoutModeForUser");
return LockoutTracker.LOCKOUT_NONE;
@@ -807,8 +741,7 @@ public class FingerprintService extends SystemService {
@Override
public void invalidateAuthenticatorId(int sensorId, int userId,
IInvalidationCallback callback) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for invalidateAuthenticatorId");
return;
@@ -819,8 +752,7 @@ public class FingerprintService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override // Binder call
public long getAuthenticatorId(int sensorId, int userId) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for getAuthenticatorId");
return 0;
@@ -832,8 +764,7 @@ public class FingerprintService extends SystemService {
@Override // Binder call
public void resetLockout(IBinder token, int sensorId, int userId,
@Nullable byte[] hardwareAuthToken, String opPackageName) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for resetLockout, caller: " + opPackageName);
return;
@@ -864,55 +795,38 @@ public class FingerprintService extends SystemService {
@Override // Binder call
public void registerAuthenticators(
@NonNull List<FingerprintSensorPropertiesInternal> hidlSensors) {
-
- // Some HAL might not be started before the system service and will cause the code below
- // to wait, and some of the operations below might take a significant amount of time to
- // complete (calls to the HALs). To avoid blocking the rest of system server we put
- // this on a background thread.
- final ServiceThread thread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND,
- true /* allowIo */);
- thread.start();
- final Handler handler = new Handler(thread.getLooper());
- handler.post(() -> {
+ mRegistry.registerAll(() -> {
+ final List<ServiceProvider> providers = new ArrayList<>();
+ providers.addAll(getHidlProviders(hidlSensors));
List<String> aidlSensors = new ArrayList<>();
- final String[] instances =
- ServiceManager.getDeclaredInstances(IFingerprint.DESCRIPTOR);
+ final String[] instances = mAidlInstanceNameSupplier.get();
if (instances != null) {
aidlSensors.addAll(Lists.newArrayList(instances));
}
- registerAuthenticatorsForService(aidlSensors, hidlSensors);
+ providers.addAll(getAidlProviders(
+ Utils.filterAvailableHalInstances(getContext(), aidlSensors)));
+ return providers;
});
- thread.quitSafely();
}
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public void addAuthenticatorsRegisteredCallback(
IFingerprintAuthenticatorsRegisteredCallback callback) {
- if (callback == null) {
- Slog.e(TAG, "addAuthenticatorsRegisteredCallback, callback is null");
- return;
- }
+ mRegistry.addAllRegisteredCallback(callback);
+ }
- final boolean registered;
- final boolean hasSensorProps;
- synchronized (mLock) {
- registered = mAuthenticatorsRegisteredCallbacks.register(callback);
- hasSensorProps = !mSensorProps.isEmpty();
- }
- if (registered && hasSensorProps) {
- broadcastAllAuthenticatorsRegistered();
- } else if (!registered) {
- Slog.e(TAG, "addAuthenticatorsRegisteredCallback failed to register callback");
- }
+ @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
+ @Override
+ public void registerBiometricStateListener(@NonNull IBiometricStateListener listener) {
+ mBiometricStateCallback.registerBiometricStateListener(listener);
}
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public void onPointerDown(long requestId, int sensorId, int x, int y,
float minor, float major) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "No matching provider for onFingerDown, sensorId: " + sensorId);
return;
@@ -923,8 +837,7 @@ public class FingerprintService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public void onPointerUp(long requestId, int sensorId) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "No matching provider for onFingerUp, sensorId: " + sensorId);
return;
@@ -935,8 +848,7 @@ public class FingerprintService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public void onUiReady(long requestId, int sensorId) {
-
- final ServiceProvider provider = getProviderForSensor(sensorId);
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "No matching provider for onUiReady, sensorId: " + sensorId);
return;
@@ -947,8 +859,7 @@ public class FingerprintService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller) {
-
- for (ServiceProvider provider : mServiceProviders) {
+ for (ServiceProvider provider : mRegistry.getProviders()) {
provider.setUdfpsOverlayController(controller);
}
}
@@ -956,22 +867,15 @@ public class FingerprintService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public void setSidefpsController(@NonNull ISidefpsController controller) {
-
- for (ServiceProvider provider : mServiceProviders) {
+ for (ServiceProvider provider : mRegistry.getProviders()) {
provider.setSidefpsController(controller);
}
}
- @Override
- public void registerBiometricStateListener(@NonNull IBiometricStateListener listener) {
- Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
- FingerprintService.this.registerBiometricStateListener(listener);
- }
-
+ @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public void onPowerPressed() {
- Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
- for (ServiceProvider provider : mServiceProviders) {
+ for (ServiceProvider provider : mRegistry.getProviders()) {
provider.onPowerPressed();
}
}
@@ -981,6 +885,7 @@ public class FingerprintService extends SystemService {
this(context, BiometricContext.getInstance(context),
() -> IBiometricService.Stub.asInterface(
ServiceManager.getService(Context.BIOMETRIC_SERVICE)),
+ () -> ServiceManager.getDeclaredInstances(IFingerprint.DESCRIPTOR),
(fqName) -> IFingerprint.Stub.asInterface(
Binder.allowBlocking(ServiceManager.waitForDeclaredService(fqName))));
}
@@ -988,61 +893,34 @@ public class FingerprintService extends SystemService {
@VisibleForTesting
FingerprintService(Context context,
BiometricContext biometricContext,
- Supplier<IBiometricService> biometricServiceProvider,
+ Supplier<IBiometricService> biometricServiceSupplier,
+ Supplier<String[]> aidlInstanceNameSupplier,
Function<String, IFingerprint> fingerprintProvider) {
super(context);
mBiometricContext = biometricContext;
- mBiometricServiceSupplier = biometricServiceProvider;
+ mAidlInstanceNameSupplier = aidlInstanceNameSupplier;
mIFingerprintProvider = fingerprintProvider;
mAppOps = context.getSystemService(AppOpsManager.class);
mGestureAvailabilityDispatcher = new GestureAvailabilityDispatcher();
mLockoutResetDispatcher = new LockoutResetDispatcher(context);
mLockPatternUtils = new LockPatternUtils(context);
- mServiceProviders = new ArrayList<>();
- mBiometricStateCallback = new BiometricStateCallback();
- mAuthenticatorsRegisteredCallbacks = new RemoteCallbackList<>();
- mSensorProps = new ArrayList<>();
+ mBiometricStateCallback = new BiometricStateCallback<>(UserManager.get(context));
mHandler = new Handler(Looper.getMainLooper());
+ mRegistry = new FingerprintServiceRegistry(mServiceWrapper, biometricServiceSupplier);
+ mRegistry.addAllRegisteredCallback(new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
+ @Override
+ public void onAllAuthenticatorsRegistered(
+ List<FingerprintSensorPropertiesInternal> sensors) {
+ mBiometricStateCallback.start(mRegistry.getProviders());
+ }
+ });
}
- @VisibleForTesting
- void registerAuthenticatorsForService(@NonNull List<String> aidlInstanceNames,
+ @NonNull
+ private List<ServiceProvider> getHidlProviders(
@NonNull List<FingerprintSensorPropertiesInternal> hidlSensors) {
- addHidlProviders(hidlSensors);
- addAidlProviders(Utils.filterAvailableHalInstances(getContext(), aidlInstanceNames));
-
- final IBiometricService biometricService = mBiometricServiceSupplier.get();
-
- // Register each sensor individually with BiometricService
- for (ServiceProvider provider : mServiceProviders) {
- final List<FingerprintSensorPropertiesInternal> props =
- provider.getSensorProperties();
- for (FingerprintSensorPropertiesInternal prop : props) {
- final int sensorId = prop.sensorId;
- @BiometricManager.Authenticators.Types final int strength =
- Utils.propertyStrengthToAuthenticatorStrength(prop.sensorStrength);
- final FingerprintAuthenticator authenticator = new FingerprintAuthenticator(
- mServiceWrapper, sensorId);
- try {
- biometricService.registerAuthenticator(sensorId, TYPE_FINGERPRINT,
- strength, authenticator);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when registering sensorId: " + sensorId);
- }
- }
- }
-
- synchronized (mLock) {
- for (ServiceProvider provider : mServiceProviders) {
- mSensorProps.addAll(provider.getSensorProperties());
- }
- }
+ final List<ServiceProvider> providers = new ArrayList<>();
- broadcastCurrentEnrollmentState(null); // broadcasts to all listeners
- broadcastAllAuthenticatorsRegistered();
- }
-
- private void addHidlProviders(List<FingerprintSensorPropertiesInternal> hidlSensors) {
for (FingerprintSensorPropertiesInternal hidlSensor : hidlSensors) {
final Fingerprint21 fingerprint21;
if ((Build.IS_USERDEBUG || Build.IS_ENG)
@@ -1059,11 +937,16 @@ public class FingerprintService extends SystemService {
mBiometricStateCallback, hidlSensor, mHandler,
mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
}
- mServiceProviders.add(fingerprint21);
+ providers.add(fingerprint21);
}
+
+ return providers;
}
- private void addAidlProviders(List<String> instances) {
+ @NonNull
+ private List<ServiceProvider> getAidlProviders(@NonNull List<String> instances) {
+ final List<ServiceProvider> providers = new ArrayList<>();
+
for (String instance : instances) {
final String fqName = IFingerprint.DESCRIPTOR + "/" + instance;
final IFingerprint fp = mIFingerprintProvider.apply(fqName);
@@ -1075,7 +958,7 @@ public class FingerprintService extends SystemService {
mLockoutResetDispatcher, mGestureAvailabilityDispatcher,
mBiometricContext);
Slog.i(TAG, "Adding AIDL provider: " + fqName);
- mServiceProviders.add(provider);
+ providers.add(provider);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception in getSensorProps: " + fqName);
}
@@ -1083,38 +966,8 @@ public class FingerprintService extends SystemService {
Slog.e(TAG, "Unable to get declared service: " + fqName);
}
}
- }
- // Notifies the callbacks that all of the authenticators have been registered and removes the
- // invoked callbacks from the callback list.
- private void broadcastAllAuthenticatorsRegistered() {
- // Make a local copy of the data so it can be used outside of the synchronized block when
- // making Binder calls.
- final List<IFingerprintAuthenticatorsRegisteredCallback> callbacks = new ArrayList<>();
- final List<FingerprintSensorPropertiesInternal> props;
- synchronized (mLock) {
- if (!mSensorProps.isEmpty()) {
- props = new ArrayList<>(mSensorProps);
- } else {
- Slog.e(TAG, "mSensorProps is empty");
- return;
- }
- final int n = mAuthenticatorsRegisteredCallbacks.beginBroadcast();
- for (int i = 0; i < n; ++i) {
- final IFingerprintAuthenticatorsRegisteredCallback cb =
- mAuthenticatorsRegisteredCallbacks.getBroadcastItem(i);
- callbacks.add(cb);
- mAuthenticatorsRegisteredCallbacks.unregister(cb);
- }
- mAuthenticatorsRegisteredCallbacks.finishBroadcast();
- }
- for (IFingerprintAuthenticatorsRegisteredCallback cb : callbacks) {
- try {
- cb.onAllAuthenticatorsRegistered(props);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception in onAllAuthenticatorsRegistered", e);
- }
- }
+ return providers;
}
@Override
@@ -1122,51 +975,9 @@ public class FingerprintService extends SystemService {
publishBinderService(Context.FINGERPRINT_SERVICE, mServiceWrapper);
}
- @Nullable
- private ServiceProvider getProviderForSensor(int sensorId) {
- for (ServiceProvider provider : mServiceProviders) {
- if (provider.containsSensor(sensorId)) {
- return provider;
- }
- }
- return null;
- }
-
- /**
- * For devices with only a single provider, returns that provider. If multiple providers,
- * returns the first one. If no providers, returns null.
- */
- @Nullable
- private Pair<Integer, ServiceProvider> getSingleProvider() {
- final List<FingerprintSensorPropertiesInternal> properties = getSensorProperties();
- if (properties.isEmpty()) {
- Slog.e(TAG, "No providers found");
- return null;
- }
-
- // Theoretically we can just return the first provider, but maybe this is easier to
- // understand.
- final int sensorId = properties.get(0).sensorId;
- for (ServiceProvider provider : mServiceProviders) {
- if (provider.containsSensor(sensorId)) {
- return new Pair<>(sensorId, provider);
- }
- }
-
- Slog.e(TAG, "Provider not found");
- return null;
- }
-
- @NonNull
- private List<FingerprintSensorPropertiesInternal> getSensorProperties() {
- synchronized (mLock) {
- return mSensorProps;
- }
- }
-
@NonNull
private List<Fingerprint> getEnrolledFingerprintsDeprecated(int userId, String opPackageName) {
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for getEnrolledFingerprintsDeprecated, caller: "
+ opPackageName);
@@ -1229,7 +1040,7 @@ public class FingerprintService extends SystemService {
if (Utils.isVirtualEnabled(getContext())) {
Slog.i(TAG, "Sync virtual enrollments");
final int userId = ActivityManager.getCurrentUser();
- for (ServiceProvider provider : mServiceProviders) {
+ for (ServiceProvider provider : mRegistry.getProviders()) {
for (FingerprintSensorPropertiesInternal props : provider.getSensorProperties()) {
provider.scheduleInternalCleanup(props.sensorId, userId, null /* callback */,
true /* favorHalEnrollments */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistry.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistry.java
new file mode 100644
index 000000000000..33810b764f23
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistry.java
@@ -0,0 +1,72 @@
+/*
+ * 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.biometrics.sensors.fingerprint;
+
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.IBiometricService;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
+import android.hardware.fingerprint.IFingerprintService;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.sensors.BiometricServiceRegistry;
+
+import java.util.List;
+import java.util.function.Supplier;
+
+/** Registry for {@link IFingerprintService} providers. */
+public class FingerprintServiceRegistry extends BiometricServiceRegistry<ServiceProvider,
+ FingerprintSensorPropertiesInternal, IFingerprintAuthenticatorsRegisteredCallback> {
+
+ private static final String TAG = "FingerprintServiceRegistry";
+
+ @NonNull
+ private final IFingerprintService mService;
+
+ /** Creates a new registry tied to the given service. */
+ public FingerprintServiceRegistry(@NonNull IFingerprintService service,
+ @Nullable Supplier<IBiometricService> biometricSupplier) {
+ super(biometricSupplier);
+ mService = service;
+ }
+
+ @Override
+ protected void registerService(@NonNull IBiometricService service,
+ @NonNull FingerprintSensorPropertiesInternal props) {
+ @BiometricManager.Authenticators.Types final int strength =
+ Utils.propertyStrengthToAuthenticatorStrength(props.sensorStrength);
+ try {
+ service.registerAuthenticator(props.sensorId, TYPE_FINGERPRINT, strength,
+ new FingerprintAuthenticator(mService, props.sensorId));
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when registering sensorId: " + props.sensorId);
+ }
+ }
+
+ @Override
+ protected void invokeRegisteredCallback(
+ @NonNull IFingerprintAuthenticatorsRegisteredCallback callback,
+ @NonNull List<FingerprintSensorPropertiesInternal> allProps) throws RemoteException {
+ callback.onAllAuthenticatorsRegistered(allProps);
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index 275d7e445a75..9075e7ec2080 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -28,14 +28,11 @@ import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.hardware.fingerprint.ISidefpsController;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.IBinder;
-import android.util.proto.ProtoOutputStream;
+import com.android.server.biometrics.sensors.BiometricServiceProvider;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
-import com.android.server.biometrics.sensors.LockoutTracker;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
import java.util.List;
/**
@@ -59,23 +56,8 @@ import java.util.List;
* fail safely.
*/
@SuppressWarnings("deprecation")
-public interface ServiceProvider {
- /**
- * Checks if the specified sensor is owned by this provider.
- */
- boolean containsSensor(int sensorId);
-
- @NonNull
- List<FingerprintSensorPropertiesInternal> getSensorProperties();
-
- /**
- * Returns the internal properties of the specified sensor, if owned by this provider.
- *
- * @param sensorId The ID of a fingerprint sensor, or -1 for any sensor.
- * @return An object representing the internal properties of the specified sensor.
- */
- @Nullable
- FingerprintSensorPropertiesInternal getSensorProperties(int sensorId);
+public interface ServiceProvider extends
+ BiometricServiceProvider<FingerprintSensorPropertiesInternal> {
void scheduleResetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken);
@@ -126,16 +108,11 @@ public interface ServiceProvider {
void scheduleInternalCleanup(int sensorId, int userId,
@Nullable ClientMonitorCallback callback, boolean favorHalEnrollments);
- boolean isHardwareDetected(int sensorId);
-
void rename(int sensorId, int fingerId, int userId, @NonNull String name);
@NonNull
List<Fingerprint> getEnrolledFingerprints(int sensorId, int userId);
- @LockoutTracker.LockoutMode
- int getLockoutModeForUser(int sensorId, int userId);
-
/**
* Requests for the authenticatorId (whose source of truth is in the TEE or equivalent) to
* be invalidated. See {@link com.android.server.biometrics.sensors.InvalidationRequesterClient}
@@ -143,7 +120,6 @@ public interface ServiceProvider {
void scheduleInvalidateAuthenticatorId(int sensorId, int userId,
@NonNull IInvalidationCallback callback);
- long getAuthenticatorId(int sensorId, int userId);
void onPointerDown(long requestId, int sensorId, int x, int y, float minor, float major);
@@ -161,13 +137,6 @@ public interface ServiceProvider {
*/
void setSidefpsController(@NonNull ISidefpsController controller);
- void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto,
- boolean clearSchedulerBuffer);
-
- void dumpProtoMetrics(int sensorId, @NonNull FileDescriptor fd);
-
- void dumpInternal(int sensorId, @NonNull PrintWriter pw);
-
@NonNull
ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback,
@NonNull String opPackageName);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 2dc005206b42..3fe6332fcaa0 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -565,6 +565,11 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
}
@Override
+ public boolean hasEnrollments(int sensorId, int userId) {
+ return !getEnrolledFingerprints(sensorId, userId).isEmpty();
+ }
+
+ @Override
public void scheduleInvalidateAuthenticatorId(int sensorId, int userId,
@NonNull IInvalidationCallback callback) {
mHandler.post(() -> {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index ed482f013869..0e6df8e0df77 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -789,6 +789,11 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
}
@Override
+ public boolean hasEnrollments(int sensorId, int userId) {
+ return !getEnrolledFingerprints(sensorId, userId).isEmpty();
+ }
+
+ @Override
@LockoutTracker.LockoutMode public int getLockoutModeForUser(int sensorId, int userId) {
return mLockoutTracker.getLockoutModeForUser(userId);
}
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index a817cea1a674..05d32d1d9023 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -148,6 +148,12 @@ class AutomaticBrightnessController {
// The currently accepted nominal ambient light level.
private float mAmbientLux;
+ // The last calculated ambient light level (long time window).
+ private float mSlowAmbientLux;
+
+ // The last calculated ambient light level (short time window).
+ private float mFastAmbientLux;
+
// The last ambient lux value prior to passing the darkening or brightening threshold.
private float mPreThresholdLux;
@@ -440,6 +446,14 @@ class AutomaticBrightnessController {
return mAmbientLux;
}
+ float getSlowAmbientLux() {
+ return mSlowAmbientLux;
+ }
+
+ float getFastAmbientLux() {
+ return mFastAmbientLux;
+ }
+
private boolean setDisplayPolicy(int policy) {
if (mDisplayPolicy == policy) {
return false;
@@ -812,20 +826,20 @@ class AutomaticBrightnessController {
// proposed ambient light value since the slow value might be sufficiently far enough away
// from the fast value to cause a recalculation while its actually just converging on
// the fast value still.
- float slowAmbientLux = calculateAmbientLux(time, mAmbientLightHorizonLong);
- float fastAmbientLux = calculateAmbientLux(time, mAmbientLightHorizonShort);
+ mSlowAmbientLux = calculateAmbientLux(time, mAmbientLightHorizonLong);
+ mFastAmbientLux = calculateAmbientLux(time, mAmbientLightHorizonShort);
- if ((slowAmbientLux >= mAmbientBrighteningThreshold
- && fastAmbientLux >= mAmbientBrighteningThreshold
+ if ((mSlowAmbientLux >= mAmbientBrighteningThreshold
+ && mFastAmbientLux >= mAmbientBrighteningThreshold
&& nextBrightenTransition <= time)
- || (slowAmbientLux <= mAmbientDarkeningThreshold
- && fastAmbientLux <= mAmbientDarkeningThreshold
+ || (mSlowAmbientLux <= mAmbientDarkeningThreshold
+ && mFastAmbientLux <= mAmbientDarkeningThreshold
&& nextDarkenTransition <= time)) {
mPreThresholdLux = mAmbientLux;
- setAmbientLux(fastAmbientLux);
+ setAmbientLux(mFastAmbientLux);
if (mLoggingEnabled) {
Slog.d(TAG, "updateAmbientLux: "
- + ((fastAmbientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": "
+ + ((mFastAmbientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": "
+ "mBrighteningLuxThreshold=" + mAmbientBrighteningThreshold + ", "
+ "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", "
+ "mAmbientLux=" + mAmbientLux);
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 6f3a0c516a5b..8807e1970061 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1640,11 +1640,16 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// brightness cap, RBC state, etc.
mTempBrightnessEvent.setTime(System.currentTimeMillis());
mTempBrightnessEvent.setBrightness(brightnessState);
+ mTempBrightnessEvent.setPhysicalDisplayId(mUniqueDisplayId);
mTempBrightnessEvent.setReason(mBrightnessReason);
mTempBrightnessEvent.setHbmMax(mHbmController.getCurrentBrightnessMax());
mTempBrightnessEvent.setHbmMode(mHbmController.getHighBrightnessMode());
mTempBrightnessEvent.setFlags(mTempBrightnessEvent.getFlags()
| (mIsRbcActive ? BrightnessEvent.FLAG_RBC : 0));
+ mTempBrightnessEvent.setRbcStrength((mCdsi != null && mCdsi.isReduceBrightColorsActivated())
+ ? mCdsi.getReduceBrightColorsStrength() : -1);
+ mTempBrightnessEvent.setPowerFactor(
+ mPowerRequest.lowPowerMode ? mPowerRequest.screenLowPowerBrightnessFactor : 1.0f);
// Temporary is what we use during slider interactions. We avoid logging those so that
// we don't spam logcat when the slider is being used.
boolean tempToTempTransition =
@@ -1653,9 +1658,17 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
== BrightnessReason.REASON_TEMPORARY;
if ((!mTempBrightnessEvent.equalsMainData(mLastBrightnessEvent) && !tempToTempTransition)
|| brightnessAdjustmentFlags != 0) {
+ float lastBrightness = mLastBrightnessEvent.getBrightness();
+ mTempBrightnessEvent.setInitialBrightness(lastBrightness);
+ mTempBrightnessEvent.setFastAmbientLux(
+ mAutomaticBrightnessController == null
+ ? -1 : mAutomaticBrightnessController.getFastAmbientLux());
+ mTempBrightnessEvent.setSlowAmbientLux(
+ mAutomaticBrightnessController == null
+ ? -1 : mAutomaticBrightnessController.getSlowAmbientLux());
+ mTempBrightnessEvent.setAutomaticBrightnessEnabled(mPowerRequest.useAutoBrightness);
mLastBrightnessEvent.copyFrom(mTempBrightnessEvent);
BrightnessEvent newEvent = new BrightnessEvent(mTempBrightnessEvent);
-
// Adjustment flags (and user-set flag) only get added after the equality checks since
// they are transient.
newEvent.setAdjustmentFlags(brightnessAdjustmentFlags);
@@ -1663,6 +1676,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
? BrightnessEvent.FLAG_USER_SET : 0));
Slog.i(mTag, newEvent.toString(/* includeTime= */ false));
+ if (userSetBrightnessChanged) {
+ logManualBrightnessEvent(newEvent);
+ }
if (mBrightnessEventRingBuffer != null) {
mBrightnessEventRingBuffer.append(newEvent);
}
@@ -2752,6 +2768,30 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
}
+ private void logManualBrightnessEvent(BrightnessEvent event) {
+ float hbmMaxNits =
+ event.getHbmMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF
+ ? -1f : convertToNits(event.getHbmMax());
+
+ // thermalCapNits set to -1 if not currently capping max brightness
+ float thermalCapNits =
+ event.getThermalMax() == PowerManager.BRIGHTNESS_MAX
+ ? -1f : convertToNits(event.getThermalMax());
+
+ FrameworkStatsLog.write(FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED,
+ convertToNits(event.getInitialBrightness()),
+ convertToNits(event.getBrightness()),
+ event.getSlowAmbientLux(),
+ event.getPhysicalDisplayId(),
+ event.isShortTermModelActive(),
+ event.getPowerFactor(),
+ event.getRbcStrength(),
+ hbmMaxNits,
+ thermalCapNits,
+ event.isAutomaticBrightnessEnabled(),
+ FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__REASON__REASON_MANUAL);
+ }
+
private final class DisplayControllerHandler extends Handler {
DisplayControllerHandler(Looper looper) {
super(looper, null, true /*async*/);
diff --git a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
index d831dbd4bb23..70415a3c81c9 100644
--- a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
+++ b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
@@ -33,17 +33,24 @@ public final class BrightnessEvent {
private BrightnessReason mReason = new BrightnessReason();
private int mDisplayId;
+ private String mPhysicalDisplayId;
+ private long mTime;
private float mLux;
+ private float mFastAmbientLux;
+ private float mSlowAmbientLux;
private float mPreThresholdLux;
- private long mTime;
+ private float mInitialBrightness;
private float mBrightness;
private float mRecommendedBrightness;
private float mPreThresholdBrightness;
+ private int mHbmMode;
private float mHbmMax;
+ private int mRbcStrength;
private float mThermalMax;
- private int mHbmMode;
+ private float mPowerFactor;
private int mFlags;
private int mAdjustmentFlags;
+ private boolean mAutomaticBrightnessEnabled;
public BrightnessEvent(BrightnessEvent that) {
copyFrom(that);
@@ -60,37 +67,59 @@ public final class BrightnessEvent {
* @param that BrightnessEvent which is to be copied
*/
public void copyFrom(BrightnessEvent that) {
+ mReason.set(that.getReason());
mDisplayId = that.getDisplayId();
+ mPhysicalDisplayId = that.getPhysicalDisplayId();
mTime = that.getTime();
+ // Lux values
mLux = that.getLux();
+ mFastAmbientLux = that.getFastAmbientLux();
+ mSlowAmbientLux = that.getSlowAmbientLux();
mPreThresholdLux = that.getPreThresholdLux();
+ // Brightness values
+ mInitialBrightness = that.getInitialBrightness();
mBrightness = that.getBrightness();
mRecommendedBrightness = that.getRecommendedBrightness();
mPreThresholdBrightness = that.getPreThresholdBrightness();
+ // Different brightness modulations
+ mHbmMode = that.getHbmMode();
mHbmMax = that.getHbmMax();
+ mRbcStrength = that.getRbcStrength();
mThermalMax = that.getThermalMax();
+ mPowerFactor = that.getPowerFactor();
mFlags = that.getFlags();
- mHbmMode = that.getHbmMode();
- mReason.set(that.getReason());
mAdjustmentFlags = that.getAdjustmentFlags();
+ // Auto-brightness setting
+ mAutomaticBrightnessEnabled = that.isAutomaticBrightnessEnabled();
}
/**
* A utility to reset the BrightnessEvent to default values
*/
public void reset() {
+ mReason.set(null);
mTime = SystemClock.uptimeMillis();
- mBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
- mRecommendedBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ mPhysicalDisplayId = "";
+ // Lux values
mLux = 0;
+ mFastAmbientLux = 0;
+ mSlowAmbientLux = 0;
mPreThresholdLux = 0;
+ // Brightness values
+ mInitialBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ mBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ mRecommendedBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
mPreThresholdBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ // Different brightness modulations
+ mHbmMode = BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
mHbmMax = PowerManager.BRIGHTNESS_MAX;
+ mRbcStrength = 0;
mThermalMax = PowerManager.BRIGHTNESS_MAX;
+ mPowerFactor = 1f;
mFlags = 0;
- mHbmMode = BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
- mReason.set(null);
mAdjustmentFlags = 0;
+ // Auto-brightness setting
+ mAutomaticBrightnessEnabled = true;
}
/**
@@ -104,23 +133,34 @@ public final class BrightnessEvent {
public boolean equalsMainData(BrightnessEvent that) {
// This equals comparison purposefully ignores time since it is regularly changing and
// we don't want to log a brightness event just because the time changed.
- return mDisplayId == that.mDisplayId
+ return mReason.equals(that.mReason)
+ && mDisplayId == that.mDisplayId
+ && mPhysicalDisplayId.equals(that.mPhysicalDisplayId)
+ && Float.floatToRawIntBits(mLux) == Float.floatToRawIntBits(that.mLux)
+ && Float.floatToRawIntBits(mFastAmbientLux)
+ == Float.floatToRawIntBits(that.mFastAmbientLux)
+ && Float.floatToRawIntBits(mSlowAmbientLux)
+ == Float.floatToRawIntBits(that.mSlowAmbientLux)
+ && Float.floatToRawIntBits(mPreThresholdLux)
+ == Float.floatToRawIntBits(that.mPreThresholdLux)
+ && Float.floatToRawIntBits(mInitialBrightness)
+ == Float.floatToRawIntBits(that.mInitialBrightness)
&& Float.floatToRawIntBits(mBrightness)
== Float.floatToRawIntBits(that.mBrightness)
&& Float.floatToRawIntBits(mRecommendedBrightness)
== Float.floatToRawIntBits(that.mRecommendedBrightness)
&& Float.floatToRawIntBits(mPreThresholdBrightness)
== Float.floatToRawIntBits(that.mPreThresholdBrightness)
- && Float.floatToRawIntBits(mLux) == Float.floatToRawIntBits(that.mLux)
- && Float.floatToRawIntBits(mPreThresholdLux)
- == Float.floatToRawIntBits(that.mPreThresholdLux)
- && Float.floatToRawIntBits(mHbmMax) == Float.floatToRawIntBits(that.mHbmMax)
&& mHbmMode == that.mHbmMode
+ && Float.floatToRawIntBits(mHbmMax) == Float.floatToRawIntBits(that.mHbmMax)
+ && mRbcStrength == that.mRbcStrength
&& Float.floatToRawIntBits(mThermalMax)
== Float.floatToRawIntBits(that.mThermalMax)
+ && Float.floatToRawIntBits(mPowerFactor)
+ == Float.floatToRawIntBits(that.mPowerFactor)
&& mFlags == that.mFlags
&& mAdjustmentFlags == that.mAdjustmentFlags
- && mReason.equals(that.mReason);
+ && mAutomaticBrightnessEnabled == that.mAutomaticBrightnessEnabled;
}
/**
@@ -133,16 +173,23 @@ public final class BrightnessEvent {
return (includeTime ? TimeUtils.formatForLogging(mTime) + " - " : "")
+ "BrightnessEvent: "
+ "disp=" + mDisplayId
+ + ", physDisp=" + mPhysicalDisplayId
+ ", brt=" + mBrightness + ((mFlags & FLAG_USER_SET) != 0 ? "(user_set)" : "")
+ + ", initBrt=" + mInitialBrightness
+ ", rcmdBrt=" + mRecommendedBrightness
+ ", preBrt=" + mPreThresholdBrightness
+ ", lux=" + mLux
+ + ", fastLux=" + mFastAmbientLux
+ + ", slowLux=" + mSlowAmbientLux
+ ", preLux=" + mPreThresholdLux
+ ", hbmMax=" + mHbmMax
+ ", hbmMode=" + BrightnessInfo.hbmToString(mHbmMode)
+ + ", rbcStrength=" + mRbcStrength
+ ", thrmMax=" + mThermalMax
+ + ", powerFactor=" + mPowerFactor
+ ", flags=" + flagsToString()
- + ", reason=" + mReason.toString(mAdjustmentFlags);
+ + ", reason=" + mReason.toString(mAdjustmentFlags)
+ + ", autoBrightness=" + mAutomaticBrightnessEnabled;
}
@Override
@@ -150,12 +197,20 @@ public final class BrightnessEvent {
return toString(/* includeTime */ true);
}
+ public BrightnessReason getReason() {
+ return mReason;
+ }
+
public void setReason(BrightnessReason reason) {
this.mReason = reason;
}
- public BrightnessReason getReason() {
- return mReason;
+ public long getTime() {
+ return mTime;
+ }
+
+ public void setTime(long time) {
+ this.mTime = time;
}
public int getDisplayId() {
@@ -166,6 +221,14 @@ public final class BrightnessEvent {
this.mDisplayId = displayId;
}
+ public String getPhysicalDisplayId() {
+ return mPhysicalDisplayId;
+ }
+
+ public void setPhysicalDisplayId(String mPhysicalDisplayId) {
+ this.mPhysicalDisplayId = mPhysicalDisplayId;
+ }
+
public float getLux() {
return mLux;
}
@@ -174,6 +237,22 @@ public final class BrightnessEvent {
this.mLux = lux;
}
+ public float getFastAmbientLux() {
+ return mFastAmbientLux;
+ }
+
+ public void setFastAmbientLux(float mFastAmbientLux) {
+ this.mFastAmbientLux = mFastAmbientLux;
+ }
+
+ public float getSlowAmbientLux() {
+ return mSlowAmbientLux;
+ }
+
+ public void setSlowAmbientLux(float mSlowAmbientLux) {
+ this.mSlowAmbientLux = mSlowAmbientLux;
+ }
+
public float getPreThresholdLux() {
return mPreThresholdLux;
}
@@ -182,12 +261,12 @@ public final class BrightnessEvent {
this.mPreThresholdLux = preThresholdLux;
}
- public long getTime() {
- return mTime;
+ public float getInitialBrightness() {
+ return mInitialBrightness;
}
- public void setTime(long time) {
- this.mTime = time;
+ public void setInitialBrightness(float mInitialBrightness) {
+ this.mInitialBrightness = mInitialBrightness;
}
public float getBrightness() {
@@ -214,6 +293,14 @@ public final class BrightnessEvent {
this.mPreThresholdBrightness = preThresholdBrightness;
}
+ public int getHbmMode() {
+ return mHbmMode;
+ }
+
+ public void setHbmMode(int hbmMode) {
+ this.mHbmMode = hbmMode;
+ }
+
public float getHbmMax() {
return mHbmMax;
}
@@ -222,6 +309,14 @@ public final class BrightnessEvent {
this.mHbmMax = hbmMax;
}
+ public int getRbcStrength() {
+ return mRbcStrength;
+ }
+
+ public void setRbcStrength(int mRbcStrength) {
+ this.mRbcStrength = mRbcStrength;
+ }
+
public float getThermalMax() {
return mThermalMax;
}
@@ -230,12 +325,12 @@ public final class BrightnessEvent {
this.mThermalMax = thermalMax;
}
- public int getHbmMode() {
- return mHbmMode;
+ public float getPowerFactor() {
+ return mPowerFactor;
}
- public void setHbmMode(int hbmMode) {
- this.mHbmMode = hbmMode;
+ public void setPowerFactor(float mPowerFactor) {
+ this.mPowerFactor = mPowerFactor;
}
public int getFlags() {
@@ -246,6 +341,10 @@ public final class BrightnessEvent {
this.mFlags = flags;
}
+ public boolean isShortTermModelActive() {
+ return (mFlags & FLAG_USER_SET) != 0;
+ }
+
public int getAdjustmentFlags() {
return mAdjustmentFlags;
}
@@ -254,6 +353,14 @@ public final class BrightnessEvent {
this.mAdjustmentFlags = adjustmentFlags;
}
+ public boolean isAutomaticBrightnessEnabled() {
+ return mAutomaticBrightnessEnabled;
+ }
+
+ public void setAutomaticBrightnessEnabled(boolean mAutomaticBrightnessEnabled) {
+ this.mAutomaticBrightnessEnabled = mAutomaticBrightnessEnabled;
+ }
+
private String flagsToString() {
return ((mFlags & FLAG_USER_SET) != 0 ? "user_set " : "")
+ ((mFlags & FLAG_RBC) != 0 ? "rbc " : "")
diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java
index 6e304163995b..366dfd1efd29 100644
--- a/services/core/java/com/android/server/display/color/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java
@@ -1515,6 +1515,10 @@ public final class ColorDisplayService extends SystemService {
return mReduceBrightColorsTintController.isActivated();
}
+ public int getReduceBrightColorsStrength() {
+ return mReduceBrightColorsTintController.getStrength();
+ }
+
/**
* Gets the computed brightness, in nits, when the reduce bright colors feature is applied
* at the current strength.
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index d48acb18722a..23f437392d2b 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -910,11 +910,14 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
final String mImeControlTargetName;
@Nullable
final String mImeTargetNameFromWm;
+ @Nullable
+ final String mImeSurfaceParentName;
Entry(ClientState client, EditorInfo editorInfo, String focusedWindowName,
@SoftInputModeFlags int softInputMode, @SoftInputShowHideReason int reason,
boolean inFullscreenMode, String requestWindowName,
- @Nullable String imeControlTargetName, @Nullable String imeTargetName) {
+ @Nullable String imeControlTargetName, @Nullable String imeTargetName,
+ @Nullable String imeSurfaceParentName) {
mClientState = client;
mEditorInfo = editorInfo;
mFocusedWindowName = focusedWindowName;
@@ -926,6 +929,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
mRequestWindowName = requestWindowName;
mImeControlTargetName = imeControlTargetName;
mImeTargetNameFromWm = imeTargetName;
+ mImeSurfaceParentName = imeSurfaceParentName;
}
}
@@ -972,6 +976,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
pw.println(" imeTargetNameFromWm=" + entry.mImeTargetNameFromWm);
pw.print(prefix);
+ pw.println(" imeSurfaceParentName=" + entry.mImeSurfaceParentName);
+
+ pw.print(prefix);
pw.print(" editorInfo: ");
pw.print(" inputType=" + entry.mEditorInfo.inputType);
pw.print(" privateImeOptions=" + entry.mEditorInfo.privateImeOptions);
@@ -4676,7 +4683,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
mSoftInputShowHideHistory.addEntry(new SoftInputShowHideHistory.Entry(
mCurFocusedWindowClient, mCurEditorInfo, info.focusedWindowName,
mCurFocusedWindowSoftInputMode, reason, mInFullscreenMode,
- info.requestWindowName, info.imeControlTargetName, info.imeLayerTargetName));
+ info.requestWindowName, info.imeControlTargetName, info.imeLayerTargetName,
+ info.imeSurfaceParentName));
}
@BinderThread
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index e27cbeaab139..bfa8af957208 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -927,8 +927,9 @@ class MediaRouter2ServiceImpl {
routerRecord.mUserRecord.mHandler, routerRecord, manager));
}
- userRecord.mHandler.sendMessage(obtainMessage(UserHandler::notifyRoutesToManager,
- userRecord.mHandler, manager));
+ userRecord.mHandler.sendMessage(
+ obtainMessage(
+ UserHandler::notifyInitialRoutesToManager, userRecord.mHandler, manager));
}
private void unregisterManagerLocked(@NonNull IMediaRouter2Manager manager, boolean died) {
@@ -1311,6 +1312,36 @@ class MediaRouter2ServiceImpl {
new CopyOnWriteArrayList<>();
private final Map<String, RouterRecord> mSessionToRouterMap = new ArrayMap<>();
+ /**
+ * Latest list of routes sent to privileged {@link android.media.MediaRouter2 routers} and
+ * {@link android.media.MediaRouter2Manager managers}.
+ *
+ * <p>Privileged routers are instances of {@link android.media.MediaRouter2 MediaRouter2}
+ * that have {@code MODIFY_AUDIO_ROUTING} permission.
+ *
+ * <p>This list contains all routes exposed by route providers. This includes routes from
+ * both system route providers and user route providers.
+ *
+ * <p>See {@link #getRouters(boolean hasModifyAudioRoutingPermission)}.
+ */
+ private final Map<String, MediaRoute2Info> mLastNotifiedRoutesToPrivilegedRouters =
+ new ArrayMap<>();
+
+ /**
+ * Latest list of routes sent to non-privileged {@link android.media.MediaRouter2 routers}.
+ *
+ * <p>Non-privileged routers are instances of {@link android.media.MediaRouter2
+ * MediaRouter2} that do <i><b>not</b></i> have {@code MODIFY_AUDIO_ROUTING} permission.
+ *
+ * <p>This list contains all routes exposed by user route providers. It might also include
+ * the current default route from {@link #mSystemProvider} to expose local route updates
+ * (e.g. volume changes) to non-privileged routers.
+ *
+ * <p>See {@link SystemMediaRoute2Provider#mDefaultRoute}.
+ */
+ private final Map<String, MediaRoute2Info> mLastNotifiedRoutesToNonPrivilegedRouters =
+ new ArrayMap<>();
+
private boolean mRunning;
// TODO: (In Android S+) Pull out SystemMediaRoute2Provider out of UserHandler.
@@ -1425,91 +1456,182 @@ class MediaRouter2ServiceImpl {
}
private void onProviderStateChangedOnHandler(@NonNull MediaRoute2Provider provider) {
- int providerInfoIndex = getLastProviderInfoIndex(provider.getUniqueId());
MediaRoute2ProviderInfo currentInfo = provider.getProviderInfo();
+
+ int providerInfoIndex =
+ indexOfRouteProviderInfoByUniqueId(provider.getUniqueId(), mLastProviderInfos);
+
MediaRoute2ProviderInfo prevInfo =
- (providerInfoIndex < 0) ? null : mLastProviderInfos.get(providerInfoIndex);
- if (Objects.equals(prevInfo, currentInfo)) return;
+ providerInfoIndex == -1 ? null : mLastProviderInfos.get(providerInfoIndex);
+
+ // Ignore if no changes
+ if (Objects.equals(prevInfo, currentInfo)) {
+ return;
+ }
+
+ boolean hasAddedOrModifiedRoutes = false;
+ boolean hasRemovedRoutes = false;
+
+ boolean isSystemProvider = provider.mIsSystemRouteProvider;
- List<MediaRoute2Info> addedRoutes = new ArrayList<>();
- List<MediaRoute2Info> removedRoutes = new ArrayList<>();
- List<MediaRoute2Info> changedRoutes = new ArrayList<>();
if (prevInfo == null) {
+ // Provider is being added.
mLastProviderInfos.add(currentInfo);
- addedRoutes.addAll(currentInfo.getRoutes());
+ addToRoutesMap(currentInfo.getRoutes(), isSystemProvider);
+ // Check if new provider exposes routes.
+ hasAddedOrModifiedRoutes = !currentInfo.getRoutes().isEmpty();
} else if (currentInfo == null) {
+ // Provider is being removed.
+ hasRemovedRoutes = true;
mLastProviderInfos.remove(prevInfo);
- removedRoutes.addAll(prevInfo.getRoutes());
+ removeFromRoutesMap(prevInfo.getRoutes(), isSystemProvider);
} else {
+ // Provider is being updated.
mLastProviderInfos.set(providerInfoIndex, currentInfo);
- final Collection<MediaRoute2Info> prevRoutes = prevInfo.getRoutes();
final Collection<MediaRoute2Info> currentRoutes = currentInfo.getRoutes();
+ // Checking for individual routes.
for (MediaRoute2Info route : currentRoutes) {
if (!route.isValid()) {
- Slog.w(TAG, "onProviderStateChangedOnHandler: Ignoring invalid route : "
- + route);
+ Slog.w(
+ TAG,
+ "onProviderStateChangedOnHandler: Ignoring invalid route : "
+ + route);
continue;
}
+
MediaRoute2Info prevRoute = prevInfo.getRoute(route.getOriginalId());
- if (prevRoute == null) {
- addedRoutes.add(route);
- } else if (!Objects.equals(prevRoute, route)) {
- changedRoutes.add(route);
+ if (prevRoute == null || !Objects.equals(prevRoute, route)) {
+ hasAddedOrModifiedRoutes = true;
+ mLastNotifiedRoutesToPrivilegedRouters.put(route.getId(), route);
+ if (!isSystemProvider) {
+ mLastNotifiedRoutesToNonPrivilegedRouters.put(route.getId(), route);
+ }
}
}
+ // Checking for individual removals
for (MediaRoute2Info prevRoute : prevInfo.getRoutes()) {
if (currentInfo.getRoute(prevRoute.getOriginalId()) == null) {
- removedRoutes.add(prevRoute);
+ hasRemovedRoutes = true;
+ mLastNotifiedRoutesToPrivilegedRouters.remove(prevRoute.getId());
+ if (!isSystemProvider) {
+ mLastNotifiedRoutesToNonPrivilegedRouters.remove(prevRoute.getId());
+ }
}
}
}
+ dispatchUpdates(
+ hasAddedOrModifiedRoutes,
+ hasRemovedRoutes,
+ isSystemProvider,
+ mSystemProvider.getDefaultRoute());
+ }
+
+ /**
+ * Adds provided routes to {@link #mLastNotifiedRoutesToPrivilegedRouters}. Also adds them
+ * to {@link #mLastNotifiedRoutesToNonPrivilegedRouters} if they were provided by a
+ * non-system route provider. Overwrites any route with matching id that already exists.
+ *
+ * @param routes list of routes to be added.
+ * @param isSystemRoutes indicates whether routes come from a system route provider.
+ */
+ private void addToRoutesMap(
+ @NonNull Collection<MediaRoute2Info> routes, boolean isSystemRoutes) {
+ for (MediaRoute2Info route : routes) {
+ if (!isSystemRoutes) {
+ mLastNotifiedRoutesToNonPrivilegedRouters.put(route.getId(), route);
+ }
+ mLastNotifiedRoutesToPrivilegedRouters.put(route.getId(), route);
+ }
+ }
+
+ /**
+ * Removes provided routes from {@link #mLastNotifiedRoutesToPrivilegedRouters}. Also
+ * removes them from {@link #mLastNotifiedRoutesToNonPrivilegedRouters} if they were
+ * provided by a non-system route provider.
+ *
+ * @param routes list of routes to be removed.
+ * @param isSystemRoutes whether routes come from a system route provider.
+ */
+ private void removeFromRoutesMap(
+ @NonNull Collection<MediaRoute2Info> routes, boolean isSystemRoutes) {
+ for (MediaRoute2Info route : routes) {
+ if (!isSystemRoutes) {
+ mLastNotifiedRoutesToNonPrivilegedRouters.remove(route.getId());
+ }
+ mLastNotifiedRoutesToPrivilegedRouters.remove(route.getId());
+ }
+ }
+
+ /**
+ * Dispatches the latest route updates in {@link #mLastNotifiedRoutesToPrivilegedRouters}
+ * and {@link #mLastNotifiedRoutesToNonPrivilegedRouters} to registered {@link
+ * android.media.MediaRouter2 routers} and {@link MediaRouter2Manager managers} after a call
+ * to {@link #onProviderStateChangedOnHandler(MediaRoute2Provider)}. Ignores if no changes
+ * were made.
+ *
+ * @param hasAddedOrModifiedRoutes whether routes were added or modified.
+ * @param hasRemovedRoutes whether routes were removed.
+ * @param isSystemProvider whether the latest update was caused by a system provider.
+ * @param defaultRoute the current default route in {@link #mSystemProvider}.
+ */
+ private void dispatchUpdates(
+ boolean hasAddedOrModifiedRoutes,
+ boolean hasRemovedRoutes,
+ boolean isSystemProvider,
+ MediaRoute2Info defaultRoute) {
+
+ // Ignore if no changes.
+ if (!hasAddedOrModifiedRoutes && !hasRemovedRoutes) {
+ return;
+ }
+
List<IMediaRouter2> routersWithModifyAudioRoutingPermission = getRouters(true);
List<IMediaRouter2> routersWithoutModifyAudioRoutingPermission = getRouters(false);
List<IMediaRouter2Manager> managers = getManagers();
- List<MediaRoute2Info> defaultRoute = new ArrayList<>();
- defaultRoute.add(mSystemProvider.getDefaultRoute());
-
- if (addedRoutes.size() > 0) {
- notifyRoutesAddedToRouters(routersWithModifyAudioRoutingPermission, addedRoutes);
- if (!provider.mIsSystemRouteProvider) {
- notifyRoutesAddedToRouters(routersWithoutModifyAudioRoutingPermission,
- addedRoutes);
- } else if (prevInfo == null) {
- notifyRoutesAddedToRouters(routersWithoutModifyAudioRoutingPermission,
- defaultRoute);
- } // 'else' is handled as changed routes
- notifyRoutesAddedToManagers(managers, addedRoutes);
- }
- if (removedRoutes.size() > 0) {
- notifyRoutesRemovedToRouters(routersWithModifyAudioRoutingPermission,
- removedRoutes);
- if (!provider.mIsSystemRouteProvider) {
- notifyRoutesRemovedToRouters(routersWithoutModifyAudioRoutingPermission,
- removedRoutes);
- }
- notifyRoutesRemovedToManagers(managers, removedRoutes);
- }
- if (changedRoutes.size() > 0) {
- notifyRoutesChangedToRouters(routersWithModifyAudioRoutingPermission,
- changedRoutes);
- if (!provider.mIsSystemRouteProvider) {
- notifyRoutesChangedToRouters(routersWithoutModifyAudioRoutingPermission,
- changedRoutes);
- } else if (prevInfo != null) {
- notifyRoutesChangedToRouters(routersWithoutModifyAudioRoutingPermission,
- defaultRoute);
- } // 'else' is handled as added routes
- notifyRoutesChangedToManagers(managers, changedRoutes);
- }
- }
-
- private int getLastProviderInfoIndex(@NonNull String providerId) {
- for (int i = 0; i < mLastProviderInfos.size(); i++) {
- MediaRoute2ProviderInfo providerInfo = mLastProviderInfos.get(i);
- if (TextUtils.equals(providerInfo.getUniqueId(), providerId)) {
+
+ // Managers receive all provider updates with all routes.
+ notifyRoutesUpdatedToManagers(
+ managers, new ArrayList<>(mLastNotifiedRoutesToPrivilegedRouters.values()));
+
+ // Routers with modify audio permission (usually system routers) receive all provider
+ // updates with all routes.
+ notifyRoutesUpdatedToRouters(
+ routersWithModifyAudioRoutingPermission,
+ new ArrayList<>(mLastNotifiedRoutesToPrivilegedRouters.values()));
+
+ if (!isSystemProvider) {
+ // Regular routers receive updates from all non-system providers with all non-system
+ // routes.
+ notifyRoutesUpdatedToRouters(
+ routersWithoutModifyAudioRoutingPermission,
+ new ArrayList<>(mLastNotifiedRoutesToNonPrivilegedRouters.values()));
+ } else if (hasAddedOrModifiedRoutes) {
+ // On system provider updates, regular routers receive the updated default route.
+ // This is the only system route they should receive.
+ mLastNotifiedRoutesToNonPrivilegedRouters.put(defaultRoute.getId(), defaultRoute);
+ notifyRoutesUpdatedToRouters(
+ routersWithoutModifyAudioRoutingPermission,
+ new ArrayList<>(mLastNotifiedRoutesToNonPrivilegedRouters.values()));
+ }
+ }
+
+ /**
+ * Returns the index of the first element in {@code lastProviderInfos} that matches the
+ * specified unique id.
+ *
+ * @param uniqueId unique id of {@link MediaRoute2ProviderInfo} to be found.
+ * @param lastProviderInfos list of {@link MediaRoute2ProviderInfo}.
+ * @return index of found element, or -1 if not found.
+ */
+ private static int indexOfRouteProviderInfoByUniqueId(
+ @NonNull String uniqueId,
+ @NonNull List<MediaRoute2ProviderInfo> lastProviderInfos) {
+ for (int i = 0; i < lastProviderInfos.size(); i++) {
+ MediaRoute2ProviderInfo providerInfo = lastProviderInfos.get(i);
+ if (TextUtils.equals(providerInfo.getUniqueId(), uniqueId)) {
return i;
}
}
@@ -1989,41 +2111,19 @@ class MediaRouter2ServiceImpl {
}
}
- private void notifyRoutesAddedToRouters(@NonNull List<IMediaRouter2> routers,
- @NonNull List<MediaRoute2Info> routes) {
- for (IMediaRouter2 router : routers) {
- try {
- router.notifyRoutesAdded(routes);
- } catch (RemoteException ex) {
- Slog.w(TAG, "Failed to notify routes added. Router probably died.", ex);
- }
- }
- }
-
- private void notifyRoutesRemovedToRouters(@NonNull List<IMediaRouter2> routers,
- @NonNull List<MediaRoute2Info> routes) {
- for (IMediaRouter2 router : routers) {
- try {
- router.notifyRoutesRemoved(routes);
- } catch (RemoteException ex) {
- Slog.w(TAG, "Failed to notify routes removed. Router probably died.", ex);
- }
- }
- }
-
- private void notifyRoutesChangedToRouters(@NonNull List<IMediaRouter2> routers,
- @NonNull List<MediaRoute2Info> routes) {
+ private void notifyRoutesUpdatedToRouters(
+ @NonNull List<IMediaRouter2> routers, @NonNull List<MediaRoute2Info> routes) {
for (IMediaRouter2 router : routers) {
try {
- router.notifyRoutesChanged(routes);
+ router.notifyRoutesUpdated(routes);
} catch (RemoteException ex) {
- Slog.w(TAG, "Failed to notify routes changed. Router probably died.", ex);
+ Slog.w(TAG, "Failed to notify routes updated. Router probably died.", ex);
}
}
}
- private void notifySessionInfoChangedToRouters(@NonNull List<IMediaRouter2> routers,
- @NonNull RoutingSessionInfo sessionInfo) {
+ private void notifySessionInfoChangedToRouters(
+ @NonNull List<IMediaRouter2> routers, @NonNull RoutingSessionInfo sessionInfo) {
for (IMediaRouter2 router : routers) {
try {
router.notifySessionInfoChanged(sessionInfo);
@@ -2033,48 +2133,31 @@ class MediaRouter2ServiceImpl {
}
}
- private void notifyRoutesToManager(@NonNull IMediaRouter2Manager manager) {
- List<MediaRoute2Info> routes = new ArrayList<>();
- for (MediaRoute2ProviderInfo providerInfo : mLastProviderInfos) {
- routes.addAll(providerInfo.getRoutes());
- }
- if (routes.size() == 0) {
+ /**
+ * Notifies {@code manager} with all known routes. This only happens once after {@code
+ * manager} is registered through {@link #registerManager(IMediaRouter2Manager, String)
+ * registerManager()}.
+ *
+ * @param manager {@link IMediaRouter2Manager} to be notified.
+ */
+ private void notifyInitialRoutesToManager(@NonNull IMediaRouter2Manager manager) {
+ if (mLastNotifiedRoutesToPrivilegedRouters.isEmpty()) {
return;
}
try {
- manager.notifyRoutesAdded(routes);
+ manager.notifyRoutesUpdated(
+ new ArrayList<>(mLastNotifiedRoutesToPrivilegedRouters.values()));
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to notify all routes. Manager probably died.", ex);
}
}
- private void notifyRoutesAddedToManagers(@NonNull List<IMediaRouter2Manager> managers,
- @NonNull List<MediaRoute2Info> routes) {
- for (IMediaRouter2Manager manager : managers) {
- try {
- manager.notifyRoutesAdded(routes);
- } catch (RemoteException ex) {
- Slog.w(TAG, "Failed to notify routes added. Manager probably died.", ex);
- }
- }
- }
-
- private void notifyRoutesRemovedToManagers(@NonNull List<IMediaRouter2Manager> managers,
- @NonNull List<MediaRoute2Info> routes) {
- for (IMediaRouter2Manager manager : managers) {
- try {
- manager.notifyRoutesRemoved(routes);
- } catch (RemoteException ex) {
- Slog.w(TAG, "Failed to notify routes removed. Manager probably died.", ex);
- }
- }
- }
-
- private void notifyRoutesChangedToManagers(@NonNull List<IMediaRouter2Manager> managers,
+ private void notifyRoutesUpdatedToManagers(
+ @NonNull List<IMediaRouter2Manager> managers,
@NonNull List<MediaRoute2Info> routes) {
for (IMediaRouter2Manager manager : managers) {
try {
- manager.notifyRoutesChanged(routes);
+ manager.notifyRoutesUpdated(routes);
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to notify routes changed. Manager probably died.", ex);
}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index a21919cdb960..d48af213dfc7 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -1,5 +1,6 @@
package com.android.server.wm;
+import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.ActivityManager.processStateAmToProto;
@@ -274,6 +275,8 @@ class ActivityMetricsLogger {
final boolean mProcessRunning;
/** whether the process of the launching activity didn't have any active activity. */
final boolean mProcessSwitch;
+ /** The process state of the launching activity prior to the launch */
+ final int mProcessState;
/** Whether the last launched activity has reported drawn. */
boolean mIsDrawn;
/** The latest activity to have been launched. */
@@ -309,8 +312,8 @@ class ActivityMetricsLogger {
@Nullable
static TransitionInfo create(@NonNull ActivityRecord r,
@NonNull LaunchingState launchingState, @Nullable ActivityOptions options,
- boolean processRunning, boolean processSwitch, boolean newActivityCreated,
- int startResult) {
+ boolean processRunning, boolean processSwitch, int processState,
+ boolean newActivityCreated, int startResult) {
if (startResult != START_SUCCESS && startResult != START_TASK_TO_FRONT) {
return null;
}
@@ -325,18 +328,19 @@ class ActivityMetricsLogger {
transitionType = TYPE_TRANSITION_COLD_LAUNCH;
}
return new TransitionInfo(r, launchingState, options, transitionType, processRunning,
- processSwitch);
+ processSwitch, processState);
}
/** Use {@link TransitionInfo#create} instead to ensure the transition type is valid. */
private TransitionInfo(ActivityRecord r, LaunchingState launchingState,
ActivityOptions options, int transitionType, boolean processRunning,
- boolean processSwitch) {
+ boolean processSwitch, int processState) {
mLaunchingState = launchingState;
mTransitionStartTimeNs = launchingState.mCurrentTransitionStartTimeNs;
mTransitionType = transitionType;
mProcessRunning = processRunning;
mProcessSwitch = processSwitch;
+ mProcessState = processState;
mTransitionDeviceUptimeMs = launchingState.mCurrentUpTimeMs;
setLatestLaunchedActivity(r);
// The launching state can be reused by consecutive launch. Its original association
@@ -640,12 +644,16 @@ class ActivityMetricsLogger {
// interesting.
final boolean processSwitch = !processRunning
|| !processRecord.hasStartedActivity(launchedActivity);
+ final int processState = processRunning
+ ? processRecord.getCurrentProcState()
+ : PROCESS_STATE_NONEXISTENT;
final TransitionInfo info = launchingState.mAssociatedTransitionInfo;
if (DEBUG_METRICS) {
Slog.i(TAG, "notifyActivityLaunched" + " resultCode=" + resultCode
+ " launchedActivity=" + launchedActivity + " processRunning=" + processRunning
+ " processSwitch=" + processSwitch
+ + " processState=" + processState
+ " newActivityCreated=" + newActivityCreated + " info=" + info);
}
@@ -681,7 +689,8 @@ class ActivityMetricsLogger {
}
final TransitionInfo newInfo = TransitionInfo.create(launchedActivity, launchingState,
- options, processRunning, processSwitch, newActivityCreated, resultCode);
+ options, processRunning, processSwitch, processState, newActivityCreated,
+ resultCode);
if (newInfo == null) {
abort(launchingState, "unrecognized launch");
return;
@@ -996,8 +1005,9 @@ class ActivityMetricsLogger {
final long timestamp = info.mTransitionStartTimeNs;
final long uptime = info.mTransitionDeviceUptimeMs;
final int transitionDelay = info.mCurrentTransitionDelayMs;
+ final int processState = info.mProcessState;
mLoggerHandler.post(() -> logAppTransition(
- timestamp, uptime, transitionDelay, infoSnapshot, isHibernating));
+ timestamp, uptime, transitionDelay, infoSnapshot, isHibernating, processState));
}
mLoggerHandler.post(() -> logAppDisplayed(infoSnapshot));
if (info.mPendingFullyDrawn != null) {
@@ -1009,7 +1019,8 @@ class ActivityMetricsLogger {
// This gets called on another thread without holding the activity manager lock.
private void logAppTransition(long transitionStartTimeNs, long transitionDeviceUptimeMs,
- int currentTransitionDelayMs, TransitionInfoSnapshot info, boolean isHibernating) {
+ int currentTransitionDelayMs, TransitionInfoSnapshot info, boolean isHibernating,
+ int processState) {
final LogMaker builder = new LogMaker(APP_TRANSITION);
builder.setPackageName(info.packageName);
builder.setType(info.type);
@@ -1075,7 +1086,8 @@ class ActivityMetricsLogger {
isIncremental,
isLoading,
info.launchedActivityName.hashCode(),
- TimeUnit.NANOSECONDS.toMillis(transitionStartTimeNs));
+ TimeUnit.NANOSECONDS.toMillis(transitionStartTimeNs),
+ processState);
if (DEBUG_METRICS) {
Slog.i(TAG, String.format("APP_START_OCCURRED(%s, %s, %s, %s, %s)",
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 2bb179327cd1..c64e5255c78c 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -78,6 +78,11 @@ import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN;
import static android.content.pm.ActivityInfo.PERSIST_ACROSS_REBOOTS;
import static android.content.pm.ActivityInfo.PERSIST_ROOT_ONLY;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
@@ -8751,15 +8756,35 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
/**
* Returns the min aspect ratio of this activity.
*/
- private float getMinAspectRatio() {
- return info.getMinAspectRatio(getRequestedOrientation());
+ float getMinAspectRatio() {
+ if (info.applicationInfo == null || !info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO) || (
+ info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY)
+ && !ActivityInfo.isFixedOrientationPortrait(getRequestedOrientation()))) {
+ return info.getMinAspectRatio();
+ }
+
+ if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN)) {
+ return Math.max(mLetterboxUiController.getSplitScreenAspectRatio(),
+ info.getMinAspectRatio());
+ }
+
+ if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_LARGE)) {
+ return Math.max(ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
+ info.getMinAspectRatio());
+ }
+
+ if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM)) {
+ return Math.max(ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE,
+ info.getMinAspectRatio());
+ }
+ return info.getMinAspectRatio();
}
/**
* Returns true if the activity has maximum or minimum aspect ratio.
*/
private boolean hasFixedAspectRatio() {
- return info.hasFixedAspectRatio(getRequestedOrientation());
+ return info.getMaxAspectRatio() != 0 || getMinAspectRatio() != 0;
}
/**
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index c8692ca81b2e..fa3fc9ffb69c 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -53,7 +53,6 @@ import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import static android.view.View.GONE;
-import static android.view.ViewRootImpl.LOCAL_LAYOUT;
import static android.view.WindowInsets.Type.displayCutout;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.systemBars;
@@ -950,7 +949,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
final int preferredModeId = getDisplayPolicy().getRefreshRatePolicy()
.getPreferredModeId(w);
- if (mTmpApplySurfaceChangesTransactionState.preferredModeId == 0
+ if (w.isFocused() && mTmpApplySurfaceChangesTransactionState.preferredModeId == 0
&& preferredModeId != 0) {
mTmpApplySurfaceChangesTransactionState.preferredModeId = preferredModeId;
}
@@ -2710,25 +2709,22 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mCurrentPrivacyIndicatorBounds =
mCurrentPrivacyIndicatorBounds.updateStaticBounds(staticBounds);
if (!Objects.equals(oldBounds, mCurrentPrivacyIndicatorBounds)) {
- updateDisplayFrames(false /* insetsSourceMayChange */, true /* notifyInsetsChange */);
+ updateDisplayFrames(true /* notifyInsetsChange */);
}
}
void onDisplayInfoChanged() {
- updateDisplayFrames(LOCAL_LAYOUT, LOCAL_LAYOUT);
+ updateDisplayFrames(false /* notifyInsetsChange */);
mMinSizeOfResizeableTaskDp = getMinimalTaskSizeDp();
mInputMonitor.layoutInputConsumers(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
mDisplayPolicy.onDisplayInfoChanged(mDisplayInfo);
}
- private void updateDisplayFrames(boolean insetsSourceMayChange, boolean notifyInsetsChange) {
+ private void updateDisplayFrames(boolean notifyInsetsChange) {
if (mDisplayFrames.update(mDisplayInfo,
calculateDisplayCutoutForRotation(mDisplayInfo.rotation),
calculateRoundedCornersForRotation(mDisplayInfo.rotation),
calculatePrivacyIndicatorBoundsForRotation(mDisplayInfo.rotation))) {
- if (insetsSourceMayChange) {
- mDisplayPolicy.updateInsetsSourceFramesExceptIme(mDisplayFrames);
- }
mInsetsStateController.onDisplayFramesUpdated(notifyInsetsChange);
}
}
@@ -4416,13 +4412,20 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
*/
@VisibleForTesting
InsetsControlTarget computeImeControlTarget() {
+ if (mImeInputTarget == null) {
+ // A special case that if there is no IME input target while the IME is being killed,
+ // in case seeing unexpected IME surface visibility change when delivering the IME leash
+ // to the remote insets target during the IME restarting, but the focus window is not in
+ // multi-windowing mode, return null target until the next input target updated.
+ return null;
+ }
+
+ final WindowState imeInputTarget = mImeInputTarget.getWindowState();
if (!isImeControlledByApp() && mRemoteInsetsControlTarget != null
- || (mImeInputTarget != null
- && getImeHostOrFallback(mImeInputTarget.getWindowState())
- == mRemoteInsetsControlTarget)) {
+ || getImeHostOrFallback(imeInputTarget) == mRemoteInsetsControlTarget) {
return mRemoteInsetsControlTarget;
} else {
- return mImeInputTarget != null ? mImeInputTarget.getWindowState() : null;
+ return imeInputTarget;
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 07694065cbd7..5221072f696d 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1576,19 +1576,6 @@ public class DisplayPolicy {
}
}
- void updateInsetsSourceFramesExceptIme(DisplayFrames displayFrames) {
- sTmpClientFrames.attachedFrame = null;
- for (int i = mInsetsSourceWindowsExceptIme.size() - 1; i >= 0; i--) {
- final WindowState win = mInsetsSourceWindowsExceptIme.valueAt(i);
- mWindowLayout.computeFrames(win.mAttrs.forRotation(displayFrames.mRotation),
- displayFrames.mInsetsState, displayFrames.mDisplayCutoutSafe,
- displayFrames.mUnrestricted, win.getWindowingMode(), UNSPECIFIED_LENGTH,
- UNSPECIFIED_LENGTH, win.getRequestedVisibilities(), win.mGlobalScale,
- sTmpClientFrames);
- win.updateSourceFrame(sTmpClientFrames.frame);
- }
- }
-
void onDisplayInfoChanged(DisplayInfo info) {
mSystemGestures.onDisplayInfoChanged(info);
}
diff --git a/services/core/java/com/android/server/wm/LaunchParamsUtil.java b/services/core/java/com/android/server/wm/LaunchParamsUtil.java
index a618f7c79238..a0e22e7d9dc5 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsUtil.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsUtil.java
@@ -63,7 +63,7 @@ class LaunchParamsUtil {
* Calculate the default size for a freeform environment. |defaultSize| is used as the default
* DP size, but if this is null, the portrait phone size is used.
*/
- static Size getDefaultFreeformSize(@NonNull ActivityInfo info,
+ static Size getDefaultFreeformSize(@NonNull ActivityRecord activityRecord,
@NonNull TaskDisplayArea displayArea,
@NonNull ActivityInfo.WindowLayout layout, int orientation,
@NonNull Rect stableBounds) {
@@ -98,8 +98,8 @@ class LaunchParamsUtil {
final float aspectRatio = (float) Math.max(width, height) / (float) Math.min(width, height);
// Aspect ratio requirements.
- final float minAspectRatio = info.getMinAspectRatio(orientation);
- final float maxAspectRatio = info.getMaxAspectRatio();
+ final float minAspectRatio = activityRecord.getMinAspectRatio();
+ final float maxAspectRatio = activityRecord.info.getMaxAspectRatio();
// Adjust the width and height to the aspect ratio requirements.
int adjWidth = width;
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index ec9ee29679a4..c8ed602b0f1d 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -257,6 +257,10 @@ final class LetterboxUiController {
: mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio();
}
+ return getSplitScreenAspectRatio();
+ }
+
+ float getSplitScreenAspectRatio() {
int dividerWindowWidth =
getResources().getDimensionPixelSize(R.dimen.docked_stack_divider_thickness);
int dividerInsets =
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 0128c187bbdd..fb68fe666c0b 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -242,16 +242,17 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
@Override
public int relayout(IWindow window, WindowManager.LayoutParams attrs,
- int requestedWidth, int requestedHeight, int viewFlags, int flags,
- ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
- SurfaceControl outSurfaceControl, InsetsState outInsetsState,
- InsetsSourceControl[] outActiveControls, Bundle outSyncSeqIdBundle) {
+ int requestedWidth, int requestedHeight, int viewFlags, int flags, int seq,
+ int lastSyncSeqId, ClientWindowFrames outFrames,
+ MergedConfiguration mergedConfiguration, SurfaceControl outSurfaceControl,
+ InsetsState outInsetsState, InsetsSourceControl[] outActiveControls,
+ Bundle outSyncSeqIdBundle) {
if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
+ Binder.getCallingPid());
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
int res = mService.relayoutWindow(this, window, attrs,
- requestedWidth, requestedHeight, viewFlags, flags,
- outFrames, mergedConfiguration, outSurfaceControl, outInsetsState,
+ requestedWidth, requestedHeight, viewFlags, flags, seq,
+ lastSyncSeqId, outFrames, mergedConfiguration, outSurfaceControl, outInsetsState,
outActiveControls, outSyncSeqIdBundle);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to "
@@ -260,6 +261,16 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
}
@Override
+ public void relayoutAsync(IWindow window, WindowManager.LayoutParams attrs,
+ int requestedWidth, int requestedHeight, int viewFlags, int flags, int seq,
+ int lastSyncSeqId) {
+ relayout(window, attrs, requestedWidth, requestedHeight, viewFlags, flags, seq,
+ lastSyncSeqId, null /* outFrames */, null /* mergedConfiguration */,
+ null /* outSurfaceControl */, null /* outInsetsState */,
+ null /* outActiveControls */, null /* outSyncIdBundle */);
+ }
+
+ @Override
public boolean outOfMemory(IWindow window) {
return mService.outOfMemoryWindow(this, window);
}
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 7b0337d52da2..136209417f39 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -746,7 +746,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
// First we get the default size we want.
displayArea.getStableRect(mTmpStableBounds);
- final Size defaultSize = LaunchParamsUtil.getDefaultFreeformSize(root.info, displayArea,
+ final Size defaultSize = LaunchParamsUtil.getDefaultFreeformSize(root, displayArea,
layout, orientation, mTmpStableBounds);
mTmpBounds.set(0, 0, defaultSize.getWidth(), defaultSize.getHeight());
if (hasInitialBounds || sizeMatches(inOutBounds, mTmpBounds)) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index da5f5d0e29fd..a71c3866ba38 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -810,12 +810,17 @@ public abstract class WindowManagerInternal {
*/
public final String imeLayerTargetName;
+ /** The surface parent of the IME container. */
+ public final String imeSurfaceParentName;
+
public ImeTargetInfo(String focusedWindowName, String requestWindowName,
- String imeControlTargetName, String imeLayerTargetName) {
+ String imeControlTargetName, String imeLayerTargetName,
+ String imeSurfaceParentName) {
this.focusedWindowName = focusedWindowName;
this.requestWindowName = requestWindowName;
this.imeControlTargetName = imeControlTargetName;
this.imeLayerTargetName = imeLayerTargetName;
+ this.imeSurfaceParentName = imeSurfaceParentName;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index fd54f78b22fe..6544f821f13c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2248,11 +2248,14 @@ public class WindowManagerService extends IWindowManager.Stub
}
public int relayoutWindow(Session session, IWindow client, LayoutParams attrs,
- int requestedWidth, int requestedHeight, int viewVisibility, int flags,
- ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
- SurfaceControl outSurfaceControl, InsetsState outInsetsState,
- InsetsSourceControl[] outActiveControls, Bundle outSyncIdBundle) {
- Arrays.fill(outActiveControls, null);
+ int requestedWidth, int requestedHeight, int viewVisibility, int flags, int seq,
+ int lastSyncSeqId, ClientWindowFrames outFrames,
+ MergedConfiguration outMergedConfiguration, SurfaceControl outSurfaceControl,
+ InsetsState outInsetsState, InsetsSourceControl[] outActiveControls,
+ Bundle outSyncIdBundle) {
+ if (outActiveControls != null) {
+ Arrays.fill(outActiveControls, null);
+ }
int result = 0;
boolean configChanged;
final int pid = Binder.getCallingPid();
@@ -2263,8 +2266,15 @@ public class WindowManagerService extends IWindowManager.Stub
if (win == null) {
return 0;
}
+ if (win.mRelayoutSeq < seq) {
+ win.mRelayoutSeq = seq;
+ } else if (win.mRelayoutSeq > seq) {
+ return 0;
+ }
- if (win.cancelAndRedraw() && win.mPrepareSyncSeqId <= win.mLastSeqIdSentToRelayout) {
+ if (win.cancelAndRedraw() && win.mPrepareSyncSeqId <= lastSyncSeqId) {
+ // The client has reported the sync draw, but we haven't finished it yet.
+ // Don't let the client perform a non-sync draw at this time.
result |= RELAYOUT_RES_CANCEL_AND_REDRAW;
}
@@ -2427,7 +2437,7 @@ public class WindowManagerService extends IWindowManager.Stub
// Create surfaceControl before surface placement otherwise layout will be skipped
// (because WS.isGoneForLayout() is true when there is no surface.
- if (shouldRelayout) {
+ if (shouldRelayout && outSurfaceControl != null) {
try {
result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
} catch (Exception e) {
@@ -2466,22 +2476,25 @@ public class WindowManagerService extends IWindowManager.Stub
winAnimator.mEnterAnimationPending = false;
winAnimator.mEnteringAnimation = false;
- if (viewVisibility == View.VISIBLE && winAnimator.hasSurface()) {
- // We already told the client to go invisible, but the message may not be
- // handled yet, or it might want to draw a last frame. If we already have a
- // surface, let the client use that, but don't create new surface at this point.
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: getSurface");
- winAnimator.mSurfaceController.getSurfaceControl(outSurfaceControl);
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- } else {
- if (DEBUG_VISIBILITY) Slog.i(TAG_WM, "Releasing surface in: " + win);
-
- try {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmReleaseOutSurface_"
- + win.mAttrs.getTitle());
- outSurfaceControl.release();
- } finally {
+ if (outSurfaceControl != null) {
+ if (viewVisibility == View.VISIBLE && winAnimator.hasSurface()) {
+ // We already told the client to go invisible, but the message may not be
+ // handled yet, or it might want to draw a last frame. If we already have a
+ // surface, let the client use that, but don't create new surface at this
+ // point.
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: getSurface");
+ winAnimator.mSurfaceController.getSurfaceControl(outSurfaceControl);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ } else {
+ if (DEBUG_VISIBILITY) Slog.i(TAG_WM, "Releasing surface in: " + win);
+
+ try {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmReleaseOutSurface_"
+ + win.mAttrs.getTitle());
+ outSurfaceControl.release();
+ } finally {
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ }
}
}
@@ -2534,20 +2547,16 @@ public class WindowManagerService extends IWindowManager.Stub
win.mResizedWhileGone = false;
}
- win.fillClientWindowFramesAndConfiguration(outFrames, mergedConfiguration,
- false /* useLatestConfig */, shouldRelayout);
+ if (outFrames != null && outMergedConfiguration != null) {
+ win.fillClientWindowFramesAndConfiguration(outFrames, outMergedConfiguration,
+ false /* useLatestConfig */, shouldRelayout);
- // Set resize-handled here because the values are sent back to the client.
- win.onResizeHandled();
+ // Set resize-handled here because the values are sent back to the client.
+ win.onResizeHandled();
+ }
- outInsetsState.set(win.getCompatInsetsState(), win.isClientLocal());
- if (DEBUG) {
- Slog.v(TAG_WM, "Relayout given client " + client.asBinder()
- + ", requestedWidth=" + requestedWidth
- + ", requestedHeight=" + requestedHeight
- + ", viewVisibility=" + viewVisibility
- + "\nRelayout returning frame=" + outFrames.frame
- + ", surface=" + outSurfaceControl);
+ if (outInsetsState != null) {
+ outInsetsState.set(win.getCompatInsetsState(), win.isClientLocal());
}
ProtoLog.v(WM_DEBUG_FOCUS, "Relayout of %s: focusMayChange=%b",
@@ -2558,14 +2567,16 @@ public class WindowManagerService extends IWindowManager.Stub
}
win.mInRelayout = false;
- if (USE_BLAST_SYNC && win.useBLASTSync() && viewVisibility != View.GONE
- && (win.mSyncSeqId > win.mLastSeqIdSentToRelayout)) {
- win.markRedrawForSyncReported();
-
- win.mLastSeqIdSentToRelayout = win.mSyncSeqId;
- outSyncIdBundle.putInt("seqid", win.mSyncSeqId);
- } else {
- outSyncIdBundle.putInt("seqid", -1);
+ if (outSyncIdBundle != null) {
+ final int maybeSyncSeqId;
+ if (USE_BLAST_SYNC && win.useBLASTSync() && viewVisibility != View.GONE
+ && win.mSyncSeqId > lastSyncSeqId) {
+ maybeSyncSeqId = win.mSyncSeqId;
+ win.markRedrawForSyncReported();
+ } else {
+ maybeSyncSeqId = -1;
+ }
+ outSyncIdBundle.putInt("seqid", maybeSyncSeqId);
}
if (configChanged) {
@@ -2574,7 +2585,9 @@ public class WindowManagerService extends IWindowManager.Stub
displayContent.sendNewConfiguration();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
- getInsetsSourceControls(win, outActiveControls);
+ if (outActiveControls != null) {
+ getInsetsSourceControls(win, outActiveControls);
+ }
}
Binder.restoreCallingIdentity(origId);
@@ -8202,6 +8215,7 @@ public class WindowManagerService extends IWindowManager.Stub
final String requestWindowName;
final String imeControlTargetName;
final String imeLayerTargetName;
+ final String imeSurfaceParentName;
synchronized (mGlobalLock) {
final WindowState focusedWin = mWindowMap.get(focusedToken);
focusedWindowName = focusedWin != null ? focusedWin.getName() : "null";
@@ -8218,15 +8232,17 @@ public class WindowManagerService extends IWindowManager.Stub
}
final InsetsControlTarget target = dc.getImeTarget(IME_TARGET_LAYERING);
imeLayerTargetName = target != null ? target.getWindow().getName() : "null";
+ final SurfaceControl imeParent = dc.mInputMethodSurfaceParent;
+ imeSurfaceParentName = imeParent != null ? imeParent.toString() : "null";
if (show) {
dc.onShowImeRequested();
}
} else {
- imeControlTargetName = imeLayerTargetName = "no-display";
+ imeControlTargetName = imeLayerTargetName = imeSurfaceParentName = "no-display";
}
}
return new ImeTargetInfo(focusedWindowName, requestWindowName, imeControlTargetName,
- imeLayerTargetName);
+ imeLayerTargetName, imeSurfaceParentName);
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 283010ea5767..d222a566bd9d 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -389,7 +389,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
* examine the git commit message introducing this comment and variable.2
*/
int mSyncSeqId = 0;
- int mLastSeqIdSentToRelayout = 0;
/** The last syncId associated with a prepareSync or 0 when no sync is active. */
int mPrepareSyncSeqId = 0;
@@ -425,6 +424,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
boolean mHaveFrame;
boolean mObscured;
+ int mRelayoutSeq = -1;
int mLayoutSeq = -1;
/**
@@ -1349,29 +1349,15 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final WindowFrames windowFrames = mWindowFrames;
mTmpRect.set(windowFrames.mParentFrame);
- if (LOCAL_LAYOUT) {
- windowFrames.mCompatFrame.set(clientWindowFrames.frame);
-
- windowFrames.mFrame.set(clientWindowFrames.frame);
- windowFrames.mDisplayFrame.set(clientWindowFrames.displayFrame);
- windowFrames.mParentFrame.set(clientWindowFrames.parentFrame);
- if (mGlobalScale != 1f) {
- // The frames sent from the client need to be adjusted to the real coordinate space.
- windowFrames.mFrame.scale(mGlobalScale);
- windowFrames.mDisplayFrame.scale(mGlobalScale);
- windowFrames.mParentFrame.scale(mGlobalScale);
- }
- } else {
- windowFrames.mDisplayFrame.set(clientWindowFrames.displayFrame);
- windowFrames.mParentFrame.set(clientWindowFrames.parentFrame);
- windowFrames.mFrame.set(clientWindowFrames.frame);
+ windowFrames.mDisplayFrame.set(clientWindowFrames.displayFrame);
+ windowFrames.mParentFrame.set(clientWindowFrames.parentFrame);
+ windowFrames.mFrame.set(clientWindowFrames.frame);
- windowFrames.mCompatFrame.set(windowFrames.mFrame);
- if (mInvGlobalScale != 1f) {
- // Also, the scaled frame that we report to the app needs to be adjusted to be in
- // its coordinate space.
- windowFrames.mCompatFrame.scale(mInvGlobalScale);
- }
+ windowFrames.mCompatFrame.set(windowFrames.mFrame);
+ if (mInvGlobalScale != 1f) {
+ // Also, the scaled frame that we report to the app needs to be adjusted to be in
+ // its coordinate space.
+ windowFrames.mCompatFrame.scale(mInvGlobalScale);
}
windowFrames.setParentFrameWasClippedByDisplayCutout(
clientWindowFrames.isParentFrameClippedByDisplayCutout);
@@ -1415,13 +1401,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
updateSourceFrame(windowFrames.mFrame);
- if (LOCAL_LAYOUT) {
- if (!mHaveFrame) {
- // The first frame should not be considered as moved.
- updateLastFrames();
- }
- }
-
if (mActivityRecord != null && !mIsChildWindow) {
mActivityRecord.layoutLetterbox(this);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/BatteryControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/BatteryControllerTest.java
index 57a80131a733..427c3b3409d2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/BatteryControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/BatteryControllerTest.java
@@ -100,7 +100,9 @@ public class BatteryControllerTest {
// Capture the listeners.
ArgumentCaptor<BroadcastReceiver> receiverCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
- mFlexibilityController = new FlexibilityController(mJobSchedulerService);
+
+ mFlexibilityController =
+ new FlexibilityController(mJobSchedulerService, mock(PrefetchController.class));
mBatteryController = new BatteryController(mJobSchedulerService, mFlexibilityController);
verify(mContext).registerReceiver(receiverCaptor.capture(),
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
index 7a70e7a09411..953a72d5085c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
@@ -116,8 +116,8 @@ public class ConnectivityControllerTest {
LocalServices.addService(NetworkPolicyManagerInternal.class, mNetPolicyManagerInternal);
when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
-
- mFlexibilityController = new FlexibilityController(mService);
+ mFlexibilityController =
+ new FlexibilityController(mService, mock(PrefetchController.class));
// Freeze the clocks at this moment in time
JobSchedulerService.sSystemClock =
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
index 6d9f48dbe35c..1b39add3b421 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
@@ -16,17 +16,25 @@
package com.android.server.job.controllers;
+import static android.app.job.JobInfo.BIAS_FOREGROUND_SERVICE;
+import static android.app.job.JobInfo.BIAS_TOP_APP;
import static android.app.job.JobInfo.NETWORK_TYPE_ANY;
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-import static com.android.server.job.controllers.FlexibilityController.FcConstants.KEY_FLEXIBILITY_ENABLED;
+import static com.android.server.job.controllers.FlexibilityController.FcConfig.DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS;
+import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_DEADLINE_PROXIMITY_LIMIT;
+import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_FALLBACK_FLEXIBILITY_DEADLINE;
+import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_FLEXIBILITY_ENABLED;
+import static com.android.server.job.controllers.FlexibilityController.FcConfig.KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_BATTERY_NOT_LOW;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CHARGING;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_FLEXIBLE;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_IDLE;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -67,12 +75,12 @@ import java.util.concurrent.Executor;
public class FlexibilityControllerTest {
private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests";
private static final int SOURCE_USER_ID = 0;
-
+ private static final long FROZEN_TIME = 100L;
private MockitoSession mMockingSession;
private FlexibilityController mFlexibilityController;
private DeviceConfig.Properties.Builder mDeviceConfigPropertiesBuilder;
private JobStore mJobStore;
- private FlexibilityController.FcConstants mFcConstants;
+ private FlexibilityController.FcConfig mFcConfig;
@Mock
private AlarmManager mAlarmManager;
@@ -80,6 +88,8 @@ public class FlexibilityControllerTest {
private Context mContext;
@Mock
private JobSchedulerService mJobSchedulerService;
+ @Mock
+ private PrefetchController mPrefetchController;
@Before
public void setup() {
@@ -117,13 +127,15 @@ public class FlexibilityControllerTest {
.when(() -> LocalServices.getService(PackageManagerInternal.class));
// Freeze the clocks at a moment in time
JobSchedulerService.sSystemClock =
- Clock.fixed(Instant.ofEpochMilli(100L), ZoneOffset.UTC);
+ Clock.fixed(Instant.ofEpochMilli(FROZEN_TIME), ZoneOffset.UTC);
JobSchedulerService.sElapsedRealtimeClock =
- Clock.fixed(Instant.ofEpochMilli(100L), ZoneOffset.UTC);
+ Clock.fixed(Instant.ofEpochMilli(FROZEN_TIME), ZoneOffset.UTC);
// Initialize real objects.
- mFlexibilityController = new FlexibilityController(mJobSchedulerService);
- mFcConstants = mFlexibilityController.getFcConstants();
+ mFlexibilityController = new FlexibilityController(mJobSchedulerService,
+ mPrefetchController);
+ mFcConfig = mFlexibilityController.getFcConfig();
+ setDeviceConfigLong(KEY_DEADLINE_PROXIMITY_LIMIT, 0L);
setDeviceConfigBoolean(KEY_FLEXIBILITY_ENABLED, true);
}
@@ -138,7 +150,25 @@ public class FlexibilityControllerTest {
mDeviceConfigPropertiesBuilder.setBoolean(key, val);
synchronized (mFlexibilityController.mLock) {
mFlexibilityController.prepareForUpdatedConstantsLocked();
- mFcConstants.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key);
+ mFcConfig.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key);
+ mFlexibilityController.onConstantsUpdatedLocked();
+ }
+ }
+
+ private void setDeviceConfigLong(String key, Long val) {
+ mDeviceConfigPropertiesBuilder.setLong(key, val);
+ synchronized (mFlexibilityController.mLock) {
+ mFlexibilityController.prepareForUpdatedConstantsLocked();
+ mFcConfig.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key);
+ mFlexibilityController.onConstantsUpdatedLocked();
+ }
+ }
+
+ private void setDeviceConfigString(String key, String val) {
+ mDeviceConfigPropertiesBuilder.setString(key, val);
+ synchronized (mFlexibilityController.mLock) {
+ mFlexibilityController.prepareForUpdatedConstantsLocked();
+ mFcConfig.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key);
mFlexibilityController.onConstantsUpdatedLocked();
}
}
@@ -149,76 +179,277 @@ public class FlexibilityControllerTest {
private JobStatus createJobStatus(String testTag, JobInfo.Builder job) {
JobInfo jobInfo = job.build();
- return JobStatus.createFromJobInfo(
+ JobStatus js = JobStatus.createFromJobInfo(
jobInfo, 1000, SOURCE_PACKAGE, SOURCE_USER_ID, testTag);
+ js.enqueueTime = FROZEN_TIME;
+ return js;
}
+ /**
+ * Tests that the there are equally many percents to drop constraints as there are constraints
+ */
@Test
- public void testGetNextConstraintDropTimeElapsed() {
+ public void testDefaultVariableValues() {
+ assertEquals(FlexibilityController.NUM_FLEXIBLE_CONSTRAINTS,
+ mFlexibilityController.mFcConfig.DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS.length
+ );
+ }
+
+ @Test
+ public void testOnConstantsUpdated_DefaultFlexibility() {
+ JobStatus js = createJobStatus("testDefaultFlexibilityConfig", createJob(0));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(js));
+ setDeviceConfigBoolean(KEY_FLEXIBILITY_ENABLED, false);
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(js));
+ setDeviceConfigBoolean(KEY_FLEXIBILITY_ENABLED, true);
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(js));
+ }
+
+ @Test
+ public void testOnConstantsUpdated_DeadlineProximity() {
+ JobStatus js = createJobStatus("testDeadlineProximityConfig", createJob(0));
+ setDeviceConfigLong(KEY_DEADLINE_PROXIMITY_LIMIT, Long.MAX_VALUE);
+ mFlexibilityController.mFlexibilityAlarmQueue.scheduleDropNumConstraintsAlarm(js);
+ assertEquals(0, js.getNumRequiredFlexibleConstraints());
+ }
+
+ @Test
+ public void testOnConstantsUpdated_FallbackDeadline() {
+ JobStatus js = createJobStatus("testFallbackDeadlineConfig", createJob(0));
+ assertEquals(DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS,
+ mFlexibilityController.getLifeCycleEndElapsedLocked(js, 0L));
+ setDeviceConfigLong(KEY_FALLBACK_FLEXIBILITY_DEADLINE, 100L);
+ assertEquals(100L, mFlexibilityController.getLifeCycleEndElapsedLocked(js, 0L));
+ }
+
+ @Test
+ public void testOnConstantsUpdated_PercentsToDropConstraints() {
+ JobInfo.Builder jb = createJob(0).setOverrideDeadline(100L);
+ JobStatus js = createJobStatus("testPercentsToDropConstraintsConfig", jb);
+ assertEquals(150L,
+ mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js));
+ setDeviceConfigString(KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS, "10,20,30,40");
+ assertArrayEquals(
+ mFlexibilityController.mFcConfig.PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS,
+ new int[] {10, 20, 30, 40});
+ assertEquals(110L,
+ mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js));
+ js.adjustNumRequiredFlexibleConstraints(-1);
+ assertEquals(120L,
+ mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js));
+ js.adjustNumRequiredFlexibleConstraints(-1);
+ assertEquals(130L,
+ mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js));
+ }
+
+ @Test
+ public void testOnConstantsUpdated_PercentsToDropConstraintsInvalidValues() {
+ JobInfo.Builder jb = createJob(0).setOverrideDeadline(100L);
+ JobStatus js = createJobStatus("testPercentsToDropConstraintsConfig", jb);
+ js.enqueueTime = 100L;
+ assertEquals(150L,
+ mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js));
+ setDeviceConfigString(KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS, "10,20a,030,40");
+ assertEquals(150L,
+ mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js));
+ setDeviceConfigString(KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS, "10,40");
+ assertEquals(150L,
+ mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js));
+ setDeviceConfigString(KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS, "50,40,10,40");
+ assertEquals(150L,
+ mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js));
+ }
+
+ @Test
+ public void testGetNextConstraintDropTimeElapsedLocked() {
long nextTimeToDropNumConstraints;
// no delay, deadline
JobInfo.Builder jb = createJob(0).setOverrideDeadline(1000);
JobStatus js = createJobStatus("time", jb);
- js.enqueueTime = 100L;
- assertEquals(0, js.getEarliestRunTime());
- assertEquals(1100L, js.getLatestRunTimeElapsed());
- assertEquals(100L, js.enqueueTime);
+ assertEquals(JobStatus.NO_EARLIEST_RUNTIME, js.getEarliestRunTime());
+ assertEquals(1000 + FROZEN_TIME, js.getLatestRunTimeElapsed());
+ assertEquals(FROZEN_TIME, js.enqueueTime);
- nextTimeToDropNumConstraints = mFlexibilityController.getNextConstraintDropTimeElapsed(js);
+ nextTimeToDropNumConstraints = mFlexibilityController
+ .getNextConstraintDropTimeElapsedLocked(js);
assertEquals(600L, nextTimeToDropNumConstraints);
js.adjustNumRequiredFlexibleConstraints(-1);
- nextTimeToDropNumConstraints = mFlexibilityController.getNextConstraintDropTimeElapsed(js);
+ nextTimeToDropNumConstraints = mFlexibilityController
+ .getNextConstraintDropTimeElapsedLocked(js);
assertEquals(700L, nextTimeToDropNumConstraints);
js.adjustNumRequiredFlexibleConstraints(-1);
- nextTimeToDropNumConstraints = mFlexibilityController.getNextConstraintDropTimeElapsed(js);
+ nextTimeToDropNumConstraints = mFlexibilityController
+ .getNextConstraintDropTimeElapsedLocked(js);
assertEquals(800L, nextTimeToDropNumConstraints);
// delay, no deadline
jb = createJob(0).setMinimumLatency(800000L);
js = createJobStatus("time", jb);
- js.enqueueTime = 100L;
- nextTimeToDropNumConstraints = mFlexibilityController.getNextConstraintDropTimeElapsed(js);
+ nextTimeToDropNumConstraints = mFlexibilityController
+ .getNextConstraintDropTimeElapsedLocked(js);
assertEquals(130400100, nextTimeToDropNumConstraints);
js.adjustNumRequiredFlexibleConstraints(-1);
- nextTimeToDropNumConstraints = mFlexibilityController.getNextConstraintDropTimeElapsed(js);
+ nextTimeToDropNumConstraints = mFlexibilityController
+ .getNextConstraintDropTimeElapsedLocked(js);
assertEquals(156320100L, nextTimeToDropNumConstraints);
js.adjustNumRequiredFlexibleConstraints(-1);
- nextTimeToDropNumConstraints = mFlexibilityController.getNextConstraintDropTimeElapsed(js);
+ nextTimeToDropNumConstraints = mFlexibilityController
+ .getNextConstraintDropTimeElapsedLocked(js);
assertEquals(182240100L, nextTimeToDropNumConstraints);
// no delay, no deadline
jb = createJob(0);
js = createJobStatus("time", jb);
- js.enqueueTime = 100L;
- nextTimeToDropNumConstraints = mFlexibilityController.getNextConstraintDropTimeElapsed(js);
+ nextTimeToDropNumConstraints = mFlexibilityController
+ .getNextConstraintDropTimeElapsedLocked(js);
assertEquals(129600100, nextTimeToDropNumConstraints);
js.adjustNumRequiredFlexibleConstraints(-1);
- nextTimeToDropNumConstraints = mFlexibilityController.getNextConstraintDropTimeElapsed(js);
+ nextTimeToDropNumConstraints = mFlexibilityController
+ .getNextConstraintDropTimeElapsedLocked(js);
assertEquals(155520100L, nextTimeToDropNumConstraints);
js.adjustNumRequiredFlexibleConstraints(-1);
- nextTimeToDropNumConstraints = mFlexibilityController.getNextConstraintDropTimeElapsed(js);
+ nextTimeToDropNumConstraints = mFlexibilityController
+ .getNextConstraintDropTimeElapsedLocked(js);
assertEquals(181440100L, nextTimeToDropNumConstraints);
// delay, deadline
jb = createJob(0).setOverrideDeadline(1100).setMinimumLatency(100);
js = createJobStatus("time", jb);
- js.enqueueTime = 100L;
- nextTimeToDropNumConstraints = mFlexibilityController.getNextConstraintDropTimeElapsed(js);
+ nextTimeToDropNumConstraints = mFlexibilityController
+ .getNextConstraintDropTimeElapsedLocked(js);
assertEquals(700L, nextTimeToDropNumConstraints);
js.adjustNumRequiredFlexibleConstraints(-1);
- nextTimeToDropNumConstraints = mFlexibilityController.getNextConstraintDropTimeElapsed(js);
+ nextTimeToDropNumConstraints = mFlexibilityController
+ .getNextConstraintDropTimeElapsedLocked(js);
assertEquals(800L, nextTimeToDropNumConstraints);
js.adjustNumRequiredFlexibleConstraints(-1);
- nextTimeToDropNumConstraints = mFlexibilityController.getNextConstraintDropTimeElapsed(js);
+ nextTimeToDropNumConstraints = mFlexibilityController
+ .getNextConstraintDropTimeElapsedLocked(js);
assertEquals(900L, nextTimeToDropNumConstraints);
}
@Test
+ public void testCurPercent() {
+ long deadline = 1000;
+ JobInfo.Builder jb = createJob(0).setOverrideDeadline(deadline);
+ JobStatus js = createJobStatus("time", jb);
+
+ assertEquals(FROZEN_TIME, mFlexibilityController.getLifeCycleBeginningElapsedLocked(js));
+ assertEquals(deadline + FROZEN_TIME,
+ mFlexibilityController.getLifeCycleEndElapsedLocked(js, FROZEN_TIME));
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(Instant.ofEpochMilli(600 + FROZEN_TIME), ZoneOffset.UTC);
+ assertEquals(60, mFlexibilityController.getCurPercentOfLifecycleLocked(js));
+
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(Instant.ofEpochMilli(1400), ZoneOffset.UTC);
+ assertEquals(100, mFlexibilityController.getCurPercentOfLifecycleLocked(js));
+
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(Instant.ofEpochMilli(950 + FROZEN_TIME), ZoneOffset.UTC);
+ assertEquals(95, mFlexibilityController.getCurPercentOfLifecycleLocked(js));
+
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(Instant.ofEpochMilli(FROZEN_TIME), ZoneOffset.UTC);
+ long delay = 100;
+ deadline = 1100;
+ jb = createJob(0).setOverrideDeadline(deadline).setMinimumLatency(delay);
+ js = createJobStatus("time", jb);
+
+ assertEquals(FROZEN_TIME + delay,
+ mFlexibilityController.getLifeCycleBeginningElapsedLocked(js));
+ assertEquals(deadline + FROZEN_TIME,
+ mFlexibilityController.getLifeCycleEndElapsedLocked(js, FROZEN_TIME + delay));
+
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(Instant.ofEpochMilli(600 + FROZEN_TIME + delay), ZoneOffset.UTC);
+
+ assertEquals(60, mFlexibilityController.getCurPercentOfLifecycleLocked(js));
+
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(Instant.ofEpochMilli(1400), ZoneOffset.UTC);
+ assertEquals(100, mFlexibilityController.getCurPercentOfLifecycleLocked(js));
+
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(Instant.ofEpochMilli(950 + FROZEN_TIME + delay), ZoneOffset.UTC);
+ assertEquals(95, mFlexibilityController.getCurPercentOfLifecycleLocked(js));
+ }
+
+ @Test
+ public void testGetLifeCycleBeginningElapsedLocked_prefetch() {
+ // prefetch with lifecycle
+ when(mPrefetchController.getLaunchTimeThresholdMs()).thenReturn(700L);
+ JobInfo.Builder jb = createJob(0).setPrefetch(true);
+ JobStatus js = createJobStatus("time", jb);
+ when(mPrefetchController.getNextEstimatedLaunchTimeLocked(js)).thenReturn(900L);
+ assertEquals(900L - 700L, mFlexibilityController.getLifeCycleBeginningElapsedLocked(js));
+ // prefetch with enqueue
+ jb = createJob(0).setPrefetch(true);
+ js = createJobStatus("time", jb);
+ assertEquals(FROZEN_TIME, mFlexibilityController.getLifeCycleBeginningElapsedLocked(js));
+ // prefetch with delay
+ jb = createJob(0).setPrefetch(true).setMinimumLatency(200);
+ js = createJobStatus("time", jb);
+ assertEquals(200 + FROZEN_TIME, js.getEarliestRunTime());
+ assertEquals(js.getEarliestRunTime(),
+ mFlexibilityController.getLifeCycleBeginningElapsedLocked(js));
+ // prefetch without estimate
+ mFlexibilityController.mPrefetchLifeCycleStart
+ .add(js.getUserId(), js.getSourcePackageName(), 500L);
+ when(mPrefetchController.getNextEstimatedLaunchTimeLocked(js)).thenReturn(Long.MAX_VALUE);
+ jb = createJob(0).setPrefetch(true);
+ js = createJobStatus("time", jb);
+ assertEquals(500L, mFlexibilityController.getLifeCycleBeginningElapsedLocked(js));
+ }
+
+ @Test
+ public void testGetLifeCycleBeginningElapsedLocked_nonPrefetch() {
+ // delay
+ long delay = 100;
+ JobInfo.Builder jb = createJob(0).setMinimumLatency(delay);
+ JobStatus js = createJobStatus("time", jb);
+ assertEquals(delay + FROZEN_TIME,
+ mFlexibilityController.getLifeCycleBeginningElapsedLocked(js));
+ // no delay
+ jb = createJob(0);
+ js = createJobStatus("time", jb);
+ assertEquals(FROZEN_TIME,
+ mFlexibilityController.getLifeCycleBeginningElapsedLocked(js));
+ }
+
+ @Test
+ public void testGetLifeCycleEndElapsedLocked_prefetch() {
+ // prefetch no estimate
+ JobInfo.Builder jb = createJob(0).setPrefetch(true);
+ JobStatus js = createJobStatus("time", jb);
+ when(mPrefetchController.getNextEstimatedLaunchTimeLocked(js)).thenReturn(Long.MAX_VALUE);
+ assertEquals(Long.MAX_VALUE, mFlexibilityController.getLifeCycleEndElapsedLocked(js, 0));
+ // prefetch with estimate
+ jb = createJob(0).setPrefetch(true);
+ js = createJobStatus("time", jb);
+ when(mPrefetchController.getNextEstimatedLaunchTimeLocked(js)).thenReturn(1000L);
+ assertEquals(1000L, mFlexibilityController.getLifeCycleEndElapsedLocked(js, 0));
+ }
+ @Test
+ public void testGetLifeCycleEndElapsedLocked_nonPrefetch() {
+ // deadline
+ JobInfo.Builder jb = createJob(0).setOverrideDeadline(1000L);
+ JobStatus js = createJobStatus("time", jb);
+ assertEquals(1000L + FROZEN_TIME,
+ mFlexibilityController.getLifeCycleEndElapsedLocked(js, 0));
+ // no deadline
+ jb = createJob(0);
+ js = createJobStatus("time", jb);
+ assertEquals(100L + DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS,
+ mFlexibilityController.getLifeCycleEndElapsedLocked(js, 100L));
+ }
+
+ @Test
public void testWontStopJobFromRunning() {
JobStatus js = createJobStatus("testWontStopJobFromRunning", createJob(101));
// Stop satisfied constraints from causing a false positive.
@@ -233,8 +464,9 @@ public class FlexibilityControllerTest {
public void testFlexibilityTracker() {
FlexibilityController.FlexibilityTracker flexTracker =
mFlexibilityController.new
- FlexibilityTracker(FlexibilityController.SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS);
+ FlexibilityTracker(FlexibilityController.NUM_FLEXIBLE_CONSTRAINTS);
+ assertEquals(4, flexTracker.size());
JobStatus[] jobs = new JobStatus[4];
JobInfo.Builder jb;
for (int i = 0; i < jobs.length; i++) {
@@ -282,6 +514,29 @@ public class FlexibilityControllerTest {
assertEquals(0, trackedJobs.get(1).size());
assertEquals(1, trackedJobs.get(2).size());
assertEquals(0, trackedJobs.get(3).size());
+
+ flexTracker.resetJobNumDroppedConstraints(jobs[0]);
+ assertEquals(0, trackedJobs.get(0).size());
+ assertEquals(0, trackedJobs.get(1).size());
+ assertEquals(2, trackedJobs.get(2).size());
+ assertEquals(0, trackedJobs.get(3).size());
+
+ flexTracker.adjustJobsRequiredConstraints(jobs[0], -2);
+
+ assertEquals(1, trackedJobs.get(0).size());
+ assertEquals(0, trackedJobs.get(1).size());
+ assertEquals(1, trackedJobs.get(2).size());
+ assertEquals(0, trackedJobs.get(3).size());
+
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(Instant.ofEpochMilli((DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS / 2)
+ + HOUR_IN_MILLIS), ZoneOffset.UTC);
+
+ flexTracker.resetJobNumDroppedConstraints(jobs[0]);
+ assertEquals(0, trackedJobs.get(0).size());
+ assertEquals(1, trackedJobs.get(1).size());
+ assertEquals(1, trackedJobs.get(2).size());
+ assertEquals(0, trackedJobs.get(3).size());
}
}
@@ -303,14 +558,6 @@ public class FlexibilityControllerTest {
}
@Test
- public void testExceptions_Prefetch() {
- JobInfo.Builder jb = createJob(0);
- jb.setPrefetch(true);
- JobStatus js = createJobStatus("testExceptions_Prefetch", jb);
- assertFalse(js.hasFlexibilityConstraint());
- }
-
- @Test
public void testExceptions_NoFlexibleConstraints() {
JobInfo.Builder jb = createJob(0);
jb.setRequiresDeviceIdle(true);
@@ -381,7 +628,7 @@ public class FlexibilityControllerTest {
@Test
public void testSetConstraintSatisfied_Jobs() {
JobInfo.Builder jb;
- int[] constraintPermutations = {
+ int[] constraintCombinations = {
CONSTRAINT_IDLE & CONSTRAINT_CHARGING & CONSTRAINT_BATTERY_NOT_LOW,
CONSTRAINT_IDLE & CONSTRAINT_BATTERY_NOT_LOW,
CONSTRAINT_IDLE & CONSTRAINT_CHARGING,
@@ -393,9 +640,9 @@ public class FlexibilityControllerTest {
};
int constraints;
- for (int i = 0; i < constraintPermutations.length; i++) {
+ for (int i = 0; i < constraintCombinations.length; i++) {
jb = createJob(i);
- constraints = constraintPermutations[i];
+ constraints = constraintCombinations[i];
jb.setRequiresDeviceIdle((constraints & CONSTRAINT_IDLE) != 0);
jb.setRequiresBatteryNotLow((constraints & CONSTRAINT_BATTERY_NOT_LOW) != 0);
jb.setRequiresCharging((constraints & CONSTRAINT_CHARGING) != 0);
@@ -410,8 +657,8 @@ public class FlexibilityControllerTest {
assertEquals(0, mFlexibilityController.mSatisfiedFlexibleConstraints);
- for (int i = 0; i < constraintPermutations.length; i++) {
- constraints = constraintPermutations[i];
+ for (int i = 0; i < constraintCombinations.length; i++) {
+ constraints = constraintCombinations[i];
mFlexibilityController.setConstraintSatisfied(CONSTRAINT_CHARGING,
(constraints & CONSTRAINT_CHARGING) != 0);
mFlexibilityController.setConstraintSatisfied(CONSTRAINT_IDLE,
@@ -427,6 +674,130 @@ public class FlexibilityControllerTest {
}
}
+ @Test
+ public void testResetJobNumDroppedConstraints() {
+ JobInfo.Builder jb = createJob(22).setOverrideDeadline(100L);
+ JobStatus js = createJobStatus("testResetJobNumDroppedConstraints", jb);
+ js.adjustNumRequiredFlexibleConstraints(3);
+
+ mFlexibilityController.mFlexibilityTracker.add(js);
+
+ assertEquals(3, js.getNumRequiredFlexibleConstraints());
+ assertEquals(0, js.getNumDroppedFlexibleConstraints());
+ assertEquals(1, mFlexibilityController
+ .mFlexibilityTracker.getJobsByNumRequiredConstraints(3).size());
+
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(Instant.ofEpochMilli(155L), ZoneOffset.UTC);
+
+ mFlexibilityController.mFlexibilityTracker.adjustJobsRequiredConstraints(js, -1);
+
+ assertEquals(2, js.getNumRequiredFlexibleConstraints());
+ assertEquals(1, js.getNumDroppedFlexibleConstraints());
+ assertEquals(1, mFlexibilityController
+ .mFlexibilityTracker.getJobsByNumRequiredConstraints(2).size());
+
+ mFlexibilityController.mFlexibilityTracker.resetJobNumDroppedConstraints(js);
+
+ assertEquals(2, js.getNumRequiredFlexibleConstraints());
+ assertEquals(1, js.getNumDroppedFlexibleConstraints());
+ assertEquals(1, mFlexibilityController
+ .mFlexibilityTracker.getJobsByNumRequiredConstraints(2).size());
+
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(Instant.ofEpochMilli(140L), ZoneOffset.UTC);
+
+ mFlexibilityController.mFlexibilityTracker.resetJobNumDroppedConstraints(js);
+
+ assertEquals(3, js.getNumRequiredFlexibleConstraints());
+ assertEquals(0, js.getNumDroppedFlexibleConstraints());
+ assertEquals(1, mFlexibilityController
+ .mFlexibilityTracker.getJobsByNumRequiredConstraints(3).size());
+
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(Instant.ofEpochMilli(175), ZoneOffset.UTC);
+
+ mFlexibilityController.mFlexibilityTracker.resetJobNumDroppedConstraints(js);
+
+ assertEquals(0, js.getNumRequiredFlexibleConstraints());
+ assertEquals(3, js.getNumDroppedFlexibleConstraints());
+
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(Instant.ofEpochMilli(165L), ZoneOffset.UTC);
+
+ mFlexibilityController.mFlexibilityTracker.resetJobNumDroppedConstraints(js);
+
+ assertEquals(1, js.getNumRequiredFlexibleConstraints());
+ assertEquals(2, js.getNumDroppedFlexibleConstraints());
+ assertEquals(1, mFlexibilityController
+ .mFlexibilityTracker.getJobsByNumRequiredConstraints(1).size());
+ }
+
+ @Test
+ public void testOnPrefetchCacheUpdated() {
+ ArraySet<JobStatus> jobs = new ArraySet<JobStatus>();
+ JobInfo.Builder jb = createJob(22).setPrefetch(true);
+ JobStatus js = createJobStatus("onPrefetchCacheUpdated", jb);
+ jobs.add(js);
+ when(mPrefetchController.getLaunchTimeThresholdMs()).thenReturn(7 * HOUR_IN_MILLIS);
+ when(mPrefetchController.getNextEstimatedLaunchTimeLocked(js)).thenReturn(
+ 1150L + mFlexibilityController.mConstants.PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS);
+
+ mFlexibilityController.maybeStartTrackingJobLocked(js, null);
+
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(Instant.ofEpochMilli(150L), ZoneOffset.UTC);
+
+ mFlexibilityController.mPrefetchChangedListener.onPrefetchCacheUpdated(
+ jobs, js.getUserId(), js.getSourcePackageName(), Long.MAX_VALUE,
+ 1150L + mFlexibilityController.mConstants.PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS);
+
+ assertEquals(150L,
+ (long) mFlexibilityController.mPrefetchLifeCycleStart
+ .get(js.getSourceUserId(), js.getSourcePackageName()));
+ assertEquals(150L, mFlexibilityController.getLifeCycleBeginningElapsedLocked(js));
+ assertEquals(1150L,
+ mFlexibilityController.getLifeCycleEndElapsedLocked(js, 150L));
+ assertEquals(0, mFlexibilityController.getCurPercentOfLifecycleLocked(js));
+ assertEquals(650L, mFlexibilityController
+ .getNextConstraintDropTimeElapsedLocked(js));
+ assertEquals(3, js.getNumRequiredFlexibleConstraints());
+ assertEquals(1, mFlexibilityController
+ .mFlexibilityTracker.getJobsByNumRequiredConstraints(3).size());
+ }
+
+ /**
+ * The beginning of a lifecycle for prefetch jobs includes the cached maximum of the last time
+ * the estimated launch time was updated and the last time the app was opened.
+ * When the UID bias updates it means the app might have been opened.
+ * This tests that the cached value is updated properly.
+ */
+ @Test
+ public void testUidUpdatesLifeCycle() {
+ JobInfo.Builder jb = createJob(0).setPrefetch(true);
+ JobStatus js = createJobStatus("uidTest", jb);
+ mFlexibilityController.maybeStartTrackingJobLocked(js, null);
+ mJobStore.add(js);
+
+ final ArraySet<String> pkgs = new ArraySet<>();
+ pkgs.add(js.getSourcePackageName());
+ when(mJobSchedulerService.getPackagesForUidLocked(js.getUid())).thenReturn(pkgs);
+
+ setUidBias(js.getUid(), BIAS_TOP_APP);
+ setUidBias(js.getUid(), BIAS_FOREGROUND_SERVICE);
+ assertEquals(100L, (long) mFlexibilityController.mPrefetchLifeCycleStart
+ .getOrDefault(js.getSourceUserId(), js.getSourcePackageName(), 0L));
+
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(Instant.ofEpochMilli(50L), ZoneOffset.UTC);
+
+ setUidBias(js.getUid(), BIAS_TOP_APP);
+ setUidBias(js.getUid(), BIAS_FOREGROUND_SERVICE);
+ assertEquals(100L, (long) mFlexibilityController
+ .mPrefetchLifeCycleStart.get(js.getSourceUserId(), js.getSourcePackageName()));
+
+ }
+
private void setUidBias(int uid, int bias) {
int prevBias = mJobSchedulerService.getUidBias(uid);
doReturn(bias).when(mJobSchedulerService).getUidBias(uid);
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
index f15e60f32fb7..df523fedc917 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
@@ -33,6 +33,7 @@ import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CONNECTIVI
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CONTENT_TRIGGER;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_DEADLINE;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_DEVICE_NOT_DOZING;
+import static com.android.server.job.controllers.JobStatus.CONSTRAINT_FLEXIBLE;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_IDLE;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_STORAGE_NOT_LOW;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_TIMING_DELAY;
@@ -790,6 +791,83 @@ public class JobStatusTest {
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_BACKGROUND_NOT_RESTRICTED));
}
+ @Test
+ public void testWouldBeReadyWithConstraint_FlexibilityDoesNotAffectReadiness() {
+ final JobStatus job = createJobStatus(
+ new JobInfo.Builder(101, new ComponentName("foo", "bar")).build());
+
+ markImplicitConstraintsSatisfied(job, false);
+ job.setFlexibilityConstraintSatisfied(0, false);
+ assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING));
+ assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_IDLE));
+ assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_BATTERY_NOT_LOW));
+ assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_STORAGE_NOT_LOW));
+ assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_TIMING_DELAY));
+ assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE));
+ assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY));
+ assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER));
+ assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_FLEXIBLE));
+
+ markImplicitConstraintsSatisfied(job, true);
+ job.setFlexibilityConstraintSatisfied(0, false);
+ assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING));
+ assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_IDLE));
+ assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_BATTERY_NOT_LOW));
+ assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_STORAGE_NOT_LOW));
+ assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_TIMING_DELAY));
+ assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE));
+ assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY));
+ assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER));
+ assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_FLEXIBLE));
+
+ markImplicitConstraintsSatisfied(job, false);
+ job.setFlexibilityConstraintSatisfied(0, true);
+ assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING));
+ assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_IDLE));
+ assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_BATTERY_NOT_LOW));
+ assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_STORAGE_NOT_LOW));
+ assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_TIMING_DELAY));
+ assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE));
+ assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY));
+ assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER));
+ assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_FLEXIBLE));
+
+ markImplicitConstraintsSatisfied(job, true);
+ job.setFlexibilityConstraintSatisfied(0, true);
+ assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING));
+ assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_IDLE));
+ assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_BATTERY_NOT_LOW));
+ assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_STORAGE_NOT_LOW));
+ assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_TIMING_DELAY));
+ assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE));
+ assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY));
+ assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER));
+ assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_FLEXIBLE));
+ }
+
+ @Test
+ public void testReadinessStatusWithConstraint_FlexibilityConstraint() {
+ final JobStatus job = createJobStatus(
+ new JobInfo.Builder(101, new ComponentName("foo", "bar")).build());
+ job.setConstraintSatisfied(CONSTRAINT_FLEXIBLE, sElapsedRealtimeClock.millis(), false);
+ markImplicitConstraintsSatisfied(job, true);
+ assertTrue(job.readinessStatusWithConstraint(CONSTRAINT_FLEXIBLE, true));
+ assertFalse(job.readinessStatusWithConstraint(CONSTRAINT_FLEXIBLE, false));
+
+ markImplicitConstraintsSatisfied(job, false);
+ assertFalse(job.readinessStatusWithConstraint(CONSTRAINT_FLEXIBLE, true));
+ assertFalse(job.readinessStatusWithConstraint(CONSTRAINT_FLEXIBLE, false));
+
+ job.setConstraintSatisfied(CONSTRAINT_FLEXIBLE, sElapsedRealtimeClock.millis(), true);
+ markImplicitConstraintsSatisfied(job, true);
+ assertTrue(job.readinessStatusWithConstraint(CONSTRAINT_FLEXIBLE, true));
+ assertFalse(job.readinessStatusWithConstraint(CONSTRAINT_FLEXIBLE, false));
+
+ markImplicitConstraintsSatisfied(job, false);
+ assertFalse(job.readinessStatusWithConstraint(CONSTRAINT_FLEXIBLE, true));
+ assertFalse(job.readinessStatusWithConstraint(CONSTRAINT_FLEXIBLE, false));
+ }
+
private void markImplicitConstraintsSatisfied(JobStatus job, boolean isSatisfied) {
job.setQuotaConstraintSatisfied(sElapsedRealtimeClock.millis(), isSatisfied);
job.setTareWealthConstraintSatisfied(sElapsedRealtimeClock.millis(), isSatisfied);
@@ -797,7 +875,6 @@ public class JobStatusTest {
sElapsedRealtimeClock.millis(), isSatisfied, false);
job.setBackgroundNotRestrictedConstraintSatisfied(
sElapsedRealtimeClock.millis(), isSatisfied, false);
- job.setFlexibilityConstraintSatisfied(sElapsedRealtimeClock.millis(), isSatisfied);
}
private static JobStatus createJobStatus(long earliestRunTimeElapsedMillis,
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
index bcdfc3500e94..7242b1bf1ccb 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
@@ -480,4 +480,32 @@ public class PrefetchControllerTest {
sSystemClock = getShiftedClock(sSystemClock, HOUR_IN_MILLIS + MINUTE_IN_MILLIS);
}
+
+ @Test
+ public void testRegisterOnPrefetchChangedListener() {
+ when(mUsageStatsManagerInternal
+ .getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID))
+ .thenReturn(sSystemClock.millis() + 10 * HOUR_IN_MILLIS);
+ // Needs to get wrapped in an array to get accessed by an inner class.
+ final boolean[] onPrefetchCacheChangedCalled = new boolean[1];
+ final PrefetchController.PrefetchChangedListener prefetchChangedListener =
+ new PrefetchController.PrefetchChangedListener() {
+ @Override
+ public void onPrefetchCacheUpdated(
+ ArraySet<JobStatus> jobs, int userId, String pkgName,
+ long prevEstimatedLaunchTime, long newEstimatedLaunchTime) {
+ onPrefetchCacheChangedCalled[0] = true;
+ }
+ };
+ mPrefetchController.registerPrefetchChangedListener(prefetchChangedListener);
+
+ JobStatus jobStatus = createJobStatus("testRegisterOnPrefetchChangedListener", 1);
+ trackJobs(jobStatus);
+
+ mEstimatedLaunchTimeChangedListener.onEstimatedLaunchTimeChanged(SOURCE_USER_ID,
+ SOURCE_PACKAGE, sSystemClock.millis() + HOUR_IN_MILLIS);
+ verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS)).onControllerStateChanged(any());
+
+ assertTrue(onPrefetchCacheChangedCalled[0]);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java
index 9e986be99e95..da7664b82294 100644
--- a/services/tests/servicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -17,7 +17,10 @@
package com.android.server.tare;
import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import android.util.ArraySet;
import android.util.SparseLongArray;
@@ -99,7 +102,9 @@ public class AgentTrendCalculatorTest {
@Before
public void setUp() {
- mEconomicPolicy = new MockEconomicPolicy(mock(InternalResourceService.class));
+ final InternalResourceService irs = mock(InternalResourceService.class);
+ when(irs.isVip(anyInt(), anyString())).thenReturn(false);
+ mEconomicPolicy = new MockEconomicPolicy(irs);
}
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java
index 13510adbb960..d90d8b8bfac0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java
@@ -68,7 +68,7 @@ public class ScribeTest {
private MockitoSession mMockingSession;
private Scribe mScribeUnderTest;
private File mTestFileDir;
- private final List<PackageInfo> mInstalledPackages = new ArrayList<>();
+ private final List<InstalledPackageInfo> mInstalledPackages = new ArrayList<>();
private final List<Analyst.Report> mReports = new ArrayList<>();
@Mock
@@ -455,6 +455,6 @@ public class ScribeTest {
ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.uid = UserHandle.getUid(userId, Math.abs(pkgName.hashCode()));
pkgInfo.applicationInfo = applicationInfo;
- mInstalledPackages.add(pkgInfo);
+ mInstalledPackages.add(new InstalledPackageInfo(pkgInfo));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceRegistryTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceRegistryTest.java
new file mode 100644
index 000000000000..8cd58abcdf0f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceRegistryTest.java
@@ -0,0 +1,165 @@
+/*
+ * 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.biometrics.sensors.face;
+
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
+import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG;
+import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_WEAK;
+import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
+import static android.hardware.biometrics.SensorProperties.STRENGTH_WEAK;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.IBiometricService;
+import android.hardware.face.FaceSensorProperties;
+import android.hardware.face.FaceSensorPropertiesInternal;
+import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
+import android.hardware.face.IFaceService;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Presubmit
+@SmallTest
+public class FaceServiceRegistryTest {
+
+ private static final int SENSOR_ID_1 = 1;
+ private static final int SENSOR_ID_2 = 2;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private IBiometricService mBiometricService;
+ @Mock
+ private IFaceService mFaceService;
+ @Mock
+ private ServiceProvider mProvider1;
+ @Mock
+ private ServiceProvider mProvider2;
+ @Captor
+ private ArgumentCaptor<Integer> mIdCaptor;
+ @Captor
+ private ArgumentCaptor<Integer> mStrengthCaptor;
+
+ private FaceSensorPropertiesInternal mProvider1Props;
+ private FaceSensorPropertiesInternal mProvider2Props;
+ private FaceServiceRegistry mRegistry;
+
+ @Before
+ public void setup() {
+ mProvider1Props = new FaceSensorPropertiesInternal(SENSOR_ID_1,
+ STRENGTH_WEAK, 5 /* maxEnrollmentsPerUser */,
+ List.of(), FaceSensorProperties.TYPE_RGB,
+ true /* supportsFace Detection */,
+ true /* supportsSelfIllumination */,
+ false /* resetLockoutRequiresHardwareAuthToken */);
+ mProvider2Props = new FaceSensorPropertiesInternal(SENSOR_ID_2,
+ STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */,
+ List.of(), FaceSensorProperties.TYPE_IR,
+ true /* supportsFace Detection */,
+ true /* supportsSelfIllumination */,
+ false /* resetLockoutRequiresHardwareAuthToken */);
+
+ when(mProvider1.getSensorProperties()).thenReturn(List.of(mProvider1Props));
+ when(mProvider1.containsSensor(eq(SENSOR_ID_1))).thenReturn(true);
+ when(mProvider2.getSensorProperties()).thenReturn(List.of(mProvider2Props));
+ when(mProvider2.containsSensor(eq(SENSOR_ID_2))).thenReturn(true);
+ mRegistry = new FaceServiceRegistry(mFaceService, () -> mBiometricService);
+ }
+
+ @Test
+ public void registersAllProviders() throws Exception {
+ mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2));
+
+ assertThat(mRegistry.getProviders()).containsExactly(mProvider1, mProvider2);
+ assertThat(mRegistry.getAllProperties()).containsExactly(mProvider1Props, mProvider2Props);
+ verify(mBiometricService, times(2)).registerAuthenticator(
+ mIdCaptor.capture(), eq(TYPE_FACE), mStrengthCaptor.capture(), any());
+ assertThat(mIdCaptor.getAllValues()).containsExactly(SENSOR_ID_1, SENSOR_ID_2);
+ assertThat(mStrengthCaptor.getAllValues())
+ .containsExactly(BIOMETRIC_WEAK, BIOMETRIC_STRONG);
+ }
+
+ @Test
+ public void getsProviderById() {
+ mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2));
+
+ assertThat(mRegistry.getProviderForSensor(SENSOR_ID_1)).isSameInstanceAs(mProvider1);
+ assertThat(mRegistry.getProviderForSensor(SENSOR_ID_2)).isSameInstanceAs(mProvider2);
+ assertThat(mRegistry.getProviderForSensor(500)).isNull();
+ }
+
+ @Test
+ public void getsSingleProvider() {
+ mRegistry.registerAllInBackground(() -> List.of(mProvider1));
+
+ assertThat(mRegistry.getSingleProvider().second).isSameInstanceAs(mProvider1);
+ assertThat(mRegistry.getProviders()).containsExactly(mProvider1);
+ assertThat(mRegistry.getProviderForSensor(SENSOR_ID_1)).isSameInstanceAs(mProvider1);
+ }
+
+ @Test
+ public void registersListenerBeforeAllRegistered() {
+ final List<FaceSensorPropertiesInternal> all = new ArrayList<>();
+ mRegistry.addAllRegisteredCallback(new IFaceAuthenticatorsRegisteredCallback.Stub() {
+ @Override
+ public void onAllAuthenticatorsRegistered(
+ List<FaceSensorPropertiesInternal> sensors) {
+ all.addAll(sensors);
+ }
+ });
+
+ mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2));
+
+ assertThat(all).containsExactly(mProvider1Props, mProvider2Props);
+ }
+
+ @Test
+ public void registersListenerAfterAllRegistered() {
+ mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2));
+
+ final List<FaceSensorPropertiesInternal> all = new ArrayList<>();
+ mRegistry.addAllRegisteredCallback(new IFaceAuthenticatorsRegisteredCallback.Stub() {
+ @Override
+ public void onAllAuthenticatorsRegistered(
+ List<FaceSensorPropertiesInternal> sensors) {
+ all.addAll(sensors);
+ }
+ });
+
+ assertThat(all).containsExactly(mProvider1Props, mProvider2Props);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java
index 5f88c99b1d1e..0e30782eaece 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/BiometricStateCallbackTest.java
@@ -16,6 +16,8 @@
package com.android.server.biometrics.sensors.fingerprint;
+import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
+
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -24,38 +26,73 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricStateListener;
+import android.hardware.biometrics.SensorPropertiesInternal;
+import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import com.android.server.biometrics.sensors.AuthenticationClient;
+import com.android.server.biometrics.sensors.BiometricServiceProvider;
import com.android.server.biometrics.sensors.BiometricStateCallback;
import com.android.server.biometrics.sensors.EnrollClient;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.List;
@Presubmit
@SmallTest
public class BiometricStateCallbackTest {
- private BiometricStateCallback mCallback;
+ private static final int USER_ID = 10;
+ private static final int SENSOR_ID = 2;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ private BiometricStateCallback<FakeProvider, SensorPropertiesInternal> mCallback;
@Mock
- BiometricStateListener mBiometricStateListener;
+ private UserManager mUserManager;
+ @Mock
+ private BiometricStateListener mBiometricStateListener;
+ @Mock
+ private FakeProvider mFakeProvider;
+
+ private SensorPropertiesInternal mFakeProviderProps;
@Before
public void setup() {
- MockitoAnnotations.initMocks(this);
-
- mCallback = new BiometricStateCallback();
+ mFakeProviderProps = new SensorPropertiesInternal(SENSOR_ID, STRENGTH_STRONG,
+ 5 /* maxEnrollmentsPerUser */, List.of(),
+ false /* resetLockoutRequiresHardwareAuthToken */,
+ false /* resetLockoutRequiresChallenge */);
+ when(mFakeProvider.getSensorProperties()).thenReturn(List.of(mFakeProviderProps));
+ when(mFakeProvider.getSensorProperties(eq(SENSOR_ID))).thenReturn(mFakeProviderProps);
+ when(mFakeProvider.hasEnrollments(eq(SENSOR_ID), eq(USER_ID))).thenReturn(true);
+ when(mUserManager.getAliveUsers()).thenReturn(
+ List.of(new UserInfo(USER_ID, "name", 0)));
+
+ mCallback = new BiometricStateCallback<>(mUserManager);
mCallback.registerBiometricStateListener(mBiometricStateListener);
}
@Test
+ public void startNotifiesEnrollments() {
+ mCallback.start(List.of(mFakeProvider));
+
+ verify(mBiometricStateListener).onEnrollmentsChanged(eq(USER_ID), eq(SENSOR_ID), eq(true));
+ }
+
+ @Test
public void testNoEnrollmentsToEnrollments_callbackNotified() {
testEnrollmentCallback(true /* changed */, true /* isNowEnrolled */,
true /* expectCallback */, true /* expectedCallbackValue */);
@@ -102,4 +139,6 @@ public class BiometricStateCallbackTest {
verify(mBiometricStateListener, never()).onEnrollmentsChanged(anyInt(), anyInt(),
anyBoolean());
}
+
+ private interface FakeProvider extends BiometricServiceProvider<SensorPropertiesInternal> {}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistryTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistryTest.java
new file mode 100644
index 000000000000..67d94a8f02d8
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceRegistryTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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.biometrics.sensors.fingerprint;
+
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG;
+import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_WEAK;
+import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
+import static android.hardware.biometrics.SensorProperties.STRENGTH_WEAK;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.IBiometricService;
+import android.hardware.fingerprint.FingerprintSensorProperties;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
+import android.hardware.fingerprint.IFingerprintService;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Presubmit
+@SmallTest
+public class FingerprintServiceRegistryTest {
+
+ private static final int SENSOR_ID_1 = 1;
+ private static final int SENSOR_ID_2 = 2;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private IBiometricService mBiometricService;
+ @Mock
+ private IFingerprintService mFingerprintService;
+ @Mock
+ private ServiceProvider mProvider1;
+ @Mock
+ private ServiceProvider mProvider2;
+ @Captor
+ private ArgumentCaptor<Integer> mIdCaptor;
+ @Captor
+ private ArgumentCaptor<Integer> mStrengthCaptor;
+
+ private FingerprintSensorPropertiesInternal mProvider1Props;
+ private FingerprintSensorPropertiesInternal mProvider2Props;
+ private FingerprintServiceRegistry mRegistry;
+
+ @Before
+ public void setup() {
+ mProvider1Props = new FingerprintSensorPropertiesInternal(SENSOR_ID_1,
+ STRENGTH_WEAK, 5 /* maxEnrollmentsPerUser */,
+ List.of(), FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
+ false /* resetLockoutRequiresHardwareAuthToken */);
+ mProvider2Props = new FingerprintSensorPropertiesInternal(SENSOR_ID_2,
+ STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */,
+ List.of(), FingerprintSensorProperties.TYPE_UNKNOWN,
+ false /* resetLockoutRequiresHardwareAuthToken */);
+
+ when(mProvider1.getSensorProperties()).thenReturn(List.of(mProvider1Props));
+ when(mProvider1.containsSensor(eq(SENSOR_ID_1))).thenReturn(true);
+ when(mProvider2.getSensorProperties()).thenReturn(List.of(mProvider2Props));
+ when(mProvider2.containsSensor(eq(SENSOR_ID_2))).thenReturn(true);
+ mRegistry = new FingerprintServiceRegistry(mFingerprintService, () -> mBiometricService);
+ }
+
+ @Test
+ public void registersAllProviders() throws Exception {
+ mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2));
+
+ assertThat(mRegistry.getProviders()).containsExactly(mProvider1, mProvider2);
+ assertThat(mRegistry.getAllProperties()).containsExactly(mProvider1Props, mProvider2Props);
+ verify(mBiometricService, times(2)).registerAuthenticator(
+ mIdCaptor.capture(), eq(TYPE_FINGERPRINT), mStrengthCaptor.capture(), any());
+ assertThat(mIdCaptor.getAllValues()).containsExactly(SENSOR_ID_1, SENSOR_ID_2);
+ assertThat(mStrengthCaptor.getAllValues())
+ .containsExactly(BIOMETRIC_WEAK, BIOMETRIC_STRONG);
+ }
+
+ @Test
+ public void getsProviderById() {
+ mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2));
+
+ assertThat(mRegistry.getProviderForSensor(SENSOR_ID_1)).isSameInstanceAs(mProvider1);
+ assertThat(mRegistry.getProviderForSensor(SENSOR_ID_2)).isSameInstanceAs(mProvider2);
+ assertThat(mRegistry.getProviderForSensor(500)).isNull();
+ }
+
+ @Test
+ public void getsSingleProvider() {
+ mRegistry.registerAllInBackground(() -> List.of(mProvider1));
+
+ assertThat(mRegistry.getSingleProvider().second).isSameInstanceAs(mProvider1);
+ assertThat(mRegistry.getProviders()).containsExactly(mProvider1);
+ assertThat(mRegistry.getProviderForSensor(SENSOR_ID_1)).isSameInstanceAs(mProvider1);
+ }
+
+ @Test
+ public void registersListenerBeforeAllRegistered() {
+ final List<FingerprintSensorPropertiesInternal> all = new ArrayList<>();
+ mRegistry.addAllRegisteredCallback(new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
+ @Override
+ public void onAllAuthenticatorsRegistered(
+ List<FingerprintSensorPropertiesInternal> sensors) {
+ all.addAll(sensors);
+ }
+ });
+
+ mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2));
+
+ assertThat(all).containsExactly(mProvider1Props, mProvider2Props);
+ }
+
+ @Test
+ public void registersListenerAfterAllRegistered() {
+ mRegistry.registerAllInBackground(() -> List.of(mProvider1, mProvider2));
+
+ final List<FingerprintSensorPropertiesInternal> all = new ArrayList<>();
+ mRegistry.addAllRegisteredCallback(new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
+ @Override
+ public void onAllAuthenticatorsRegistered(
+ List<FingerprintSensorPropertiesInternal> sensors) {
+ all.addAll(sensors);
+ }
+ });
+
+ assertThat(all).containsExactly(mProvider1Props, mProvider2Props);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
index ca3677e3b5ff..a4048a27dfb5 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
@@ -32,7 +32,8 @@ import android.hardware.biometrics.fingerprint.FingerprintSensorType;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.SensorLocation;
import android.hardware.biometrics.fingerprint.SensorProps;
-import android.os.RemoteException;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
import android.testing.TestableContext;
@@ -52,6 +53,8 @@ import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
@Presubmit
@SmallTest
@@ -94,9 +97,12 @@ public class FingerprintServiceTest {
mContext.getTestablePermissions().setPermission(
USE_BIOMETRIC_INTERNAL, PackageManager.PERMISSION_GRANTED);
+ }
+ private void initServiceWith(String... aidlInstances) {
mService = new FingerprintService(mContext, mBiometricContext,
() -> mIBiometricService,
+ () -> aidlInstances,
(fqName) -> {
if (fqName.endsWith(NAME_DEFAULT)) return mIFingerprintDefault;
if (fqName.endsWith(NAME_VIRTUAL)) return mIFingerprintVirtual;
@@ -105,29 +111,50 @@ public class FingerprintServiceTest {
}
@Test
- public void registerAuthenticators_defaultOnly() throws RemoteException {
- mService.registerAuthenticatorsForService(List.of(NAME_DEFAULT, NAME_VIRTUAL), List.of());
+ public void registerAuthenticators_defaultOnly() throws Exception {
+ initServiceWith(NAME_DEFAULT, NAME_VIRTUAL);
+
+ mService.mServiceWrapper.registerAuthenticators(List.of());
+ waitForRegistration();
verify(mIBiometricService).registerAuthenticator(eq(ID_DEFAULT), anyInt(), anyInt(), any());
}
@Test
- public void registerAuthenticators_virtualOnly() throws RemoteException {
+ public void registerAuthenticators_virtualOnly() throws Exception {
+ initServiceWith(NAME_DEFAULT, NAME_VIRTUAL);
Settings.Secure.putInt(mSettingsRule.mockContentResolver(mContext),
Settings.Secure.BIOMETRIC_VIRTUAL_ENABLED, 1);
- mService.registerAuthenticatorsForService(List.of(NAME_DEFAULT, NAME_VIRTUAL), List.of());
+ mService.mServiceWrapper.registerAuthenticators(List.of());
+ waitForRegistration();
verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL), anyInt(), anyInt(), any());
}
@Test
- public void registerAuthenticators_virtualAlwaysWhenNoOther() throws RemoteException {
- mService.registerAuthenticatorsForService(List.of(NAME_VIRTUAL), List.of());
+ public void registerAuthenticators_virtualAlwaysWhenNoOther() throws Exception {
+ initServiceWith(NAME_VIRTUAL);
+
+ mService.mServiceWrapper.registerAuthenticators(List.of());
+ waitForRegistration();
verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL), anyInt(), anyInt(), any());
}
+ private void waitForRegistration() throws Exception {
+ final CountDownLatch latch = new CountDownLatch(1);
+ mService.mServiceWrapper.addAuthenticatorsRegisteredCallback(
+ new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
+ @Override
+ public void onAllAuthenticatorsRegistered(
+ List<FingerprintSensorPropertiesInternal> sensors) {
+ latch.countDown();
+ }
+ });
+ latch.await(5, TimeUnit.SECONDS);
+ }
+
private static SensorProps createProps(int id, byte strength, byte type) {
final SensorProps props = new SensorProps();
props.commonProps = new CommonProps();
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java
index e305957c6290..c4435b77628a 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java
@@ -39,16 +39,23 @@ public final class BrightnessEventTest {
mBrightnessEvent = new BrightnessEvent(1);
mBrightnessEvent.setReason(
getReason(BrightnessReason.REASON_DOZE, BrightnessReason.MODIFIER_LOW_POWER));
+ mBrightnessEvent.setPhysicalDisplayId("test");
mBrightnessEvent.setLux(100.0f);
+ mBrightnessEvent.setFastAmbientLux(90.0f);
+ mBrightnessEvent.setSlowAmbientLux(85.0f);
mBrightnessEvent.setPreThresholdLux(150.0f);
mBrightnessEvent.setTime(System.currentTimeMillis());
+ mBrightnessEvent.setInitialBrightness(25.0f);
mBrightnessEvent.setBrightness(0.6f);
mBrightnessEvent.setRecommendedBrightness(0.6f);
mBrightnessEvent.setHbmMax(0.62f);
+ mBrightnessEvent.setRbcStrength(-1);
mBrightnessEvent.setThermalMax(0.65f);
+ mBrightnessEvent.setPowerFactor(0.2f);
mBrightnessEvent.setHbmMode(BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF);
mBrightnessEvent.setFlags(0);
mBrightnessEvent.setAdjustmentFlags(0);
+ mBrightnessEvent.setAutomaticBrightnessEnabled(true);
}
@Test
@@ -63,12 +70,15 @@ public final class BrightnessEventTest {
public void testToStringWorksAsExpected() {
String actualString = mBrightnessEvent.toString(false);
String expectedString =
- "BrightnessEvent: disp=1, brt=0.6, rcmdBrt=0.6, preBrt=NaN, lux=100.0, preLux=150"
- + ".0, hbmMax=0.62, hbmMode=off, thrmMax=0.65, flags=, reason=doze [ "
- + "low_pwr ]";
+ "BrightnessEvent: disp=1, physDisp=test, brt=0.6, initBrt=25.0, rcmdBrt=0.6,"
+ + " preBrt=NaN, lux=100.0, fastLux=90.0, slowLux=85.0, preLux=150.0, hbmMax=0.62,"
+ + " hbmMode=off, rbcStrength=-1, thrmMax=0.65, powerFactor=0.2, flags=, reason=doze"
+ + " [ low_pwr ], autoBrightness=true";
assertEquals(actualString, expectedString);
}
+
+
private BrightnessReason getReason(int reason, int modifier) {
BrightnessReason brightnessReason = new BrightnessReason();
brightnessReason.setReason(reason);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 2cf9c01a4476..28d2aa157e0a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1252,7 +1252,15 @@ public class DisplayContentTests extends WindowTestsBase {
public void testComputeImeControlTarget() throws Exception {
final DisplayContent dc = createNewDisplay();
dc.setRemoteInsetsController(createDisplayWindowInsetsController());
- dc.setImeInputTarget(createWindow(null, TYPE_BASE_APPLICATION, "app"));
+ dc.mCurrentFocus = createWindow(null, TYPE_BASE_APPLICATION, "app");
+
+ // Expect returning null IME control target when the focus window has not yet been the
+ // IME input target (e.g. IME is restarting) in fullscreen windowing mode.
+ dc.setImeInputTarget(null);
+ assertFalse(dc.mCurrentFocus.inMultiWindowMode());
+ assertNull(dc.computeImeControlTarget());
+
+ dc.setImeInputTarget(dc.mCurrentFocus);
dc.setImeLayeringTarget(dc.getImeInputTarget().getWindowState());
assertEquals(dc.getImeInputTarget().getWindowState(), dc.computeImeControlTarget());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index fde6e3cc0b4f..6d33aaffdf24 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -97,6 +97,7 @@ import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
import org.junit.After;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -1537,6 +1538,108 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN})
+ public void testOverrideSplitScreenAspectRatioForUnresizablePortraitApps() {
+ final int displayWidth = 1400;
+ final int displayHeight = 1600;
+ setUpDisplaySizeWithApp(displayWidth, displayHeight);
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setMinAspectRatio(1.1f)
+ .setUid(android.os.Process.myUid())
+ .build();
+ // Setup Letterbox Configuration
+ activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ activity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f);
+ // Non-resizable portrait activity
+ prepareUnresizable(activity, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+ float expectedAspectRatio = 1f * displayWidth / getExpectedSplitSize(displayHeight);
+ final Rect afterBounds = activity.getBounds();
+ final float afterAspectRatio = (float) (afterBounds.height()) / afterBounds.width();
+ Assert.assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN})
+ public void testOverrideSplitScreenAspectRatioForUnresizablePortraitAppsFromLandscape() {
+ final int displayWidth = 1600;
+ final int displayHeight = 1400;
+ setUpDisplaySizeWithApp(displayWidth, displayHeight);
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setMinAspectRatio(1.1f)
+ .setUid(android.os.Process.myUid())
+ .build();
+ // Setup Letterbox Configuration
+ activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ activity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f);
+ // Non-resizable portrait activity
+ prepareUnresizable(activity, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+ float expectedAspectRatio = 1f * displayHeight / getExpectedSplitSize(displayWidth);
+ final Rect afterBounds = activity.getBounds();
+ final float afterAspectRatio = (float) (afterBounds.height()) / afterBounds.width();
+ Assert.assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN})
+ @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY})
+ public void testOverrideSplitScreenAspectRatioForUnresizableLandscapeApps() {
+ final int displayWidth = 1400;
+ final int displayHeight = 1600;
+ setUpDisplaySizeWithApp(displayWidth, displayHeight);
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setMinAspectRatio(1.1f)
+ .setUid(android.os.Process.myUid())
+ .build();
+ // Setup Letterbox Configuration
+ activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ activity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f);
+ // Non-resizable portrait activity
+ prepareUnresizable(activity, ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+ float expectedAspectRatio = 1f * displayWidth / getExpectedSplitSize(displayHeight);
+ final Rect afterBounds = activity.getBounds();
+ final float afterAspectRatio = (float) (afterBounds.width()) / afterBounds.height();
+ Assert.assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN})
+ @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY})
+ public void testOverrideSplitScreenAspectRatioForUnresizableLandscapeAppsFromLandscape() {
+ final int displayWidth = 1600;
+ final int displayHeight = 1400;
+ setUpDisplaySizeWithApp(displayWidth, displayHeight);
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setMinAspectRatio(1.1f)
+ .setUid(android.os.Process.myUid())
+ .build();
+ // Setup Letterbox Configuration
+ activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ activity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f);
+ // Non-resizable portrait activity
+ prepareUnresizable(activity, ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+ float expectedAspectRatio = 1f * displayHeight / getExpectedSplitSize(displayWidth);
+ final Rect afterBounds = activity.getBounds();
+ final float afterAspectRatio = (float) (afterBounds.width()) / afterBounds.height();
+ Assert.assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
+ }
+
+ @Test
public void testSplitAspectRatioForUnresizableLandscapeApps() {
// Set up a display in portrait and ignoring orientation request.
int screenWidth = 1400;
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 1e0e8366c385..a1310849d84f 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -18,6 +18,8 @@ package com.android.server.voiceinteraction;
import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD;
import static android.Manifest.permission.RECORD_AUDIO;
+import static android.service.attention.AttentionService.PROXIMITY_UNKNOWN;
+import static android.service.voice.HotwordDetectedResult.EXTRA_PROXIMITY_METERS;
import static android.service.voice.HotwordDetectionService.AUDIO_SOURCE_EXTERNAL;
import static android.service.voice.HotwordDetectionService.AUDIO_SOURCE_MICROPHONE;
import static android.service.voice.HotwordDetectionService.INITIALIZATION_STATUS_SUCCESS;
@@ -58,6 +60,7 @@ import static com.android.server.voiceinteraction.SoundTriggerSessionPermissions
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
+import android.attention.AttentionManagerInternal;
import android.content.ComponentName;
import android.content.ContentCaptureOptions;
import android.content.Context;
@@ -186,6 +189,12 @@ final class HotwordDetectionConnection {
final int mUser;
final Context mContext;
+ @Nullable final AttentionManagerInternal mAttentionManagerInternal;
+
+ final AttentionManagerInternal.ProximityUpdateCallbackInternal mProximityCallbackInternal =
+ this::setProximityMeters;
+
+
volatile HotwordDetectionServiceIdentity mIdentity;
private IMicrophoneHotwordDetectionVoiceInteractionCallback mSoftwareCallback;
private Instant mLastRestartInstant;
@@ -206,6 +215,8 @@ final class HotwordDetectionConnection {
private @NonNull ServiceConnection mRemoteHotwordDetectionService;
private IBinder mAudioFlinger;
private boolean mDebugHotwordLogging = false;
+ @GuardedBy("mLock")
+ private double mProximityMeters = PROXIMITY_UNKNOWN;
HotwordDetectionConnection(Object lock, Context context, int voiceInteractionServiceUid,
Identity voiceInteractorIdentity, ComponentName serviceName, int userId,
@@ -233,6 +244,10 @@ final class HotwordDetectionConnection {
mServiceConnectionFactory = new ServiceConnectionFactory(intent, bindInstantServiceAllowed);
mRemoteHotwordDetectionService = mServiceConnectionFactory.createLocked();
+ mAttentionManagerInternal = LocalServices.getService(AttentionManagerInternal.class);
+ if (mAttentionManagerInternal != null) {
+ mAttentionManagerInternal.onStartProximityUpdates(mProximityCallbackInternal);
+ }
mLastRestartInstant = Instant.now();
updateStateAfterProcessStart(options, sharedMemory);
@@ -397,6 +412,9 @@ final class HotwordDetectionConnection {
if (mAudioFlinger != null) {
mAudioFlinger.unlinkToDeath(mAudioServerDeathRecipient, /* flags= */ 0);
}
+ if (mAttentionManagerInternal != null) {
+ mAttentionManagerInternal.onStopProximityUpdates(mProximityCallbackInternal);
+ }
}
void updateStateLocked(PersistableBundle options, SharedMemory sharedMemory) {
@@ -464,6 +482,7 @@ final class HotwordDetectionConnection {
mSoftwareCallback.onError();
return;
}
+ saveProximityMetersToBundle(result);
mSoftwareCallback.onDetected(result, null, null);
if (result != null) {
Slog.i(TAG, "Egressed " + HotwordDetectedResult.getUsageSize(result)
@@ -591,6 +610,7 @@ final class HotwordDetectionConnection {
externalCallback.onError(CALLBACK_ONDETECTED_GOT_SECURITY_EXCEPTION);
return;
}
+ saveProximityMetersToBundle(result);
externalCallback.onKeyphraseDetected(recognitionEvent, result);
if (result != null) {
Slog.i(TAG, "Egressed " + HotwordDetectedResult.getUsageSize(result)
@@ -1159,6 +1179,20 @@ final class HotwordDetectionConnection {
});
}
+ private void saveProximityMetersToBundle(HotwordDetectedResult result) {
+ synchronized (mLock) {
+ if (result != null && mProximityMeters != PROXIMITY_UNKNOWN) {
+ result.getExtras().putDouble(EXTRA_PROXIMITY_METERS, mProximityMeters);
+ }
+ }
+ }
+
+ private void setProximityMeters(double proximityMeters) {
+ synchronized (mLock) {
+ mProximityMeters = proximityMeters;
+ }
+ }
+
private static void bestEffortClose(Closeable... closeables) {
for (Closeable closeable : closeables) {
bestEffortClose(closeable);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 4d18dfe6a62c..5ad4edf71075 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -39,6 +39,7 @@ import android.service.carrier.CarrierService;
import android.telecom.TelecomManager;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.telephony.data.ApnSetting;
+import android.telephony.data.DataCallResponse;
import android.telephony.gba.TlsParams;
import android.telephony.gba.UaSecurityProtocolIdentifier;
import android.telephony.ims.ImsReasonInfo;
@@ -1125,6 +1126,27 @@ public class CarrierConfigManager {
public static final String KEY_DEFAULT_MTU_INT = "default_mtu_int";
/**
+ * The data call retry configuration for different types of APN.
+ * @hide
+ */
+ public static final String KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS =
+ "carrier_data_call_retry_config_strings";
+
+ /**
+ * Delay in milliseconds between trying APN from the pool
+ * @hide
+ */
+ public static final String KEY_CARRIER_DATA_CALL_APN_DELAY_DEFAULT_LONG =
+ "carrier_data_call_apn_delay_default_long";
+
+ /**
+ * Faster delay in milliseconds between trying APN from the pool
+ * @hide
+ */
+ public static final String KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG =
+ "carrier_data_call_apn_delay_faster_long";
+
+ /**
* Delay in milliseconds for retrying APN after disconnect
* @hide
*/
@@ -1132,6 +1154,21 @@ public class CarrierConfigManager {
"carrier_data_call_apn_retry_after_disconnect_long";
/**
+ * The maximum times for telephony to retry data setup on the same APN requested by
+ * network through the data setup response retry timer
+ * {@link DataCallResponse#getRetryDurationMillis()}. This is to prevent that network keeps
+ * asking device to retry data setup forever and causes power consumption issue. For infinite
+ * retring same APN, configure this as 2147483647 (i.e. {@link Integer#MAX_VALUE}).
+ *
+ * Note if network does not suggest any retry timer, frameworks uses the retry configuration
+ * from {@link #KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS}, and the maximum retry times could
+ * be configured there.
+ * @hide
+ */
+ public static final String KEY_CARRIER_DATA_CALL_RETRY_NETWORK_REQUESTED_MAX_COUNT_INT =
+ "carrier_data_call_retry_network_requested_max_count_int";
+
+ /**
* Data call setup permanent failure causes by the carrier
*/
public static final String KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS =
@@ -1151,6 +1188,19 @@ public class CarrierConfigManager {
"carrier_metered_roaming_apn_types_strings";
/**
+ * APN types that are not allowed on cellular
+ * @hide
+ */
+ public static final String KEY_CARRIER_WWAN_DISALLOWED_APN_TYPES_STRING_ARRAY =
+ "carrier_wwan_disallowed_apn_types_string_array";
+
+ /**
+ * APN types that are not allowed on IWLAN
+ * @hide
+ */
+ public static final String KEY_CARRIER_WLAN_DISALLOWED_APN_TYPES_STRING_ARRAY =
+ "carrier_wlan_disallowed_apn_types_string_array";
+ /**
* CDMA carrier ERI (Enhanced Roaming Indicator) file name
* @hide
*/
@@ -8369,6 +8419,7 @@ public class CarrierConfigManager {
* "1800000, maximum_retries=20" means for those capabilities, retry happens in 2.5s, 3s, 5s,
* 10s, 15s, 20s, 40s, 1m, 2m, 4m, 10m, 20m, 30m, 30m, 30m, until reaching 20 retries.
*
+ * // TODO: remove KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS
* @hide
*/
public static final String KEY_TELEPHONY_DATA_SETUP_RETRY_RULES_STRING_ARRAY =
@@ -8770,13 +8821,27 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL, false);
sDefaults.putBoolean(KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL, false);
sDefaults.putInt(KEY_DEFAULT_MTU_INT, 1500);
+ sDefaults.putStringArray(KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS, new String[]{
+ "default:default_randomization=2000,5000,10000,20000,40000,80000:5000,160000:5000,"
+ + "320000:5000,640000:5000,1280000:5000,1800000:5000",
+ "mms:default_randomization=2000,5000,10000,20000,40000,80000:5000,160000:5000,"
+ + "320000:5000,640000:5000,1280000:5000,1800000:5000",
+ "ims:max_retries=10, 5000, 5000, 5000",
+ "others:max_retries=3, 5000, 5000, 5000"});
+ sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_DELAY_DEFAULT_LONG, 20000);
+ sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG, 3000);
sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_RETRY_AFTER_DISCONNECT_LONG, 3000);
+ sDefaults.putInt(KEY_CARRIER_DATA_CALL_RETRY_NETWORK_REQUESTED_MAX_COUNT_INT, 3);
sDefaults.putString(KEY_CARRIER_ERI_FILE_NAME_STRING, "eri.xml");
sDefaults.putInt(KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT, 7200);
sDefaults.putStringArray(KEY_CARRIER_METERED_APN_TYPES_STRINGS,
new String[]{"default", "mms", "dun", "supl"});
sDefaults.putStringArray(KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
new String[]{"default", "mms", "dun", "supl"});
+ sDefaults.putStringArray(KEY_CARRIER_WWAN_DISALLOWED_APN_TYPES_STRING_ARRAY,
+ new String[]{""});
+ sDefaults.putStringArray(KEY_CARRIER_WLAN_DISALLOWED_APN_TYPES_STRING_ARRAY,
+ new String[]{""});
sDefaults.putIntArray(KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY,
new int[] {TelephonyManager.NETWORK_TYPE_CDMA, TelephonyManager.NETWORK_TYPE_1xRTT,
TelephonyManager.NETWORK_TYPE_EVDO_0, TelephonyManager.NETWORK_TYPE_EVDO_A,
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 3ed87e1b9fe2..f794a7971343 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -24,7 +24,6 @@ import android.content.ContentValues;
import android.database.Cursor;
import android.hardware.radio.V1_5.ApnTypes;
import android.net.Uri;
-import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.provider.Telephony;
@@ -964,7 +963,7 @@ public class ApnSetting implements Parcelable {
ServiceState.convertBearerBitmaskToNetworkTypeBitmask(bearerBitmask);
}
int mtuV4 = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU_V4));
- if (mtuV4 == -1) {
+ if (mtuV4 == UNSET_MTU) {
mtuV4 = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU));
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index da1ffcdea812..0ce6b14ce3b0 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2510,6 +2510,9 @@ interface ITelephony {
CellIdentity getLastKnownCellIdentity(int subId, String callingPackage,
String callingFeatureId);
+ /** Check if telephony new data stack is enabled. */
+ boolean isUsingNewDataStack();
+
/**
* @return true if the modem service is set successfully, false otherwise.
*/
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
index e138d332bb17..be7fb7315955 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
@@ -38,7 +38,10 @@ abstract class BaseTest @JvmOverloads constructor(
) {
init {
testSpec.setIsTablet(
- WindowManagerStateHelper(instrumentation).currentState.wmState.isTablet
+ WindowManagerStateHelper(
+ instrumentation,
+ clearCacheAfterParsing = false
+ ).currentState.wmState.isTablet
)
tapl.setExpectedRotationCheckEnabled(true)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
index 75900df978df..d08cb5525d5e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
@@ -26,4 +26,9 @@ import com.android.server.wm.flicker.rules.ChangeDisplayOrientationRule
* @param rotation New device rotation
*/
fun Flicker.setRotation(rotation: Int) =
- ChangeDisplayOrientationRule.setRotation(rotation, instrumentation, wmHelper)
+ ChangeDisplayOrientationRule.setRotation(
+ rotation,
+ instrumentation,
+ clearCacheAfterParsing = false,
+ wmHelper = wmHelper
+)
diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
index c4cb33da4a6d..44265511ee50 100644
--- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
@@ -123,16 +123,27 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
}
boolean found = false;
+ boolean remountSystem = false;
+ boolean remountVendor = false;
for (String file : files) {
CommandResult result = getDevice().executeShellV2Command("ls " + file);
if (result.getStatus() == CommandStatus.SUCCESS) {
found = true;
- break;
+ if (file.startsWith("/system")) {
+ remountSystem = true;
+ } else if (file.startsWith("/vendor")) {
+ remountVendor = true;
+ }
}
}
if (found) {
- getDevice().remountSystemWritable();
+ if (remountSystem) {
+ getDevice().remountSystemWritable();
+ }
+ if (remountVendor) {
+ getDevice().remountVendorWritable();
+ }
for (String file : files) {
getDevice().executeShellCommand("rm -rf " + file);
}
@@ -150,7 +161,11 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
if (!getDevice().isAdbRoot()) {
getDevice().enableAdbRoot();
}
- getDevice().remountSystemWritable();
+ if ("system".equals(partition)) {
+ getDevice().remountSystemWritable();
+ } else if ("vendor".equals(partition)) {
+ getDevice().remountVendorWritable();
+ }
assertTrue(getDevice().pushFile(apex, "/" + partition + "/apex/" + fileName));
}
@@ -158,7 +173,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
if (!getDevice().isAdbRoot()) {
getDevice().enableAdbRoot();
}
- getDevice().remountSystemWritable();
+ getDevice().remountVendorWritable();
File file = File.createTempFile("test-vendor-apex-allow-list", ".xml");
try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
final String fmt =
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index fecc7b3cbf37..d02fd83df6af 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -1028,7 +1028,6 @@ int doDump(Bundle* bundle)
// These permissions are required by services implementing services
// the system binds to (IME, Accessibility, PrintServices, etc.)
bool hasBindDeviceAdminPermission = false;
- bool hasBindInputMethodPermission = false;
bool hasBindAccessibilityServicePermission = false;
bool hasBindPrintServicePermission = false;
bool hasBindNfcServicePermission = false;
@@ -1757,7 +1756,6 @@ int doDump(Bundle* bundle)
hasMetaHostPaymentCategory = false;
hasMetaOffHostPaymentCategory = false;
hasBindDeviceAdminPermission = false;
- hasBindInputMethodPermission = false;
hasBindAccessibilityServicePermission = false;
hasBindPrintServicePermission = false;
hasBindNfcServicePermission = false;
@@ -1871,9 +1869,7 @@ int doDump(Bundle* bundle)
String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR,
&error);
if (error == "") {
- if (permission == "android.permission.BIND_INPUT_METHOD") {
- hasBindInputMethodPermission = true;
- } else if (permission ==
+ if (permission ==
"android.permission.BIND_ACCESSIBILITY_SERVICE") {
hasBindAccessibilityServicePermission = true;
} else if (permission ==
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index b9de11b0026b..47750fc11a6e 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -2970,14 +2970,6 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>&
}
e->setNameIndex(keyStrings.add(e->getName(), true));
- // If this entry has no values for other configs,
- // and is the default config, then it is special. Otherwise
- // we want to add it with the config info.
- ConfigDescription* valueConfig = NULL;
- if (N != 1 || config == nullConfig) {
- valueConfig = &config;
- }
-
status_t err = e->prepareFlatten(&valueStrings, this,
&configTypeName, &config);
if (err != NO_ERROR) {
diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt b/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
index d19f4cceeaaf..12f3a1659313 100644
--- a/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
+++ b/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
@@ -19,6 +19,8 @@ package com.google.android.lint
import com.android.tools.lint.client.api.IssueRegistry
import com.android.tools.lint.client.api.Vendor
import com.android.tools.lint.detector.api.CURRENT_API
+import com.google.android.lint.aidl.EnforcePermissionDetector
+import com.google.android.lint.aidl.ManualPermissionCheckDetector
import com.google.android.lint.parcel.SaferParcelChecker
import com.google.auto.service.AutoService
@@ -36,6 +38,7 @@ class AndroidFrameworkIssueRegistry : IssueRegistry() {
CallingSettingsNonUserGetterMethodsDetector.ISSUE_NON_USER_GETTER_CALLED,
EnforcePermissionDetector.ISSUE_MISSING_ENFORCE_PERMISSION,
EnforcePermissionDetector.ISSUE_MISMATCHING_ENFORCE_PERMISSION,
+ ManualPermissionCheckDetector.ISSUE_USE_ENFORCE_PERMISSION_ANNOTATION,
SaferParcelChecker.ISSUE_UNSAFE_API_USAGE,
PackageVisibilityDetector.ISSUE_PACKAGE_NAME_NO_PACKAGE_VISIBILITY_FILTERS
)
diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/Constants.kt b/tools/lint/checks/src/main/java/com/google/android/lint/Constants.kt
new file mode 100644
index 000000000000..82eb8ed8f621
--- /dev/null
+++ b/tools/lint/checks/src/main/java/com/google/android/lint/Constants.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.google.android.lint
+
+import com.google.android.lint.model.Method
+
+const val CLASS_STUB = "Stub"
+const val CLASS_CONTEXT = "android.content.Context"
+const val CLASS_ACTIVITY_MANAGER_SERVICE = "com.android.server.am.ActivityManagerService"
+const val CLASS_ACTIVITY_MANAGER_INTERNAL = "android.app.ActivityManagerInternal"
+
+// Enforce permission APIs
+val ENFORCE_PERMISSION_METHODS = listOf(
+ Method(CLASS_CONTEXT, "checkPermission"),
+ Method(CLASS_CONTEXT, "checkCallingPermission"),
+ Method(CLASS_CONTEXT, "checkCallingOrSelfPermission"),
+ Method(CLASS_CONTEXT, "enforcePermission"),
+ Method(CLASS_CONTEXT, "enforceCallingPermission"),
+ Method(CLASS_CONTEXT, "enforceCallingOrSelfPermission"),
+ Method(CLASS_ACTIVITY_MANAGER_SERVICE, "checkPermission"),
+ Method(CLASS_ACTIVITY_MANAGER_INTERNAL, "enforceCallingPermission")
+)
diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/PackageVisibilityDetector.kt b/tools/lint/checks/src/main/java/com/google/android/lint/PackageVisibilityDetector.kt
index 192dba17dd5b..48540b1da565 100644
--- a/tools/lint/checks/src/main/java/com/google/android/lint/PackageVisibilityDetector.kt
+++ b/tools/lint/checks/src/main/java/com/google/android/lint/PackageVisibilityDetector.kt
@@ -30,13 +30,13 @@ import com.android.tools.lint.detector.api.interprocedural.CallGraphResult
import com.android.tools.lint.detector.api.interprocedural.searchForPaths
import com.intellij.psi.PsiAnonymousClass
import com.intellij.psi.PsiMethod
+import java.util.LinkedList
import org.jetbrains.uast.UCallExpression
import org.jetbrains.uast.UElement
import org.jetbrains.uast.UMethod
import org.jetbrains.uast.UParameter
import org.jetbrains.uast.USimpleNameReferenceExpression
import org.jetbrains.uast.visitor.AbstractUastVisitor
-import java.util.LinkedList
/**
* A lint checker to detect potential package visibility issues for system's APIs. APIs working
@@ -362,14 +362,18 @@ class PackageVisibilityDetector : Detector(), SourceCodeScanner {
name: String,
matchArgument: Boolean = true,
checkCaller: Boolean = false
- ): this(clazz, name) {
+ ) : this(clazz, name) {
this.matchArgument = matchArgument
this.checkCaller = checkCaller
}
constructor(
method: PsiMethod
- ): this(method.containingClass?.qualifiedName ?: "", method.name)
+ ) : this(method.containingClass?.qualifiedName ?: "", method.name)
+
+ constructor(
+ method: com.google.android.lint.model.Method
+ ) : this(method.clazz, method.name)
}
/**
@@ -380,7 +384,7 @@ class PackageVisibilityDetector : Detector(), SourceCodeScanner {
val typeName: String,
val parameterName: String
) {
- constructor(uParameter: UParameter): this(
+ constructor(uParameter: UParameter) : this(
uParameter.type.canonicalText,
uParameter.name.lowercase()
)
@@ -405,19 +409,13 @@ class PackageVisibilityDetector : Detector(), SourceCodeScanner {
// A valid call path list needs to contain a start node and a sink node
private const val VALID_CALL_PATH_NODES_SIZE = 2
- private const val CLASS_STUB = "Stub"
private const val CLASS_STRING = "java.lang.String"
private const val CLASS_PACKAGE_MANAGER = "android.content.pm.PackageManager"
private const val CLASS_IPACKAGE_MANAGER = "android.content.pm.IPackageManager"
private const val CLASS_APPOPS_MANAGER = "android.app.AppOpsManager"
- private const val CLASS_CONTEXT = "android.content.Context"
private const val CLASS_BINDER = "android.os.Binder"
private const val CLASS_PACKAGE_MANAGER_INTERNAL =
"android.content.pm.PackageManagerInternal"
- private const val CLASS_ACTIVITY_MANAGER_SERVICE =
- "com.android.server.am.ActivityManagerService"
- private const val CLASS_ACTIVITY_MANAGER_INTERNAL =
- "android.app.ActivityManagerInternal"
// Patterns of package name parameter
private val PACKAGE_NAME_PATTERNS = setOf(
@@ -455,16 +453,9 @@ class PackageVisibilityDetector : Detector(), SourceCodeScanner {
)
// Enforce permission APIs
- private val ENFORCE_PERMISSION_METHODS = listOf(
- Method(CLASS_CONTEXT, "checkPermission"),
- Method(CLASS_CONTEXT, "checkCallingPermission"),
- Method(CLASS_CONTEXT, "checkCallingOrSelfPermission"),
- Method(CLASS_CONTEXT, "enforcePermission"),
- Method(CLASS_CONTEXT, "enforceCallingPermission"),
- Method(CLASS_CONTEXT, "enforceCallingOrSelfPermission"),
- Method(CLASS_ACTIVITY_MANAGER_SERVICE, "checkPermission"),
- Method(CLASS_ACTIVITY_MANAGER_INTERNAL, "enforceCallingPermission")
- )
+ private val ENFORCE_PERMISSION_METHODS =
+ com.google.android.lint.ENFORCE_PERMISSION_METHODS
+ .map(PackageVisibilityDetector::Method)
private val BYPASS_STUBS = listOf(
"android.content.pm.IPackageDataObserver.Stub",
diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/aidl/Constants.kt b/tools/lint/checks/src/main/java/com/google/android/lint/aidl/Constants.kt
new file mode 100644
index 000000000000..8ee3763e5079
--- /dev/null
+++ b/tools/lint/checks/src/main/java/com/google/android/lint/aidl/Constants.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.google.android.lint.aidl
+
+const val ANNOTATION_ENFORCE_PERMISSION = "android.annotation.EnforcePermission"
+const val ANNOTATION_REQUIRES_NO_PERMISSION = "android.annotation.RequiresNoPermission"
+const val ANNOTATION_PERMISSION_MANUALLY_ENFORCED = "android.annotation.PermissionManuallyEnforced"
+
+val AIDL_PERMISSION_ANNOTATIONS = listOf(
+ ANNOTATION_ENFORCE_PERMISSION,
+ ANNOTATION_REQUIRES_NO_PERMISSION,
+ ANNOTATION_PERMISSION_MANUALLY_ENFORCED
+)
+
+const val IINTERFACE_INTERFACE = "android.os.IInterface"
+
+/**
+ * If a non java (e.g. c++) backend is enabled, the @EnforcePermission
+ * annotation cannot be used. At time of writing, the mechanism
+ * is not implemented for non java backends.
+ * TODO: b/242564874 (have lint know which interfaces have the c++ backend enabled)
+ * rather than hard coding this list?
+ */
+val EXCLUDED_CPP_INTERFACES = listOf(
+ "AdbTransportType",
+ "FingerprintAndPairDevice",
+ "IAdbCallback",
+ "IAdbManager",
+ "PairDevice",
+ "IStatsBootstrapAtomService",
+ "StatsBootstrapAtom",
+ "StatsBootstrapAtomValue",
+ "FixedSizeArrayExample",
+ "PlaybackTrackMetadata",
+ "RecordTrackMetadata",
+ "SinkMetadata",
+ "SourceMetadata",
+ "IUpdateEngineStable",
+ "IUpdateEngineStableCallback",
+ "AudioCapabilities",
+ "ConfidenceLevel",
+ "ModelParameter",
+ "ModelParameterRange",
+ "Phrase",
+ "PhraseRecognitionEvent",
+ "PhraseRecognitionExtra",
+ "PhraseSoundModel",
+ "Properties",
+ "RecognitionConfig",
+ "RecognitionEvent",
+ "RecognitionMode",
+ "RecognitionStatus",
+ "SoundModel",
+ "SoundModelType",
+ "Status",
+ "IThermalService",
+ "IPowerManager",
+ "ITunerResourceManager"
+)
diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/EnforcePermissionDetector.kt b/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
index 9f216189ad62..a415217105a8 100644
--- a/tools/lint/checks/src/main/java/com/google/android/lint/EnforcePermissionDetector.kt
+++ b/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-package com.google.android.lint
+package com.google.android.lint.aidl
import com.android.tools.lint.client.api.UElementHandler
import com.android.tools.lint.detector.api.AnnotationInfo
import com.android.tools.lint.detector.api.AnnotationOrigin
import com.android.tools.lint.detector.api.AnnotationUsageInfo
import com.android.tools.lint.detector.api.AnnotationUsageType
-import com.android.tools.lint.detector.api.ConstantEvaluator
import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.ConstantEvaluator
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Implementation
import com.android.tools.lint.detector.api.Issue
@@ -34,8 +34,8 @@ import com.intellij.psi.PsiAnnotation
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiMethod
import org.jetbrains.uast.UAnnotation
-import org.jetbrains.uast.UElement
import org.jetbrains.uast.UClass
+import org.jetbrains.uast.UElement
import org.jetbrains.uast.UMethod
/**
@@ -54,12 +54,11 @@ import org.jetbrains.uast.UMethod
*/
class EnforcePermissionDetector : Detector(), SourceCodeScanner {
- val ENFORCE_PERMISSION = "android.annotation.EnforcePermission"
val BINDER_CLASS = "android.os.Binder"
val JAVA_OBJECT = "java.lang.Object"
override fun applicableAnnotations(): List<String> {
- return listOf(ENFORCE_PERMISSION)
+ return listOf(ANNOTATION_ENFORCE_PERMISSION)
}
override fun getApplicableUastTypes(): List<Class<out UElement>> {
@@ -99,8 +98,8 @@ class EnforcePermissionDetector : Detector(), SourceCodeScanner {
overriddenMethod: PsiMethod,
checkEquivalence: Boolean = true
) {
- val overridingAnnotation = overridingMethod.getAnnotation(ENFORCE_PERMISSION)
- val overriddenAnnotation = overriddenMethod.getAnnotation(ENFORCE_PERMISSION)
+ val overridingAnnotation = overridingMethod.getAnnotation(ANNOTATION_ENFORCE_PERMISSION)
+ val overriddenAnnotation = overriddenMethod.getAnnotation(ANNOTATION_ENFORCE_PERMISSION)
val location = context.getLocation(element)
val overridingClass = overridingMethod.parent as PsiClass
val overriddenClass = overriddenMethod.parent as PsiClass
@@ -133,8 +132,8 @@ class EnforcePermissionDetector : Detector(), SourceCodeScanner {
extendedClass: PsiClass,
checkEquivalence: Boolean = true
) {
- val newAnnotation = newClass.getAnnotation(ENFORCE_PERMISSION)
- val extendedAnnotation = extendedClass.getAnnotation(ENFORCE_PERMISSION)
+ val newAnnotation = newClass.getAnnotation(ANNOTATION_ENFORCE_PERMISSION)
+ val extendedAnnotation = extendedClass.getAnnotation(ANNOTATION_ENFORCE_PERMISSION)
val location = context.getLocation(element)
val newClassName = newClass.qualifiedName
@@ -180,7 +179,7 @@ class EnforcePermissionDetector : Detector(), SourceCodeScanner {
override fun createUastHandler(context: JavaContext): UElementHandler {
return object : UElementHandler() {
override fun visitAnnotation(node: UAnnotation) {
- if (node.qualifiedName != ENFORCE_PERMISSION) {
+ if (node.qualifiedName != ANNOTATION_ENFORCE_PERMISSION) {
return
}
val method = node.uastParent as? UMethod
diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt b/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt
new file mode 100644
index 000000000000..510611161ea8
--- /dev/null
+++ b/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt
@@ -0,0 +1,118 @@
+/*
+ * 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.google.android.lint.aidl
+
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Location
+import com.intellij.psi.PsiVariable
+import org.jetbrains.uast.UCallExpression
+import org.jetbrains.uast.ULiteralExpression
+import org.jetbrains.uast.UQualifiedReferenceExpression
+import org.jetbrains.uast.USimpleNameReferenceExpression
+import org.jetbrains.uast.asRecursiveLogString
+
+/**
+ * Helper ADT class that facilitates the creation of lint auto fixes
+ *
+ * Handles "Single" permission checks that should be migrated to @EnforcePermission(...), as well as consecutive checks
+ * that should be migrated to @EnforcePermission(allOf={...})
+ *
+ * TODO: handle anyOf style annotations
+ */
+sealed class EnforcePermissionFix {
+ abstract fun locations(): List<Location>
+ abstract fun javaAnnotationParameter(): String
+
+ fun javaAnnotation(): String = "@$ANNOTATION_ENFORCE_PERMISSION(${javaAnnotationParameter()})"
+
+ companion object {
+ fun fromCallExpression(callExpression: UCallExpression, context: JavaContext): SingleFix =
+ SingleFix(
+ getPermissionCheckLocation(context, callExpression),
+ getPermissionCheckArgumentValue(callExpression)
+ )
+
+ fun maybeAddManifestPrefix(permissionName: String): String =
+ if (permissionName.contains(".")) permissionName
+ else "android.Manifest.permission.$permissionName"
+
+ /**
+ * Given a permission check, get its proper location
+ * so that a lint fix can remove the entire expression
+ */
+ private fun getPermissionCheckLocation(
+ context: JavaContext,
+ callExpression: UCallExpression
+ ):
+ Location {
+ val javaPsi = callExpression.javaPsi!!
+ return Location.create(
+ context.file,
+ javaPsi.containingFile?.text,
+ javaPsi.textRange.startOffset,
+ // unfortunately the element doesn't include the ending semicolon
+ javaPsi.textRange.endOffset + 1
+ )
+ }
+
+ /**
+ * Given a permission check and an argument,
+ * pull out the permission value that is being used
+ */
+ private fun getPermissionCheckArgumentValue(
+ callExpression: UCallExpression,
+ argumentPosition: Int = 0
+ ): String {
+
+ val identifier = when (
+ val argument = callExpression.valueArguments.getOrNull(argumentPosition)
+ ) {
+ is UQualifiedReferenceExpression -> when (val selector = argument.selector) {
+ is USimpleNameReferenceExpression ->
+ ((selector.resolve() as PsiVariable).computeConstantValue() as String)
+
+ else -> throw RuntimeException(
+ "Couldn't resolve argument: ${selector.asRecursiveLogString()}"
+ )
+ }
+
+ is USimpleNameReferenceExpression -> (
+ (argument.resolve() as PsiVariable).computeConstantValue() as String)
+
+ is ULiteralExpression -> argument.value as String
+
+ else -> throw RuntimeException(
+ "Couldn't resolve argument: ${argument?.asRecursiveLogString()}"
+ )
+ }
+
+ return identifier.substringAfterLast(".")
+ }
+ }
+}
+
+data class SingleFix(val location: Location, val permissionName: String) : EnforcePermissionFix() {
+ override fun locations(): List<Location> = listOf(this.location)
+ override fun javaAnnotationParameter(): String = maybeAddManifestPrefix(this.permissionName)
+}
+data class AllOfFix(val checks: List<SingleFix>) : EnforcePermissionFix() {
+ override fun locations(): List<Location> = this.checks.map { it.location }
+ override fun javaAnnotationParameter(): String =
+ "allOf={${
+ this.checks.joinToString(", ") { maybeAddManifestPrefix(it.permissionName) }
+ }}"
+}
diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/aidl/ManualPermissionCheckDetector.kt b/tools/lint/checks/src/main/java/com/google/android/lint/aidl/ManualPermissionCheckDetector.kt
new file mode 100644
index 000000000000..2cea39423f1d
--- /dev/null
+++ b/tools/lint/checks/src/main/java/com/google/android/lint/aidl/ManualPermissionCheckDetector.kt
@@ -0,0 +1,199 @@
+/*
+ * 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.google.android.lint.aidl
+
+import com.android.tools.lint.client.api.UElementHandler
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.google.android.lint.CLASS_STUB
+import com.google.android.lint.ENFORCE_PERMISSION_METHODS
+import com.intellij.psi.PsiAnonymousClass
+import org.jetbrains.uast.UBlockExpression
+import org.jetbrains.uast.UCallExpression
+import org.jetbrains.uast.UElement
+import org.jetbrains.uast.UIfExpression
+import org.jetbrains.uast.UMethod
+import org.jetbrains.uast.UQualifiedReferenceExpression
+
+/**
+ * Looks for methods implementing generated AIDL interface stubs
+ * that can have simple permission checks migrated to
+ * @EnforcePermission annotations
+ *
+ * TODO: b/242564870 (enable parse and autoFix of .aidl files)
+ */
+@Suppress("UnstableApiUsage")
+class ManualPermissionCheckDetector : Detector(), SourceCodeScanner {
+ override fun getApplicableUastTypes(): List<Class<out UElement?>> =
+ listOf(UMethod::class.java)
+
+ override fun createUastHandler(context: JavaContext): UElementHandler = AidlStubHandler(context)
+
+ private inner class AidlStubHandler(val context: JavaContext) : UElementHandler() {
+ override fun visitMethod(node: UMethod) {
+ val interfaceName = getContainingAidlInterface(node)
+ .takeUnless(EXCLUDED_CPP_INTERFACES::contains) ?: return
+ val body = (node.uastBody as? UBlockExpression) ?: return
+ val fix = accumulateSimplePermissionCheckFixes(body) ?: return
+
+ val javaRemoveFixes = fix.locations().map {
+ fix()
+ .replace()
+ .reformat(true)
+ .range(it)
+ .with("")
+ .autoFix()
+ .build()
+ }
+
+ val javaAnnotateFix = fix()
+ .annotate(fix.javaAnnotation())
+ .range(context.getLocation(node))
+ .autoFix()
+ .build()
+
+ val message =
+ "$interfaceName permission check can be converted to @EnforcePermission annotation"
+
+ context.report(
+ ISSUE_USE_ENFORCE_PERMISSION_ANNOTATION,
+ fix.locations().last(),
+ message,
+ fix().composite(*javaRemoveFixes.toTypedArray(), javaAnnotateFix)
+ )
+ }
+
+ /**
+ * Walk the expressions in the method, looking for simple permission checks.
+ *
+ * If a single permission check is found at the beginning of the method,
+ * this should be migrated to @EnforcePermission(value).
+ *
+ * If multiple consecutive permission checks are found,
+ * these should be migrated to @EnforcePermission(allOf={value1, value2, ...})
+ *
+ * As soon as something other than a permission check is encountered, stop looking,
+ * as some other business logic is happening that prevents an automated fix.
+ */
+ private fun accumulateSimplePermissionCheckFixes(methodBody: UBlockExpression):
+ EnforcePermissionFix? {
+ val singleFixes = mutableListOf<SingleFix>()
+ for (expression in methodBody.expressions) {
+ singleFixes.add(getPermissionCheckFix(expression) ?: break)
+ }
+ return when (singleFixes.size) {
+ 0 -> null
+ 1 -> singleFixes[0]
+ else -> AllOfFix(singleFixes)
+ }
+ }
+
+ /**
+ * If an expression boils down to a permission check, return
+ * the helper for creating a lint auto fix to @EnforcePermission
+ */
+ private fun getPermissionCheckFix(startingExpression: UElement?):
+ SingleFix? {
+ return when (startingExpression) {
+ is UQualifiedReferenceExpression -> getPermissionCheckFix(
+ startingExpression.selector
+ )
+
+ is UIfExpression -> getPermissionCheckFix(startingExpression.condition)
+
+ is UCallExpression -> {
+ return if (isPermissionCheck(startingExpression))
+ EnforcePermissionFix.fromCallExpression(startingExpression, context)
+ else null
+ }
+
+ else -> null
+ }
+ }
+ }
+
+ companion object {
+
+ private val EXPLANATION = """
+ Whenever possible, method implementations of AIDL interfaces should use the @EnforcePermission
+ annotation to declare the permissions to be enforced. The verification code is then
+ generated by the AIDL compiler, which also takes care of annotating the generated java
+ code.
+
+ This reduces the risk of bugs around these permission checks (that often become vulnerabilities).
+ It also enables easier auditing and review.
+
+ Please migrate to an @EnforcePermission annotation. (See: go/aidl-enforce-howto)
+ """.trimIndent()
+
+ @JvmField
+ val ISSUE_USE_ENFORCE_PERMISSION_ANNOTATION = Issue.create(
+ id = "UseEnforcePermissionAnnotation",
+ briefDescription = "Manual permission check can be @EnforcePermission annotation",
+ explanation = EXPLANATION,
+ category = Category.SECURITY,
+ priority = 5,
+ severity = Severity.WARNING,
+ implementation = Implementation(
+ ManualPermissionCheckDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ ),
+ enabledByDefault = false, // TODO: enable once b/241171714 is resolved
+ )
+
+ private fun isPermissionCheck(callExpression: UCallExpression): Boolean {
+ val method = callExpression.resolve() ?: return false
+ val className = method.containingClass?.qualifiedName
+ return ENFORCE_PERMISSION_METHODS.any {
+ it.clazz == className && it.name == method.name
+ }
+ }
+
+ /**
+ * given a UMethod, determine if this method is
+ * an entrypoint to an interface generated by AIDL,
+ * returning the interface name if so
+ */
+ fun getContainingAidlInterface(node: UMethod): String? {
+ if (!isInClassCalledStub(node)) return null
+ for (superMethod in node.findSuperMethods()) {
+ for (extendsInterface in superMethod.containingClass?.extendsList?.referenceElements
+ ?: continue) {
+ if (extendsInterface.qualifiedName == IINTERFACE_INTERFACE) {
+ return superMethod.containingClass?.name
+ }
+ }
+ }
+ return null
+ }
+
+ private fun isInClassCalledStub(node: UMethod): Boolean {
+ (node.containingClass as? PsiAnonymousClass)?.let {
+ return it.baseClassReference.referenceName == CLASS_STUB
+ }
+ return node.containingClass?.extendsList?.referenceElements?.any {
+ it.referenceName == CLASS_STUB
+ } ?: false
+ }
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsTheme.kt b/tools/lint/checks/src/main/java/com/google/android/lint/model/Method.kt
index fce9f2b19ee0..3939b6109eaa 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsTheme.kt
+++ b/tools/lint/checks/src/main/java/com/google/android/lint/model/Method.kt
@@ -14,21 +14,13 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.theme
-
-import androidx.compose.foundation.isSystemInDarkTheme
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.runtime.Composable
+package com.google.android.lint.model
/**
- * The Material 3 Theme for Settings.
+ * Data class to represent a Method
*/
-@Composable
-fun SettingsTheme(content: @Composable () -> Unit) {
- val isDarkTheme = isSystemInDarkTheme()
- val colorScheme = materialColorScheme(isDarkTheme)
-
- MaterialTheme(colorScheme = colorScheme) {
- content()
+data class Method(val clazz: String, val name: String) {
+ override fun toString(): String {
+ return "$clazz#$name"
}
}
diff --git a/tools/lint/checks/src/test/java/com/google/android/lint/EnforcePermissionDetectorTest.kt b/tools/lint/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt
index 2cfc3fbcefcb..3c1d1e8e8a59 100644
--- a/tools/lint/checks/src/test/java/com/google/android/lint/EnforcePermissionDetectorTest.kt
+++ b/tools/lint/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.google.android.lint
+package com.google.android.lint.aidl
import com.android.tools.lint.checks.infrastructure.LintDetectorTest
import com.android.tools.lint.checks.infrastructure.TestFile
diff --git a/tools/lint/checks/src/test/java/com/google/android/lint/aidl/ManualPermissionCheckDetectorTest.kt b/tools/lint/checks/src/test/java/com/google/android/lint/aidl/ManualPermissionCheckDetectorTest.kt
new file mode 100644
index 000000000000..1a1c6bc77785
--- /dev/null
+++ b/tools/lint/checks/src/test/java/com/google/android/lint/aidl/ManualPermissionCheckDetectorTest.kt
@@ -0,0 +1,211 @@
+/*
+ * 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.google.android.lint.aidl
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestLintTask
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+
+@Suppress("UnstableApiUsage")
+class ManualPermissionCheckDetectorTest : LintDetectorTest() {
+ override fun getDetector(): Detector = ManualPermissionCheckDetector()
+ override fun getIssues(): List<Issue> = listOf(
+ ManualPermissionCheckDetector
+ .ISSUE_USE_ENFORCE_PERMISSION_ANNOTATION
+ )
+
+ override fun lint(): TestLintTask = super.lint().allowMissingSdk()
+
+ fun testClass() {
+ 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.Manifest.permission.READ_CONTACTS", "foo");
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/Foo.java:7: Warning: ITest permission check can be converted to @EnforcePermission annotation [UseEnforcePermissionAnnotation]
+ mContext.enforceCallingOrSelfPermission("android.Manifest.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.Manifest.permission.READ_CONTACTS)
+ @@ -7 +8
+ - mContext.enforceCallingOrSelfPermission("android.Manifest.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.Manifest.permission.READ_CONTACTS", "foo");
+ }
+ };
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/Foo.java:8: Warning: ITest permission check can be converted to @EnforcePermission annotation [UseEnforcePermissionAnnotation]
+ mContext.enforceCallingOrSelfPermission(
+ ^
+ 0 errors, 1 warnings
+ """
+ )
+ .expectFixDiffs(
+ """
+ Fix for src/Foo.java line 8: Annotate with @EnforcePermission:
+ @@ -6 +6
+ + @android.annotation.EnforcePermission(android.Manifest.permission.READ_CONTACTS)
+ @@ -8 +9
+ - mContext.enforceCallingOrSelfPermission(
+ - "android.Manifest.permission.READ_CONTACTS", "foo");
+ """
+ )
+ }
+
+ fun testAllOf() {
+ 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.Manifest.permission.READ_CONTACTS", "foo");
+ mContext.enforceCallingOrSelfPermission(
+ "android.Manifest.permission.WRITE_CONTACTS", "foo");
+ }
+ };
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/Foo.java:10: Warning: ITest permission check can be converted to @EnforcePermission annotation [UseEnforcePermissionAnnotation]
+ mContext.enforceCallingOrSelfPermission(
+ ^
+ 0 errors, 1 warnings
+ """
+ )
+ .expectFixDiffs(
+ """
+ Fix for src/Foo.java line 10: Annotate with @EnforcePermission:
+ @@ -6 +6
+ + @android.annotation.EnforcePermission(allOf={android.Manifest.permission.READ_CONTACTS, android.Manifest.permission.WRITE_CONTACTS})
+ @@ -8 +9
+ - mContext.enforceCallingOrSelfPermission(
+ - "android.Manifest.permission.READ_CONTACTS", "foo");
+ - mContext.enforceCallingOrSelfPermission(
+ - "android.Manifest.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.Manifest.permission.READ_CONTACTS", "foo");
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
+ companion object {
+ private val aidlStub: TestFile = java(
+ """
+ package android.test;
+ public interface ITest extends android.os.IInterface {
+ public static abstract class Stub extends android.os.Binder implements android.test.ITest {}
+ public void test() throws android.os.RemoteException;
+ }
+ """
+ ).indented()
+
+ private val contextStub: TestFile = java(
+ """
+ package android.content;
+ public class Context {
+ public void enforceCallingOrSelfPermission(String permission, String message) {}
+ }
+ """
+ ).indented()
+
+ private val binderStub: TestFile = java(
+ """
+ package android.os;
+ public class Binder {
+ public static int getCallingUid() {}
+ }
+ """
+ ).indented()
+
+ val stubs = arrayOf(aidlStub, contextStub, binderStub)
+ }
+}