summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apct-tests/perftests/surfaceflinger/AndroidManifest.xml2
-rw-r--r--apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/BufferFlinger.java79
-rw-r--r--apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java24
-rw-r--r--apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerTestActivity.java82
-rw-r--r--apct-tests/perftests/utils/src/android/perftests/utils/SurfaceFlingerTestActivity.java44
-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.java225
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java1
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java26
-rw-r--r--core/api/current.txt16
-rw-r--r--core/api/test-current.txt2
-rw-r--r--core/java/android/accessibilityservice/AccessibilityServiceInfo.java21
-rw-r--r--core/java/android/app/ActivityManagerInternal.java9
-rw-r--r--core/java/android/app/UiAutomationConnection.java1
-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/camera2/CameraDevice.java2
-rw-r--r--core/java/android/hardware/display/DisplayManagerInternal.java9
-rw-r--r--core/java/android/os/AggregateBatteryConsumer.java14
-rw-r--r--core/java/android/os/BatteryConsumer.java15
-rw-r--r--core/java/android/os/BatteryUsageStats.java7
-rw-r--r--core/java/android/os/NewUserResponse.java9
-rw-r--r--core/java/android/os/UserManager.java11
-rw-r--r--core/java/android/provider/DeviceConfig.java2
-rw-r--r--core/java/android/provider/Settings.java59
-rw-r--r--core/java/android/service/voice/HotwordDetectedResult.java57
-rw-r--r--core/java/android/view/AccessibilityInteractionController.java77
-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/View.java130
-rw-r--r--core/java/android/view/ViewRootImpl.java1
-rw-r--r--core/java/android/view/ViewStructure.java24
-rw-r--r--core/java/android/view/WindowManager.java31
-rw-r--r--core/java/android/view/accessibility/AccessibilityEvent.java57
-rw-r--r--core/java/android/view/accessibility/AccessibilityManager.java35
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java25
-rw-r--r--core/java/android/view/accessibility/AccessibilityRecord.java24
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureEvent.java9
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureManager.java9
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java7
-rw-r--r--core/java/android/widget/AbsListView.java141
-rw-r--r--core/java/android/widget/Editor.java28
-rw-r--r--core/java/android/widget/OWNERS2
-rw-r--r--core/java/android/widget/SelectionActionModeHelper.java6
-rw-r--r--core/java/android/widget/TextView.java9
-rw-r--r--core/java/android/window/WindowContainerTransaction.java30
-rw-r--r--core/java/com/android/internal/inputmethod/ImeTracingServerImpl.java16
-rw-r--r--core/java/com/android/internal/os/TimeoutRecord.java130
-rw-r--r--core/java/com/android/internal/protolog/ProtoLogGroup.java2
-rw-r--r--core/java/com/android/internal/util/ScreenshotHelper.java79
-rw-r--r--core/proto/android/os/batteryusagestats.proto17
-rw-r--r--core/proto/android/os/processstarttime.proto92
-rw-r--r--core/proto/android/providers/settings/secure.proto1
-rw-r--r--core/proto/android/server/accessibilitytrace.proto4
-rw-r--r--core/proto/android/server/windowmanagertrace.proto4
-rw-r--r--core/proto/android/view/inputmethod/inputmethodeditortrace.proto12
-rw-r--r--core/res/res/layout/side_fps_toast.xml43
-rw-r--r--core/res/res/values/attrs.xml14
-rw-r--r--core/res/res/values/config.xml9
-rw-r--r--core/res/res/values/public-staging.xml1
-rw-r--r--core/res/res/values/strings.xml14
-rw-r--r--core/res/res/values/symbols.xml11
-rw-r--r--core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java50
-rw-r--r--core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java47
-rw-r--r--core/tests/coretests/src/android/view/WindowParamsTest.java100
-rw-r--r--data/etc/services.core.protolog.json36
-rw-r--r--libs/WindowManager/Shell/res/drawable/decor_minimize_button_dark.xml24
-rw-r--r--libs/WindowManager/Shell/res/layout/caption_window_decoration.xml12
-rw-r--r--libs/WindowManager/Shell/res/values-es/strings_tv.xml2
-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/animation/PhysicsAnimator.kt41
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java9
-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/PipAnimationController.java30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java25
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java38
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java6
-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/CommonAssertions.kt61
-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.kt18
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt210
-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/unittest/src/com/android/wm/shell/TaskViewTest.java7
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java7
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java234
-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/Android.bp3
-rw-r--r--packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java9
-rw-r--r--packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java9
-rw-r--r--packages/SettingsLib/Spa/OWNERS4
-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/HomePage.kt8
-rw-r--r--packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PageRepository.kt10
-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/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/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)2
-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)2
-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.kt (renamed from packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsTheme.kt)22
-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.kt8
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BasePreference.kt6
-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/ui/SettingsSlider.kt185
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/PreferenceTest.kt2
-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/license/LicenseHtmlGeneratorFromXml.java179
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java12
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java36
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java12
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java3
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java11
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java1
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java3
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java3
-rw-r--r--packages/SystemUI/TEST_MAPPING16
-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/docs/device-entry/quickaffordance.md8
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml4
-rw-r--r--packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml10
-rw-r--r--packages/SystemUI/res/values/dimens.xml1
-rw-r--r--packages/SystemUI/res/values/strings.xml9
-rw-r--r--packages/SystemUI/res/xml/qqs_header.xml10
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java2
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java9
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java92
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java5
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java7
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherAnchor.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/Dependency.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProvider.java117
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt65
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordanceModel.kt (renamed from packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardQuickAffordanceModel.kt)22
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordancePosition.kt (renamed from packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardQuickAffordancePosition.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt (renamed from packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceConfig.kt (renamed from packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceModule.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceRegistry.kt (renamed from packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceConfigs.kt)16
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt (renamed from packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt (renamed from packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/KeyguardUseCaseModule.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/ObserveKeyguardQuickAffordanceUseCase.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/OnKeyguardQuickAffordanceClickedUseCase.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt57
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFragment.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java48
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt48
-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/shade/NotificationsQSContainerController.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java93
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java65
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java247
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java860
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java144
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt227
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java65
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/BindEventManagerImpl.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationRanker.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationRankerStub.java66
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LowPriorityInflationHelper.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java274
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/VisualStabilityManager.java217
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpController.java185
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinder.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java58
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt216
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProvider.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java717
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUserSwitcherAnchorTest.kt53
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java96
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardQuickAffordanceConfigs.kt44
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardQuickAffordanceRepository.kt55
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt27
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryImplTest.kt193
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardQuickAffordanceConfig.kt)30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/FakeKeyguardQuickAffordanceRegistry.kt43
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt)23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt)4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt)4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt)2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/FakeObserveKeyguardQuickAffordanceUseCase.kt46
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/ObserveKeyguardQuickAffordanceUseCaseImplTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/ObserveKeyguardQuickAffordanceUseCaseTest.kt)119
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt254
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt189
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java34
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java142
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTest.java101
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shared/system/RemoteTransitionTest.java32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java38
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java44
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java684
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java268
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java293
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt509
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java202
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java59
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java37
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java679
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt60
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java490
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java406
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/FakeSharedPreferencesTest.kt259
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeSharedPreferences.kt164
-rw-r--r--packages/VpnDialogs/res/values-es/strings.xml2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java25
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java1
-rw-r--r--services/backup/backuplib/java/com/android/server/backup/TransportManager.java20
-rw-r--r--services/backup/java/com/android/server/backup/BackupManagerService.java2
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java8
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java20
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java42
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java50
-rw-r--r--services/core/java/com/android/server/am/AnrHelper.java26
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java2
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java79
-rw-r--r--services/core/java/com/android/server/am/BroadcastRecord.java2
-rw-r--r--services/core/java/com/android/server/am/CachedAppOptimizer.java14
-rw-r--r--services/core/java/com/android/server/am/ContentProviderHelper.java5
-rw-r--r--services/core/java/com/android/server/am/HostingRecord.java97
-rw-r--r--services/core/java/com/android/server/am/ProcessErrorStateRecord.java5
-rw-r--r--services/core/java/com/android/server/app/GameManagerShellCommand.java32
-rw-r--r--services/core/java/com/android/server/attention/AttentionManagerService.java9
-rw-r--r--services/core/java/com/android/server/audio/RecordingActivityMonitor.java2
-rw-r--r--services/core/java/com/android/server/display/DisplayDevice.java7
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceConfig.java5
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java59
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java7
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java163
-rw-r--r--services/core/java/com/android/server/location/contexthub/TEST_MAPPING26
-rw-r--r--services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java319
-rw-r--r--services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java13
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageVerificationState.java9
-rw-r--r--services/core/java/com/android/server/policy/SideFpsEventHandler.java164
-rw-r--r--services/core/java/com/android/server/policy/SideFpsToast.java69
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java7
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java38
-rw-r--r--services/core/java/com/android/server/wm/AnrController.java42
-rw-r--r--services/core/java/com/android/server/wm/ContentRecorder.java110
-rw-r--r--services/core/java/com/android/server/wm/ContentRecordingController.java10
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java90
-rw-r--r--services/core/java/com/android/server/wm/InputManagerCallback.java19
-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/Task.java12
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java6
-rw-r--r--services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java4
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java17
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowTracing.java6
-rw-r--r--services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java29
-rw-r--r--services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java42
-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.java294
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java28
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInteractionControllerNodeRequestsTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java16
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java52
-rw-r--r--services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubClientBrokerTest.java243
-rw-r--r--services/tests/servicestests/src/com/android/server/location/contexthub/OWNERS2
-rw-r--r--services/tests/servicestests/src/com/android/server/location/contexthub/TEST_MAPPING40
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageVerificationStateTest.java21
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/policy/SideFpsEventHandlerTest.java82
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java69
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ContentRecordingControllerTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java48
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java103
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java16
-rw-r--r--services/usb/java/com/android/server/usb/UsbDeviceManager.java21
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java34
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java30
-rw-r--r--telephony/java/android/telephony/data/ApnSetting.java3
-rw-r--r--tools/aapt/Command.cpp6
-rw-r--r--tools/aapt/ResourceTable.cpp8
-rw-r--r--tools/aapt2/format/binary/TableFlattener.cpp18
-rw-r--r--tools/aapt2/format/binary/TableFlattener_test.cpp48
502 files changed, 10563 insertions, 9979 deletions
diff --git a/apct-tests/perftests/surfaceflinger/AndroidManifest.xml b/apct-tests/perftests/surfaceflinger/AndroidManifest.xml
index 89a5219a8cff..c908d6ae4cff 100644
--- a/apct-tests/perftests/surfaceflinger/AndroidManifest.xml
+++ b/apct-tests/perftests/surfaceflinger/AndroidManifest.xml
@@ -18,7 +18,7 @@
<application android:label="SurfaceFlingerPerfTests">
<uses-library android:name="android.test.runner" />
- <activity android:name="android.perftests.utils.SurfaceFlingerTestActivity"
+ <activity android:name="android.surfaceflinger.SurfaceFlingerTestActivity"
android:exported="true">
<intent-filter>
diff --git a/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/BufferFlinger.java b/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/BufferFlinger.java
new file mode 100644
index 000000000000..52fb8a6023f1
--- /dev/null
+++ b/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/BufferFlinger.java
@@ -0,0 +1,79 @@
+/*
+ * 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.surfaceflinger;
+
+import android.annotation.ColorInt;
+import android.graphics.Canvas;
+import android.graphics.GraphicBuffer;
+import android.graphics.PixelFormat;
+import android.hardware.HardwareBuffer;
+import android.hardware.SyncFence;
+import android.view.SurfaceControl;
+
+import java.util.concurrent.ArrayBlockingQueue;
+
+/**
+ * Allocates n amount of buffers to a SurfaceControl using a Queue implementation. Executes a
+ * releaseCallback so a buffer can be safely re-used.
+ *
+ * @hide
+ */
+public class BufferFlinger {
+ ArrayBlockingQueue<GraphicBuffer> mBufferQ;
+
+ public BufferFlinger(int numOfBuffers, @ColorInt int color) {
+ mBufferQ = new ArrayBlockingQueue<>(numOfBuffers);
+
+ while (numOfBuffers > 0) {
+ GraphicBuffer buffer = GraphicBuffer.create(500, 500,
+ PixelFormat.RGBA_8888,
+ GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_HW_COMPOSER
+ | GraphicBuffer.USAGE_SW_WRITE_RARELY);
+
+ Canvas canvas = buffer.lockCanvas();
+ canvas.drawColor(color);
+ buffer.unlockCanvasAndPost(canvas);
+
+ mBufferQ.add(buffer);
+ numOfBuffers--;
+ }
+ }
+
+ public void addBuffer(SurfaceControl.Transaction t, SurfaceControl surfaceControl)
+ throws InterruptedException {
+ GraphicBuffer buffer = mBufferQ.take();
+ t.setBuffer(surfaceControl,
+ HardwareBuffer.createFromGraphicBuffer(buffer),
+ null,
+ (SyncFence fence) -> {
+ releaseCallback(fence, buffer);
+ });
+ }
+
+ public void releaseCallback(SyncFence fence, GraphicBuffer buffer) {
+ if (fence != null) {
+ fence.awaitForever();
+ }
+ mBufferQ.add(buffer);
+ }
+
+ public void freeBuffers() {
+ for (GraphicBuffer buffer : mBufferQ) {
+ buffer.destroy();
+ }
+ }
+}
diff --git a/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java b/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java
index 8efe48daed22..f4d0c053fb66 100644
--- a/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java
+++ b/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java
@@ -16,37 +16,51 @@
package android.surfaceflinger;
+import android.graphics.Color;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.SurfaceFlingerTestActivity;
+import android.view.SurfaceControl;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
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.rules.RuleChain;
import org.junit.runner.RunWith;
-
@LargeTest
@RunWith(AndroidJUnit4.class)
public class SurfaceFlingerPerfTest {
protected ActivityScenarioRule<SurfaceFlingerTestActivity> mActivityRule =
new ActivityScenarioRule<>(SurfaceFlingerTestActivity.class);
protected PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+ private SurfaceFlingerTestActivity mActivity;
+ static final int BUFFER_COUNT = 2;
@Rule
public final RuleChain mAllRules = RuleChain
.outerRule(mPerfStatusReporter)
.around(mActivityRule);
-
+ @Before
+ public void setup() {
+ mActivityRule.getScenario().onActivity(activity -> mActivity = activity);
+ }
@Test
- public void helloWorld() throws Exception {
+ public void submitSingleBuffer() throws Exception {
+ SurfaceControl sc = mActivity.getChildSurfaceControl();
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ BufferFlinger bufferflinger = new BufferFlinger(BUFFER_COUNT, Color.GREEN);
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ t.show(sc);
+
while (state.keepRunning()) {
- // Do Something
+ bufferflinger.addBuffer(t, sc);
+ t.apply();
}
+ bufferflinger.freeBuffers();
}
}
+
diff --git a/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerTestActivity.java b/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerTestActivity.java
new file mode 100644
index 000000000000..a9b2a3118bc1
--- /dev/null
+++ b/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerTestActivity.java
@@ -0,0 +1,82 @@
+/*
+ * 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.surfaceflinger;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.SurfaceControl;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.WindowManager;
+
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * A simple activity used for testing, e.g. performance of activity switching, or as a base
+ * container of testing view.
+ */
+public class SurfaceFlingerTestActivity extends Activity {
+ public TestSurfaceView mTestSurfaceView;
+ SurfaceControl mSurfaceControl;
+ CountDownLatch mIsReady = new CountDownLatch(1);
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ mTestSurfaceView = new TestSurfaceView(this);
+ setContentView(mTestSurfaceView);
+ }
+
+ public SurfaceControl getChildSurfaceControl() throws InterruptedException {
+ return mTestSurfaceView.getChildSurfaceControlHelper();
+ }
+
+ public class TestSurfaceView extends SurfaceView {
+ public TestSurfaceView(Context context) {
+ super(context);
+ SurfaceHolder holder = getHolder();
+ holder.addCallback(new SurfaceHolder.Callback() {
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ mIsReady.countDown();
+ }
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width,
+ int height) {}
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ }
+ });
+ }
+
+ public SurfaceControl getChildSurfaceControlHelper() throws InterruptedException {
+ mIsReady.await();
+ SurfaceHolder holder = getHolder();
+
+ // check to see if surface is valid
+ if (holder.getSurface().isValid()) {
+ mSurfaceControl = getSurfaceControl();
+ }
+ return new SurfaceControl.Builder()
+ .setName("ChildSurfaceControl")
+ .setParent(mSurfaceControl)
+ .build();
+ }
+ }
+}
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/SurfaceFlingerTestActivity.java b/apct-tests/perftests/utils/src/android/perftests/utils/SurfaceFlingerTestActivity.java
deleted file mode 100644
index 511ebbecf17e..000000000000
--- a/apct-tests/perftests/utils/src/android/perftests/utils/SurfaceFlingerTestActivity.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.perftests.utils;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.SurfaceView;
-import android.view.WindowManager;
-import android.widget.LinearLayout;
-
-/**
- * A simple activity used for testing, e.g. performance of activity switching, or as a base
- * container of testing view.
- */
-public class SurfaceFlingerTestActivity extends Activity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
-
- final LinearLayout layout = new LinearLayout(this);
- layout.setOrientation(LinearLayout.VERTICAL);
-
- final SurfaceView surfaceview = new SurfaceView(this);
- layout.addView(surfaceview);
- setContentView(layout);
-
- }
-}
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..e69cbedb955e 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;
@@ -50,8 +52,6 @@ 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";
@@ -78,6 +78,8 @@ public final class FlexibilityController extends StateController {
@VisibleForTesting
static final int NUM_FLEXIBLE_CONSTRAINTS = Integer.bitCount(FLEXIBLE_CONSTRAINTS);
+ private static final long NO_LIFECYCLE_END = Long.MAX_VALUE;
+
/** Hard cutoff to remove flexible constraints. */
private static final long DEADLINE_PROXIMITY_LIMIT_MS = 15 * MINUTE_IN_MILLIS;
@@ -85,7 +87,8 @@ public final class FlexibilityController extends StateController {
* 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;
+ @VisibleForTesting
+ static final long DEFAULT_FLEXIBILITY_DEADLINE = 72 * HOUR_IN_MILLIS;
/**
* Keeps track of what flexible constraints are satisfied at the moment.
@@ -102,6 +105,10 @@ public final class FlexibilityController extends StateController {
final FlexibilityTracker mFlexibilityTracker;
private final FcConstants mFcConstants;
+ @VisibleForTesting
+ final PrefetchController mPrefetchController;
+
+ @GuardedBy("mLock")
private final FlexibilityAlarmQueue mFlexibilityAlarmQueue;
private static final long MIN_TIME_BETWEEN_ALARMS_MS = MINUTE_IN_MILLIS;
@@ -112,12 +119,58 @@ public final class FlexibilityController extends StateController {
*/
private static final int[] PERCENT_TO_DROP_CONSTRAINTS = {50, 60, 70, 80};
- public FlexibilityController(JobSchedulerService service) {
+ /**
+ * 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.
+ */
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ final SparseArrayMap<String, Long> mPrefetchLifeCycleStart = new SparseArrayMap<>();
+
+ @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();
mFlexibilityAlarmQueue = new FlexibilityAlarmQueue(
mContext, JobSchedulerBackgroundThread.get().getLooper());
+ mPrefetchController = prefetchController;
+ if (mFlexibilityEnabled) {
+ mPrefetchController.registerPrefetchChangedListener(mPrefetchChangedListener);
+ }
}
/**
@@ -131,7 +184,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 +197,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 +231,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 +254,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,15 +277,77 @@ public final class FlexibilityController extends StateController {
return (mSatisfiedFlexibleConstraints & constraint) != 0;
}
- /** 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
+ @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();
- final long latest = js.getLatestRunTimeElapsed() == JobStatus.NO_LATEST_RUNTIME
+ }
+
+ @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 + DEFAULT_FLEXIBILITY_DEADLINE
: 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
+ @GuardedBy("mLock")
+ long getNextConstraintDropTimeElapsedLocked(JobStatus js) {
+ final long earliest = getLifeCycleBeginningElapsedLocked(js);
+ final long latest = getLifeCycleEndElapsedLocked(js, earliest);
+ if (latest == NO_LIFECYCLE_END
+ || js.getNumDroppedFlexibleConstraints() == PERCENT_TO_DROP_CONSTRAINTS.length) {
+ return NO_LIFECYCLE_END;
+ }
final int percent = PERCENT_TO_DROP_CONSTRAINTS[js.getNumDroppedFlexibleConstraints()];
final long percentInTime = ((latest - earliest) * percent) / 100;
return earliest + percentInTime;
@@ -233,10 +361,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));
}
}
}
@@ -281,7 +427,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 +458,17 @@ 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;
+ for (int i = 0; i < PERCENT_TO_DROP_CONSTRAINTS.length; i++) {
+ if (curPercent >= PERCENT_TO_DROP_CONSTRAINTS[i]) {
+ toDrop++;
+ }
+ }
+ adjustJobsRequiredConstraints(js, js.getNumDroppedFlexibleConstraints() - toDrop);
+ }
+
/** Returns all tracked jobs. */
public ArrayList<ArraySet<JobStatus>> getArrayList() {
return mTrackedJobs;
@@ -369,20 +526,39 @@ public final class FlexibilityController extends StateController {
return js.getSourceUserId() == userId;
}
+ protected void scheduleDropNumConstraintsAlarm(JobStatus js) {
+ long nextTimeElapsed;
+ synchronized (mLock) {
+ nextTimeElapsed = getNextConstraintDropTimeElapsedLocked(js);
+ if (nextTimeElapsed == NO_LIFECYCLE_END) {
+ // There is no known or estimated next time to drop a constraint.
+ removeAlarmForKey(js);
+ return;
+ }
+ mFlexibilityAlarmQueue.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);
+ JobStatus js = expired.valueAt(i);
+ boolean wasFlexibilitySatisfied = js.isConstraintSatisfied(CONSTRAINT_FLEXIBLE);
+ long time = getNextConstraintDropTimeElapsedLocked(js);
int toDecrease =
js.getLatestRunTimeElapsed() - time < DEADLINE_PROXIMITY_LIMIT_MS
- ? -js.getNumRequiredFlexibleConstraints() : -1;
- if (mFlexibilityTracker.adjustJobsRequiredConstraints(js, toDecrease)) {
+ ? -js.getNumRequiredFlexibleConstraints() : -1;
+ if (mFlexibilityTracker.adjustJobsRequiredConstraints(js, toDecrease)
+ && time != NO_LIFECYCLE_END) {
mFlexibilityAlarmQueue.addAlarm(js, time);
}
+ if (wasFlexibilitySatisfied != js.isConstraintSatisfied(CONSTRAINT_FLEXIBLE)) {
+ changedJobs.add(js);
+ }
}
+ mStateChangedListener.onControllerStateChanged(changedJobs);
}
}
}
@@ -410,6 +586,13 @@ 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;
}
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 ff19bedcbf20..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
@@ -575,7 +575,6 @@ public final class JobStatus {
if (!isRequestedExpeditedJob()
&& satisfiesMinWindowException
- && !job.isPrefetch()
&& lacksSomeFlexibleConstraints) {
mNumRequiredFlexibleConstraints =
NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS + (mPreferUnmetered ? 1 : 0);
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/core/api/current.txt b/core/api/current.txt
index 7ed1a4382054..4734e8a75de6 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -275,6 +275,7 @@ package android {
public static final class R.attr {
ctor public R.attr();
field public static final int absListViewStyle = 16842858; // 0x101006a
+ field public static final int accessibilityDataPrivate;
field public static final int accessibilityEventTypes = 16843648; // 0x1010380
field public static final int accessibilityFeedbackType = 16843650; // 0x1010382
field public static final int accessibilityFlags = 16843652; // 0x1010384
@@ -23188,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 {
@@ -49955,6 +49957,7 @@ package android.view {
method public void invalidate();
method public void invalidateDrawable(@NonNull android.graphics.drawable.Drawable);
method public void invalidateOutline();
+ method public boolean isAccessibilityDataPrivate();
method public boolean isAccessibilityFocused();
method public boolean isAccessibilityHeading();
method public boolean isActivated();
@@ -50132,6 +50135,7 @@ package android.view {
method public void scrollTo(int, int);
method public void sendAccessibilityEvent(int);
method public void sendAccessibilityEventUnchecked(android.view.accessibility.AccessibilityEvent);
+ method public void setAccessibilityDataPrivate(int);
method public void setAccessibilityDelegate(@Nullable android.view.View.AccessibilityDelegate);
method public void setAccessibilityHeading(boolean);
method public void setAccessibilityLiveRegion(int);
@@ -50312,6 +50316,9 @@ package android.view {
method @CallSuper protected boolean verifyDrawable(@NonNull android.graphics.drawable.Drawable);
method @Deprecated public boolean willNotCacheDrawing();
method public boolean willNotDraw();
+ field public static final int ACCESSIBILITY_DATA_PRIVATE_AUTO = 0; // 0x0
+ field public static final int ACCESSIBILITY_DATA_PRIVATE_NO = 2; // 0x2
+ field public static final int ACCESSIBILITY_DATA_PRIVATE_YES = 1; // 0x1
field public static final int ACCESSIBILITY_LIVE_REGION_ASSERTIVE = 2; // 0x2
field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
@@ -51764,9 +51771,11 @@ package android.view.accessibility {
method public int getSpeechStateChangeTypes();
method public int getWindowChanges();
method public void initFromParcel(android.os.Parcel);
+ method public boolean isAccessibilityDataPrivate();
method @Deprecated public static android.view.accessibility.AccessibilityEvent obtain(int);
method @Deprecated public static android.view.accessibility.AccessibilityEvent obtain(android.view.accessibility.AccessibilityEvent);
method @Deprecated public static android.view.accessibility.AccessibilityEvent obtain();
+ method public void setAccessibilityDataPrivate(boolean);
method public void setAction(int);
method public void setContentChangeTypes(int);
method public void setEventTime(long);
@@ -51857,6 +51866,7 @@ package android.view.accessibility {
method public static boolean isAccessibilityButtonSupported();
method public boolean isAudioDescriptionRequested();
method public boolean isEnabled();
+ method public boolean isRequestFromAccessibilityTool();
method public boolean isTouchExplorationEnabled();
method public void removeAccessibilityRequestPreparer(android.view.accessibility.AccessibilityRequestPreparer);
method public boolean removeAccessibilityServicesStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index ddbccfa8f404..c87ea2adbd77 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -90,6 +90,7 @@ package android.accessibilityservice {
public class AccessibilityServiceInfo implements android.os.Parcelable {
method @NonNull public android.content.ComponentName getComponentName();
+ method public void setAccessibilityTool(boolean);
}
}
@@ -797,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/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 2e89ce83cd36..8f6bfd3b13db 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -784,6 +784,7 @@ public class AccessibilityServiceInfo implements Parcelable {
mNonInteractiveUiTimeout = other.mNonInteractiveUiTimeout;
mInteractiveUiTimeout = other.mInteractiveUiTimeout;
flags = other.flags;
+ mIsAccessibilityTool = other.mIsAccessibilityTool;
}
private boolean isRequestAccessibilityButtonChangeEnabled(IPlatformCompat platformCompat) {
@@ -1112,6 +1113,26 @@ public class AccessibilityServiceInfo implements Parcelable {
}
/**
+ * Sets whether the service is used to assist users with disabilities.
+ *
+ * <p>
+ * This property is normally provided in the service's {@link #mResolveInfo ResolveInfo}.
+ * </p>
+ *
+ * <p>
+ * This method is helpful for unit testing. However, this property is not dynamically
+ * configurable by a standard {@link AccessibilityService} so it's not possible to update the
+ * copy held by the system with this method.
+ * </p>
+ *
+ * @hide
+ */
+ @TestApi
+ public void setAccessibilityTool(boolean isAccessibilityTool) {
+ mIsAccessibilityTool = isAccessibilityTool;
+ }
+
+ /**
* Indicates if the service is used to assist users with disabilities.
*
* @return {@code true} if the property is set to true.
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index f2ea060b1694..92109584627d 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -41,6 +41,8 @@ import android.os.WorkSource;
import android.util.ArraySet;
import android.util.Pair;
+import com.android.internal.os.TimeoutRecord;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -440,10 +442,13 @@ public abstract class ActivityManagerInternal {
/** Input dispatch timeout to a window, start the ANR process. Return the timeout extension,
* in milliseconds, or 0 to abort dispatch. */
- public abstract long inputDispatchingTimedOut(int pid, boolean aboveSystem, String reason);
+ public abstract long inputDispatchingTimedOut(int pid, boolean aboveSystem,
+ TimeoutRecord timeoutRecord);
+
public abstract boolean inputDispatchingTimedOut(Object proc, String activityShortComponentName,
ApplicationInfo aInfo, String parentShortComponentName, Object parentProc,
- boolean aboveSystem, String reason);
+ boolean aboveSystem, TimeoutRecord timeoutRecord);
+
/**
* App started responding to input events. This signal can be used to abort the ANR process and
* hide the ANR dialog.
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index a045157e02db..8d57e32a763c 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -550,6 +550,7 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
info.setCapabilities(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT
| AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
| AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS);
+ info.setAccessibilityTool(true);
try {
// Calling out with a lock held is fine since if the system
// process is gone the client calling in will be killed.
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/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 0f1b39c01289..df1c0d7c63bc 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -892,7 +892,7 @@ public abstract class CameraDevice implements AutoCloseable {
* <tr><th colspan="7">Preview stabilization guaranteed stream configurations</th></tr>
* <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th rowspan="2">Sample use case(s)</th> </tr>
* <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr>
- * <tr> <td>{@code PRIV / YUV}</td><td id="rb">{@code s1440p}</td><td colspan="4" id="rb"></td> <td>Stabilized preview, GPU video processing, or no-preview stabilized video recording.</td> </tr>
+ * <tr> <td>{@code PRIV / YUV}</td><td id="rb">{@code s1440p}</td><td colspan="2" id="rb"></td> <td>Stabilized preview, GPU video processing, or no-preview stabilized video recording.</td> </tr>
* <tr> <td>{@code PRIV / YUV}</td><td id="rb">{@code s1440p}</td> <td>{@code JPEG / YUV}</td><td id="rb">{@code MAXIMUM }</td><td>Standard still imaging with stabilized preview.</td> </tr>
* <tr> <td>{@code PRIV / YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV / YUV}</td><td id="rb">{@code s1440p }</td><td>High-resolution recording with stabilized preview and recording stream.</td> </tr>
* </table><br>
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index b0b3e9e743cf..52cef0f1efd0 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -374,6 +374,15 @@ public abstract class DisplayManagerInternal {
public abstract Point getDisplaySurfaceDefaultSize(int displayId);
/**
+ * Get a new displayId which represents the display you want to mirror. If mirroring is not
+ * enabled on the display, {@link Display#INVALID_DISPLAY} will be returned.
+ *
+ * @param displayId The id of the display.
+ * @return The displayId that should be mirrored or INVALID_DISPLAY if mirroring is not enabled.
+ */
+ public abstract int getDisplayIdToMirror(int displayId);
+
+ /**
* Receives early interactivity changes from power manager.
*
* @param interactive The interactive state that the device is moving into.
diff --git a/core/java/android/os/AggregateBatteryConsumer.java b/core/java/android/os/AggregateBatteryConsumer.java
index e088f306cd98..068df22e222b 100644
--- a/core/java/android/os/AggregateBatteryConsumer.java
+++ b/core/java/android/os/AggregateBatteryConsumer.java
@@ -19,6 +19,7 @@ package android.os;
import android.annotation.NonNull;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
+import android.util.proto.ProtoOutputStream;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -97,6 +98,19 @@ public final class AggregateBatteryConsumer extends BatteryConsumer {
}
}
+ void writePowerComponentModelProto(@NonNull ProtoOutputStream proto) {
+ for (int i = 0; i < POWER_COMPONENT_COUNT; i++) {
+ final int powerModel = getPowerModel(i);
+ if (powerModel == BatteryConsumer.POWER_MODEL_UNDEFINED) continue;
+
+ final long token = proto.start(BatteryUsageStatsAtomsProto.COMPONENT_MODELS);
+ proto.write(BatteryUsageStatsAtomsProto.PowerComponentModel.COMPONENT, i);
+ proto.write(BatteryUsageStatsAtomsProto.PowerComponentModel.POWER_MODEL,
+ powerModelToProtoEnum(powerModel));
+ proto.end(token);
+ }
+ }
+
/**
* Builder for DeviceBatteryConsumer.
*/
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index f4ca1b50fe86..de2dec779c99 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -479,6 +479,21 @@ public abstract class BatteryConsumer {
}
/**
+ * Returns the equivalent PowerModel enum for the specified power model.
+ * {@see BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsage.PowerModel}
+ */
+ public static int powerModelToProtoEnum(@BatteryConsumer.PowerModel int powerModel) {
+ switch (powerModel) {
+ case BatteryConsumer.POWER_MODEL_MEASURED_ENERGY:
+ return BatteryUsageStatsAtomsProto.PowerComponentModel.MEASURED_ENERGY;
+ case BatteryConsumer.POWER_MODEL_POWER_PROFILE:
+ return BatteryUsageStatsAtomsProto.PowerComponentModel.POWER_PROFILE;
+ default:
+ return BatteryUsageStatsAtomsProto.PowerComponentModel.UNDEFINED;
+ }
+ }
+
+ /**
* Returns the name of the specified process state. Intended for logging and debugging.
*/
public static String processStateToString(@BatteryConsumer.ProcessState int processState) {
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index b76592d4be58..7105b1ad8b38 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -276,7 +276,7 @@ public final class BatteryUsageStats implements Parcelable, Closeable {
/**
* Returns a battery consumer for the specified battery consumer type.
*/
- public BatteryConsumer getAggregateBatteryConsumer(
+ public AggregateBatteryConsumer getAggregateBatteryConsumer(
@AggregateBatteryConsumerScope int scope) {
return mAggregateBatteryConsumers[scope];
}
@@ -450,7 +450,7 @@ public final class BatteryUsageStats implements Parcelable, Closeable {
@NonNull
private void writeStatsProto(ProtoOutputStream proto, int maxRawSize) {
- final BatteryConsumer deviceBatteryConsumer = getAggregateBatteryConsumer(
+ final AggregateBatteryConsumer deviceBatteryConsumer = getAggregateBatteryConsumer(
AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
proto.write(BatteryUsageStatsAtomsProto.SESSION_START_MILLIS, getStatsStartTimestamp());
@@ -462,6 +462,9 @@ public final class BatteryUsageStats implements Parcelable, Closeable {
getDischargeDurationMs());
deviceBatteryConsumer.writeStatsProto(proto,
BatteryUsageStatsAtomsProto.DEVICE_BATTERY_CONSUMER);
+ if (mIncludesPowerModels) {
+ deviceBatteryConsumer.writePowerComponentModelProto(proto);
+ }
writeUidBatteryConsumersProto(proto, maxRawSize);
}
diff --git a/core/java/android/os/NewUserResponse.java b/core/java/android/os/NewUserResponse.java
index c2aef8e62078..f48d9e536cd5 100644
--- a/core/java/android/os/NewUserResponse.java
+++ b/core/java/android/os/NewUserResponse.java
@@ -61,4 +61,13 @@ public final class NewUserResponse {
public @UserManager.UserOperationResult int getOperationResult() {
return mOperationResult;
}
+
+ @Override
+ public String toString() {
+ return "NewUserResponse{"
+ + "mUser="
+ + mUser
+ + ", mOperationResult=" + mOperationResult
+ + '}';
+ }
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index c802e56c8dc7..16352b8fbeb3 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -90,6 +90,7 @@ import java.util.Set;
public class UserManager {
private static final String TAG = "UserManager";
+ private static final boolean VERBOSE = false;
@UnsupportedAppUsage
private final IUserManager mService;
@@ -2056,10 +2057,10 @@ public class UserManager {
final String emulatedMode = SystemProperties.get(SYSTEM_USER_MODE_EMULATION_PROPERTY);
switch (emulatedMode) {
case SYSTEM_USER_MODE_EMULATION_FULL:
- Log.d(TAG, "isHeadlessSystemUserMode(): emulating as false");
+ if (VERBOSE) Log.v(TAG, "isHeadlessSystemUserMode(): emulating as false");
return false;
case SYSTEM_USER_MODE_EMULATION_HEADLESS:
- Log.d(TAG, "isHeadlessSystemUserMode(): emulating as true");
+ if (VERBOSE) Log.v(TAG, "isHeadlessSystemUserMode(): emulating as true");
return true;
case SYSTEM_USER_MODE_EMULATION_DEFAULT:
case "": // property not set
@@ -4190,7 +4191,11 @@ public class UserManager {
})
public boolean canAddMoreUsers(@NonNull String userType) {
try {
- return canAddMoreUsers() && mService.canAddMoreUsersOfType(userType);
+ if (userType.equals(USER_TYPE_FULL_GUEST)) {
+ return mService.canAddMoreUsersOfType(userType);
+ } else {
+ return canAddMoreUsers() && mService.canAddMoreUsersOfType(userType);
+ }
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 9cd23250d673..2483f99a07ec 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -630,7 +630,7 @@ public final class DeviceConfig {
private static final List<String> PUBLIC_NAMESPACES =
Arrays.asList(NAMESPACE_TEXTCLASSIFIER, NAMESPACE_RUNTIME, NAMESPACE_STATSD_JAVA,
NAMESPACE_STATSD_JAVA_BOOT, NAMESPACE_SELECTION_TOOLBAR, NAMESPACE_AUTOFILL,
- NAMESPACE_DEVICE_POLICY_MANAGER);
+ NAMESPACE_DEVICE_POLICY_MANAGER, NAMESPACE_CONTENT_CAPTURE);
/**
* Privacy related properties definitions.
*
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 6fc2811572af..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);
}
/**
@@ -10814,6 +10824,13 @@ public final class Settings {
"accessibility_floating_menu_migration_tooltip_prompt";
/**
+ * Setting that specifies whether the software cursor accessibility service is enabled.
+ * @hide
+ */
+ public static final String ACCESSIBILITY_SOFTWARE_CURSOR_ENABLED =
+ "accessibility_software_cursor_enabled";
+
+ /**
* Whether the Adaptive connectivity option is enabled.
*
* @hide
@@ -14257,7 +14274,7 @@ public final class Settings {
*
* @hide
*/
- public static final int DEFAULT_ENABLE_TARE = 0;
+ public static final int DEFAULT_ENABLE_TARE = 1;
/**
* Whether to enable the TARE AlarmManager economic policy or not.
@@ -17681,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
*/
@@ -17755,6 +17765,37 @@ public final class Settings {
* @hide
*/
public static final String CHARGING_SOUNDS_ENABLED = "wear_charging_sounds_enabled";
+
+ /** The status of the early updates process.
+ * @hide
+ */
+ public static final String EARLY_UPDATES_STATUS = "early_updates_status";
+
+ /**
+ * Early updates not started
+ * @hide
+ */
+ public static final int EARLY_UPDATES_STATUS_NOT_STARTED = 0;
+ /**
+ * Early updates started and in progress
+ * @hide
+ */
+ public static final int EARLY_UPDATES_STATUS_STARTED = 1;
+ /**
+ * Early updates completed and was successful
+ * @hide
+ */
+ public static final int EARLY_UPDATES_STATUS_SUCCESS = 2;
+ /**
+ * Early updates skipped
+ * @hide
+ */
+ public static final int EARLY_UPDATES_STATUS_SKIPPED = 3;
+ /**
+ * Early updates aborted due to timeout
+ * @hide
+ */
+ public static final int EARLY_UPDATES_STATUS_ABORTED = 4;
}
}
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/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index a6f63e859049..52dc34298ae7 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -89,9 +89,7 @@ public final class AccessibilityInteractionController {
// Callbacks should have the same configuration of the flags below to allow satisfying a pending
// node request on prefetch
- private static final int FLAGS_AFFECTING_REPORTED_DATA =
- AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
- | AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS;
+ private static final int FLAGS_AFFECTING_REPORTED_DATA = AccessibilityNodeInfo.FLAG_REPORT_MASK;
private final ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList =
new ArrayList<AccessibilityNodeInfo>();
@@ -167,6 +165,11 @@ public final class AccessibilityInteractionController {
return (view != null) && (view.getWindowVisibility() == View.VISIBLE && view.isShown());
}
+ private boolean isVisibleToAccessibilityService(View view) {
+ return view != null && (!view.isAccessibilityDataPrivate()
+ || mA11yManager.isRequestFromAccessibilityTool());
+ }
+
public void findAccessibilityNodeInfoByAccessibilityIdClientThread(
long accessibilityNodeId, Region interactiveRegion, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
@@ -358,7 +361,7 @@ public final class AccessibilityInteractionController {
if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
return;
}
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
+ setAccessibilityFetchFlags(flags);
requestedView = findViewByAccessibilityId(accessibilityViewId);
if (requestedView != null && isShown(requestedView)) {
requestedNode = populateAccessibilityNodeInfoForView(
@@ -371,7 +374,7 @@ public final class AccessibilityInteractionController {
mPrefetcher.prefetchAccessibilityNodeInfos(requestedView,
requestedNode == null ? null : new AccessibilityNodeInfo(requestedNode),
infos);
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ resetAccessibilityFetchFlags();
}
}
} finally {
@@ -396,7 +399,7 @@ public final class AccessibilityInteractionController {
}
mPrefetcher.prefetchAccessibilityNodeInfos(requestedView,
requestedNode == null ? null : new AccessibilityNodeInfo(requestedNode), infos);
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ resetAccessibilityFetchFlags();
updateInfosForViewPort(infos, spec, matrixValues, interactiveRegion);
final SatisfiedFindAccessibilityNodeByAccessibilityIdRequest satisfiedRequest =
getSatisfiedRequestInPrefetch(requestedNode == null ? null : requestedNode, infos,
@@ -478,7 +481,7 @@ public final class AccessibilityInteractionController {
|| viewId == null) {
return;
}
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
+ setAccessibilityFetchFlags(flags);
final View root = findViewByAccessibilityId(accessibilityViewId);
if (root != null) {
final int resolvedViewId = root.getContext().getResources()
@@ -494,7 +497,7 @@ public final class AccessibilityInteractionController {
mAddNodeInfosForViewId.reset();
}
} finally {
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ resetAccessibilityFetchFlags();
updateInfosForViewportAndReturnFindNodeResult(
infos, callback, interactionId, spec, matrixValues, interactiveRegion);
}
@@ -542,7 +545,7 @@ public final class AccessibilityInteractionController {
if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
return;
}
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
+ setAccessibilityFetchFlags(flags);
final View root = findViewByAccessibilityId(accessibilityViewId);
if (root != null && isShown(root)) {
AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
@@ -561,7 +564,7 @@ public final class AccessibilityInteractionController {
final int viewCount = foundViews.size();
for (int i = 0; i < viewCount; i++) {
View foundView = foundViews.get(i);
- if (isShown(foundView)) {
+ if (isShown(foundView) && isVisibleToAccessibilityService(foundView)) {
provider = foundView.getAccessibilityNodeProvider();
if (provider != null) {
List<AccessibilityNodeInfo> infosFromProvider =
@@ -579,7 +582,7 @@ public final class AccessibilityInteractionController {
}
}
} finally {
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ resetAccessibilityFetchFlags();
updateInfosForViewportAndReturnFindNodeResult(
infos, callback, interactionId, spec, matrixValues, interactiveRegion);
}
@@ -627,7 +630,7 @@ public final class AccessibilityInteractionController {
if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
return;
}
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
+ setAccessibilityFetchFlags(flags);
final View root = findViewByAccessibilityId(accessibilityViewId);
if (root != null && isShown(root)) {
switch (focusType) {
@@ -642,6 +645,9 @@ public final class AccessibilityInteractionController {
if (!isShown(host)) {
break;
}
+ if (!isVisibleToAccessibilityService(host)) {
+ break;
+ }
// If the host has a provider ask this provider to search for the
// focus instead fetching all provider nodes to do the search here.
AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
@@ -662,6 +668,9 @@ public final class AccessibilityInteractionController {
if (!isShown(target)) {
break;
}
+ if (!isVisibleToAccessibilityService(target)) {
+ break;
+ }
AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
if (provider != null) {
focused = provider.findFocus(focusType);
@@ -675,7 +684,7 @@ public final class AccessibilityInteractionController {
}
}
} finally {
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ resetAccessibilityFetchFlags();
updateInfoForViewportAndReturnFindNodeResult(
focused, callback, interactionId, spec, matrixValues, interactiveRegion);
}
@@ -722,7 +731,7 @@ public final class AccessibilityInteractionController {
if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
return;
}
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
+ setAccessibilityFetchFlags(flags);
final View root = findViewByAccessibilityId(accessibilityViewId);
if (root != null && isShown(root)) {
View nextView = root.focusSearch(direction);
@@ -731,7 +740,7 @@ public final class AccessibilityInteractionController {
}
}
} finally {
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ resetAccessibilityFetchFlags();
updateInfoForViewportAndReturnFindNodeResult(
next, callback, interactionId, spec, matrixValues, interactiveRegion);
}
@@ -778,9 +787,9 @@ public final class AccessibilityInteractionController {
mViewRootImpl.mStopped || mViewRootImpl.mPausedForTransition) {
return;
}
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
+ setAccessibilityFetchFlags(flags);
final View target = findViewByAccessibilityId(accessibilityViewId);
- if (target != null && isShown(target)) {
+ if (target != null && isShown(target) && isVisibleToAccessibilityService(target)) {
mA11yManager.notifyPerformingAction(action);
if (action == R.id.accessibilityActionClickOnClickableSpan) {
// Handle this hidden action separately
@@ -799,7 +808,7 @@ public final class AccessibilityInteractionController {
}
} finally {
try {
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ resetAccessibilityFetchFlags();
callback.setPerformAccessibilityActionResult(succeeded, interactionId);
} catch (RemoteException re) {
/* ignore - the other side will time out */
@@ -823,9 +832,9 @@ public final class AccessibilityInteractionController {
return;
}
try {
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags =
- AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
- final View root = mViewRootImpl.mView;
+ setAccessibilityFetchFlags(
+ AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS);
+ final View root = getRootView();
if (root != null && isShown(root)) {
final View host = mViewRootImpl.mAccessibilityFocusedHost;
// If there is no accessibility focus host or it is not a descendant
@@ -849,7 +858,7 @@ public final class AccessibilityInteractionController {
}
}
} finally {
- mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ resetAccessibilityFetchFlags();
}
}
@@ -869,7 +878,7 @@ public final class AccessibilityInteractionController {
|| mViewRootImpl.mStopped || mViewRootImpl.mPausedForTransition) {
return;
}
- final View root = mViewRootImpl.mView;
+ final View root = getRootView();
if (root != null && isShown(root)) {
// trigger ACTION_OUTSIDE to notify windows
final long now = SystemClock.uptimeMillis();
@@ -882,12 +891,30 @@ public final class AccessibilityInteractionController {
private View findViewByAccessibilityId(int accessibilityId) {
if (accessibilityId == AccessibilityNodeInfo.ROOT_ITEM_ID) {
- return mViewRootImpl.mView;
+ return getRootView();
} else {
return AccessibilityNodeIdManager.getInstance().findView(accessibilityId);
}
}
+ private View getRootView() {
+ if (!isVisibleToAccessibilityService(mViewRootImpl.mView)) {
+ return null;
+ }
+ return mViewRootImpl.mView;
+ }
+
+ private void setAccessibilityFetchFlags(int flags) {
+ mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
+ mA11yManager.setRequestFromAccessibilityTool(
+ (flags & AccessibilityNodeInfo.FLAG_SERVICE_IS_ACCESSIBILITY_TOOL) != 0);
+ }
+
+ private void resetAccessibilityFetchFlags() {
+ mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ mA11yManager.setRequestFromAccessibilityTool(false);
+ }
+
// The boundInScreen includes magnification effect, so we need to normalize it before
// determine the visibility.
private void adjustIsVisibleToUserIfNeeded(AccessibilityNodeInfo info,
@@ -1706,7 +1733,7 @@ public final class AccessibilityInteractionController {
@Override
public boolean test(View view) {
- if (view.getId() == mViewId && isShown(view)) {
+ if (view.getId() == mViewId && isShown(view) && isVisibleToAccessibilityService(view)) {
mInfos.add(view.createAccessibilityNodeInfo());
}
return false;
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/View.java b/core/java/android/view/View.java
index 48937770eddb..84edb3a7bdee 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3085,6 +3085,45 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
static final int IMPORTANT_FOR_ACCESSIBILITY_DEFAULT = IMPORTANT_FOR_ACCESSIBILITY_AUTO;
/**
+ * Automatically determine whether the view should only allow interactions from
+ * {@link android.accessibilityservice.AccessibilityService}s with the
+ * {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool} property
+ * set to true.
+ *
+ * <p>
+ * Accessibility interactions from services without {@code isAccessibilityTool} set to true are
+ * disallowed for any of the following conditions:
+ * <li>this view's window sets {@link WindowManager.LayoutParams#FLAG_SECURE}.</li>
+ * <li>this view sets {@link #getFilterTouchesWhenObscured()}.</li>
+ * <li>any parent of this view returns true from {@link #isAccessibilityDataPrivate()}.</li>
+ * </p>
+ */
+ public static final int ACCESSIBILITY_DATA_PRIVATE_AUTO = 0x00000000;
+
+ /**
+ * Only allow interactions from {@link android.accessibilityservice.AccessibilityService}s
+ * with the {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool}
+ * property set to true.
+ */
+ public static final int ACCESSIBILITY_DATA_PRIVATE_YES = 0x00000001;
+
+ /**
+ * Allow interactions from all {@link android.accessibilityservice.AccessibilityService}s,
+ * regardless of their
+ * {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool} property.
+ */
+ public static final int ACCESSIBILITY_DATA_PRIVATE_NO = 0x00000002;
+
+ /** @hide */
+ @IntDef(prefix = { "ACCESSIBILITY_DATA_PRIVATE_" }, value = {
+ ACCESSIBILITY_DATA_PRIVATE_AUTO,
+ ACCESSIBILITY_DATA_PRIVATE_YES,
+ ACCESSIBILITY_DATA_PRIVATE_NO,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AccessibilityDataPrivate {}
+
+ /**
* Mask for obtaining the bits which specify how to determine
* whether a view is important for accessibility.
*/
@@ -4527,6 +4566,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private CharSequence mAccessibilityPaneTitle;
/**
+ * Describes whether this view should only allow interactions from
+ * {@link android.accessibilityservice.AccessibilityService}s with the
+ * {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool} property
+ * set to true.
+ */
+ private int mAccessibilityDataPrivate = ACCESSIBILITY_DATA_PRIVATE_AUTO;
+
+ /**
* Specifies the id of a view for which this view serves as a label for
* accessibility purposes.
*/
@@ -5919,6 +5966,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
setImportantForAccessibility(a.getInt(attr,
IMPORTANT_FOR_ACCESSIBILITY_DEFAULT));
break;
+ case R.styleable.View_accessibilityDataPrivate:
+ setAccessibilityDataPrivate(a.getInt(attr,
+ ACCESSIBILITY_DATA_PRIVATE_AUTO));
+ break;
case R.styleable.View_accessibilityLiveRegion:
setAccessibilityLiveRegion(a.getInt(attr, ACCESSIBILITY_LIVE_REGION_DEFAULT));
break;
@@ -8518,6 +8569,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* is responsible for handling this call.
* </p>
* <p>
+ * If this view sets {@link #isAccessibilityDataPrivate()} then this view should only append
+ * sensitive information to an event that also sets
+ * {@link AccessibilityEvent#isAccessibilityDataPrivate()}.
+ * </p>
+ * <p>
* <em>Note:</em> Accessibility events of certain types are not dispatched for
* populating the event text via this method. For details refer to {@link AccessibilityEvent}.
* </p>
@@ -10419,7 +10475,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
if ((mAttachInfo.mAccessibilityFetchFlags
- & AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS) != 0
+ & AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_REPORT_VIEW_IDS) != 0
&& Resources.resourceHasPackage(mID)) {
try {
String viewId = getResources().getResourceName(mID);
@@ -14402,14 +14458,75 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
@UnsupportedAppUsage
public boolean includeForAccessibility() {
if (mAttachInfo != null) {
+ if (isAccessibilityDataPrivate() && !AccessibilityManager.getInstance(
+ mContext).isRequestFromAccessibilityTool()) {
+ return false;
+ }
+
return (mAttachInfo.mAccessibilityFetchFlags
- & AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0
+ & AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS) != 0
|| isImportantForAccessibility();
}
return false;
}
/**
+ * Whether this view should restrict accessibility service access only to services that have the
+ * {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool} property
+ * set to true.
+ *
+ * <p>
+ * See default behavior provided by {@link #ACCESSIBILITY_DATA_PRIVATE_AUTO}. Otherwise,
+ * returns true for {@link #ACCESSIBILITY_DATA_PRIVATE_YES} or false for {@link
+ * #ACCESSIBILITY_DATA_PRIVATE_NO}.
+ * </p>
+ *
+ * @return True if this view should restrict accessibility service access to services that have
+ * the isAccessibilityTool property.
+ */
+ @ViewDebug.ExportedProperty(category = "accessibility")
+ public boolean isAccessibilityDataPrivate() {
+ if (mAccessibilityDataPrivate == ACCESSIBILITY_DATA_PRIVATE_YES) {
+ return true;
+ }
+ if (mAccessibilityDataPrivate == ACCESSIBILITY_DATA_PRIVATE_NO) {
+ return false;
+ }
+
+ // Views inside FLAG_SECURE windows default to accessibilityDataPrivate.
+ if (mAttachInfo != null && mAttachInfo.mWindowSecure) {
+ return true;
+ }
+ // Views that set filterTouchesWhenObscured default to accessibilityDataPrivate.
+ if (getFilterTouchesWhenObscured()) {
+ return true;
+ }
+
+ // Descendants of an accessibilityDataPrivate View are also accessibilityDataPrivate.
+ ViewParent parent = mParent;
+ while (parent instanceof View) {
+ if (((View) parent).isAccessibilityDataPrivate()) {
+ return true;
+ }
+ parent = parent.getParent();
+ }
+
+ // Otherwise, default to not accessibilityDataPrivate.
+ return false;
+ }
+
+ /**
+ * Specifies whether this view should only allow interactions from
+ * {@link android.accessibilityservice.AccessibilityService}s with the
+ * {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool} property
+ * set to true.
+ */
+ public void setAccessibilityDataPrivate(
+ @AccessibilityDataPrivate int accessibilityDataPrivate) {
+ mAccessibilityDataPrivate = accessibilityDataPrivate;
+ }
+
+ /**
* Returns whether the View is considered actionable from
* accessibility perspective. Such view are important for
* accessibility.
@@ -30096,6 +30213,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
int mWindowVisibility;
/**
+ * Indicates whether the view's window sets {@link WindowManager.LayoutParams#FLAG_SECURE}.
+ */
+ boolean mWindowSecure;
+
+ /**
* Indicates the time at which drawing started to occur.
*/
@UnsupportedAppUsage
@@ -30272,8 +30394,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* Flags related to accessibility processing.
*
- * @see AccessibilityNodeInfo#FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
- * @see AccessibilityNodeInfo#FLAG_REPORT_VIEW_IDS
+ * @see AccessibilityNodeInfo#FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS
+ * @see AccessibilityNodeInfo#FLAG_SERVICE_REQUESTS_REPORT_VIEW_IDS
*/
int mAccessibilityFetchFlags;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index b2854a3fec44..f79f0d49959d 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2839,6 +2839,7 @@ public final class ViewRootImpl implements ViewParent,
// However, windows are now always 32 bits by default, so choose 32 bits
mAttachInfo.mUse32BitDrawingCache = true;
mAttachInfo.mWindowVisibility = viewVisibility;
+ mAttachInfo.mWindowSecure = (lp.flags & WindowManager.LayoutParams.FLAG_SECURE) != 0;
mAttachInfo.mRecomputeGlobalAttributes = false;
mLastConfigurationFromResources.setTo(config);
mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index e2466345ff4a..2c2ae06e9186 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -45,6 +45,30 @@ import java.util.List;
public abstract class ViewStructure {
/**
+ * Key used for writing active child view information to the content capture bundle.
+ *
+ * The value stored under this key will be an ordered list of Autofill IDs of child views.
+ *
+ * TODO(b/241498401): Add @TestApi in Android U
+ * @hide
+ */
+ public static final String EXTRA_ACTIVE_CHILDREN_IDS =
+ "android.view.ViewStructure.extra.ACTIVE_CHILDREN_IDS";
+
+ /**
+ * Key used for writing the first active child's position to the content capture bundle.
+ *
+ * When active child view information is provided under the
+ * {@link #EXTRA_ACTIVE_CHILDREN_IDS}, the value stored under this key will be the
+ * 0-based position of the first child view in the list relative to the positions of child views
+ * in the containing View's dataset.
+ *
+ * TODO(b/241498401): Add @TestApi in Android U
+ * @hide */
+ public static final String EXTRA_FIRST_ACTIVE_POSITION =
+ "android.view.ViewStructure.extra.FIRST_ACTIVE_POSITION";
+
+ /**
* Set the identifier for this view.
*
* @param id The view's identifier, as per {@link View#getId View.getId()}.
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 6f6936127cde..fe8d64f0152f 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -4470,10 +4470,22 @@ public interface WindowManager extends ViewManager {
changes |= LAYOUT_CHANGED;
}
- if (!Arrays.equals(paramsForRotation, o.paramsForRotation)) {
+ if (paramsForRotation != o.paramsForRotation) {
+ if ((changes & LAYOUT_CHANGED) == 0) {
+ if (paramsForRotation != null && o.paramsForRotation != null
+ && paramsForRotation.length == o.paramsForRotation.length) {
+ for (int i = paramsForRotation.length - 1; i >= 0; i--) {
+ if (hasLayoutDiff(paramsForRotation[i], o.paramsForRotation[i])) {
+ changes |= LAYOUT_CHANGED;
+ break;
+ }
+ }
+ } else {
+ changes |= LAYOUT_CHANGED;
+ }
+ }
paramsForRotation = o.paramsForRotation;
checkNonRecursiveParams();
- changes |= LAYOUT_CHANGED;
}
if (mWallpaperTouchEventsEnabled != o.mWallpaperTouchEventsEnabled) {
@@ -4484,6 +4496,21 @@ public interface WindowManager extends ViewManager {
return changes;
}
+ /**
+ * Returns {@code true} if the 2 params may have difference results of
+ * {@link WindowLayout#computeFrames}.
+ */
+ private static boolean hasLayoutDiff(LayoutParams a, LayoutParams b) {
+ return a.width != b.width || a.height != b.height || a.x != b.x || a.y != b.y
+ || a.horizontalMargin != b.horizontalMargin
+ || a.verticalMargin != b.verticalMargin
+ || a.layoutInDisplayCutoutMode != b.layoutInDisplayCutoutMode
+ || a.gravity != b.gravity || !Arrays.equals(a.providedInsets, b.providedInsets)
+ || a.mFitInsetsTypes != b.mFitInsetsTypes
+ || a.mFitInsetsSides != b.mFitInsetsSides
+ || a.mFitInsetsIgnoringVisibility != b.mFitInsetsIgnoringVisibility;
+ }
+
@Override
public String debug(String output) {
output += "Contents of " + this + ":";
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index cd0dd1df1249..2db0dcbce45e 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -46,15 +46,17 @@ import java.util.List;
* </p>
* <p>
* The main purpose of an accessibility event is to communicate changes in the UI to an
- * {@link android.accessibilityservice.AccessibilityService}. The service may then inspect,
- * if needed the user interface by examining the View hierarchy, as represented by a tree of
- * {@link AccessibilityNodeInfo}s (snapshot of a View state)
- * which can be used for exploring the window content. Note that the privilege for accessing
- * an event's source, thus the window content, has to be explicitly requested. For more
+ * {@link android.accessibilityservice.AccessibilityService}. If needed, the service may then
+ * inspect the user interface by examining the View hierarchy through the event's
+ * {@link #getSource() source}, as represented by a tree of {@link AccessibilityNodeInfo}s (snapshot
+ * of a View state) which can be used for exploring the window content. Note that the privilege for
+ * accessing an event's source, thus the window content, has to be explicitly requested. For more
* details refer to {@link android.accessibilityservice.AccessibilityService}. If an
* accessibility service has not requested to retrieve the window content the event will
- * not contain reference to its source. Also for events of type
- * {@link #TYPE_NOTIFICATION_STATE_CHANGED} the source is never available.
+ * not contain reference to its source. <strong>Note: </strong> for events of type
+ * {@link #TYPE_NOTIFICATION_STATE_CHANGED} the source is never available, and Views that set
+ * {@link android.view.View#isAccessibilityDataPrivate()} may not populate all event properties on
+ * events sent from higher up in the view hierarchy.
* </p>
* <p>
* This class represents various semantically different accessibility event
@@ -1092,6 +1094,47 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
}
/**
+ * Whether the event should only be delivered to an
+ * {@link android.accessibilityservice.AccessibilityService} with the
+ * {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool} property
+ * set to true.
+ *
+ * <p>
+ * Initial value matches the {@link android.view.View#isAccessibilityDataPrivate} property from
+ * the event's source node, if present, or false by default.
+ * </p>
+ *
+ * @return True if the event should be delivered only to isAccessibilityTool services, false
+ * otherwise.
+ * @see #setAccessibilityDataPrivate
+ */
+ @Override
+ public boolean isAccessibilityDataPrivate() {
+ return super.isAccessibilityDataPrivate();
+ }
+
+ /**
+ * Sets whether the event should only be delivered to an
+ * {@link android.accessibilityservice.AccessibilityService} with the
+ * {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool} property
+ * set to true.
+ *
+ * <p>
+ * This will be set automatically based on the event's source (if present). If creating and
+ * sending an event directly through {@link AccessibilityManager} (where an event may have
+ * no source) then this method must be called explicitly if you want non-default behavior.
+ * </p>
+ *
+ * @param accessibilityDataPrivate True if the event should be delivered only to
+ * isAccessibilityTool services, false otherwise.
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ @Override
+ public void setAccessibilityDataPrivate(boolean accessibilityDataPrivate) {
+ super.setAccessibilityDataPrivate(accessibilityDataPrivate);
+ }
+
+ /**
* Gets the bit mask of the speech state signaled by a {@link #TYPE_SPEECH_STATE_CHANGE} event
*
* @see #SPEECH_STATE_SPEAKING_START
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 9e3195aec8a6..e89f836aaac1 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -276,6 +276,8 @@ public final class AccessibilityManager {
private final ArrayMap<AudioDescriptionRequestedChangeListener, Executor>
mAudioDescriptionRequestedChangeListeners = new ArrayMap<>();
+ private boolean mRequestFromAccessibilityTool;
+
/**
* Map from a view's accessibility id to the list of request preparers set for that view
*/
@@ -983,6 +985,39 @@ public final class AccessibilityManager {
}
/**
+ * Whether the current accessibility request comes from an
+ * {@link AccessibilityService} with the {@link AccessibilityServiceInfo#isAccessibilityTool}
+ * property set to true.
+ *
+ * <p>
+ * You can use this method inside {@link AccessibilityNodeProvider} to decide how to populate
+ * your nodes.
+ * </p>
+ *
+ * <p>
+ * <strong>Note:</strong> The return value is valid only when an {@link AccessibilityNodeInfo}
+ * request is in progress, can change from one request to another, and has no meaning when a
+ * request is not in progress.
+ * </p>
+ *
+ * @return True if the current request is from a tool that sets isAccessibilityTool.
+ */
+ public boolean isRequestFromAccessibilityTool() {
+ return mRequestFromAccessibilityTool;
+ }
+
+ /**
+ * Specifies whether the current accessibility request comes from an
+ * {@link AccessibilityService} with the {@link AccessibilityServiceInfo#isAccessibilityTool}
+ * property set to true.
+ *
+ * @hide
+ */
+ public void setRequestFromAccessibilityTool(boolean requestFromAccessibilityTool) {
+ mRequestFromAccessibilityTool = requestFromAccessibilityTool;
+ }
+
+ /**
* Registers a {@link AccessibilityRequestPreparer}.
*/
public void addAccessibilityRequestPreparer(AccessibilityRequestPreparer preparer) {
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 953f2615b539..15718c4af26f 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -217,14 +217,29 @@ public class AccessibilityNodeInfo implements Parcelable {
@Retention(RetentionPolicy.SOURCE)
public @interface PrefetchingStrategy {}
- /** @hide */
- public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x00000080;
+ /**
+ * @see AccessibilityServiceInfo#FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
+ * @hide
+ */
+ public static final int FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS = 0x00000080;
- /** @hide */
- public static final int FLAG_REPORT_VIEW_IDS = 0x00000100;
+ /**
+ * @see AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS
+ * @hide
+ */
+ public static final int FLAG_SERVICE_REQUESTS_REPORT_VIEW_IDS = 0x00000100;
+
+ /**
+ * @see AccessibilityServiceInfo#isAccessibilityTool()
+ * @hide
+ */
+ public static final int FLAG_SERVICE_IS_ACCESSIBILITY_TOOL = 0x00000200;
/** @hide */
- public static final int FLAG_REPORT_MASK = 0x00000180;
+ public static final int FLAG_REPORT_MASK =
+ FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS
+ | FLAG_SERVICE_REQUESTS_REPORT_VIEW_IDS
+ | FLAG_SERVICE_IS_ACCESSIBILITY_TOOL;
// Actions.
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index 036316e15cb9..789c740bbba2 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -72,6 +72,7 @@ public class AccessibilityRecord {
private static final int PROPERTY_FULL_SCREEN = 0x00000080;
private static final int PROPERTY_SCROLLABLE = 0x00000100;
private static final int PROPERTY_IMPORTANT_FOR_ACCESSIBILITY = 0x00000200;
+ private static final int PROPERTY_ACCESSIBILITY_DATA_PRIVATE = 0x00000400;
private static final int GET_SOURCE_PREFETCH_FLAGS =
AccessibilityNodeInfo.FLAG_PREFETCH_ANCESTORS
@@ -159,6 +160,8 @@ public class AccessibilityRecord {
important = root.isImportantForAccessibility();
rootViewId = root.getAccessibilityViewId();
mSourceWindowId = root.getAccessibilityWindowId();
+ setBooleanProperty(PROPERTY_ACCESSIBILITY_DATA_PRIVATE,
+ root.isAccessibilityDataPrivate());
}
setBooleanProperty(PROPERTY_IMPORTANT_FOR_ACCESSIBILITY, important);
mSourceNodeId = AccessibilityNodeInfo.makeNodeId(rootViewId, virtualDescendantId);
@@ -388,6 +391,23 @@ public class AccessibilityRecord {
}
/**
+ * @see AccessibilityEvent#isAccessibilityDataPrivate
+ * @hide
+ */
+ boolean isAccessibilityDataPrivate() {
+ return getBooleanProperty(PROPERTY_ACCESSIBILITY_DATA_PRIVATE);
+ }
+
+ /**
+ * @see AccessibilityEvent#setAccessibilityDataPrivate
+ * @hide
+ */
+ void setAccessibilityDataPrivate(boolean accessibilityDataPrivate) {
+ enforceNotSealed();
+ setBooleanProperty(PROPERTY_ACCESSIBILITY_DATA_PRIVATE, accessibilityDataPrivate);
+ }
+
+ /**
* Gets the number of items that can be visited.
*
* @return The number of items.
@@ -941,6 +961,8 @@ public class AccessibilityRecord {
appendUnless(false, PROPERTY_CHECKED, builder);
appendUnless(false, PROPERTY_FULL_SCREEN, builder);
appendUnless(false, PROPERTY_SCROLLABLE, builder);
+ appendUnless(false, PROPERTY_IMPORTANT_FOR_ACCESSIBILITY, builder);
+ appendUnless(false, PROPERTY_ACCESSIBILITY_DATA_PRIVATE, builder);
append(builder, "BeforeText", mBeforeText);
append(builder, "FromIndex", mFromIndex);
@@ -974,6 +996,8 @@ public class AccessibilityRecord {
case PROPERTY_SCROLLABLE: return "Scrollable";
case PROPERTY_IMPORTANT_FOR_ACCESSIBILITY:
return "ImportantForAccessibility";
+ case PROPERTY_ACCESSIBILITY_DATA_PRIVATE:
+ return "AccessibilityDataPrivate";
default: return Integer.toHexString(prop);
}
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index ba4176faa283..db4ac5de0b49 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -55,6 +55,15 @@ public final class ContentCaptureEvent implements Parcelable {
/**
* Called when a node has been added to the screen and is visible to the user.
*
+ * On API level 33, this event may be re-sent with additional information if a view's children
+ * have changed, e.g. scrolling Views inside of a ListView. This information will be stored in
+ * the extras Bundle associated with the event's ViewNode. Within the Bundle, the
+ * "android.view.ViewStructure.extra.ACTIVE_CHILDREN_IDS" key may be used to get a list of
+ * Autofill IDs of active child views, and the
+ * "android.view.ViewStructure.extra.FIRST_ACTIVE_POSITION" key may be used to get the 0-based
+ * position of the first active child view in the list relative to the positions of child views
+ * in the container View's dataset.
+ *
* <p>The metadata of the node is available through {@link #getViewNode()}.
*/
public static final int TYPE_VIEW_APPEARED = 1;
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 48d29706a1e9..1664637eac56 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -280,6 +280,15 @@ public final class ContentCaptureManager {
"service_explicitly_enabled";
/**
+ * Device config property used by {@code android.widget.AbsListView} to determine whether or
+ * not it should report the positions of its children to Content Capture.
+ *
+ * @hide
+ */
+ public static final String DEVICE_CONFIG_PROPERTY_REPORT_LIST_VIEW_CHILDREN =
+ "report_list_view_children";
+
+ /**
* Maximum number of events that are buffered before sent to the app.
*
* @hide
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 0b4a29899020..0c03d109f649 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -3605,7 +3605,9 @@ public final class InputMethodManager {
* specifying one of those statically defined subtypes in {@code subtypes}.</p>
*
* @param imiId Id of InputMethodInfo which additional input method subtypes will be added to.
+ * If the imiId is {@code null}, system would do nothing for this operation.
* @param subtypes subtypes will be added as additional subtypes of the current input method.
+ * If the subtypes is {@code null}, system would do nothing for this operation.
* @deprecated For IMEs that have already implemented features like customizable/downloadable
* keyboard layouts/languages, please start migration to other approaches. One idea
* would be exposing only one unified {@link InputMethodSubtype} then implement
@@ -3618,6 +3620,11 @@ public final class InputMethodManager {
mServiceInvoker.setAdditionalInputMethodSubtypes(imiId, subtypes, UserHandle.myUserId());
}
+ /**
+ * Returns the last used {@link InputMethodSubtype} in system history.
+ *
+ * @return the last {@link InputMethodSubtype}, {@code null} if last IME have no subtype.
+ */
@Nullable
public InputMethodSubtype getLastInputMethodSubtype() {
return mServiceInvoker.getLastInputMethodSubtype(UserHandle.myUserId());
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 1cb96b15b8ef..1e1b9baaffdd 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -20,6 +20,7 @@ import android.annotation.ColorInt;
import android.annotation.DrawableRes;
import android.annotation.NonNull;
import android.annotation.TestApi;
+import android.app.ActivityThread;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
@@ -37,6 +38,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.StrictMode;
import android.os.Trace;
+import android.provider.DeviceConfig;
import android.text.Editable;
import android.text.InputType;
import android.text.TextUtils;
@@ -65,6 +67,7 @@ import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.ViewHierarchyEncoder;
import android.view.ViewParent;
+import android.view.ViewStructure;
import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
@@ -73,6 +76,9 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.accessibility.AccessibilityNodeInfo.CollectionInfo;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
+import android.view.autofill.AutofillId;
+import android.view.contentcapture.ContentCaptureManager;
+import android.view.contentcapture.ContentCaptureSession;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
@@ -642,6 +648,23 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
private int mLastScrollState = OnScrollListener.SCROLL_STATE_IDLE;
/**
+ * Indicates that reporting positions of child views to content capture is enabled via
+ * DeviceConfig.
+ */
+ private static boolean sContentCaptureReportingEnabledByDeviceConfig = false;
+
+ /**
+ * Listens for changes to DeviceConfig properties and updates stored values accordingly.
+ */
+ private static DeviceConfig.OnPropertiesChangedListener sDeviceConfigChangeListener = null;
+
+ /**
+ * Indicates that child positions of views should be reported to Content Capture the next time
+ * that active views are refreshed.
+ */
+ private boolean mReportChildrenToContentCaptureOnNextUpdate = true;
+
+ /**
* Helper object that renders and controls the fast scroll thumb.
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768941)
@@ -858,8 +881,44 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
public void adjustListItemSelectionBounds(Rect bounds);
}
+ private static class DeviceConfigChangeListener
+ implements DeviceConfig.OnPropertiesChangedListener {
+ @Override
+ public void onPropertiesChanged(
+ @NonNull DeviceConfig.Properties properties) {
+ if (!DeviceConfig.NAMESPACE_CONTENT_CAPTURE.equals(properties.getNamespace())) {
+ return;
+ }
+
+ for (String key : properties.getKeyset()) {
+ if (!ContentCaptureManager.DEVICE_CONFIG_PROPERTY_REPORT_LIST_VIEW_CHILDREN
+ .equals(key)) {
+ continue;
+ }
+
+ sContentCaptureReportingEnabledByDeviceConfig = properties.getBoolean(key,
+ false);
+ }
+ }
+ }
+
+ private static void setupDeviceConfigProperties() {
+ if (sDeviceConfigChangeListener == null) {
+ sContentCaptureReportingEnabledByDeviceConfig = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
+ ContentCaptureManager.DEVICE_CONFIG_PROPERTY_REPORT_LIST_VIEW_CHILDREN,
+ false);
+ sDeviceConfigChangeListener = new DeviceConfigChangeListener();
+ DeviceConfig.addOnPropertiesChangedListener(
+ DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
+ ActivityThread.currentApplication().getMainExecutor(),
+ sDeviceConfigChangeListener);
+ }
+ }
+
public AbsListView(Context context) {
super(context);
+ setupDeviceConfigProperties();
mEdgeGlowBottom = new EdgeEffect(context);
mEdgeGlowTop = new EdgeEffect(context);
initAbsListView();
@@ -882,6 +941,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
public AbsListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ setupDeviceConfigProperties();
mEdgeGlowBottom = new EdgeEffect(context, attrs);
mEdgeGlowTop = new EdgeEffect(context, attrs);
initAbsListView();
@@ -4802,6 +4862,14 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
mOnScrollListener.onScrollStateChanged(this, newState);
}
}
+
+ // When scrolling, we want to report changes in the active children to Content Capture,
+ // so set the flag to report on the next update only when scrolling has stopped or a fling
+ // scroll is performed.
+ if (newState == OnScrollListener.SCROLL_STATE_IDLE
+ || newState == OnScrollListener.SCROLL_STATE_FLING) {
+ mReportChildrenToContentCaptureOnNextUpdate = true;
+ }
}
/**
@@ -6763,10 +6831,77 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
mRecycler.mRecyclerListener = listener;
}
+ /**
+ * {@inheritDoc}
+ *
+ * This method will initialize the fields of the {@link ViewStructure}
+ * using the base implementation in {@link View}. On API level 33 and higher, it may also
+ * write information about the positions of active views to the extras bundle provided by the
+ * {@link ViewStructure}.
+ *
+ * NOTE: When overriding this method on API level 33, if not calling super() or if changing the
+ * logic for child views, be sure to provide values for the first active child view position and
+ * the list of active child views in the {@link ViewStructure}'s extras {@link Bundle} using the
+ * "android.view.ViewStructure.extra.ACTIVE_CHILDREN_IDS" and
+ * "android.view.ViewStructure.extra.FIRST_ACTIVE_POSITION" keys.
+ *
+ * @param structure {@link ViewStructure} to be filled in with structured view data.
+ * @param flags optional flags.
+ *
+ * @see View#AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
+ */
+ @Override
+ public void onProvideContentCaptureStructure(
+ @NonNull ViewStructure structure, int flags) {
+ super.onProvideContentCaptureStructure(structure, flags);
+ if (!sContentCaptureReportingEnabledByDeviceConfig) {
+ return;
+ }
+
+ Bundle extras = structure.getExtras();
+
+ if (extras == null) {
+ Log.wtf(TAG, "Unexpected null extras Bundle in ViewStructure");
+ return;
+ }
+
+ int childCount = getChildCount();
+ ArrayList<AutofillId> idsList = new ArrayList<>(childCount);
+
+ for (int i = 0; i < childCount; ++i) {
+ View activeView = getChildAt(i);
+ if (activeView == null) {
+ continue;
+ }
+
+ idsList.add(activeView.getAutofillId());
+ }
+
+ extras.putParcelableArrayList(ViewStructure.EXTRA_ACTIVE_CHILDREN_IDS,
+ idsList);
+
+ extras.putInt(ViewStructure.EXTRA_FIRST_ACTIVE_POSITION,
+ getFirstVisiblePosition());
+ }
+
+ private void reportActiveViewsToContentCapture() {
+ if (!sContentCaptureReportingEnabledByDeviceConfig) {
+ return;
+ }
+
+ ContentCaptureSession session = getContentCaptureSession();
+ if (session != null) {
+ ViewStructure structure = session.newViewStructure(this);
+ onProvideContentCaptureStructure(structure, /* flags= */ 0);
+ session.notifyViewAppeared(structure);
+ }
+ }
+
class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
@Override
public void onChanged() {
super.onChanged();
+ mReportChildrenToContentCaptureOnNextUpdate = true;
if (mFastScroll != null) {
mFastScroll.onSectionsChanged();
}
@@ -6775,6 +6910,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
@Override
public void onInvalidated() {
super.onInvalidated();
+ mReportChildrenToContentCaptureOnNextUpdate = true;
if (mFastScroll != null) {
mFastScroll.onSectionsChanged();
}
@@ -7093,6 +7229,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
lp.scrappedFromPosition = firstActivePosition + i;
}
}
+
+ if (mReportChildrenToContentCaptureOnNextUpdate && childCount > 0) {
+ AbsListView.this.reportActiveViewsToContentCapture();
+ mReportChildrenToContentCaptureOnNextUpdate = false;
+ }
}
/**
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/OWNERS b/core/java/android/widget/OWNERS
index bd0a5162a036..56310aef7752 100644
--- a/core/java/android/widget/OWNERS
+++ b/core/java/android/widget/OWNERS
@@ -11,3 +11,5 @@ njawad@google.com
per-file TextView*,EditText.java,Editor.java,EditorTouchState.java = file:../text/OWNERS
per-file SpellChecker.java = file:../view/inputmethod/OWNERS
+
+per-file RemoteViews* = file:../appwidget/OWNERS \ No newline at end of file
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/android/widget/TextView.java b/core/java/android/widget/TextView.java
index fe0cbcb4eb7e..3f87ec2beae5 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -12076,6 +12076,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
public void onPopulateAccessibilityEventInternal(AccessibilityEvent event) {
super.onPopulateAccessibilityEventInternal(event);
+ if (this.isAccessibilityDataPrivate() && !event.isAccessibilityDataPrivate()) {
+ // This view's accessibility data is private, but another view that generated this event
+ // is not, so don't append this view's text to the event in order to prevent sharing
+ // this view's contents with non-accessibility-tool services.
+ return;
+ }
+
final CharSequence text = getTextForAccessibility();
if (!TextUtils.isEmpty(text)) {
event.getText().add(text);
@@ -12489,7 +12496,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
RectF[] boundingRects = new RectF[positionInfoLength];
final CursorAnchorInfo.Builder builder = new CursorAnchorInfo.Builder();
populateCharacterBounds(builder, positionInfoStartIndex,
- positionInfoStartIndex + positionInfoLength,
+ Math.min(positionInfoStartIndex + positionInfoLength, length()),
viewportToContentHorizontalOffset(), viewportToContentVerticalOffset());
CursorAnchorInfo cursorAnchorInfo = builder.setMatrix(null).build();
for (int i = 0; i < positionInfoLength; i++) {
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index 212616609f35..ff7ea317d3f8 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -269,6 +269,20 @@ public final class WindowContainerTransaction implements Parcelable {
}
/**
+ * Sets whether a task should be translucent. When {@code false}, the existing translucent of
+ * the task applies, but when {@code true} the task will be forced to be translucent.
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction setForceTranslucent(
+ @NonNull WindowContainerToken container, boolean forceTranslucent) {
+ Change chg = getOrCreateChange(container.asBinder());
+ chg.mForceTranslucent = forceTranslucent;
+ chg.mChangeMask |= Change.CHANGE_FORCE_TRANSLUCENT;
+ return this;
+ }
+
+ /**
* Used in conjunction with a shell-transition call (usually finishTransition). This is
* basically a message to the transition system that a particular task should NOT go into
* PIP even though it normally would. This is to deal with some edge-case situations where
@@ -854,11 +868,13 @@ public final class WindowContainerTransaction implements Parcelable {
public static final int CHANGE_BOUNDS_TRANSACTION_RECT = 1 << 4;
public static final int CHANGE_IGNORE_ORIENTATION_REQUEST = 1 << 5;
public static final int CHANGE_FORCE_NO_PIP = 1 << 6;
+ public static final int CHANGE_FORCE_TRANSLUCENT = 1 << 7;
private final Configuration mConfiguration = new Configuration();
private boolean mFocusable = true;
private boolean mHidden = false;
private boolean mIgnoreOrientationRequest = false;
+ private boolean mForceTranslucent = false;
private int mChangeMask = 0;
private @ActivityInfo.Config int mConfigSetMask = 0;
@@ -878,6 +894,7 @@ public final class WindowContainerTransaction implements Parcelable {
mFocusable = in.readBoolean();
mHidden = in.readBoolean();
mIgnoreOrientationRequest = in.readBoolean();
+ mForceTranslucent = in.readBoolean();
mChangeMask = in.readInt();
mConfigSetMask = in.readInt();
mWindowSetMask = in.readInt();
@@ -923,6 +940,9 @@ public final class WindowContainerTransaction implements Parcelable {
if ((other.mChangeMask & CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) {
mIgnoreOrientationRequest = other.mIgnoreOrientationRequest;
}
+ if ((other.mChangeMask & CHANGE_FORCE_TRANSLUCENT) != 0) {
+ mForceTranslucent = other.mForceTranslucent;
+ }
mChangeMask |= other.mChangeMask;
if (other.mActivityWindowingMode >= 0) {
mActivityWindowingMode = other.mActivityWindowingMode;
@@ -973,6 +993,15 @@ public final class WindowContainerTransaction implements Parcelable {
return mIgnoreOrientationRequest;
}
+ /** Gets the requested force translucent state. */
+ public boolean getForceTranslucent() {
+ if ((mChangeMask & CHANGE_FORCE_TRANSLUCENT) == 0) {
+ throw new RuntimeException("Force translucent not set. "
+ + "Check CHANGE_FORCE_TRANSLUCENT first");
+ }
+ return mForceTranslucent;
+ }
+
public int getChangeMask() {
return mChangeMask;
}
@@ -1050,6 +1079,7 @@ public final class WindowContainerTransaction implements Parcelable {
dest.writeBoolean(mFocusable);
dest.writeBoolean(mHidden);
dest.writeBoolean(mIgnoreOrientationRequest);
+ dest.writeBoolean(mForceTranslucent);
dest.writeInt(mChangeMask);
dest.writeInt(mConfigSetMask);
dest.writeInt(mWindowSetMask);
diff --git a/core/java/com/android/internal/inputmethod/ImeTracingServerImpl.java b/core/java/com/android/internal/inputmethod/ImeTracingServerImpl.java
index 20ff83f5f68e..f885a7e775ed 100644
--- a/core/java/com/android/internal/inputmethod/ImeTracingServerImpl.java
+++ b/core/java/com/android/internal/inputmethod/ImeTracingServerImpl.java
@@ -21,6 +21,7 @@ import static android.os.Build.IS_USER;
import android.annotation.Nullable;
import android.os.RemoteException;
import android.os.ServiceManager.ServiceNotFoundException;
+import android.os.SystemClock;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
import android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceFileProto;
@@ -34,6 +35,7 @@ import com.android.internal.util.TraceBuffer;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
+import java.util.concurrent.TimeUnit;
/**
* An implementation of {@link ImeTracing} for the system_server process.
@@ -139,18 +141,30 @@ class ImeTracingServerImpl extends ImeTracing {
private void writeTracesToFilesLocked() {
try {
+ long timeOffsetNs =
+ TimeUnit.NANOSECONDS.convert(System.currentTimeMillis(), TimeUnit.NANOSECONDS)
+ - SystemClock.elapsedRealtimeNanos();
+
ProtoOutputStream clientsProto = new ProtoOutputStream();
clientsProto.write(InputMethodClientsTraceFileProto.MAGIC_NUMBER,
MAGIC_NUMBER_CLIENTS_VALUE);
+ clientsProto.write(InputMethodClientsTraceFileProto.REAL_TO_ELAPSED_TIME_OFFSET_NANOS,
+ timeOffsetNs);
mBufferClients.writeTraceToFile(mTraceFileClients, clientsProto);
ProtoOutputStream imsProto = new ProtoOutputStream();
- imsProto.write(InputMethodServiceTraceFileProto.MAGIC_NUMBER, MAGIC_NUMBER_IMS_VALUE);
+ imsProto.write(InputMethodServiceTraceFileProto.MAGIC_NUMBER,
+ MAGIC_NUMBER_IMS_VALUE);
+ imsProto.write(InputMethodServiceTraceFileProto.REAL_TO_ELAPSED_TIME_OFFSET_NANOS,
+ timeOffsetNs);
mBufferIms.writeTraceToFile(mTraceFileIms, imsProto);
ProtoOutputStream immsProto = new ProtoOutputStream();
immsProto.write(InputMethodManagerServiceTraceFileProto.MAGIC_NUMBER,
MAGIC_NUMBER_IMMS_VALUE);
+ immsProto.write(
+ InputMethodManagerServiceTraceFileProto.REAL_TO_ELAPSED_TIME_OFFSET_NANOS,
+ timeOffsetNs);
mBufferImms.writeTraceToFile(mTraceFileImms, immsProto);
resetBuffers();
diff --git a/core/java/com/android/internal/os/TimeoutRecord.java b/core/java/com/android/internal/os/TimeoutRecord.java
new file mode 100644
index 000000000000..a8885f9ef25b
--- /dev/null
+++ b/core/java/com/android/internal/os/TimeoutRecord.java
@@ -0,0 +1,130 @@
+/*
+ * 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.os;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.SystemClock;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A timeout that has triggered on the system.
+ *
+ * @hide
+ */
+public class TimeoutRecord {
+ /** Kind of timeout, e.g. BROADCAST_RECEIVER, etc. */
+ @IntDef(value = {
+ TimeoutKind.INPUT_DISPATCH_NO_FOCUSED_WINDOW,
+ TimeoutKind.INPUT_DISPATCH_WINDOW_UNRESPONSIVE,
+ TimeoutKind.BROADCAST_RECEIVER,
+ TimeoutKind.SERVICE_START,
+ TimeoutKind.SERVICE_EXEC,
+ TimeoutKind.CONTENT_PROVIDER,
+ TimeoutKind.APP_REGISTERED})
+
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface TimeoutKind {
+ int INPUT_DISPATCH_NO_FOCUSED_WINDOW = 1;
+ int INPUT_DISPATCH_WINDOW_UNRESPONSIVE = 2;
+ int BROADCAST_RECEIVER = 3;
+ int SERVICE_START = 4;
+ int SERVICE_EXEC = 5;
+ int CONTENT_PROVIDER = 6;
+ int APP_REGISTERED = 7;
+ }
+
+ /** Kind of timeout, e.g. BROADCAST_RECEIVER, etc. */
+ @TimeoutKind
+ public final int mKind;
+
+ /** Reason for the timeout. */
+ public final String mReason;
+
+ /** System uptime in millis when the timeout was triggered. */
+ public final long mEndUptimeMillis;
+
+ /**
+ * Was the end timestamp taken right after the timeout triggered, before any potentially
+ * expensive operations such as taking locks?
+ */
+ public final boolean mEndTakenBeforeLocks;
+
+ private TimeoutRecord(@TimeoutKind int kind, @NonNull String reason, long endUptimeMillis,
+ boolean endTakenBeforeLocks) {
+ this.mKind = kind;
+ this.mReason = reason;
+ this.mEndUptimeMillis = endUptimeMillis;
+ this.mEndTakenBeforeLocks = endTakenBeforeLocks;
+ }
+
+ private static TimeoutRecord endingNow(@TimeoutKind int kind, String reason) {
+ long endUptimeMillis = SystemClock.uptimeMillis();
+ return new TimeoutRecord(kind, reason, endUptimeMillis, /* endTakenBeforeLocks */ true);
+ }
+
+ private static TimeoutRecord endingApproximatelyNow(@TimeoutKind int kind, String reason) {
+ long endUptimeMillis = SystemClock.uptimeMillis();
+ return new TimeoutRecord(kind, reason, endUptimeMillis, /* endTakenBeforeLocks */ false);
+ }
+
+ /** Record for a broadcast receiver timeout. */
+ @NonNull
+ public static TimeoutRecord forBroadcastReceiver(@NonNull String reason) {
+ return TimeoutRecord.endingNow(TimeoutKind.BROADCAST_RECEIVER, reason);
+ }
+
+ /** Record for an input dispatch no focused window timeout */
+ @NonNull
+ public static TimeoutRecord forInputDispatchNoFocusedWindow(@NonNull String reason) {
+ return TimeoutRecord.endingNow(TimeoutKind.INPUT_DISPATCH_NO_FOCUSED_WINDOW, reason);
+ }
+
+ /** Record for an input dispatch window unresponsive timeout. */
+ @NonNull
+ public static TimeoutRecord forInputDispatchWindowUnresponsive(@NonNull String reason) {
+ return TimeoutRecord.endingNow(TimeoutKind.INPUT_DISPATCH_WINDOW_UNRESPONSIVE, reason);
+ }
+
+ /** Record for a service exec timeout. */
+ @NonNull
+ public static TimeoutRecord forServiceExec(@NonNull String reason) {
+ return TimeoutRecord.endingNow(TimeoutKind.SERVICE_EXEC, reason);
+ }
+
+ /** Record for a service start timeout. */
+ @NonNull
+ public static TimeoutRecord forServiceStartWithEndTime(@NonNull String reason,
+ long endUptimeMillis) {
+ return new TimeoutRecord(TimeoutKind.SERVICE_START, reason,
+ endUptimeMillis, /* endTakenBeforeLocks */ true);
+ }
+
+ /** Record for a content provider timeout. */
+ @NonNull
+ public static TimeoutRecord forContentProvider(@NonNull String reason) {
+ return TimeoutRecord.endingApproximatelyNow(TimeoutKind.CONTENT_PROVIDER, reason);
+ }
+
+ /** Record for an app registered timeout. */
+ @NonNull
+ public static TimeoutRecord forApp(@NonNull String reason) {
+ return TimeoutRecord.endingApproximatelyNow(TimeoutKind.APP_REGISTERED, reason);
+ }
+}
diff --git a/core/java/com/android/internal/protolog/ProtoLogGroup.java b/core/java/com/android/internal/protolog/ProtoLogGroup.java
index 7f36c79591b3..0e8dc071cbdc 100644
--- a/core/java/com/android/internal/protolog/ProtoLogGroup.java
+++ b/core/java/com/android/internal/protolog/ProtoLogGroup.java
@@ -83,7 +83,7 @@ public enum ProtoLogGroup implements IProtoLogGroup {
Consts.TAG_WM),
WM_DEBUG_WINDOW_INSETS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM),
- WM_DEBUG_CONTENT_RECORDING(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
+ WM_DEBUG_CONTENT_RECORDING(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM),
WM_DEBUG_WALLPAPER(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM),
WM_DEBUG_BACK_PREVIEW(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
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/proto/android/os/batteryusagestats.proto b/core/proto/android/os/batteryusagestats.proto
index 856bc839aee5..2b74220a1471 100644
--- a/core/proto/android/os/batteryusagestats.proto
+++ b/core/proto/android/os/batteryusagestats.proto
@@ -102,4 +102,21 @@ message BatteryUsageStatsAtomsProto {
// Total amount of time battery was discharging during the reported session
optional int64 discharge_duration_millis = 7;
+
+ // Notes the power model used for a power component.
+ message PowerComponentModel {
+ // Holds android.os.PowerComponentEnum, or custom component value between 1000 and 9999.
+ optional int32 component = 1;
+
+ enum PowerModel {
+ UNDEFINED = 0;
+ POWER_PROFILE = 1;
+ MEASURED_ENERGY = 2;
+ }
+
+ optional PowerModel power_model = 2;
+ }
+
+ // The power model used for each power component.
+ repeated PowerComponentModel component_models = 8;
}
diff --git a/core/proto/android/os/processstarttime.proto b/core/proto/android/os/processstarttime.proto
deleted file mode 100644
index d0f8baee7da2..000000000000
--- a/core/proto/android/os/processstarttime.proto
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-syntax = "proto2";
-package android.os;
-
-option java_multiple_files = true;
-
-// This message is used for statsd logging and should be kept in sync with
-// frameworks/proto_logging/stats/atoms.proto
-/**
- * Logs information about process start time.
- *
- * Logged from:
- * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
- */
-message ProcessStartTime {
- // The uid of the ProcessRecord.
- optional int32 uid = 1;
-
- // The process pid.
- optional int32 pid = 2;
-
- // The process name.
- // Usually package name, "system" for system server.
- // Provided by ActivityManagerService.
- optional string process_name = 3;
-
- enum StartType {
- UNKNOWN = 0;
- WARM = 1;
- HOT = 2;
- COLD = 3;
- }
-
- // The start type.
- optional StartType type = 4;
-
- // The elapsed realtime at the start of the process.
- optional int64 process_start_time_millis = 5;
-
- // Number of milliseconds it takes to reach bind application.
- optional int32 bind_application_delay_millis = 6;
-
- // Number of milliseconds it takes to finish start of the process.
- optional int32 process_start_delay_millis = 7;
-
- // hostingType field in ProcessRecord, the component type such as "activity",
- // "service", "content provider", "broadcast" or other strings.
- optional string hosting_type = 8;
-
- // hostingNameStr field in ProcessRecord. The component class name that runs
- // in this process.
- optional string hosting_name = 9;
-
- // Broadcast action name.
- optional string broadcast_action_name = 10;
-
- enum HostingTypeId {
- HOSTING_TYPE_UNKNOWN = 0;
- HOSTING_TYPE_ACTIVITY = 1;
- HOSTING_TYPE_ADDED_APPLICATION = 2;
- HOSTING_TYPE_BACKUP = 3;
- HOSTING_TYPE_BROADCAST = 4;
- HOSTING_TYPE_CONTENT_PROVIDER = 5;
- HOSTING_TYPE_LINK_FAIL = 6;
- HOSTING_TYPE_ON_HOLD = 7;
- HOSTING_TYPE_NEXT_ACTIVITY = 8;
- HOSTING_TYPE_NEXT_TOP_ACTIVITY = 9;
- HOSTING_TYPE_RESTART = 10;
- HOSTING_TYPE_SERVICE = 11;
- HOSTING_TYPE_SYSTEM = 12;
- HOSTING_TYPE_TOP_ACTIVITY = 13;
- HOSTING_TYPE_EMPTY = 14;
- }
-
- optional HostingTypeId hosting_type_id = 11;
-}
-
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 285258a979e4..322354b5ad60 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -88,6 +88,7 @@ message SecureSettingsProto {
optional SettingProto odi_captions_volume_ui_enabled = 42 [ (android.privacy).dest = DEST_AUTOMATIC ];
// Setting for accessibility magnification for following typing.
optional SettingProto accessibility_magnification_follow_typing_enabled = 43 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto accessibility_software_cursor_enabled = 44 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional Accessibility accessibility = 2;
diff --git a/core/proto/android/server/accessibilitytrace.proto b/core/proto/android/server/accessibilitytrace.proto
index 0ce39abfe352..6b77be3c748d 100644
--- a/core/proto/android/server/accessibilitytrace.proto
+++ b/core/proto/android/server/accessibilitytrace.proto
@@ -39,6 +39,10 @@ message AccessibilityTraceFileProto {
optional fixed64 magic_number = 1; /* Must be the first field, set to value in MagicNumber */
repeated AccessibilityTraceProto entry = 2;
+
+ /* offset between real-time clock and elapsed time clock in nanoseconds.
+ Calculated as: 1000000 * System.currentTimeMillis() - SystemClock.elapsedRealtimeNanos() */
+ optional fixed64 real_to_elapsed_time_offset_nanos = 3;
}
/* one accessibility trace entry. */
diff --git a/core/proto/android/server/windowmanagertrace.proto b/core/proto/android/server/windowmanagertrace.proto
index f502961d2c45..257f79b0d6bb 100644
--- a/core/proto/android/server/windowmanagertrace.proto
+++ b/core/proto/android/server/windowmanagertrace.proto
@@ -38,6 +38,10 @@ message WindowManagerTraceFileProto {
optional fixed64 magic_number = 1; /* Must be the first field, set to value in MagicNumber */
repeated WindowManagerTraceProto entry = 2;
+
+ /* offset between real-time clock and elapsed time clock in nanoseconds.
+ Calculated as: 1000000 * System.currentTimeMillis() - SystemClock.elapsedRealtimeNanos() */
+ optional fixed64 real_to_elapsed_time_offset_nanos = 3;
}
/* one window manager trace entry. */
diff --git a/core/proto/android/view/inputmethod/inputmethodeditortrace.proto b/core/proto/android/view/inputmethod/inputmethodeditortrace.proto
index 8e4377ca124c..a4281a72fb8e 100644
--- a/core/proto/android/view/inputmethod/inputmethodeditortrace.proto
+++ b/core/proto/android/view/inputmethod/inputmethodeditortrace.proto
@@ -50,6 +50,10 @@ message InputMethodClientsTraceFileProto {
in MagicNumber */
optional fixed64 magic_number = 1;
repeated InputMethodClientsTraceProto entry = 2;
+
+ /* offset between real-time clock and elapsed time clock in nanoseconds.
+ Calculated as: 1000000 * System.currentTimeMillis() - SystemClock.elapsedRealtimeNanos() */
+ optional fixed64 real_to_elapsed_time_offset_nanos = 3;
}
/* One dump entry for clients that use InputMethod. */
@@ -96,6 +100,10 @@ message InputMethodServiceTraceFileProto {
in MagicNumber */
optional fixed64 magic_number = 1;
repeated InputMethodServiceTraceProto entry = 2;
+
+ /* offset between real-time clock and elapsed time clock in nanoseconds.
+ Calculated as: 1000000 * System.currentTimeMillis() - SystemClock.elapsedRealtimeNanos() */
+ optional fixed64 real_to_elapsed_time_offset_nanos = 3;
}
/* One dump entry for InputMethodService. */
@@ -129,6 +137,10 @@ message InputMethodManagerServiceTraceFileProto {
in MagicNumber */
optional fixed64 magic_number = 1;
repeated InputMethodManagerServiceTraceProto entry = 2;
+
+ /* offset between real-time clock and elapsed time clock in nanoseconds.
+ Calculated as: 1000000 * System.currentTimeMillis() - SystemClock.elapsedRealtimeNanos() */
+ optional fixed64 real_to_elapsed_time_offset_nanos = 3;
}
/* One dump entry for InputMethodManagerService. */
diff --git a/core/res/res/layout/side_fps_toast.xml b/core/res/res/layout/side_fps_toast.xml
new file mode 100644
index 000000000000..58b8cc9438ae
--- /dev/null
+++ b/core/res/res/layout/side_fps_toast.xml
@@ -0,0 +1,43 @@
+<?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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minWidth="350dp"
+ android:layout_gravity="center"
+ android:theme="?attr/alertDialogTheme">
+ <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:paddingLeft="20dp"/>
+ <Space
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_weight="1"/>
+ <Button
+ android:id="@+id/turn_off_screen"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/fp_power_button_enrollment_button_text"
+ android:paddingRight="20dp"
+ style="?android:attr/buttonBarNegativeButtonStyle"
+ android:maxLines="1"/>
+</LinearLayout> \ No newline at end of file
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 08471c3429e3..d43a6c59a477 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3075,6 +3075,20 @@
<enum name="noHideDescendants" value="4" />
</attr>
+ <!-- Describes whether this view should allow interactions from AccessibilityServices only
+ if the service sets the isAccessibilityTool property. -->
+ <attr name="accessibilityDataPrivate" format="integer">
+ <!-- The system determines whether the view's accessibility data is private
+ - default (recommended). -->
+ <enum name="auto" value="0" />
+ <!-- Allow interactions from AccessibilityServices only if the service sets the
+ isAccessibilityTool property. -->
+ <enum name="yes" value="1" />
+ <!-- Allow interactions from all AccessibilityServices, regardless of their
+ isAccessibilityTool property. -->
+ <enum name="no" value="2" />
+ </attr>
+
<!-- Indicates to accessibility services whether the user should be notified when
this view changes. -->
<attr name="accessibilityLiveRegion" format="integer">
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d176a1d97494..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>
@@ -3492,9 +3495,9 @@
(for side fingerprint) -->
<integer name="config_sidefpsPostAuthDowntime">400</integer>
- <!-- The time (in millis) that a finger tap will wait for a power button
- before dismissing the power dialog during enrollment(for side fingerprint) -->
- <integer name="config_sidefpsEnrollPowerPressWindow">300</integer>
+ <!-- The time (in millis) the clickable toast dialog will last until
+ automatically dismissing. This is currently used in SideFpsEventHandler -->
+ <integer name="config_sideFpsToastTimeout">3000</integer>
<!-- This config is used to force VoiceInteractionService to start on certain low ram devices.
It declares the package name of VoiceInteractionService that should be started. -->
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 11c245b4c85c..ad7d03e43d29 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -114,6 +114,7 @@
<public name="handwritingBoundsOffsetTop" />
<public name="handwritingBoundsOffsetRight" />
<public name="handwritingBoundsOffsetBottom" />
+ <public name="accessibilityDataPrivate" />
</staging-public-group>
<staging-public-group type="id" first-id="0x01cd0000">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d3f2607db920..d3d049382fd0 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3561,21 +3561,17 @@
<!-- [CHAR LIMIT=NONE] Message to show in upgrading dialog when the bulk of the upgrade work is done. -->
<string name="android_upgrading_complete">Finishing boot.</string>
- <!-- [CHAR LIMIT=40] Title of dialog shown to confirm device going to sleep if the power button
- is pressed during fingerprint enrollment. -->
- <string name="fp_power_button_enrollment_title">Continue setup?</string>
-
<!-- [CHAR LIMIT=NONE] Message of dialog shown to confirm device going to sleep if the power
button is pressed during fingerprint enrollment. -->
<string name="fp_power_button_enrollment_message">You pressed the power button — this usually turns off the screen.\n\nTry tapping lightly while setting up your fingerprint.</string>
- <!-- [CHAR LIMIT=20] Positive button of dialog shown to confirm device going to sleep if the
- power button is pressed during fingerprint enrollment. -->
- <string name="fp_power_button_enrollment_positive_button">Turn off screen</string>
+ <!-- [CHAR LIMIT=40] Title of dialog shown to confirm device going to sleep if the power button
+ is pressed during fingerprint enrollment. -->
+ <string name="fp_power_button_enrollment_title">Tap to turn off screen</string>
- <!-- [CHAR LIMIT=20] Negative button of dialog shown to confirm device going to sleep if the
+ <!-- [CHAR LIMIT=20] Positive button of dialog shown to confirm device going to sleep if the
power button is pressed during fingerprint enrollment. -->
- <string name="fp_power_button_enrollment_negative_button">Continue setup</string>
+ <string name="fp_power_button_enrollment_button_text">Turn off screen</string>
<!-- [CHAR LIMIT=40] Title of dialog shown to confirm device going to sleep if the power button
is pressed during biometric prompt when a side fingerprint sensor is present. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e6ae7b1c8b7a..51712ff5e35f 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1849,8 +1849,7 @@
<java-symbol type="string" name="fp_power_button_bp_negative_button" />
<java-symbol type="string" name="fp_power_button_enrollment_title" />
<java-symbol type="string" name="fp_power_button_enrollment_message" />
- <java-symbol type="string" name="fp_power_button_enrollment_positive_button" />
- <java-symbol type="string" name="fp_power_button_enrollment_negative_button" />
+ <java-symbol type="string" name="fp_power_button_enrollment_button_text" />
<java-symbol type="string" name="global_actions" />
<java-symbol type="string" name="global_action_power_off" />
<java-symbol type="string" name="global_action_power_options" />
@@ -2627,7 +2626,11 @@
<java-symbol type="integer" name="config_sidefpsBpPowerPressWindow"/>
<java-symbol type="integer" name="config_sidefpsKeyguardPowerPressWindow"/>
<java-symbol type="integer" name="config_sidefpsPostAuthDowntime"/>
- <java-symbol type="integer" name="config_sidefpsEnrollPowerPressWindow"/>
+ <java-symbol type="integer" name="config_sideFpsToastTimeout"/>
+
+ <!-- Clickable toast used during sidefps enrollment -->
+ <java-symbol type="layout" name="side_fps_toast" />
+ <java-symbol type="id" name="turn_off_screen" />
<!-- Face authentication messages -->
<java-symbol type="string" name="face_recalibrate_notification_name" />
@@ -4706,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/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java
index 2ce305440ef5..f82523c5e3a1 100644
--- a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java
+++ b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java
@@ -24,6 +24,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
+import android.os.AggregateBatteryConsumer;
import android.os.BatteryConsumer;
import android.os.BatteryUsageStats;
import android.os.UidBatteryConsumer;
@@ -69,10 +70,17 @@ public class BatteryUsageStatsPulledTest {
assertEquals(bus.getDischargeDurationMs(), proto.dischargeDurationMillis);
assertEquals(3, proto.deviceBatteryConsumer.powerComponents.length); // Only 3 are non-empty
+
+ final AggregateBatteryConsumer abc = bus.getAggregateBatteryConsumer(
+ AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
assertSameBatteryConsumer("For deviceBatteryConsumer",
bus.getAggregateBatteryConsumer(AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE),
proto.deviceBatteryConsumer);
+ for (int i = 0; i < BatteryConsumer.POWER_COMPONENT_COUNT; i++) {
+ assertPowerComponentModel(i, abc.getPowerModel(i), proto);
+ }
+
// Now for the UidBatteryConsumers.
final List<android.os.UidBatteryConsumer> uidConsumers = bus.getUidBatteryConsumers();
uidConsumers.sort((a, b) -> a.getUid() - b.getUid());
@@ -196,6 +204,34 @@ public class BatteryUsageStatsPulledTest {
}
}
+ /**
+ * Validates the PowerComponentModel object that matches powerComponent.
+ */
+ private void assertPowerComponentModel(int powerComponent,
+ @BatteryConsumer.PowerModel int powerModel, BatteryUsageStatsAtomsProto proto) {
+ boolean found = false;
+ for (BatteryUsageStatsAtomsProto.PowerComponentModel powerComponentModel :
+ proto.componentModels) {
+ if (powerComponentModel.component == powerComponent) {
+ if (found) {
+ fail("Power component " + BatteryConsumer.powerComponentIdToString(
+ powerComponent) + " found multiple times in the proto");
+ }
+ found = true;
+ final int expectedPowerModel = BatteryConsumer.powerModelToProtoEnum(powerModel);
+ assertEquals(expectedPowerModel, powerComponentModel.powerModel);
+ }
+ }
+ if (!found) {
+ final int model = BatteryConsumer.powerModelToProtoEnum(powerModel);
+ assertEquals(
+ "Power component " + BatteryConsumer.powerComponentIdToString(powerComponent)
+ + " was not found in the proto but has a defined power model.",
+ BatteryUsageStatsAtomsProto.PowerComponentModel.UNDEFINED,
+ model);
+ }
+ }
+
/** Converts charge from milliamp hours (mAh) to decicoulombs (dC). */
private long convertMahToDc(double powerMah) {
return (long) (powerMah * 36 + 0.5);
@@ -259,11 +295,14 @@ public class BatteryUsageStatsPulledTest {
builder.getAggregateBatteryConsumerBuilder(AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
.setConsumedPower(30000)
.setConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU, 20100)
+ BatteryConsumer.POWER_COMPONENT_CPU, 20100,
+ BatteryConsumer.POWER_MODEL_POWER_PROFILE)
.setConsumedPower(
- BatteryConsumer.POWER_COMPONENT_AUDIO, 0) // Empty
+ BatteryConsumer.POWER_COMPONENT_AUDIO, 0,
+ BatteryConsumer.POWER_MODEL_POWER_PROFILE) // Empty
.setConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CAMERA, 20150)
+ BatteryConsumer.POWER_COMPONENT_CAMERA, 20150,
+ BatteryConsumer.POWER_MODEL_MEASURED_ENERGY)
.setConsumedPowerForCustomComponent(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20200)
.setUsageDurationMillis(
@@ -275,7 +314,8 @@ public class BatteryUsageStatsPulledTest {
builder.getAggregateBatteryConsumerBuilder(
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
.setConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU, 10100)
+ BatteryConsumer.POWER_COMPONENT_CPU, 10100,
+ BatteryConsumer.POWER_MODEL_POWER_PROFILE)
.setConsumedPowerForCustomComponent(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200);
@@ -285,7 +325,7 @@ public class BatteryUsageStatsPulledTest {
@Test
public void testLargeAtomTruncated() {
final BatteryUsageStats.Builder builder =
- new BatteryUsageStats.Builder(new String[0]);
+ new BatteryUsageStats.Builder(new String[0], true, false);
// If not truncated, this BatteryUsageStats object would generate a proto buffer
// significantly larger than 50 Kb
for (int i = 0; i < 3000; i++) {
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/core/tests/coretests/src/android/view/WindowParamsTest.java b/core/tests/coretests/src/android/view/WindowParamsTest.java
new file mode 100644
index 000000000000..49d4872922e2
--- /dev/null
+++ b/core/tests/coretests/src/android/view/WindowParamsTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.view;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.Parcel;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+@Presubmit
+@SmallTest
+public class WindowParamsTest {
+
+ @Test
+ public void testParamsForRotation() {
+ final WindowManager.LayoutParams paramsA = new WindowManager.LayoutParams();
+ initParamsForRotation(paramsA);
+ final Parcel parcel = Parcel.obtain();
+ paramsA.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ final WindowManager.LayoutParams paramsB = new WindowManager.LayoutParams(parcel);
+ assertEquals(0, paramsA.copyFrom(paramsB));
+
+ for (int i = 1; i <= 12; i++) {
+ initParamsForRotation(paramsA);
+ changeField(i, paramsA.paramsForRotation[0]);
+ assertEquals("Change not found for case " + i,
+ WindowManager.LayoutParams.LAYOUT_CHANGED, paramsA.copyFrom(paramsB));
+ }
+
+ parcel.recycle();
+ }
+
+ private static void initParamsForRotation(WindowManager.LayoutParams params) {
+ params.paramsForRotation = new WindowManager.LayoutParams[4];
+ for (int i = 0; i < 4; i++) {
+ params.paramsForRotation[i] = new WindowManager.LayoutParams();
+ }
+ }
+
+ private static void changeField(int fieldCase, WindowManager.LayoutParams params) {
+ switch (fieldCase) {
+ case 1:
+ params.width++;
+ break;
+ case 2:
+ params.height++;
+ break;
+ case 3:
+ params.x++;
+ break;
+ case 4:
+ params.y++;
+ break;
+ case 5:
+ params.horizontalMargin++;
+ break;
+ case 6:
+ params.verticalMargin++;
+ break;
+ case 7:
+ params.layoutInDisplayCutoutMode++;
+ break;
+ case 8:
+ params.gravity++;
+ break;
+ case 9:
+ params.providedInsets = new InsetsFrameProvider[0];
+ break;
+ case 10:
+ params.setFitInsetsTypes(0);
+ break;
+ case 11:
+ params.setFitInsetsSides(0);
+ break;
+ case 12:
+ params.setFitInsetsIgnoringVisibility(!params.isFitInsetsIgnoringVisibility());
+ break;
+ }
+ }
+}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 505d2a3804d8..03d22ac44f6a 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -13,12 +13,24 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "-2123789565": {
+ "message": "Found no matching mirror display for id=%d for DEFAULT_DISPLAY. Nothing to mirror.",
+ "level": "WARN",
+ "group": "WM_DEBUG_CONTENT_RECORDING",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
"-2121056984": {
"message": "%s",
"level": "WARN",
"group": "WM_DEBUG_LOCKTASK",
"at": "com\/android\/server\/wm\/LockTaskController.java"
},
+ "-2113780196": {
+ "message": "Successfully created a ContentRecordingSession for displayId=%d to mirror content from displayId=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_CONTENT_RECORDING",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
"-2111539867": {
"message": "remove IME snapshot, caller=%s",
"level": "INFO",
@@ -1111,6 +1123,12 @@
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
+ "-1018968224": {
+ "message": "Recorded task is removed, so stop recording on display %d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_CONTENT_RECORDING",
+ "at": "com\/android\/server\/wm\/ContentRecorder.java"
+ },
"-1016578046": {
"message": "Moving to %s Relaunching %s callers=%s",
"level": "INFO",
@@ -1303,6 +1321,12 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "-838378223": {
+ "message": "Attempting to mirror self on %d",
+ "level": "WARN",
+ "group": "WM_DEBUG_CONTENT_RECORDING",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
"-814760297": {
"message": "Looking for task of %s in %s",
"level": "DEBUG",
@@ -1411,6 +1435,12 @@
"group": "WM_DEBUG_CONTENT_RECORDING",
"at": "com\/android\/server\/wm\/ContentRecorder.java"
},
+ "-729864558": {
+ "message": "Attempting to mirror %d from %d but no DisplayContent associated. Changing to mirror default display.",
+ "level": "WARN",
+ "group": "WM_DEBUG_CONTENT_RECORDING",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
"-729530161": {
"message": "Moving to DESTROYED: %s (no app)",
"level": "VERBOSE",
@@ -2251,6 +2281,12 @@
"group": "WM_DEBUG_FOCUS",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "96494268": {
+ "message": "Stop MediaProjection on virtual display %d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_CONTENT_RECORDING",
+ "at": "com\/android\/server\/wm\/ContentRecorder.java"
+ },
"100936473": {
"message": "Wallpaper animation!",
"level": "VERBOSE",
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/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/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/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/animation/PhysicsAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
index 312af4ff7bc2..ee8c41417458 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
@@ -22,7 +22,6 @@ import android.view.View
import androidx.dynamicanimation.animation.DynamicAnimation
import androidx.dynamicanimation.animation.FlingAnimation
import androidx.dynamicanimation.animation.FloatPropertyCompat
-import androidx.dynamicanimation.animation.FrameCallbackScheduler
import androidx.dynamicanimation.animation.SpringAnimation
import androidx.dynamicanimation.animation.SpringForce
@@ -125,12 +124,6 @@ class PhysicsAnimator<T> private constructor (target: T) {
private var defaultFling: FlingConfig = globalDefaultFling
/**
- * FrameCallbackScheduler to use if it need custom FrameCallbackScheduler, if this is null,
- * it will use the default FrameCallbackScheduler in the DynamicAnimation.
- */
- private var customScheduler: FrameCallbackScheduler? = null
-
- /**
* Internal listeners that respond to DynamicAnimations updating and ending, and dispatch to
* the listeners provided via [addUpdateListener] and [addEndListener]. This allows us to add
* just one permanent update and end listener to the DynamicAnimations.
@@ -454,14 +447,6 @@ class PhysicsAnimator<T> private constructor (target: T) {
this.defaultFling = defaultFling
}
- /**
- * Set the custom FrameCallbackScheduler for all aniatmion in this animator. Set this with null for
- * restoring to default FrameCallbackScheduler.
- */
- fun setCustomScheduler(scheduler: FrameCallbackScheduler) {
- this.customScheduler = scheduler
- }
-
/** Starts the animations! */
fun start() {
startAction()
@@ -511,12 +496,9 @@ class PhysicsAnimator<T> private constructor (target: T) {
// springs) on this property before flinging.
cancel(animatedProperty)
- // Apply the custom animation scheduler if it not null
- val flingAnim = getFlingAnimation(animatedProperty, target)
- flingAnim.scheduler = customScheduler ?: flingAnim.scheduler
-
// Apply the configuration and start the animation.
- flingAnim.also { flingConfig.applyToAnimation(it) }.start()
+ getFlingAnimation(animatedProperty, target)
+ .also { flingConfig.applyToAnimation(it) }.start()
}
}
@@ -529,18 +511,6 @@ class PhysicsAnimator<T> private constructor (target: T) {
// Apply the configuration and start the animation.
val springAnim = getSpringAnimation(animatedProperty, target)
- // If customScheduler is exist and has not been set to the animation,
- // it should set here.
- if (customScheduler != null &&
- springAnim.scheduler != customScheduler) {
- // Cancel the animation before set animation handler
- if (springAnim.isRunning) {
- cancel(animatedProperty)
- }
- // Apply the custom scheduler handler if it not null
- springAnim.scheduler = customScheduler ?: springAnim.scheduler
- }
-
// Apply the configuration and start the animation.
springConfig.applyToAnimation(springAnim)
animationStartActions.add(springAnim::start)
@@ -596,12 +566,9 @@ class PhysicsAnimator<T> private constructor (target: T) {
}
}
- // Apply the custom animation scheduler if it not null
- val springAnim = getSpringAnimation(animatedProperty, target)
- springAnim.scheduler = customScheduler ?: springAnim.scheduler
-
// Apply the configuration and start the spring animation.
- springAnim.also { springConfig.applyToAnimation(it) }.start()
+ getSpringAnimation(animatedProperty, target)
+ .also { springConfig.applyToAnimation(it) }.start()
}
}
})
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 0283be3f551b..ebf8c0354c1b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -98,6 +98,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
/** Tracks if an uninterruptible transition is in progress */
private boolean mTransitionInProgress = false;
+ /** Tracks if we should start the back gesture on the next motion move event */
+ private boolean mShouldStartOnNextMoveEvent = false;
/** @see #setTriggerBack(boolean) */
private boolean mTriggerBack;
@@ -301,12 +303,17 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
if (mTransitionInProgress) {
return;
}
- if (keyAction == MotionEvent.ACTION_MOVE) {
+ if (keyAction == MotionEvent.ACTION_DOWN) {
if (!mBackGestureStarted) {
+ mShouldStartOnNextMoveEvent = true;
+ }
+ } else if (keyAction == MotionEvent.ACTION_MOVE) {
+ if (!mBackGestureStarted && mShouldStartOnNextMoveEvent) {
// Let the animation initialized here to make sure the onPointerDownOutsideFocus
// could be happened when ACTION_DOWN, it may change the current focus that we
// would access it when startBackNavigation.
onGestureStarted(touchX, touchY);
+ mShouldStartOnNextMoveEvent = false;
}
onMove(touchX, touchY, swipeEdge);
} else if (keyAction == MotionEvent.ACTION_UP || keyAction == MotionEvent.ACTION_CANCEL) {
@@ -440,6 +447,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
private void onGestureFinished(boolean fromTouch) {
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "onGestureFinished() mTriggerBack == %s", mTriggerBack);
+ if (!mBackGestureStarted) {
+ finishAnimation();
+ return;
+ }
+
if (fromTouch) {
// Let touch reset the flag otherwise it will start a new back navigation and refresh
// the info when received a new move event.
@@ -555,6 +567,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
boolean triggerBack = mTriggerBack;
mBackNavigationInfo = null;
mTriggerBack = false;
+ mShouldStartOnNextMoveEvent = false;
if (backNavigationInfo == null) {
return;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java
index afc706ee9c8e..b8204d013105 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java
@@ -19,6 +19,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.window.TransitionInfo.FLAG_FIRST_CUSTOM;
import android.annotation.IntDef;
@@ -55,4 +56,7 @@ public class SplitScreenConstants {
{WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED};
public static final int[] CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE =
{WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED, WINDOWING_MODE_MULTI_WINDOW};
+
+ /** Flag applied to a transition change to identify it as a divider bar for animation. */
+ public static final int FLAG_IS_DIVIDER_BAR = FLAG_FIRST_CUSTOM;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index bbaf51f7c54c..a6a04cf67b3c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -128,15 +128,9 @@ public abstract class WMShellBaseModule {
mainExecutor);
}
- // Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
- @BindsOptionalOf
- @DynamicOverride
- abstract DisplayImeController optionalDisplayImeController();
-
@WMSingleton
@Provides
static DisplayImeController provideDisplayImeController(
- @DynamicOverride Optional<DisplayImeController> overrideDisplayImeController,
IWindowManager wmService,
ShellInit shellInit,
DisplayController displayController,
@@ -144,9 +138,6 @@ public abstract class WMShellBaseModule {
TransactionPool transactionPool,
@ShellMainThread ShellExecutor mainExecutor
) {
- if (overrideDisplayImeController.isPresent()) {
- return overrideDisplayImeController.get();
- }
return new DisplayImeController(wmService, shellInit, displayController,
displayInsetsController, transactionPool, mainExecutor);
}
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/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index 81e49f884503..b32c3eed2fb4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -29,7 +29,6 @@ import android.annotation.NonNull;
import android.app.TaskInfo;
import android.content.Context;
import android.graphics.Rect;
-import android.view.Choreographer;
import android.view.Surface;
import android.view.SurfaceControl;
import android.window.TaskSnapshot;
@@ -279,14 +278,15 @@ public class PipAnimationController {
mEndValue = endValue;
addListener(this);
addUpdateListener(this);
- mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
+ mSurfaceControlTransactionFactory =
+ new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
mTransitionDirection = TRANSITION_DIRECTION_NONE;
}
@Override
public void onAnimationStart(Animator animation) {
mCurrentValue = mStartValue;
- onStartTransaction(mLeash, newSurfaceControlTransaction());
+ onStartTransaction(mLeash, mSurfaceControlTransactionFactory.getTransaction());
if (mPipAnimationCallback != null) {
mPipAnimationCallback.onPipAnimationStart(mTaskInfo, this);
}
@@ -294,14 +294,16 @@ public class PipAnimationController {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
- applySurfaceControlTransaction(mLeash, newSurfaceControlTransaction(),
+ applySurfaceControlTransaction(mLeash,
+ mSurfaceControlTransactionFactory.getTransaction(),
animation.getAnimatedFraction());
}
@Override
public void onAnimationEnd(Animator animation) {
mCurrentValue = mEndValue;
- final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
+ final SurfaceControl.Transaction tx =
+ mSurfaceControlTransactionFactory.getTransaction();
onEndTransaction(mLeash, tx, mTransitionDirection);
if (mPipAnimationCallback != null) {
mPipAnimationCallback.onPipAnimationEnd(mTaskInfo, tx, this);
@@ -348,7 +350,8 @@ public class PipAnimationController {
}
void setColorContentOverlay(Context context) {
- final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
+ final SurfaceControl.Transaction tx =
+ mSurfaceControlTransactionFactory.getTransaction();
if (mContentOverlay != null) {
mContentOverlay.detach(tx);
}
@@ -357,7 +360,8 @@ public class PipAnimationController {
}
void setSnapshotContentOverlay(TaskSnapshot snapshot, Rect sourceRectHint) {
- final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
+ final SurfaceControl.Transaction tx =
+ mSurfaceControlTransactionFactory.getTransaction();
if (mContentOverlay != null) {
mContentOverlay.detach(tx);
}
@@ -406,7 +410,7 @@ public class PipAnimationController {
void setDestinationBounds(Rect destinationBounds) {
mDestinationBounds.set(destinationBounds);
if (mAnimationType == ANIM_TYPE_ALPHA) {
- onStartTransaction(mLeash, newSurfaceControlTransaction());
+ onStartTransaction(mLeash, mSurfaceControlTransactionFactory.getTransaction());
}
}
@@ -441,16 +445,6 @@ public class PipAnimationController {
mEndValue = endValue;
}
- /**
- * @return {@link SurfaceControl.Transaction} instance with vsync-id.
- */
- protected SurfaceControl.Transaction newSurfaceControlTransaction() {
- final SurfaceControl.Transaction tx =
- mSurfaceControlTransactionFactory.getTransaction();
- tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
- return tx;
- }
-
@VisibleForTesting
public void setSurfaceControlTransactionFactory(
PipSurfaceTransactionHelper.SurfaceControlTransactionFactory factory) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
index 3ac08a66100a..b9746e338ced 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
@@ -20,6 +20,7 @@ import android.content.Context;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.view.Choreographer;
import android.view.SurfaceControl;
import com.android.wm.shell.R;
@@ -234,4 +235,18 @@ public class PipSurfaceTransactionHelper {
public interface SurfaceControlTransactionFactory {
SurfaceControl.Transaction getTransaction();
}
+
+ /**
+ * Implementation of {@link SurfaceControlTransactionFactory} that returns
+ * {@link SurfaceControl.Transaction} with VsyncId being set.
+ */
+ public static class VsyncSurfaceControlTransactionFactory
+ implements SurfaceControlTransactionFactory {
+ @Override
+ public SurfaceControl.Transaction getTransaction() {
+ final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
+ tx.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
+ return tx;
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index f747b5e00759..b46eff6c55d4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -304,7 +304,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mSurfaceTransactionHelper = surfaceTransactionHelper;
mPipAnimationController = pipAnimationController;
mPipUiEventLoggerLogger = pipUiEventLogger;
- mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
+ mSurfaceControlTransactionFactory =
+ new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
mSplitScreenOptional = splitScreenOptional;
mTaskOrganizer = shellTaskOrganizer;
mMainExecutor = mainExecutor;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index 5a21e0734277..44d22029a5e9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -33,10 +33,6 @@ import android.content.Context;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Debug;
-import android.os.Looper;
-import android.view.Choreographer;
-
-import androidx.dynamicanimation.animation.FrameCallbackScheduler;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
@@ -89,25 +85,6 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
/** Coordinator instance for resolving conflicts with other floating content. */
private FloatingContentCoordinator mFloatingContentCoordinator;
- private ThreadLocal<FrameCallbackScheduler> mSfSchedulerThreadLocal =
- ThreadLocal.withInitial(() -> {
- final Looper initialLooper = Looper.myLooper();
- final FrameCallbackScheduler scheduler = new FrameCallbackScheduler() {
- @Override
- public void postFrameCallback(@androidx.annotation.NonNull Runnable runnable) {
- // TODO(b/222697646): remove getSfInstance usage and use vsyncId for
- // transactions
- Choreographer.getSfInstance().postFrameCallback(t -> runnable.run());
- }
-
- @Override
- public boolean isCurrentThread() {
- return Looper.myLooper() == initialLooper;
- }
- };
- return scheduler;
- });
-
/**
* PhysicsAnimator instance for animating {@link PipBoundsState#getMotionBoundsState()}
* using physics animations.
@@ -210,10 +187,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
}
public void init() {
- // Note: Needs to get the shell main thread sf vsync animation handler
mTemporaryBoundsPhysicsAnimator = PhysicsAnimator.getInstance(
mPipBoundsState.getMotionBoundsState().getBoundsInMotion());
- mTemporaryBoundsPhysicsAnimator.setCustomScheduler(mSfSchedulerThreadLocal.get());
}
@NonNull
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 8e1ae397ea64..4bc8e913ec4e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -32,13 +32,13 @@ import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.transitTypeToString;
-import static android.window.TransitionInfo.FLAG_FIRST_CUSTOM;
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER;
import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES;
+import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
@@ -147,9 +147,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private static final String TAG = StageCoordinator.class.getSimpleName();
- /** Flag applied to a transition change to identify it as a divider bar for animation. */
- public static final int FLAG_IS_DIVIDER_BAR = FLAG_FIRST_CUSTOM;
-
private final SurfaceSession mSurfaceSession = new SurfaceSession();
private final MainStage mMainStage;
@@ -894,6 +891,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
});
mShouldUpdateRecents = false;
+ mIsDividerRemoteAnimating = false;
if (childrenToTop == null) {
mSideStage.removeAllTasks(wct, false /* toTop */);
@@ -1808,7 +1806,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
boolean shouldAnimate = true;
if (mSplitTransitions.isPendingEnter(transition)) {
- shouldAnimate = startPendingEnterAnimation(transition, info, startTransaction);
+ shouldAnimate = startPendingEnterAnimation(
+ transition, info, startTransaction, finishTransaction);
} else if (mSplitTransitions.isPendingRecent(transition)) {
shouldAnimate = startPendingRecentAnimation(transition, info, startTransaction);
} else if (mSplitTransitions.isPendingDismiss(transition)) {
@@ -1836,7 +1835,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
private boolean startPendingEnterAnimation(@NonNull IBinder transition,
- @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) {
+ @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction finishT) {
// First, verify that we actually have opened apps in both splits.
TransitionInfo.Change mainChild = null;
TransitionInfo.Change sideChild = null;
@@ -1883,8 +1883,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
+ " before startAnimation().");
}
- finishEnterSplitScreen(t);
- addDividerBarToTransition(info, t, true /* show */);
+ finishEnterSplitScreen(finishT);
+ addDividerBarToTransition(info, finishT, true /* show */);
return true;
}
@@ -1969,7 +1969,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return false;
}
- addDividerBarToTransition(info, t, false /* show */);
+ addDividerBarToTransition(info, finishT, false /* show */);
return true;
}
@@ -1980,23 +1980,25 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
private void addDividerBarToTransition(@NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t, boolean show) {
+ @NonNull SurfaceControl.Transaction finishT, boolean show) {
final SurfaceControl leash = mSplitLayout.getDividerLeash();
final TransitionInfo.Change barChange = new TransitionInfo.Change(null /* token */, leash);
- final Rect bounds = mSplitLayout.getDividerBounds();
- barChange.setStartAbsBounds(bounds);
- barChange.setEndAbsBounds(bounds);
+ mSplitLayout.getRefDividerBounds(mTempRect1);
+ barChange.setStartAbsBounds(mTempRect1);
+ barChange.setEndAbsBounds(mTempRect1);
barChange.setMode(show ? TRANSIT_TO_FRONT : TRANSIT_TO_BACK);
barChange.setFlags(FLAG_IS_DIVIDER_BAR);
// Technically this should be order-0, but this is running after layer assignment
// and it's a special case, so just add to end.
info.addChange(barChange);
- // Be default, make it visible. The remote animator can adjust alpha if it plans to animate.
+
if (show) {
- t.setAlpha(leash, 1.f);
- t.setLayer(leash, Integer.MAX_VALUE);
- t.setPosition(leash, bounds.left, bounds.top);
- t.show(leash);
+ finishT.setLayer(leash, Integer.MAX_VALUE);
+ finishT.setPosition(leash, mTempRect1.left, mTempRect1.top);
+ finishT.show(leash);
+ // Ensure divider surface are re-parented back into the hierarchy at the end of the
+ // transition. See Transition#buildFinishTransaction for more detail.
+ finishT.reparent(leash, mRootTaskLeash);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 5cce6b99fb11..e26c259b2397 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -20,9 +20,9 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
+import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
-import static com.android.wm.shell.splitscreen.StageCoordinator.FLAG_IS_DIVIDER_BAR;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
index a843b2a0ac39..45b69f17a861 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
@@ -162,13 +162,12 @@ class ScreenRotationAnimation {
.setParent(mAnimLeash)
.setBLASTLayer()
.setSecure(screenshotBuffer.containsSecureLayers())
+ .setOpaque(true)
.setCallsite("ShellRotationAnimation")
.setName("RotationLayer")
.build();
t.setLayer(mAnimLeash, SCREEN_FREEZE_LAYER_BASE);
- t.setPosition(mAnimLeash, 0, 0);
- t.setAlpha(mAnimLeash, 1);
t.show(mAnimLeash);
final ColorSpace colorSpace = screenshotBuffer.getColorSpace();
@@ -181,6 +180,7 @@ class ScreenRotationAnimation {
mBackColorSurface = new SurfaceControl.Builder(session)
.setParent(rootLeash)
.setColorLayer()
+ .setOpaque(true)
.setCallsite("ShellRotationAnimation")
.setName("BackColorSurface")
.build();
@@ -189,7 +189,6 @@ class ScreenRotationAnimation {
t.setLayer(mBackColorSurface, -1);
t.setColor(mBackColorSurface, new float[]{mStartLuma, mStartLuma, mStartLuma});
- t.setAlpha(mBackColorSurface, 1);
t.show(mBackColorSurface);
}
@@ -242,7 +241,6 @@ class ScreenRotationAnimation {
t.setMatrix(mScreenshotLayer,
mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
- t.setAlpha(mScreenshotLayer, (float) 1.0);
}
/**
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/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index 1e4d23c924e7..9da51796a8c1 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
@@ -88,18 +89,13 @@ fun FlickerTestParameter.splitAppLayerBoundsBecomesVisible(
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 +104,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 +128,40 @@ fun FlickerTestParameter.splitAppLayerBoundsIsVisibleAtEnd(
}
}
+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
) {
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..42b7b1162d93 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
@@ -220,6 +221,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
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..cf2dc39637df
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
@@ -0,0 +1,210 @@
+/*
+ * 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.helpers.SplitScreenHelper
+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.assertLayers {
+ this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun primaryAppLayerKeepVisible() {
+ testSpec.assertLayers {
+ this.isVisible(primaryApp)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun secondaryAppLayerVisibilityChanges() {
+ testSpec.assertLayers {
+ this.isVisible(secondaryApp)
+ .then()
+ .isInvisible(secondaryApp)
+ .then()
+ .isVisible(secondaryApp)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun primaryAppWindowKeepVisible() {
+ testSpec.assertWm {
+ this.isAppWindowVisible(primaryApp)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun secondaryAppWindowKeepVisible() {
+ testSpec.assertWm {
+ this.isAppWindowVisible(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/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/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/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index b0dd7811c53c..5b3b8fd7ad71 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -284,9 +284,14 @@ public class BackAnimationControllerTest extends ShellTestCase {
// the previous transition is finished.
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
verifyNoMoreInteractions(mIOnBackInvokedCallback);
+ mController.onBackToLauncherAnimationFinished();
+
+ // Verify that more events from a rejected swipe cannot start animation.
+ doMotionEvent(MotionEvent.ACTION_MOVE, 100);
+ doMotionEvent(MotionEvent.ACTION_UP, 0);
+ verifyNoMoreInteractions(mIOnBackInvokedCallback);
// Verify that we start accepting gestures again once transition finishes.
- mController.onBackToLauncherAnimationFinished();
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
verify(mIOnBackInvokedCallback).onBackStarted();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java
index 514390fa52f9..d467b399ebbb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java
@@ -47,6 +47,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
/**
* Tests for {@link DisplayLayout}.
@@ -62,6 +63,7 @@ public class DisplayLayoutTest extends ShellTestCase {
public void setup() {
mMockitoSession = mockitoSession()
.initMocks(this)
+ .strictness(Strictness.WARN)
.mockStatic(SystemBarUtils.class)
.startMocking();
}
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/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/Android.bp b/packages/SettingsLib/Android.bp
index a83c090df771..1df9059a7c73 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -58,9 +58,6 @@ android_library {
"zxing-core-1.7",
],
- // ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
- // LOCAL_SHARED_JAVA_LIBRARIES := androidx.lifecycle_lifecycle-common
-
resource_dirs: ["res"],
srcs: [
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
index f4af9c7e0e78..01698b7937aa 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
@@ -19,6 +19,7 @@ package com.android.settingslib.collapsingtoolbar;
import static android.text.Layout.HYPHENATION_FREQUENCY_NORMAL_FAST;
import android.app.ActionBar;
+import android.content.res.Configuration;
import android.graphics.text.LineBreakConfig;
import android.os.Build;
import android.view.LayoutInflater;
@@ -97,7 +98,7 @@ public class CollapsingToolbarDelegate {
.build()));
}
}
- disableCollapsingToolbarLayoutScrollingBehavior();
+ autoSetCollapsingToolbarLayoutScrolling();
mToolbar = view.findViewById(R.id.action_bar);
mContentFrameLayout = view.findViewById(R.id.content_frame);
final ActionBar actionBar = mHostCallback.setActionBar(mToolbar);
@@ -148,7 +149,7 @@ public class CollapsingToolbarDelegate {
return mAppBarLayout;
}
- private void disableCollapsingToolbarLayoutScrollingBehavior() {
+ private void autoSetCollapsingToolbarLayoutScrolling() {
if (mAppBarLayout == null) {
return;
}
@@ -159,7 +160,9 @@ public class CollapsingToolbarDelegate {
new AppBarLayout.Behavior.DragCallback() {
@Override
public boolean canDrag(@NonNull AppBarLayout appBarLayout) {
- return false;
+ // Header can be scrolling while device in landscape mode.
+ return appBarLayout.getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_LANDSCAPE;
}
});
params.setBehavior(behavior);
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
index 522de93be1ff..d67ac3b86050 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
@@ -21,6 +21,7 @@ import static android.text.Layout.HYPHENATION_FREQUENCY_NORMAL_FAST;
import android.app.ActionBar;
import android.app.Activity;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.text.LineBreakConfig;
import android.os.Build;
@@ -122,7 +123,7 @@ public class CollapsingCoordinatorLayout extends CoordinatorLayout {
mCollapsingToolbarLayout.setTitle(mToolbarTitle);
}
}
- disableCollapsingToolbarLayoutScrollingBehavior();
+ autoSetCollapsingToolbarLayoutScrolling();
}
/**
@@ -243,7 +244,7 @@ public class CollapsingCoordinatorLayout extends CoordinatorLayout {
mCollapsingToolbarLayout.findViewById(R.id.support_action_bar);
}
- private void disableCollapsingToolbarLayoutScrollingBehavior() {
+ private void autoSetCollapsingToolbarLayoutScrolling() {
if (mAppBarLayout == null) {
return;
}
@@ -254,7 +255,9 @@ public class CollapsingCoordinatorLayout extends CoordinatorLayout {
new AppBarLayout.Behavior.DragCallback() {
@Override
public boolean canDrag(@NonNull AppBarLayout appBarLayout) {
- return false;
+ // Header can be scrolling while device in landscape mode.
+ return appBarLayout.getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_LANDSCAPE;
}
});
params.setBehavior(behavior);
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/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/HomePage.kt b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/HomePage.kt
index 27c70e450555..171a16118052 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
@@ -52,6 +52,8 @@ private fun HomePage() {
PreferencePageProvider.EntryItem()
ArgumentPageProvider.EntryItem(stringParam = "foo", intParam = 0)
+
+ SliderPageProvider.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..c24541a903da 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,21 @@
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 Argument = "Argument"
+ const val Slider = "Slider"
}
val codelabPageRepository = SettingsPageRepository(
- allPages = listOf(HomePageProvider, PreferencePageProvider, ArgumentPageProvider),
+ allPages = listOf(
+ HomePageProvider,
+ PreferencePageProvider,
+ ArgumentPageProvider,
+ SliderPageProvider,
+ ),
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/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/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..7c8608da6724 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,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.State
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..9a67c12dfd0b 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,7 +14,7 @@
* 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
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/theme/SettingsTheme.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt
index fce9f2b19ee0..e6fa74e34cc8 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsTheme.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt
@@ -14,11 +14,13 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.theme
+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.
@@ -26,9 +28,21 @@ import androidx.compose.runtime.Composable
@Composable
fun SettingsTheme(content: @Composable () -> Unit) {
val isDarkTheme = isSystemInDarkTheme()
- val colorScheme = materialColorScheme(isDarkTheme)
+ val settingsColorScheme = settingsColorScheme(isDarkTheme)
+ val colorScheme = materialColorScheme(isDarkTheme).copy(
+ background = settingsColorScheme.background,
+ )
- MaterialTheme(colorScheme = colorScheme) {
- content()
+ 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..6bdc294d888a 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
@@ -35,10 +35,10 @@ 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
@Composable
internal fun BaseLayout(
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..3b99d36e630b 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
@@ -26,9 +26,9 @@ 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
@Composable
internal fun BasePreference(
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/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/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/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/license/LicenseHtmlGeneratorFromXml.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
index c7eb68240c5b..fb3f382af192 100644
--- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
@@ -27,7 +27,6 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
@@ -76,14 +75,22 @@ class LicenseHtmlGeneratorFromXml {
private static final String LIBRARY_TAIL_STRING = "</ul>\n<strong>Files</strong>";
private static final String FILES_HEAD_STRING = "<ul class=\"files\">";
+ private static final String FILES_TAIL_STRING = "</ul>\n</div><!-- table of contents -->";
- private static final String HTML_MIDDLE_STRING =
- "</ul>\n"
- + "</div><!-- table of contents -->\n"
- + "<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\">";
+ private static final String CONTENT_HEAD_STRING =
+ "<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\">";
+ private static final String CONTENT_TAIL_STRING = "</table>";
- private static final String HTML_REAR_STRING =
- "</table></body></html>";
+ private static final String IMAGES_HEAD_STRING =
+ "<div class=\"images-list\"><strong>Images</strong>\n<ul class=\"images\">";
+ private static final String IMAGES_TAIL_STRING = "</ul></div>\n";
+
+ private static final String PATH_COUNTS_HEAD_STRING =
+ "<div class=\"path-counts\"><table>\n <tr><th>Path prefix</th><th>Count</th></tr>\n";
+ private static final String PATH_COUNTS_TAIL_STRING = "</table></div>\n";
+
+ private static final String HTML_TAIL_STRING =
+ "</body></html>";
private final List<File> mXmlFiles;
@@ -137,13 +144,13 @@ class LicenseHtmlGeneratorFromXml {
try {
writer = new PrintWriter(outputFile);
- generateHtml(mFileNameToLibraryToContentIdMap, mContentIdToFileContentMap, writer,
- noticeHeader);
+ generateHtml(mXmlFiles, mFileNameToLibraryToContentIdMap, mContentIdToFileContentMap,
+ writer, noticeHeader);
writer.flush();
writer.close();
return true;
- } catch (FileNotFoundException | SecurityException e) {
+ } catch (IOException | SecurityException e) {
Log.e(TAG, "Failed to generate " + outputFile, e);
if (writer != null) {
@@ -271,14 +278,33 @@ class LicenseHtmlGeneratorFromXml {
return result.toString();
}
+ private static String pathPrefix(String path) {
+ String prefix = path;
+ while (prefix.length() > 0 && prefix.substring(0, 1).equals("/")) {
+ prefix = prefix.substring(1);
+ }
+ int idx = prefix.indexOf("/");
+ if (idx > 0) {
+ prefix = prefix.substring(0, idx);
+ }
+ return prefix;
+ }
+
@VisibleForTesting
- static void generateHtml(Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap,
+ static void generateHtml(List<File> xmlFiles,
+ Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap,
Map<String, String> contentIdToFileContentMap, PrintWriter writer,
- String noticeHeader) {
+ String noticeHeader) throws IOException {
List<String> fileNameList = new ArrayList();
fileNameList.addAll(fileNameToLibraryToContentIdMap.keySet());
Collections.sort(fileNameList);
+ SortedMap<String, Integer> prefixToCount = new TreeMap();
+ for (String f : fileNameList) {
+ String prefix = pathPrefix(f);
+ prefixToCount.merge(prefix, 1, Integer::sum);
+ }
+
SortedMap<String, Set<String>> libraryToContentIdMap = new TreeMap();
for (Map<String, Set<String>> libraryToContentValue :
fileNameToLibraryToContentIdMap.values()) {
@@ -324,70 +350,95 @@ class LicenseHtmlGeneratorFromXml {
writer.println(LIBRARY_TAIL_STRING);
}
- writer.println(FILES_HEAD_STRING);
-
- // Prints all the file list with a link to its license file content.
- for (String fileName : fileNameList) {
- for (Map.Entry<String, Set<String>> libToContentId :
- fileNameToLibraryToContentIdMap.get(fileName).entrySet()) {
- String libraryName = libToContentId.getKey();
- if (libraryName == null) {
- libraryName = "";
- }
- for (String contentId : libToContentId.getValue()) {
- // Assigns an id to a newly referred license file content.
- if (!contentIdToOrderMap.containsKey(contentId)) {
- contentIdToOrderMap.put(contentId, count);
+ if (!fileNameList.isEmpty()) {
+ writer.println(FILES_HEAD_STRING);
+ // Prints all the file list with a link to its license file content.
+ for (String fileName : fileNameList) {
+ for (Map.Entry<String, Set<String>> libToContentId :
+ fileNameToLibraryToContentIdMap.get(fileName).entrySet()) {
+ String libraryName = libToContentId.getKey();
+ if (libraryName == null) {
+ libraryName = "";
+ }
+ for (String contentId : libToContentId.getValue()) {
+ // Assigns an id to a newly referred license file content.
+ if (!contentIdToOrderMap.containsKey(contentId)) {
+ contentIdToOrderMap.put(contentId, count);
+
+ // An index in contentIdAndFileNamesList is the order of each element.
+ contentIdAndFileNamesList.add(new ContentIdAndFileNames(contentId));
+ count++;
+ }
- // An index in contentIdAndFileNamesList is the order of each element.
- contentIdAndFileNamesList.add(new ContentIdAndFileNames(contentId));
- count++;
+ int id = contentIdToOrderMap.get(contentId);
+ ContentIdAndFileNames elem = contentIdAndFileNamesList.get(id);
+ List<String> files = elem.mLibraryToFileNameMap.computeIfAbsent(
+ libraryName, k -> new ArrayList());
+ files.add(fileName);
+ if (TextUtils.isEmpty(libraryName)) {
+ writer.format("<li><a href=\"#id%d\">%s</a></li>\n", id, fileName);
+ } else {
+ writer.format("<li><a href=\"#id%d\">%s - %s</a></li>\n",
+ id, fileName, libraryName);
+ }
}
+ }
+ }
+ writer.println(FILES_TAIL_STRING);
+ }
- int id = contentIdToOrderMap.get(contentId);
- ContentIdAndFileNames elem = contentIdAndFileNamesList.get(id);
- List<String> files = elem.mLibraryToFileNameMap.computeIfAbsent(
- libraryName, k -> new ArrayList());
- files.add(fileName);
+ if (!contentIdAndFileNamesList.isEmpty()) {
+ writer.println(CONTENT_HEAD_STRING);
+ // Prints all contents of the license files in order of id.
+ for (ContentIdAndFileNames contentIdAndFileNames : contentIdAndFileNamesList) {
+ // Assigns an id to a newly referred license file content (should never happen here)
+ if (!contentIdToOrderMap.containsKey(contentIdAndFileNames.mContentId)) {
+ contentIdToOrderMap.put(contentIdAndFileNames.mContentId, count);
+ count++;
+ }
+ int id = contentIdToOrderMap.get(contentIdAndFileNames.mContentId);
+ writer.format("<tr id=\"id%d\"><td class=\"same-license\">\n", id);
+ for (Map.Entry<String, List<String>> libraryFiles :
+ contentIdAndFileNames.mLibraryToFileNameMap.entrySet()) {
+ String libraryName = libraryFiles.getKey();
if (TextUtils.isEmpty(libraryName)) {
- writer.format("<li><a href=\"#id%d\">%s</a></li>\n", id, fileName);
+ writer.println("<div class=\"label\">Notices for file(s):</div>");
} else {
- writer.format("<li><a href=\"#id%d\">%s - %s</a></li>\n",
- id, fileName, libraryName);
+ writer.format("<div class=\"label\"><strong>%s</strong> used by:</div>\n",
+ libraryName);
+ }
+ writer.println("<div class=\"file-list\">");
+ for (String fileName : libraryFiles.getValue()) {
+ writer.format("%s <br/>\n", fileName);
}
+ writer.println("</div><!-- file-list -->");
}
+ writer.println("<pre class=\"license-text\">");
+ writer.println(contentIdToFileContentMap.get(
+ contentIdAndFileNames.mContentId));
+ writer.println("</pre><!-- license-text -->");
+ writer.println("</td></tr><!-- same-license -->");
}
+ writer.println(CONTENT_TAIL_STRING);
}
- writer.println(HTML_MIDDLE_STRING);
-
- count = 0;
- // Prints all contents of the license files in order of id.
- for (ContentIdAndFileNames contentIdAndFileNames : contentIdAndFileNamesList) {
- writer.format("<tr id=\"id%d\"><td class=\"same-license\">\n", count);
- for (Map.Entry<String, List<String>> libraryFiles :
- contentIdAndFileNames.mLibraryToFileNameMap.entrySet()) {
- String libraryName = libraryFiles.getKey();
- if (TextUtils.isEmpty(libraryName)) {
- writer.println("<div class=\"label\">Notices for file(s):</div>");
- } else {
- writer.format("<div class=\"label\"><strong>%s</strong> used by:</div>\n",
- libraryName);
- }
- writer.println("<div class=\"file-list\">");
- for (String fileName : libraryFiles.getValue()) {
- writer.format("%s <br/>\n", fileName);
- }
- writer.println("</div><!-- file-list -->");
- count++;
+ if (!xmlFiles.isEmpty()) {
+ writer.println(IMAGES_HEAD_STRING);
+ for (File file : xmlFiles) {
+ writer.format(" <li>%s</li>\n", pathPrefix(file.getCanonicalPath()));
+ }
+ writer.println(IMAGES_TAIL_STRING);
+ }
+
+ if (!prefixToCount.isEmpty()) {
+ writer.println(PATH_COUNTS_HEAD_STRING);
+ for (Map.Entry<String, Integer> entry : prefixToCount.entrySet()) {
+ writer.format(" <tr><td>%s</td><td>%d</td></tr>\n",
+ entry.getKey(), entry.getValue());
}
- writer.println("<pre class=\"license-text\">");
- writer.println(contentIdToFileContentMap.get(
- contentIdAndFileNames.mContentId));
- writer.println("</pre><!-- license-text -->");
- writer.println("</td></tr><!-- same-license -->");
+ writer.println(PATH_COUNTS_TAIL_STRING);
}
- writer.println(HTML_REAR_STRING);
+ writer.println(HTML_TAIL_STRING);
}
}
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/license/LicenseHtmlGeneratorFromXmlTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
index 09b0d7f56e18..fe337d267f25 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
@@ -24,13 +24,16 @@ import org.robolectric.RobolectricTestRunner;
import org.xmlpull.v1.XmlPullParserException;
import java.io.ByteArrayInputStream;
+import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -116,6 +119,7 @@ public class LicenseHtmlGeneratorFromXmlTest {
+ "<li><a href=\"#id0\">/file0 - libA</a></li>\n"
+ "<li><a href=\"#id1\">/file0 - libB</a></li>\n"
+ "<li><a href=\"#id0\">/file1 - libA</a></li>\n"
+ + "<li><a href=\"#id0\">/file2 - libC</a></li>\n"
+ "</ul>\n"
+ "</div><!-- table of contents -->\n"
+ "<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\">\n"
@@ -125,6 +129,10 @@ public class LicenseHtmlGeneratorFromXmlTest {
+ "/file0 <br/>\n"
+ "/file1 <br/>\n"
+ "</div><!-- file-list -->\n"
+ + "<div class=\"label\"><strong>libC</strong> used by:</div>\n"
+ + "<div class=\"file-list\">\n"
+ + "/file2 <br/>\n"
+ + "</div><!-- file-list -->\n"
+ "<pre class=\"license-text\">\n"
+ "license content #0\n"
+ "</pre><!-- license-text -->\n"
@@ -197,7 +205,8 @@ public class LicenseHtmlGeneratorFromXmlTest {
}
@Test
- public void testGenerateHtml() {
+ public void testGenerateHtml() throws Exception {
+ List<File> xmlFiles = new ArrayList<>();
Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap = new HashMap<>();
Map<String, String> contentIdToFileContentMap = new HashMap<>();
Map<String, Set<String>> toBoth = new HashMap<>();
@@ -213,43 +222,48 @@ public class LicenseHtmlGeneratorFromXmlTest {
StringWriter output = new StringWriter();
LicenseHtmlGeneratorFromXml.generateHtml(
- fileNameToLibraryToContentIdMap, contentIdToFileContentMap,
+ xmlFiles, fileNameToLibraryToContentIdMap, contentIdToFileContentMap,
new PrintWriter(output), "");
assertThat(output.toString()).isEqualTo(EXPECTED_OLD_HTML_STRING);
}
@Test
- public void testGenerateNewHtml() {
+ public void testGenerateNewHtml() throws Exception {
+ List<File> xmlFiles = new ArrayList<>();
Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap = new HashMap<>();
Map<String, String> contentIdToFileContentMap = new HashMap<>();
Map<String, Set<String>> toBoth = new HashMap<>();
Map<String, Set<String>> toOne = new HashMap<>();
+ Map<String, Set<String>> toOther = new HashMap<>();
toBoth.put("libA", new HashSet<String>(Arrays.asList("0")));
toBoth.put("libB", new HashSet<String>(Arrays.asList("1")));
toOne.put("libA", new HashSet<String>(Arrays.asList("0")));
+ toOther.put("libC", new HashSet<String>(Arrays.asList("0")));
fileNameToLibraryToContentIdMap.put("/file0", toBoth);
fileNameToLibraryToContentIdMap.put("/file1", toOne);
+ fileNameToLibraryToContentIdMap.put("/file2", toOther);
contentIdToFileContentMap.put("0", "license content #0");
contentIdToFileContentMap.put("1", "license content #1");
StringWriter output = new StringWriter();
LicenseHtmlGeneratorFromXml.generateHtml(
- fileNameToLibraryToContentIdMap, contentIdToFileContentMap,
+ xmlFiles, fileNameToLibraryToContentIdMap, contentIdToFileContentMap,
new PrintWriter(output), "");
assertThat(output.toString()).isEqualTo(EXPECTED_NEW_HTML_STRING);
}
@Test
- public void testGenerateHtmlWithCustomHeading() {
+ public void testGenerateHtmlWithCustomHeading() throws Exception {
+ List<File> xmlFiles = new ArrayList<>();
Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap = new HashMap<>();
Map<String, String> contentIdToFileContentMap = new HashMap<>();
Map<String, Set<String>> toBoth = new HashMap<>();
Map<String, Set<String>> toOne = new HashMap<>();
toBoth.put("", new HashSet<String>(Arrays.asList("0", "1")));
- toOne.put("", new HashSet<String>(Arrays.asList("0")));
+ toOne.put("", new HashSet<String>(Arrays.asList("0", "1")));
fileNameToLibraryToContentIdMap.put("/file0", toBoth);
fileNameToLibraryToContentIdMap.put("/file1", toOne);
@@ -258,30 +272,34 @@ public class LicenseHtmlGeneratorFromXmlTest {
StringWriter output = new StringWriter();
LicenseHtmlGeneratorFromXml.generateHtml(
- fileNameToLibraryToContentIdMap, contentIdToFileContentMap,
+ xmlFiles, fileNameToLibraryToContentIdMap, contentIdToFileContentMap,
new PrintWriter(output), HTML_CUSTOM_HEADING);
assertThat(output.toString()).isEqualTo(EXPECTED_OLD_HTML_STRING_WITH_CUSTOM_HEADING);
}
@Test
- public void testGenerateNewHtmlWithCustomHeading() {
+ public void testGenerateNewHtmlWithCustomHeading() throws Exception {
+ List<File> xmlFiles = new ArrayList<>();
Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap = new HashMap<>();
Map<String, String> contentIdToFileContentMap = new HashMap<>();
Map<String, Set<String>> toBoth = new HashMap<>();
Map<String, Set<String>> toOne = new HashMap<>();
+ Map<String, Set<String>> toOther = new HashMap<>();
toBoth.put("libA", new HashSet<String>(Arrays.asList("0")));
toBoth.put("libB", new HashSet<String>(Arrays.asList("1")));
toOne.put("libA", new HashSet<String>(Arrays.asList("0")));
+ toOther.put("libC", new HashSet<String>(Arrays.asList("0")));
fileNameToLibraryToContentIdMap.put("/file0", toBoth);
fileNameToLibraryToContentIdMap.put("/file1", toOne);
+ fileNameToLibraryToContentIdMap.put("/file2", toOther);
contentIdToFileContentMap.put("0", "license content #0");
contentIdToFileContentMap.put("1", "license content #1");
StringWriter output = new StringWriter();
LicenseHtmlGeneratorFromXml.generateHtml(
- fileNameToLibraryToContentIdMap, contentIdToFileContentMap,
+ xmlFiles, fileNameToLibraryToContentIdMap, contentIdToFileContentMap,
new PrintWriter(output), HTML_CUSTOM_HEADING);
assertThat(output.toString()).isEqualTo(EXPECTED_NEW_HTML_STRING_WITH_CUSTOM_HEADING);
}
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/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 133b52d18637..42b992fb44d3 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -211,6 +211,7 @@ public class SecureSettings {
Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK,
Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON,
Settings.Secure.WEAR_TALKBACK_ENABLED,
- Settings.Secure.HBM_SETTING_KEY
+ Settings.Secure.HBM_SETTING_KEY,
+ Settings.Secure.ACCESSIBILITY_SOFTWARE_CURSOR_ENABLED
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 796b4c453f00..f0915f8be287 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -337,8 +337,15 @@ 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);
+ Global.Wearable.EARLY_UPDATES_STATUS,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(Global.Wearable.EARLY_UPDATES_STATUS_NOT_STARTED),
+ String.valueOf(Global.Wearable.EARLY_UPDATES_STATUS_STARTED),
+ String.valueOf(Global.Wearable.EARLY_UPDATES_STATUS_SUCCESS),
+ String.valueOf(Global.Wearable.EARLY_UPDATES_STATUS_SKIPPED),
+ String.valueOf(Global.Wearable.EARLY_UPDATES_STATUS_ABORTED),
+ }));
}
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index bb01f4f03869..14b58550d162 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -345,5 +345,6 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.ODI_CAPTIONS_VOLUME_UI_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.WEAR_TALKBACK_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.HBM_SETTING_KEY, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.ACCESSIBILITY_SOFTWARE_CURSOR_ENABLED, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 4e2bce226d6c..c3b645e7bc0d 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1825,6 +1825,9 @@ class SettingsProtoDumpUtil {
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED,
SecureSettingsProto.Accessibility
.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED);
+ dumpSetting(s, p,
+ Settings.Secure.ACCESSIBILITY_SOFTWARE_CURSOR_ENABLED,
+ SecureSettingsProto.Accessibility.ACCESSIBILITY_SOFTWARE_CURSOR_ENABLED);
p.end(accessibilityToken);
final long adaptiveSleepToken = p.start(SecureSettingsProto.ADAPTIVE_SLEEP);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 760f147d43c6..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,7 @@ 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 =
newHashSet(
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 154a6fca9d09..26feaf979b20 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -137,6 +137,22 @@
]
}
],
+ "ironwood-postsubmit": [
+ {
+ "name": "PlatformScenarioTests",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.IwTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ },
+ {
+ "include-filter": "android.platform.test.scenario.sysui"
+ }
+ ]
+ }
+ ],
"auto-end-to-end-postsubmit": [
{
"name": "AndroidAutomotiveHomeTests",
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/docs/device-entry/quickaffordance.md b/packages/SystemUI/docs/device-entry/quickaffordance.md
index a96e533933f4..38d636d7ff82 100644
--- a/packages/SystemUI/docs/device-entry/quickaffordance.md
+++ b/packages/SystemUI/docs/device-entry/quickaffordance.md
@@ -6,16 +6,16 @@ credit card, etc.
## Adding a new Quick Affordance
### Step 1: create a new quick affordance config
-* Create a new class under the [systemui/keyguard/data/quickaffordance](../../src/com/android/systemui/keyguard/data/quickaffordance) directory
+* Create a new class under the [systemui/keyguard/domain/quickaffordance](../../src/com/android/systemui/keyguard/domain/quickaffordance) directory
* Please make sure that the class is injected through the Dagger dependency injection system by using the `@Inject` annotation on its main constructor and the `@SysUISingleton` annotation at class level, to make sure only one instance of the class is ever instantiated
-* Have the class implement the [KeyguardQuickAffordanceConfig](../../src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt) interface, notes:
+* Have the class implement the [KeyguardQuickAffordanceConfig](../../src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceConfig.kt) interface, notes:
* The `state` Flow property must emit `State.Hidden` when the feature is not enabled!
* It is safe to assume that `onQuickAffordanceClicked` will not be invoked if-and-only-if the previous rule is followed
* When implementing `onQuickAffordanceClicked`, the implementation can do something or it can ask the framework to start an activity using an `Intent` provided by the implementation
-* Please include a unit test for your new implementation under [the correct directory](../../tests/src/com/android/systemui/keyguard/data/quickaffordance)
+* Please include a unit test for your new implementation under [the correct directory](../../tests/src/com/android/systemui/keyguard/domain/quickaffordance)
### Step 2: choose a position and priority
-* Add the new class as a dependency in the constructor of [KeyguardQuickAffordanceConfigs](../../src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceConfigs.kt)
+* Add the new class as a dependency in the constructor of [KeyguardQuickAffordanceRegistry](../../src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceRegistry.kt)
* Place the new class in one of the available positions in the `configsByPosition` property, note:
* In each position, there is a list. The order matters. The order of that list is the priority order in which the framework considers each config. The first config whose state property returns `State.Visible` determines the button that is shown for that position
* Please only add to one position. The framework treats each position individually and there is no good way to prevent the same config from making its button appear in more than one position at the same time
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml
index 01e3de2315af..898935fc7e99 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml
@@ -35,7 +35,7 @@
<!-- need to keep this outer view in order to have a correctly sized anchor
for the dropdown menu, as well as dropdown background in the right place -->
- <LinearLayout
+ <com.android.keyguard.KeyguardUserSwitcherAnchor
android:id="@+id/user_switcher_anchor"
android:orientation="horizontal"
android:layout_height="wrap_content"
@@ -48,7 +48,7 @@
android:textDirection="locale"
android:layout_width="@dimen/bouncer_user_switcher_width"
android:layout_height="wrap_content" />
- </LinearLayout>>
+ </com.android.keyguard.KeyguardUserSwitcherAnchor>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml b/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
index 70a770912c7f..c972624c04b1 100644
--- a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
@@ -35,10 +35,20 @@
app:layout_constraintBottom_toBottomOf="parent" />
<LinearLayout
+ android:id="@+id/dream_overlay_extra_items"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:layout_marginEnd="@dimen/dream_overlay_status_bar_extra_margin"
+ app:layout_constraintEnd_toStartOf="@+id/dream_overlay_system_status" />
+
+ <LinearLayout
android:id="@+id/dream_overlay_system_status"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
+ android:layout_marginStart="@dimen/dream_overlay_status_bar_extra_margin"
app:layout_constraintEnd_toEndOf="parent">
<com.android.systemui.statusbar.AlphaOptimizedImageView
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 60932a7e6f2e..786b6b884605 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1450,6 +1450,7 @@
<dimen name="dream_overlay_camera_mic_off_indicator_size">8dp</dimen>
<dimen name="dream_overlay_notification_indicator_size">6dp</dimen>
<dimen name="dream_overlay_grey_chip_width">56dp</dimen>
+ <dimen name="dream_overlay_status_bar_extra_margin">16dp</dimen>
<!-- Dream overlay complications related dimensions -->
<dimen name="dream_overlay_complication_clock_time_text_size">100sp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 9c2542cbd05f..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">
@@ -887,7 +891,8 @@
<!-- Accessibility label for the button that opens the user switcher. -->
<string name="accessibility_multi_user_switch_switcher">Switch user</string>
- <!-- Accessibility label for the button that opens the user switcher and announces the current user. -->
+ <!-- Accessibility role description for the element that opens the user switcher list. -->
+ <string name="accessibility_multi_user_list_switcher">pulldown menu</string>
<!-- Accessibility label for the user icon on the lock screen. -->
diff --git a/packages/SystemUI/res/xml/qqs_header.xml b/packages/SystemUI/res/xml/qqs_header.xml
index ee0c4fb6bab8..a82684d0358b 100644
--- a/packages/SystemUI/res/xml/qqs_header.xml
+++ b/packages/SystemUI/res/xml/qqs_header.xml
@@ -43,7 +43,7 @@
android:id="@+id/date">
<Layout
android:layout_width="0dp"
- android:layout_height="0dp"
+ android:layout_height="@dimen/qs_header_non_clickable_element_height"
app:layout_constrainedWidth="true"
app:layout_constraintStart_toEndOf="@id/clock"
app:layout_constraintEnd_toStartOf="@id/barrier"
@@ -61,7 +61,7 @@
app:layout_constraintHeight_min="@dimen/qs_header_non_clickable_element_height"
app:layout_constraintStart_toEndOf="@id/date"
app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon"
- app:layout_constraintTop_toTopOf="@id/date"
+ app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="1"
/>
@@ -76,7 +76,7 @@
app:layout_constraintHeight_min="@dimen/qs_header_non_clickable_element_height"
app:layout_constraintStart_toEndOf="@id/statusIcons"
app:layout_constraintEnd_toEndOf="@id/end_guide"
- app:layout_constraintTop_toTopOf="@id/date"
+ app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="1"
/>
@@ -100,8 +100,8 @@
android:layout_height="0dp"
app:layout_constraintStart_toEndOf="@id/date"
app:layout_constraintEnd_toEndOf="@id/end_guide"
- app:layout_constraintTop_toTopOf="@id/date"
- app:layout_constraintBottom_toBottomOf="@id/date"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="1"
/>
</Constraint>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
index 203b236fcdd6..7e42e1b89b1f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
@@ -170,7 +170,7 @@ public class PipSurfaceTransactionHelper {
/** @return {@link SurfaceControl.Transaction} instance with vsync-id */
public static SurfaceControl.Transaction newSurfaceControlTransaction() {
final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
- tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
+ tx.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
return tx;
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
index 9265f07ad284..33e8e3555eba 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
@@ -122,12 +122,13 @@ public class RemoteAnimationAdapterCompat {
IRemoteTransitionFinishedCallback finishCallback) {
final ArrayMap<SurfaceControl, SurfaceControl> leashMap = new ArrayMap<>();
final RemoteAnimationTargetCompat[] appsCompat =
- RemoteAnimationTargetCompat.wrap(info, false /* wallpapers */, t, leashMap);
+ RemoteAnimationTargetCompat.wrapApps(info, t, leashMap);
final RemoteAnimationTargetCompat[] wallpapersCompat =
- RemoteAnimationTargetCompat.wrap(info, true /* wallpapers */, t, leashMap);
- // TODO(bc-unlock): Build wrapped object for non-apps target.
+ RemoteAnimationTargetCompat.wrapNonApps(
+ info, true /* wallpapers */, t, leashMap);
final RemoteAnimationTargetCompat[] nonAppsCompat =
- new RemoteAnimationTargetCompat[0];
+ RemoteAnimationTargetCompat.wrapNonApps(
+ info, false /* wallpapers */, t, leashMap);
// TODO(b/177438007): Move this set-up logic into launcher's animation impl.
boolean isReturnToHome = false;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
index ef9e0951abf0..7c3b5fc52f0a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
@@ -16,7 +16,9 @@
package com.android.systemui.shared.system;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
@@ -24,6 +26,8 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
+import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
+
import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
@@ -76,7 +80,7 @@ public class RemoteAnimationTargetCompat {
private final SurfaceControl mStartLeash;
- // Fields used only to unrap into RemoteAnimationTarget
+ // Fields used only to unwrap into RemoteAnimationTarget
private final Rect startBounds;
public final boolean willShowImeOnTarget;
@@ -203,8 +207,19 @@ public class RemoteAnimationTargetCompat {
public RemoteAnimationTargetCompat(TransitionInfo.Change change, int order,
TransitionInfo info, SurfaceControl.Transaction t) {
- taskId = change.getTaskInfo() != null ? change.getTaskInfo().taskId : -1;
mode = newModeToLegacyMode(change.getMode());
+ taskInfo = change.getTaskInfo();
+ if (taskInfo != null) {
+ taskId = taskInfo.taskId;
+ isNotInRecents = !taskInfo.isRunning;
+ activityType = taskInfo.getActivityType();
+ windowConfiguration = taskInfo.configuration.windowConfiguration;
+ } else {
+ taskId = INVALID_TASK_ID;
+ isNotInRecents = true;
+ activityType = ACTIVITY_TYPE_UNDEFINED;
+ windowConfiguration = new WindowConfiguration();
+ }
// TODO: once we can properly sync transactions across process, then get rid of this leash.
leash = createLeash(info, change, order, t);
@@ -221,22 +236,12 @@ public class RemoteAnimationTargetCompat {
prefixOrderIndex = order;
// TODO(shell-transitions): I guess we need to send content insets? evaluate how its used.
contentInsets = new Rect(0, 0, 0, 0);
- if (change.getTaskInfo() != null) {
- isNotInRecents = !change.getTaskInfo().isRunning;
- activityType = change.getTaskInfo().getActivityType();
- } else {
- isNotInRecents = true;
- activityType = ACTIVITY_TYPE_UNDEFINED;
- }
- taskInfo = change.getTaskInfo();
allowEnterPip = change.getAllowEnterPip();
mStartLeash = null;
rotationChange = change.getEndRotation() - change.getStartRotation();
- windowType = INVALID_WINDOW_TYPE;
+ windowType = (change.getFlags() & FLAG_IS_DIVIDER_BAR) != 0
+ ? TYPE_DOCK_DIVIDER : INVALID_WINDOW_TYPE;
- windowConfiguration = change.getTaskInfo() != null
- ? change.getTaskInfo().configuration.windowConfiguration
- : new WindowConfiguration();
startBounds = change.getStartAbsBounds();
willShowImeOnTarget = (change.getFlags() & TransitionInfo.FLAG_WILL_IME_SHOWN) != 0;
}
@@ -251,37 +256,62 @@ public class RemoteAnimationTargetCompat {
}
/**
- * Represents a TransitionInfo object as an array of old-style targets
+ * Represents a TransitionInfo object as an array of old-style app targets
+ *
+ * @param leashMap Temporary map of change leash -> launcher leash. Is an output, so should be
+ * populated by this function. If null, it is ignored.
+ */
+ public static RemoteAnimationTargetCompat[] wrapApps(TransitionInfo info,
+ SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> leashMap) {
+ final ArrayList<RemoteAnimationTargetCompat> out = new ArrayList<>();
+ final SparseArray<TransitionInfo.Change> childTaskTargets = new SparseArray<>();
+ for (int i = 0; i < info.getChanges().size(); i++) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if (change.getTaskInfo() == null) continue;
+
+ final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+ // Children always come before parent since changes are in top-to-bottom z-order.
+ if (taskInfo != null) {
+ if (childTaskTargets.contains(taskInfo.taskId)) {
+ // has children, so not a leaf. Skip.
+ continue;
+ }
+ if (taskInfo.hasParentTask()) {
+ childTaskTargets.put(taskInfo.parentTaskId, change);
+ }
+ }
+
+ final RemoteAnimationTargetCompat targetCompat =
+ new RemoteAnimationTargetCompat(change, info.getChanges().size() - i, info, t);
+ if (leashMap != null) {
+ leashMap.put(change.getLeash(), targetCompat.leash);
+ }
+ out.add(targetCompat);
+ }
+
+ return out.toArray(new RemoteAnimationTargetCompat[out.size()]);
+ }
+
+ /**
+ * Represents a TransitionInfo object as an array of old-style non-app targets
*
* @param wallpapers If true, this will return wallpaper targets; otherwise it returns
* non-wallpaper targets.
* @param leashMap Temporary map of change leash -> launcher leash. Is an output, so should be
* populated by this function. If null, it is ignored.
*/
- public static RemoteAnimationTargetCompat[] wrap(TransitionInfo info, boolean wallpapers,
+ public static RemoteAnimationTargetCompat[] wrapNonApps(TransitionInfo info, boolean wallpapers,
SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> leashMap) {
final ArrayList<RemoteAnimationTargetCompat> out = new ArrayList<>();
- final SparseArray<TransitionInfo.Change> childTaskTargets = new SparseArray<>();
+
for (int i = 0; i < info.getChanges().size(); i++) {
final TransitionInfo.Change change = info.getChanges().get(i);
+ if (change.getTaskInfo() != null) continue;
+
final boolean changeIsWallpaper =
(change.getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0;
if (wallpapers != changeIsWallpaper) continue;
- if (!wallpapers) {
- final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
- // Children always come before parent since changes are in top-to-bottom z-order.
- if (taskInfo != null) {
- if (childTaskTargets.contains(taskInfo.taskId)) {
- // has children, so not a leaf. Skip.
- continue;
- }
- if (taskInfo.hasParentTask()) {
- childTaskTargets.put(taskInfo.parentTaskId, change);
- }
- }
- }
-
final RemoteAnimationTargetCompat targetCompat =
new RemoteAnimationTargetCompat(change, info.getChanges().size() - i, info, t);
if (leashMap != null) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index 7c1ef8c76926..f6792251d282 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -128,9 +128,10 @@ public class RemoteTransitionCompat implements Parcelable {
IRemoteTransitionFinishedCallback finishedCallback) {
final ArrayMap<SurfaceControl, SurfaceControl> leashMap = new ArrayMap<>();
final RemoteAnimationTargetCompat[] apps =
- RemoteAnimationTargetCompat.wrap(info, false /* wallpapers */, t, leashMap);
+ RemoteAnimationTargetCompat.wrapApps(info, t, leashMap);
final RemoteAnimationTargetCompat[] wallpapers =
- RemoteAnimationTargetCompat.wrap(info, true /* wallpapers */, t, leashMap);
+ RemoteAnimationTargetCompat.wrapNonApps(
+ info, true /* wallpapers */, t, leashMap);
// TODO(b/177438007): Move this set-up logic into launcher's animation impl.
mToken = transition;
// This transition is for opening recents, so recents is on-top. We want to draw
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index f697e256dfb0..3517d22ae50d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -1129,11 +1129,13 @@ public class KeyguardSecurityContainer extends FrameLayout {
Log.e(TAG, "Current user in user switcher is null.");
return;
}
+ final String currentUserName = mUserSwitcherController.getCurrentUserName();
Drawable userIcon = findUserIcon(currentUser.info.id);
((ImageView) mView.findViewById(R.id.user_icon)).setImageDrawable(userIcon);
- mUserSwitcher.setText(mUserSwitcherController.getCurrentUserName());
+ mUserSwitcher.setText(currentUserName);
+
+ KeyguardUserSwitcherAnchor anchor = mView.findViewById(R.id.user_switcher_anchor);
- ViewGroup anchor = mView.findViewById(R.id.user_switcher_anchor);
BaseUserAdapter adapter = new BaseUserAdapter(mUserSwitcherController) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
@@ -1213,7 +1215,6 @@ public class KeyguardSecurityContainer extends FrameLayout {
anchor.setOnClickListener((v) -> {
if (mFalsingManager.isFalseTap(LOW_PENALTY)) return;
-
mPopup = new KeyguardUserSwitcherPopupMenu(v.getContext(), mFalsingManager);
mPopup.setAnchorView(anchor);
mPopup.setAdapter(adapter);
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/KeyguardUserSwitcherAnchor.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherAnchor.kt
new file mode 100644
index 000000000000..5f3ba72d445b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherAnchor.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.accessibility.AccessibilityNodeInfo
+import android.widget.LinearLayout
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
+import com.android.systemui.R
+
+/**
+ * Custom View for the multi-user switcher pull-down menu anchor
+ */
+class KeyguardUserSwitcherAnchor @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null
+) : LinearLayout(context, attrs) {
+
+ override fun createAccessibilityNodeInfo(): AccessibilityNodeInfo {
+ val info = super.createAccessibilityNodeInfo()
+ AccessibilityNodeInfoCompat.wrap(info).roleDescription =
+ context.getString(R.string.accessibility_multi_user_list_switcher)
+ return info
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 5c84ff38d761..614a87fc758c 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -87,7 +87,6 @@ import com.android.systemui.statusbar.events.PrivacyDotViewController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
-import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
@@ -102,7 +101,6 @@ import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
import com.android.systemui.statusbar.phone.ManagedProfileController;
-import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -313,7 +311,6 @@ public class Dependency {
@Inject Lazy<AccessibilityFloatingMenuController> mAccessibilityFloatingMenuController;
@Inject Lazy<StatusBarStateController> mStatusBarStateController;
@Inject Lazy<NotificationLockscreenUserManager> mNotificationLockscreenUserManager;
- @Inject Lazy<NotificationGroupAlertTransferHelper> mNotificationGroupAlertTransferHelper;
@Inject Lazy<NotificationGroupManagerLegacy> mNotificationGroupManager;
@Inject Lazy<VisualStabilityManager> mVisualStabilityManager;
@Inject Lazy<NotificationGutsManager> mNotificationGutsManager;
@@ -322,7 +319,6 @@ public class Dependency {
@Inject Lazy<SmartReplyConstants> mSmartReplyConstants;
@Inject Lazy<NotificationListener> mNotificationListener;
@Inject Lazy<NotificationLogger> mNotificationLogger;
- @Inject Lazy<NotificationFilter> mNotificationFilter;
@Inject Lazy<KeyguardDismissUtil> mKeyguardDismissUtil;
@Inject Lazy<SmartReplyController> mSmartReplyController;
@Inject Lazy<RemoteInputQuickSettingsDisabler> mRemoteInputQuickSettingsDisabler;
@@ -529,8 +525,6 @@ public class Dependency {
mNotificationLockscreenUserManager::get);
mProviders.put(VisualStabilityManager.class, mVisualStabilityManager::get);
mProviders.put(NotificationGroupManagerLegacy.class, mNotificationGroupManager::get);
- mProviders.put(NotificationGroupAlertTransferHelper.class,
- mNotificationGroupAlertTransferHelper::get);
mProviders.put(NotificationMediaManager.class, mNotificationMediaManager::get);
mProviders.put(NotificationGutsManager.class, mNotificationGutsManager::get);
mProviders.put(NotificationRemoteInputManager.class,
@@ -538,7 +532,6 @@ public class Dependency {
mProviders.put(SmartReplyConstants.class, mSmartReplyConstants::get);
mProviders.put(NotificationListener.class, mNotificationListener::get);
mProviders.put(NotificationLogger.class, mNotificationLogger::get);
- mProviders.put(NotificationFilter.class, mNotificationFilter::get);
mProviders.put(KeyguardDismissUtil.class, mKeyguardDismissUtil::get);
mProviders.put(SmartReplyController.class, mSmartReplyController::get);
mProviders.put(RemoteInputQuickSettingsDisabler.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index 78a45f9d3310..b6923a867507 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -75,25 +75,12 @@ public interface WMComponent {
* Initializes all the WMShell components before starting any of the SystemUI components.
*/
default void init() {
- // TODO(238217847): To be removed once the dependencies are inverted and ShellController can
- // inject these classes directly, otherwise, it's currently needed to ensure that these
- // classes are created and set on the controller before onInit() is called
- getShellInit();
- getShellCommandHandler();
getShell().onInit();
}
@WMSingleton
ShellInterface getShell();
- // TODO(238217847): To be removed once ShellController can inject ShellInit directly
- @WMSingleton
- ShellInit getShellInit();
-
- // TODO(238217847): To be removed once ShellController can inject ShellCommandHandler directly
- @WMSingleton
- ShellCommandHandler getShellCommandHandler();
-
@WMSingleton
Optional<OneHanded> getOneHanded();
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProvider.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProvider.java
new file mode 100644
index 000000000000..193d6f5c0061
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProvider.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams;
+
+import android.view.View;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.statusbar.policy.CallbackController;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+
+/**
+ * {@link DreamOverlayStatusBarItemsProvider} provides extra dream overlay status bar items. A
+ * callback can be registered that will be informed of items being added or removed from the
+ * provider.
+ */
+@SysUISingleton
+public class DreamOverlayStatusBarItemsProvider implements
+ CallbackController<DreamOverlayStatusBarItemsProvider.Callback> {
+ /**
+ * Represents one item in the dream overlay status bar.
+ */
+ public interface StatusBarItem {
+ /**
+ * Return the {@link View} associated with this item.
+ */
+ View getView();
+ }
+
+ /**
+ * A callback to be registered with the provider to be informed of when the list of status bar
+ * items has changed.
+ */
+ public interface Callback {
+ /**
+ * Inform the callback that status bar items have changed.
+ */
+ void onStatusBarItemsChanged(List<StatusBarItem> newItems);
+ }
+
+ private final Executor mExecutor;
+ private final List<StatusBarItem> mItems = new ArrayList<>();
+ private final List<Callback> mCallbacks = new ArrayList<>();
+
+ @Inject
+ public DreamOverlayStatusBarItemsProvider(@Main Executor executor) {
+ mExecutor = executor;
+ }
+
+ @Override
+ public void addCallback(@NonNull Callback callback) {
+ mExecutor.execute(() -> {
+ Objects.requireNonNull(callback, "Callback must not be null.");
+ if (mCallbacks.contains(callback)) {
+ return;
+ }
+
+ mCallbacks.add(callback);
+ if (!mItems.isEmpty()) {
+ callback.onStatusBarItemsChanged(mItems);
+ }
+ });
+ }
+
+ @Override
+ public void removeCallback(@NonNull Callback callback) {
+ mExecutor.execute(() -> {
+ Objects.requireNonNull(callback, "Callback must not be null.");
+ mCallbacks.remove(callback);
+ });
+ }
+
+ /**
+ * Adds an item to the dream overlay status bar.
+ */
+ public void addStatusBarItem(StatusBarItem item) {
+ mExecutor.execute(() -> {
+ if (!mItems.contains(item)) {
+ mItems.add(item);
+ mCallbacks.forEach(callback -> callback.onStatusBarItemsChanged(mItems));
+ }
+ });
+ }
+
+ /**
+ * Removes an item from the dream overlay status bar.
+ */
+ public void removeStatusBarItem(StatusBarItem item) {
+ mExecutor.execute(() -> {
+ if (mItems.remove(item)) {
+ mCallbacks.forEach(callback -> callback.onStatusBarItemsChanged(mItems));
+ }
+ });
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
index a25257d6cf42..7e4a108aadf1 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
+import android.view.ViewGroup;
import androidx.constraintlayout.widget.ConstraintLayout;
@@ -29,6 +30,7 @@ import com.android.systemui.R;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -58,6 +60,7 @@ public class DreamOverlayStatusBarView extends ConstraintLayout {
public static final int STATUS_ICON_PRIORITY_MODE_ON = 6;
private final Map<Integer, View> mStatusIcons = new HashMap<>();
+ private ViewGroup mSystemStatusViewGroup;
public DreamOverlayStatusBarView(Context context) {
this(context, null);
@@ -94,6 +97,8 @@ public class DreamOverlayStatusBarView extends ConstraintLayout {
fetchStatusIconForResId(R.id.dream_overlay_notification_indicator));
mStatusIcons.put(STATUS_ICON_PRIORITY_MODE_ON,
fetchStatusIconForResId(R.id.dream_overlay_priority_mode));
+
+ mSystemStatusViewGroup = findViewById(R.id.dream_overlay_extra_items);
}
void showIcon(@StatusIconType int iconType, boolean show, @Nullable String contentDescription) {
@@ -107,6 +112,11 @@ public class DreamOverlayStatusBarView extends ConstraintLayout {
icon.setVisibility(show ? View.VISIBLE : View.GONE);
}
+ void setExtraStatusBarItemViews(List<View> views) {
+ mSystemStatusViewGroup.removeAllViews();
+ views.forEach(view -> mSystemStatusViewGroup.addView(view));
+ }
+
private View fetchStatusIconForResId(int resId) {
final View statusIcon = findViewById(resId);
return Objects.requireNonNull(statusIcon);
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
index 55c1806e1899..65cfae1ac14b 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
@@ -38,6 +38,7 @@ import android.view.View;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dreams.DreamOverlayStatusBarItemsProvider.StatusBarItem;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
import com.android.systemui.statusbar.policy.NextAlarmController;
@@ -47,10 +48,13 @@ import com.android.systemui.touch.TouchInsetManager;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.time.DateFormatUtil;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Executor;
+import java.util.stream.Collectors;
import javax.inject.Inject;
@@ -69,7 +73,10 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
private final Optional<DreamOverlayNotificationCountProvider>
mDreamOverlayNotificationCountProvider;
private final ZenModeController mZenModeController;
+ private final DreamOverlayStatusBarItemsProvider mStatusBarItemsProvider;
private final Executor mMainExecutor;
+ private final List<DreamOverlayStatusBarItemsProvider.StatusBarItem> mExtraStatusBarItems =
+ new ArrayList<>();
private boolean mIsAttached;
@@ -116,6 +123,9 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
? buildNotificationsContentDescription(notificationCount)
: null);
+ private final DreamOverlayStatusBarItemsProvider.Callback mStatusBarItemsProviderCallback =
+ this::onStatusBarItemsChanged;
+
@Inject
public DreamOverlayStatusBarViewController(
DreamOverlayStatusBarView view,
@@ -129,7 +139,8 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
IndividualSensorPrivacyController sensorPrivacyController,
Optional<DreamOverlayNotificationCountProvider> dreamOverlayNotificationCountProvider,
ZenModeController zenModeController,
- StatusBarWindowStateController statusBarWindowStateController) {
+ StatusBarWindowStateController statusBarWindowStateController,
+ DreamOverlayStatusBarItemsProvider statusBarItemsProvider) {
super(view);
mResources = resources;
mMainExecutor = mainExecutor;
@@ -140,6 +151,7 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
mDateFormatUtil = dateFormatUtil;
mSensorPrivacyController = sensorPrivacyController;
mDreamOverlayNotificationCountProvider = dreamOverlayNotificationCountProvider;
+ mStatusBarItemsProvider = statusBarItemsProvider;
mZenModeController = zenModeController;
// Register to receive show/hide updates for the system status bar. Our custom status bar
@@ -166,6 +178,8 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
mDreamOverlayNotificationCountProvider.ifPresent(
provider -> provider.addCallback(mNotificationCountCallback));
+ mStatusBarItemsProvider.addCallback(mStatusBarItemsProviderCallback);
+
mTouchInsetSession.addViewToTracking(mView);
}
@@ -177,6 +191,7 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
mDreamOverlayNotificationCountProvider.ifPresent(
provider -> provider.removeCallback(mNotificationCountCallback));
+ mStatusBarItemsProvider.removeCallback(mStatusBarItemsProviderCallback);
mTouchInsetSession.clear();
mIsAttached = false;
@@ -271,4 +286,16 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
}
});
}
+
+ private void onStatusBarItemsChanged(List<StatusBarItem> newItems) {
+ mMainExecutor.execute(() -> {
+ mExtraStatusBarItems.clear();
+ mExtraStatusBarItems.addAll(newItems);
+ mView.setExtraStatusBarItemViews(
+ newItems
+ .stream()
+ .map(StatusBarItem::getView)
+ .collect(Collectors.toList()));
+ });
+ }
}
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/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 4ff008ffb33a..430b59cd4027 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -43,6 +43,7 @@ import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.data.repository.KeyguardRepositoryModule;
+import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceModule;
import com.android.systemui.keyguard.domain.usecase.KeyguardUseCaseModule;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.statusbar.NotificationShadeDepthController;
@@ -70,6 +71,7 @@ import dagger.Provides;
KeyguardUserSwitcherComponent.class},
includes = {
FalsingModule.class,
+ KeyguardQuickAffordanceModule.class,
KeyguardRepositoryModule.class,
KeyguardUseCaseModule.class,
})
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
deleted file mode 100644
index 43c4fa06367b..000000000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyguard.data.repository
-
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.data.config.KeyguardQuickAffordanceConfigs
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.State
-import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordanceModel
-import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePosition
-import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.combine
-
-/** Defines interface for classes that encapsulate quick affordance state for the keyguard. */
-interface KeyguardQuickAffordanceRepository {
- fun affordance(position: KeyguardQuickAffordancePosition): Flow<KeyguardQuickAffordanceModel>
-}
-
-/** Real implementation of [KeyguardQuickAffordanceRepository] */
-@SysUISingleton
-class KeyguardQuickAffordanceRepositoryImpl
-@Inject
-constructor(
- private val configs: KeyguardQuickAffordanceConfigs,
-) : KeyguardQuickAffordanceRepository {
-
- /** Returns an observable for the quick affordance model in the given position. */
- override fun affordance(
- position: KeyguardQuickAffordancePosition
- ): Flow<KeyguardQuickAffordanceModel> {
- val configs = configs.getAll(position)
- return combine(configs.map { config -> config.state }) { states ->
- val index = states.indexOfFirst { state -> state is State.Visible }
- val visibleState =
- if (index != -1) {
- states[index] as State.Visible
- } else {
- null
- }
- if (visibleState != null) {
- KeyguardQuickAffordanceModel.Visible(
- configKey = configs[index]::class,
- icon = visibleState.icon,
- contentDescriptionResourceId = visibleState.contentDescriptionResourceId,
- )
- } else {
- KeyguardQuickAffordanceModel.Hidden
- }
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
index 1a5670c7e807..d15d7f25bbde 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
@@ -16,22 +16,10 @@
package com.android.systemui.keyguard.data.repository
-import com.android.systemui.keyguard.data.config.KeyguardQuickAffordanceConfigs
-import com.android.systemui.keyguard.data.config.KeyguardQuickAffordanceConfigsImpl
import dagger.Binds
import dagger.Module
@Module
interface KeyguardRepositoryModule {
@Binds fun keyguardRepository(impl: KeyguardRepositoryImpl): KeyguardRepository
-
- @Binds
- fun keyguardQuickAffordanceRepository(
- impl: KeyguardQuickAffordanceRepositoryImpl
- ): KeyguardQuickAffordanceRepository
-
- @Binds
- fun keyguardQuickAffordanceConfigs(
- impl: KeyguardQuickAffordanceConfigsImpl
- ): KeyguardQuickAffordanceConfigs
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardQuickAffordanceModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordanceModel.kt
index 09785dfa3c03..411a2ca5ffe2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardQuickAffordanceModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordanceModel.kt
@@ -15,11 +15,11 @@
*
*/
-package com.android.systemui.keyguard.shared.model
+package com.android.systemui.keyguard.domain.model
import androidx.annotation.StringRes
import com.android.systemui.containeddrawable.ContainedDrawable
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig
import kotlin.reflect.KClass
/**
@@ -27,7 +27,6 @@ import kotlin.reflect.KClass
* lock-screen).
*/
sealed class KeyguardQuickAffordanceModel {
-
/** No affordance should show up. */
object Hidden : KeyguardQuickAffordanceModel()
@@ -43,4 +42,21 @@ sealed class KeyguardQuickAffordanceModel {
*/
@StringRes val contentDescriptionResourceId: Int,
) : KeyguardQuickAffordanceModel()
+
+ companion object {
+ fun from(
+ state: KeyguardQuickAffordanceConfig.State?,
+ configKey: KClass<out KeyguardQuickAffordanceConfig>,
+ ): KeyguardQuickAffordanceModel {
+ return when (state) {
+ is KeyguardQuickAffordanceConfig.State.Visible ->
+ Visible(
+ configKey = configKey,
+ icon = state.icon,
+ contentDescriptionResourceId = state.contentDescriptionResourceId,
+ )
+ else -> Hidden
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardQuickAffordancePosition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordancePosition.kt
index b71e15d34afe..581dafa33df7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardQuickAffordancePosition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordancePosition.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.keyguard.shared.model
+package com.android.systemui.keyguard.domain.model
/** Enumerates all possible positions for quick affordances that can appear on the lock-screen. */
enum class KeyguardQuickAffordancePosition {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
index df44957ec591..8f32ff9db50c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
@@ -15,7 +15,7 @@
*
*/
-package com.android.systemui.keyguard.data.quickaffordance
+package com.android.systemui.keyguard.domain.quickaffordance
import android.content.Context
import android.content.Intent
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceConfig.kt
index 67a776eddccb..8fb952c217bd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceConfig.kt
@@ -15,7 +15,7 @@
*
*/
-package com.android.systemui.keyguard.data.quickaffordance
+package com.android.systemui.keyguard.domain.quickaffordance
import android.content.Intent
import androidx.annotation.StringRes
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceModule.kt
new file mode 100644
index 000000000000..a7b38282d0aa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceModule.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.domain.quickaffordance
+
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface KeyguardQuickAffordanceModule {
+ @Binds
+ fun keyguardQuickAffordanceRegistry(
+ impl: KeyguardQuickAffordanceRegistryImpl
+ ): KeyguardQuickAffordanceRegistry
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceConfigs.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceRegistry.kt
index 7164215eb2ae..2c37f93de435 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceConfigs.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceRegistry.kt
@@ -15,29 +15,25 @@
*
*/
-package com.android.systemui.keyguard.data.config
+package com.android.systemui.keyguard.domain.quickaffordance
-import com.android.systemui.keyguard.data.quickaffordance.HomeControlsKeyguardQuickAffordanceConfig
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
-import com.android.systemui.keyguard.data.quickaffordance.QrCodeScannerKeyguardQuickAffordanceConfig
-import com.android.systemui.keyguard.data.quickaffordance.QuickAccessWalletKeyguardQuickAffordanceConfig
-import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePosition
+import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordancePosition
import javax.inject.Inject
import kotlin.reflect.KClass
-/** Injectable provider of the positioning of the known quick affordance configs. */
-interface KeyguardQuickAffordanceConfigs {
+/** Central registry of all known quick affordance configs. */
+interface KeyguardQuickAffordanceRegistry {
fun getAll(position: KeyguardQuickAffordancePosition): List<KeyguardQuickAffordanceConfig>
fun get(configClass: KClass<out KeyguardQuickAffordanceConfig>): KeyguardQuickAffordanceConfig
}
-class KeyguardQuickAffordanceConfigsImpl
+class KeyguardQuickAffordanceRegistryImpl
@Inject
constructor(
homeControls: HomeControlsKeyguardQuickAffordanceConfig,
quickAccessWallet: QuickAccessWalletKeyguardQuickAffordanceConfig,
qrCodeScanner: QrCodeScannerKeyguardQuickAffordanceConfig,
-) : KeyguardQuickAffordanceConfigs {
+) : KeyguardQuickAffordanceRegistry {
private val configsByPosition =
mapOf(
KeyguardQuickAffordancePosition.BOTTOM_START to
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
index ea6497ebcf3a..c8e5e4aebea0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
@@ -15,7 +15,7 @@
*
*/
-package com.android.systemui.keyguard.data.quickaffordance
+package com.android.systemui.keyguard.domain.quickaffordance
import com.android.systemui.R
import com.android.systemui.animation.ActivityLaunchAnimator
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
index cc5a9972af93..885af3343533 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
@@ -15,7 +15,7 @@
*
*/
-package com.android.systemui.keyguard.data.quickaffordance
+package com.android.systemui.keyguard.domain.quickaffordance
import android.graphics.drawable.Drawable
import android.service.quickaccesswallet.GetWalletCardsError
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/KeyguardUseCaseModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/KeyguardUseCaseModule.kt
index c44c2c9901a1..403d34352c7b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/KeyguardUseCaseModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/KeyguardUseCaseModule.kt
@@ -26,4 +26,9 @@ interface KeyguardUseCaseModule {
fun launchQuickAffordance(
impl: LaunchKeyguardQuickAffordanceUseCaseImpl
): LaunchKeyguardQuickAffordanceUseCase
+
+ @Binds
+ fun observeKeyguardQuickAffordance(
+ impl: ObserveKeyguardQuickAffordanceUseCaseImpl
+ ): ObserveKeyguardQuickAffordanceUseCase
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/ObserveKeyguardQuickAffordanceUseCase.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/ObserveKeyguardQuickAffordanceUseCase.kt
index eef8ec3e68f5..8dee8b38bdb8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/ObserveKeyguardQuickAffordanceUseCase.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/ObserveKeyguardQuickAffordanceUseCase.kt
@@ -16,26 +16,33 @@
package com.android.systemui.keyguard.domain.usecase
-import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
-import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordanceModel
-import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePosition
+import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
+import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordancePosition
+import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceRegistry
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
-/** Use-case for observing the model of a quick affordance in the keyguard. */
-class ObserveKeyguardQuickAffordanceUseCase
+/** Defines interface for use-case for observing the model of a quick affordance in the keyguard. */
+interface ObserveKeyguardQuickAffordanceUseCase {
+ operator fun invoke(
+ position: KeyguardQuickAffordancePosition
+ ): Flow<KeyguardQuickAffordanceModel>
+}
+
+class ObserveKeyguardQuickAffordanceUseCaseImpl
@Inject
constructor(
- private val repository: KeyguardQuickAffordanceRepository,
+ private val registry: KeyguardQuickAffordanceRegistry,
private val isDozingUseCase: ObserveIsDozingUseCase,
private val isKeyguardShowingUseCase: ObserveIsKeyguardShowingUseCase,
-) {
- operator fun invoke(
+) : ObserveKeyguardQuickAffordanceUseCase {
+ override fun invoke(
position: KeyguardQuickAffordancePosition
): Flow<KeyguardQuickAffordanceModel> {
return combine(
- repository.affordance(position),
+ affordance(position),
isDozingUseCase(),
isKeyguardShowingUseCase(),
) { affordance, isDozing, isKeyguardShowing ->
@@ -46,4 +53,23 @@ constructor(
}
}
}
+
+ private fun affordance(
+ position: KeyguardQuickAffordancePosition
+ ): Flow<KeyguardQuickAffordanceModel> {
+ val configs = registry.getAll(position)
+ return combine(configs.map { config -> config.state }) { states ->
+ val index =
+ states.indexOfFirst { state ->
+ state is KeyguardQuickAffordanceConfig.State.Visible
+ }
+ val visibleState =
+ if (index != -1) {
+ states[index] as KeyguardQuickAffordanceConfig.State.Visible
+ } else {
+ null
+ }
+ KeyguardQuickAffordanceModel.from(visibleState, configs[index]::class)
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/OnKeyguardQuickAffordanceClickedUseCase.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/OnKeyguardQuickAffordanceClickedUseCase.kt
index f8db90f02540..93153391ca41 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/OnKeyguardQuickAffordanceClickedUseCase.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/OnKeyguardQuickAffordanceClickedUseCase.kt
@@ -17,9 +17,9 @@
package com.android.systemui.keyguard.domain.usecase
import com.android.systemui.animation.ActivityLaunchAnimator
-import com.android.systemui.keyguard.data.config.KeyguardQuickAffordanceConfigs
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnClickedResult
+import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig.OnClickedResult
+import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceRegistry
import javax.inject.Inject
import kotlin.reflect.KClass
@@ -27,7 +27,7 @@ import kotlin.reflect.KClass
class OnKeyguardQuickAffordanceClickedUseCase
@Inject
constructor(
- private val configs: KeyguardQuickAffordanceConfigs,
+ private val registry: KeyguardQuickAffordanceRegistry,
private val launchAffordanceUseCase: LaunchKeyguardQuickAffordanceUseCase,
) {
operator fun invoke(
@@ -35,7 +35,7 @@ constructor(
animationController: ActivityLaunchAnimator.Controller?,
) {
@Suppress("UNCHECKED_CAST")
- val config = configs.get(configKey as KClass<out KeyguardQuickAffordanceConfig>)
+ val config = registry.get(configKey as KClass<out KeyguardQuickAffordanceConfig>)
when (val result = config.onQuickAffordanceClicked(animationController)) {
is OnClickedResult.StartActivity ->
launchAffordanceUseCase(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
index 4b69a81c8996..d296e76482ad 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
@@ -17,6 +17,8 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.doze.util.BurnInHelperWrapper
+import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
+import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordancePosition
import com.android.systemui.keyguard.domain.usecase.ObserveAnimateBottomAreaTransitionsUseCase
import com.android.systemui.keyguard.domain.usecase.ObserveBottomAreaAlphaUseCase
import com.android.systemui.keyguard.domain.usecase.ObserveClockPositionUseCase
@@ -24,8 +26,6 @@ import com.android.systemui.keyguard.domain.usecase.ObserveDozeAmountUseCase
import com.android.systemui.keyguard.domain.usecase.ObserveIsDozingUseCase
import com.android.systemui.keyguard.domain.usecase.ObserveKeyguardQuickAffordanceUseCase
import com.android.systemui.keyguard.domain.usecase.OnKeyguardQuickAffordanceClickedUseCase
-import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordanceModel
-import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePosition
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
index d701f33c4c66..c790cfe7b7b7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs
import android.content.Intent
+import android.content.res.Configuration
import android.os.Handler
import android.os.UserManager
import android.provider.Settings
@@ -38,9 +39,11 @@ import com.android.systemui.qs.dagger.QSFlagsModule.PM_LITE_ENABLED
import com.android.systemui.qs.dagger.QSScope
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.MultiUserSwitchController
+import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.UserInfoController
import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener
+import com.android.systemui.util.LargeScreenUtils
import com.android.systemui.util.ViewController
import com.android.systemui.util.settings.GlobalSettings
import javax.inject.Inject
@@ -69,18 +72,43 @@ internal class FooterActionsController @Inject constructor(
private val uiEventLogger: UiEventLogger,
@Named(PM_LITE_ENABLED) private val showPMLiteButton: Boolean,
private val globalSetting: GlobalSettings,
- private val handler: Handler
+ private val handler: Handler,
+ private val configurationController: ConfigurationController,
) : ViewController<FooterActionsView>(view) {
private var globalActionsDialog: GlobalActionsDialogLite? = null
private var lastExpansion = -1f
private var listening: Boolean = false
+ private var inSplitShade = false
- private val alphaAnimator = TouchAnimator.Builder()
- .addFloat(mView, "alpha", 0f, 1f)
- .setStartDelay(0.9f)
+ private val singleShadeAnimator by lazy {
+ // In single shade, the actions footer should only appear at the end of the expansion,
+ // so that it doesn't overlap with the notifications panel.
+ TouchAnimator.Builder().addFloat(mView, "alpha", 0f, 1f).setStartDelay(0.9f).build()
+ }
+
+ private val splitShadeAnimator by lazy {
+ // The Actions footer view has its own background which is the same color as the qs panel's
+ // background.
+ // We don't want it to fade in at the same time as the rest of the panel, otherwise it is
+ // more opaque than the rest of the panel's background. Only applies to split shade.
+ val alphaAnimator = TouchAnimator.Builder().addFloat(mView, "alpha", 0f, 1f).build()
+ val bgAlphaAnimator =
+ TouchAnimator.Builder()
+ .addFloat(mView, "backgroundAlpha", 0f, 1f)
+ .setStartDelay(0.9f)
+ .build()
+ // In split shade, we want the actions footer to fade in exactly at the same time as the
+ // rest of the shade, as there is no overlap.
+ TouchAnimator.Builder()
+ .addFloat(alphaAnimator, "position", 0f, 1f)
+ .addFloat(bgAlphaAnimator, "position", 0f, 1f)
.build()
+ }
+
+ private val animators: TouchAnimator
+ get() = if (inSplitShade) splitShadeAnimator else singleShadeAnimator
var visible = true
set(value) {
@@ -95,9 +123,7 @@ internal class FooterActionsController @Inject constructor(
private val multiUserSwitchController = multiUserSwitchControllerFactory.create(view)
@VisibleForTesting
- internal val securityFootersSeparator = View(context).apply {
- visibility = View.GONE
- }
+ internal val securityFootersSeparator = View(context).apply { visibility = View.GONE }
private val onUserInfoChangedListener = OnUserInfoChangedListener { _, picture, _ ->
val isGuestUser: Boolean = userManager.isGuestUser(KeyguardUpdateMonitor.getCurrentUser())
@@ -133,6 +159,17 @@ internal class FooterActionsController @Inject constructor(
}
}
+ private val configurationListener =
+ object : ConfigurationController.ConfigurationListener {
+ override fun onConfigChanged(newConfig: Configuration?) {
+ updateResources()
+ }
+ }
+
+ private fun updateResources() {
+ inSplitShade = LargeScreenUtils.shouldUseSplitNotificationShade(resources)
+ }
+
override fun onInit() {
multiUserSwitchController.init()
securityFooterController.init()
@@ -189,6 +226,9 @@ internal class FooterActionsController @Inject constructor(
securityFooterController.setOnVisibilityChangedListener(visibilityListener)
fgsManagerFooterController.setOnVisibilityChangedListener(visibilityListener)
+ configurationController.addCallback(configurationListener)
+
+ updateResources()
updateView()
}
@@ -201,6 +241,7 @@ internal class FooterActionsController @Inject constructor(
globalActionsDialog = null
setListening(false)
multiUserSetting.isListening = false
+ configurationController.removeCallback(configurationListener)
}
fun setListening(listening: Boolean) {
@@ -224,7 +265,7 @@ internal class FooterActionsController @Inject constructor(
}
fun setExpansion(headerExpansionFraction: Float) {
- alphaAnimator.setPosition(headerExpansionFraction)
+ animators.setPosition(headerExpansionFraction)
}
fun setKeyguardShowing(showing: Boolean) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt
index 05038b7b1b1d..309ac2a66e6b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt
@@ -27,6 +27,7 @@ import android.view.MotionEvent
import android.view.View
import android.widget.ImageView
import android.widget.LinearLayout
+import androidx.annotation.Keep
import com.android.settingslib.Utils
import com.android.settingslib.drawable.UserIconDrawable
import com.android.systemui.R
@@ -45,6 +46,19 @@ class FooterActionsView(context: Context?, attrs: AttributeSet?) : LinearLayout(
private var qsDisabled = false
private var expansionAmount = 0f
+ /**
+ * Sets the alpha of the background of this view.
+ *
+ * Used from a [TouchAnimator] in the controller.
+ */
+ var backgroundAlpha: Float = 1f
+ @Keep
+ set(value) {
+ field = value
+ background?.alpha = (value * 255).toInt()
+ }
+ @Keep get
+
override fun onFinishInflate() {
super.onFinishInflate()
settingsContainer = findViewById(R.id.settings_button_container)
@@ -117,4 +131,4 @@ class FooterActionsView(context: Context?, attrs: AttributeSet?) : LinearLayout(
private const val TAG = "FooterActionsView"
private val VERBOSE = Log.isLoggable(TAG, Log.VERBOSE)
private val MotionEvent.string
- get() = "($id): ($x,$y)" \ No newline at end of file
+ get() = "($id): ($x,$y)"
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 24168089f77a..7a98081fd1b6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -557,9 +557,9 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
public void setQsExpansion(float expansion, float panelExpansionFraction,
float proposedTranslation, float squishinessFraction) {
float headerTranslation = mTransitioningToFullShade ? 0 : proposedTranslation;
- float progress = mTransitioningToFullShade || mState == StatusBarState.KEYGUARD
+ float alphaProgress = mTransitioningToFullShade || mState == StatusBarState.KEYGUARD
? mFullShadeProgress : panelExpansionFraction;
- setAlphaAnimationProgress(mInSplitShade ? progress : 1);
+ setAlphaAnimationProgress(mInSplitShade ? alphaProgress : 1);
mContainer.setExpansion(expansion);
final float translationScaleY = (mInSplitShade
? 1 : QSAnimator.SHORT_PARALLAX_AMOUNT) * (expansion - 1);
@@ -600,7 +600,9 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
}
mQSPanelController.setIsOnKeyguard(onKeyguard);
mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion);
- mQSFooterActionController.setExpansion(onKeyguardAndExpanded ? 1 : expansion);
+ float footerActionsExpansion =
+ onKeyguardAndExpanded ? 1 : mInSplitShade ? alphaProgress : expansion;
+ mQSFooterActionController.setExpansion(footerActionsExpansion);
mQSPanelController.setRevealExpansion(expansion);
mQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation);
mQuickQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 324c01959084..7155626a1aa1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -128,6 +128,8 @@ public class QSPanel extends LinearLayout implements Tunable {
if (mUsingMediaPlayer) {
mHorizontalLinearLayout = new RemeasuringLinearLayout(mContext);
mHorizontalLinearLayout.setOrientation(LinearLayout.HORIZONTAL);
+ mHorizontalLinearLayout.setVisibility(
+ mUsingHorizontalLayout ? View.VISIBLE : View.GONE);
mHorizontalLinearLayout.setClipChildren(false);
mHorizontalLinearLayout.setClipToPadding(false);
@@ -445,6 +447,8 @@ public class QSPanel extends LinearLayout implements Tunable {
mMediaHostView = hostView;
ViewGroup newParent = horizontal ? mHorizontalLinearLayout : this;
ViewGroup currentParent = (ViewGroup) hostView.getParent();
+ Log.d(getDumpableTag(), "Reattaching media host: " + horizontal
+ + ", current " + currentParent + ", new " + newParent);
if (currentParent != newParent) {
if (currentParent != null) {
currentParent.removeView(hostView);
@@ -589,6 +593,7 @@ public class QSPanel extends LinearLayout implements Tunable {
void setUsingHorizontalLayout(boolean horizontal, ViewGroup mediaHostView, boolean force) {
if (horizontal != mUsingHorizontalLayout || force) {
+ Log.d(getDumpableTag(), "setUsingHorizontalLayout: " + horizontal + ", " + force);
mUsingHorizontalLayout = horizontal;
ViewGroup newParent = horizontal ? mHorizontalContentContainer : this;
switchAllContentToParent(newParent, mTileLayout);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index ec61ea6fffa8..6d5f844667e7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -88,6 +88,8 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
public void onConfigurationChange(Configuration newConfig) {
mShouldUseSplitNotificationShade =
LargeScreenUtils.shouldUseSplitNotificationShade(getResources());
+ mQSLogger.logOnConfigurationChanged(mLastOrientation, newConfig.orientation,
+ mView.getDumpableTag());
onConfigurationChanged();
if (newConfig.orientation != mLastOrientation) {
mLastOrientation = newConfig.orientation;
@@ -164,6 +166,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
mHost.addCallback(mQSHostCallback);
setTiles();
mLastOrientation = getResources().getConfiguration().orientation;
+ mQSLogger.logOnViewAttached(mLastOrientation, mView.getDumpableTag());
switchTileLayout(true);
mDumpManager.registerDumpable(mView.getDumpableTag(), this);
@@ -171,6 +174,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
@Override
protected void onViewDetached() {
+ mQSLogger.logOnViewDetached(mLastOrientation, mView.getDumpableTag());
mView.removeOnConfigurationChangedListener(mOnConfigurationChangedListener);
mHost.removeCallback(mQSHostCallback);
@@ -325,6 +329,8 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
/* Whether or not the panel currently contains a media player. */
boolean horizontal = shouldUseHorizontalLayout();
if (horizontal != mUsingHorizontalLayout || force) {
+ mQSLogger.logSwitchTileLayout(horizontal, mUsingHorizontalLayout, force,
+ mView.getDumpableTag());
mUsingHorizontalLayout = horizontal;
mView.setUsingHorizontalLayout(mUsingHorizontalLayout, mMediaHost.getHostView(), force);
updateMediaDisappearParameters();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 77652c9ddf2f..ac46c85c10a4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -29,6 +29,7 @@ import android.util.Log;
import androidx.annotation.MainThread;
import androidx.annotation.Nullable;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.logging.UiEventLogger;
@@ -47,6 +48,7 @@ import com.android.systemui.qs.external.TileLifecycleManager;
import com.android.systemui.qs.external.TileServiceKey;
import com.android.systemui.qs.external.TileServiceRequestController;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.settings.UserFileManager;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.phone.AutoTileManager;
@@ -88,6 +90,10 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
public static final int POSITION_AT_END = -1;
public static final String TILES_SETTING = Secure.QS_TILES;
+ // Shared prefs that hold tile lifecycle info.
+ @VisibleForTesting
+ static final String TILES = "tiles_prefs";
+
private final Context mContext;
private final LinkedHashMap<String, QSTile> mTiles = new LinkedHashMap<>();
protected final ArrayList<String> mTileSpecs = new ArrayList<>();
@@ -99,6 +105,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
private final InstanceIdSequence mInstanceIdSequence;
private final CustomTileStatePersister mCustomTileStatePersister;
private final Executor mMainExecutor;
+ private final UserFileManager mUserFileManager;
private final List<Callback> mCallbacks = new ArrayList<>();
@Nullable
@@ -135,7 +142,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
SecureSettings secureSettings,
CustomTileStatePersister customTileStatePersister,
TileServiceRequestController.Builder tileServiceRequestControllerBuilder,
- TileLifecycleManager.Factory tileLifecycleManagerFactory
+ TileLifecycleManager.Factory tileLifecycleManagerFactory,
+ UserFileManager userFileManager
) {
mIconController = iconController;
mContext = context;
@@ -148,6 +156,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
mMainExecutor = mainExecutor;
mTileServiceRequestController = tileServiceRequestControllerBuilder.create(this);
mTileLifeCycleManagerFactory = tileLifecycleManagerFactory;
+ mUserFileManager = userFileManager;
mInstanceIdSequence = new InstanceIdSequence(MAX_QS_INSTANCE_ID);
mCentralSurfacesOptional = centralSurfacesOptional;
@@ -392,6 +401,11 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
*/
@Override
public void removeTile(String spec) {
+ if (spec.startsWith(CustomTile.PREFIX)) {
+ // If the tile is removed (due to it not actually existing), mark it as removed. That
+ // way it will be marked as newly added if it appears in the future.
+ setTileAdded(CustomTile.getComponentFromSpec(spec), mCurrentUser, false);
+ }
mMainExecutor.execute(() -> changeTileSpecs(tileSpecs-> tileSpecs.remove(spec)));
}
@@ -515,7 +529,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
lifecycleManager.onStopListening();
lifecycleManager.onTileRemoved();
mCustomTileStatePersister.removeState(new TileServiceKey(component, mCurrentUser));
- TileLifecycleManager.setTileAdded(mContext, component, false);
+ setTileAdded(component, mCurrentUser, false);
lifecycleManager.flushMessagesAndUnbind();
}
}
@@ -552,6 +566,36 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
throw new RuntimeException("Default factory didn't create view for " + tile.getTileSpec());
}
+ /**
+ * Check if a particular {@link CustomTile} has been added for a user and has not been removed
+ * since.
+ * @param componentName the {@link ComponentName} of the
+ * {@link android.service.quicksettings.TileService} associated with the
+ * tile.
+ * @param userId the user to check
+ */
+ public boolean isTileAdded(ComponentName componentName, int userId) {
+ return mUserFileManager
+ .getSharedPreferences(TILES, 0, userId)
+ .getBoolean(componentName.flattenToString(), false);
+ }
+
+ /**
+ * Persists whether a particular {@link CustomTile} has been added and it's currently in the
+ * set of selected tiles ({@link #mTiles}.
+ * @param componentName the {@link ComponentName} of the
+ * {@link android.service.quicksettings.TileService} associated
+ * with the tile.
+ * @param userId the user for this tile
+ * @param added {@code true} if the tile is being added, {@code false} otherwise
+ */
+ public void setTileAdded(ComponentName componentName, int userId, boolean added) {
+ mUserFileManager.getSharedPreferences(TILES, 0, userId)
+ .edit()
+ .putBoolean(componentName.flattenToString(), added)
+ .apply();
+ }
+
protected static List<String> loadTileSpecs(Context context, String tileList) {
final Resources res = context.getResources();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index a49d3fd16591..3e445ddfc2a1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -127,6 +127,10 @@ public class TileLifecycleManager extends BroadcastReceiver implements
TileLifecycleManager create(Intent intent, UserHandle userHandle);
}
+ public int getUserId() {
+ return mUser.getIdentifier();
+ }
+
public ComponentName getComponent() {
return mIntent.getComponent();
}
@@ -507,13 +511,4 @@ public class TileLifecycleManager extends BroadcastReceiver implements
public interface TileChangeListener {
void onTileChanged(ComponentName tile);
}
-
- public static boolean isTileAdded(Context context, ComponentName component) {
- return context.getSharedPreferences(TILES, 0).getBoolean(component.flattenToString(), false);
- }
-
- public static void setTileAdded(Context context, ComponentName component, boolean added) {
- context.getSharedPreferences(TILES, 0).edit().putBoolean(component.flattenToString(),
- added).commit();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
index cfc57db2eeb8..e86bd7a30490 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
@@ -109,9 +109,9 @@ public class TileServiceManager {
void startLifecycleManagerAndAddTile() {
mStarted = true;
ComponentName component = mStateManager.getComponent();
- Context context = mServices.getContext();
- if (!TileLifecycleManager.isTileAdded(context, component)) {
- TileLifecycleManager.setTileAdded(context, component, true);
+ final int userId = mStateManager.getUserId();
+ if (!mServices.getHost().isTileAdded(component, userId)) {
+ mServices.getHost().setTileAdded(component, userId, true);
mStateManager.onTileAdded();
mStateManager.flushMessagesAndUnbind();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
index ab795faf57e6..948fb1428780 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
@@ -155,6 +155,54 @@ class QSLogger @Inject constructor(
})
}
+ fun logOnViewAttached(orientation: Int, containerName: String) {
+ log(DEBUG, {
+ str1 = containerName
+ int1 = orientation
+ }, {
+ "onViewAttached: $str1 orientation $int1"
+ })
+ }
+
+ fun logOnViewDetached(orientation: Int, containerName: String) {
+ log(DEBUG, {
+ str1 = containerName
+ int1 = orientation
+ }, {
+ "onViewDetached: $str1 orientation $int1"
+ })
+ }
+
+ fun logOnConfigurationChanged(
+ lastOrientation: Int,
+ newOrientation: Int,
+ containerName: String
+ ) {
+ log(DEBUG, {
+ str1 = containerName
+ int1 = lastOrientation
+ int2 = newOrientation
+ }, {
+ "configuration change: $str1 orientation was $int1, now $int2"
+ })
+ }
+
+ fun logSwitchTileLayout(
+ after: Boolean,
+ before: Boolean,
+ force: Boolean,
+ containerName: String
+ ) {
+ log(DEBUG, {
+ str1 = containerName
+ bool1 = after
+ bool2 = before
+ bool3 = force
+ }, {
+ "change tile layout: $str1 horizontal=$bool1 (was $bool2), force? $bool3"
+ })
+ }
+
private fun toStateString(state: Int): String {
return when (state) {
Tile.STATE_ACTIVE -> "active"
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/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
index 13a5615a8b54..2a467763951c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
@@ -6,7 +6,11 @@ import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.view.WindowInsets
import androidx.annotation.VisibleForTesting
import androidx.constraintlayout.widget.ConstraintSet
-import androidx.constraintlayout.widget.ConstraintSet.*
+import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
+import androidx.constraintlayout.widget.ConstraintSet.END
+import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
+import androidx.constraintlayout.widget.ConstraintSet.START
+import androidx.constraintlayout.widget.ConstraintSet.TOP
import com.android.systemui.R
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
@@ -171,33 +175,23 @@ class NotificationsQSContainerController @Inject constructor(
private fun calculateBottomSpacing(): Paddings {
val containerPadding: Int
- var stackScrollMargin = notificationsBottomMargin
- if (splitShadeEnabled) {
- if (isGestureNavigation) {
- // only default cutout padding, taskbar always hides
- containerPadding = bottomCutoutInsets
- } else if (taskbarVisible) {
- // navigation buttons + visible taskbar means we're NOT on homescreen
- containerPadding = bottomStableInsets
- } else {
- // navigation buttons + hidden taskbar means we're on homescreen
- containerPadding = 0
- // we need extra margin for notifications as navigation buttons are below them
- stackScrollMargin = bottomStableInsets + notificationsBottomMargin
- }
+ val stackScrollMargin: Int
+ if (!splitShadeEnabled && (isQSCustomizing || isQSDetailShowing)) {
+ // Clear out bottom paddings/margins so the qs customization can be full height.
+ containerPadding = 0
+ stackScrollMargin = 0
+ } else if (isGestureNavigation) {
+ // only default cutout padding, taskbar always hides
+ containerPadding = bottomCutoutInsets
+ stackScrollMargin = notificationsBottomMargin
+ } else if (taskbarVisible) {
+ // navigation buttons + visible taskbar means we're NOT on homescreen
+ containerPadding = bottomStableInsets
+ stackScrollMargin = notificationsBottomMargin
} else {
- if (isQSCustomizing || isQSDetailShowing) {
- // Clear out bottom paddings/margins so the qs customization can be full height.
- containerPadding = 0
- stackScrollMargin = 0
- } else if (isGestureNavigation) {
- containerPadding = bottomCutoutInsets
- } else if (taskbarVisible) {
- containerPadding = bottomStableInsets
- } else {
- containerPadding = 0
- stackScrollMargin = bottomStableInsets + notificationsBottomMargin
- }
+ // navigation buttons + hidden taskbar means we're on homescreen
+ containerPadding = 0
+ stackScrollMargin = bottomStableInsets + notificationsBottomMargin
}
val qsContainerPadding = if (!(isQSCustomizing || isQSDetailShowing)) {
// We also want this padding in the bottom in these cases
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index decc02c98b61..0898d6329f75 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -36,7 +36,7 @@ import java.util.stream.Stream;
* remove notifications that appear on screen for a period of time and dismiss themselves at the
* appropriate time. These include heads up notifications and ambient pulses.
*/
-public abstract class AlertingNotificationManager implements NotificationLifetimeExtender {
+public abstract class AlertingNotificationManager {
private static final String TAG = "AlertNotifManager";
protected final Clock mClock = new Clock();
protected final ArrayMap<String, AlertEntry> mAlertEntries = new ArrayMap<>();
@@ -47,14 +47,6 @@ public abstract class AlertingNotificationManager implements NotificationLifetim
mHandler = handler;
}
- /**
- * This is the list of entries that have already been removed from the
- * NotificationManagerService side, but we keep it to prevent the UI from looking weird and
- * will remove when possible. See {@link NotificationLifetimeExtender}
- */
- protected final ArraySet<NotificationEntry> mExtendedLifetimeAlertEntries = new ArraySet<>();
-
- protected NotificationSafeToRemoveCallback mNotificationLifetimeFinishedCallback;
protected int mMinimumDisplayTime;
protected int mAutoDismissNotificationDecay;
private final Handler mHandler;
@@ -209,12 +201,6 @@ public abstract class AlertingNotificationManager implements NotificationLifetim
onAlertEntryRemoved(alertEntry);
entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
alertEntry.reset();
- if (mExtendedLifetimeAlertEntries.contains(entry)) {
- if (mNotificationLifetimeFinishedCallback != null) {
- mNotificationLifetimeFinishedCallback.onSafeToRemove(key);
- }
- mExtendedLifetimeAlertEntries.remove(entry);
- }
}
/**
@@ -243,19 +229,6 @@ public abstract class AlertingNotificationManager implements NotificationLifetim
|| alertEntry.mEntry.isRowDismissed();
}
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // NotificationLifetimeExtender Methods
-
- @Override
- public void setCallback(NotificationSafeToRemoveCallback callback) {
- mNotificationLifetimeFinishedCallback = callback;
- }
-
- @Override
- public boolean shouldExtendLifetime(NotificationEntry entry) {
- return !canRemoveImmediately(entry.getKey());
- }
-
/**
* @param key
* @return true if the entry is pinned
@@ -280,20 +253,6 @@ public abstract class AlertingNotificationManager implements NotificationLifetim
return 0;
}
- @Override
- public void setShouldManageLifetime(NotificationEntry entry, boolean shouldExtend) {
- if (shouldExtend) {
- mExtendedLifetimeAlertEntries.add(entry);
- // We need to make sure that entries are stopping to alert eventually, let's remove
- // this as soon as possible.
- AlertEntry alertEntry = mAlertEntries.get(entry.getKey());
- alertEntry.removeAsSoonAsPossible();
- } else {
- mExtendedLifetimeAlertEntries.remove(entry);
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////
-
protected class AlertEntry implements Comparable<AlertEntry> {
@Nullable public NotificationEntry mEntry;
public long mPostTime;
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/NotificationLifetimeExtender.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java
deleted file mode 100644
index 48e2923c97d9..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package com.android.systemui.statusbar;
-
-import androidx.annotation.NonNull;
-
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-
-/**
- * Interface for anything that may need to keep notifications managed even after
- * {@link NotificationListener} removes it. The lifetime extender is in charge of performing the
- * callback when the notification is then safe to remove.
- */
-public interface NotificationLifetimeExtender {
-
- /**
- * Set the handler to callback to when the notification is safe to remove.
- *
- * @param callback the handler to callback
- */
- void setCallback(@NonNull NotificationSafeToRemoveCallback callback);
-
- /**
- * Determines whether or not the extender needs the notification kept after removal.
- *
- * @param entry the entry containing the notification to check
- * @return true if the notification lifetime should be extended
- */
- boolean shouldExtendLifetime(@NonNull NotificationEntry entry);
-
- /**
- * It's possible that a notification was canceled before it ever became visible. This callback
- * gives lifetime extenders a chance to make sure it shows up. For example if a foreground
- * service is canceled too quickly but we still want to make sure a FGS notification shows.
- * @param pendingEntry the canceled (but pending) entry
- * @return true if the notification lifetime should be extended
- */
- default boolean shouldExtendLifetimeForPendingNotification(
- @NonNull NotificationEntry pendingEntry) {
- return false;
- }
-
- /**
- * Sets whether or not the lifetime should be managed by the extender. In practice, if
- * shouldManage is true, this is where the extender starts managing the entry internally and is
- * now responsible for calling {@link NotificationSafeToRemoveCallback#onSafeToRemove(String)}
- * when the entry is safe to remove. If shouldManage is false, the extender no longer needs to
- * worry about it (either because we will be removing it anyway or the entry is no longer
- * removed due to an update).
- *
- * @param entry the entry that needs an extended lifetime
- * @param shouldManage true if the extender should manage the entry now, false otherwise
- */
- void setShouldManageLifetime(@NonNull NotificationEntry entry, boolean shouldManage);
-
- /**
- * The callback for when the notification is now safe to remove (i.e. its lifetime has ended).
- */
- interface NotificationSafeToRemoveCallback {
- /**
- * Called when the lifetime extender determines it's safe to remove.
- *
- * @param key key of the entry that is now safe to remove
- */
- void onSafeToRemove(String key);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 78b3b0a65351..d74d408ae59e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -24,7 +24,6 @@ import android.app.RemoteInput;
import android.content.Context;
import android.content.Intent;
import android.content.pm.UserInfo;
-import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -32,7 +31,6 @@ import android.os.SystemProperties;
import android.os.UserManager;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
-import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Pair;
@@ -47,12 +45,10 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
-import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.dagger.CentralSurfacesDependenciesModule;
@@ -75,7 +71,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
-import java.util.Set;
import java.util.function.Consumer;
import dagger.Lazy;
@@ -100,8 +95,6 @@ public class NotificationRemoteInputManager implements Dumpable {
private final NotificationLockscreenUserManager mLockscreenUserManager;
private final SmartReplyController mSmartReplyController;
private final NotificationVisibilityProvider mVisibilityProvider;
- private final NotificationEntryManager mEntryManager;
- private final Handler mMainHandler;
private final ActionClickLogger mLogger;
private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
@@ -110,7 +103,6 @@ public class NotificationRemoteInputManager implements Dumpable {
protected final NotifPipelineFlags mNotifPipelineFlags;
private final UserManager mUserManager;
private final KeyguardManager mKeyguardManager;
- private final RemoteInputNotificationRebuilder mRebuilder;
private final StatusBarStateController mStatusBarStateController;
private final RemoteInputUriController mRemoteInputUriController;
private final NotificationClickNotifier mClickNotifier;
@@ -265,10 +257,8 @@ public class NotificationRemoteInputManager implements Dumpable {
SmartReplyController smartReplyController,
NotificationVisibilityProvider visibilityProvider,
NotificationEntryManager notificationEntryManager,
- RemoteInputNotificationRebuilder rebuilder,
Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
StatusBarStateController statusBarStateController,
- @Main Handler mainHandler,
RemoteInputUriController remoteInputUriController,
NotificationClickNotifier clickNotifier,
ActionClickLogger logger,
@@ -278,14 +268,11 @@ public class NotificationRemoteInputManager implements Dumpable {
mLockscreenUserManager = lockscreenUserManager;
mSmartReplyController = smartReplyController;
mVisibilityProvider = visibilityProvider;
- mEntryManager = notificationEntryManager;
mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
- mMainHandler = mainHandler;
mLogger = logger;
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- mRebuilder = rebuilder;
mKeyguardManager = context.getSystemService(KeyguardManager.class);
mStatusBarStateController = statusBarStateController;
mRemoteInputUriController = remoteInputUriController;
@@ -788,238 +775,4 @@ public class NotificationRemoteInputManager implements Dumpable {
/** Called when the RemoteInputController is attached to the manager */
void setRemoteInputController(@NonNull RemoteInputController remoteInputController);
}
-
- @VisibleForTesting
- protected class LegacyRemoteInputLifetimeExtender implements RemoteInputListener, Dumpable {
-
- /**
- * How long to wait before auto-dismissing a notification that was kept for remote input,
- * and has now sent a remote input. We auto-dismiss, because the app may not see a reason to
- * cancel these given that they technically don't exist anymore. We wait a bit in case the
- * app issues an update.
- */
- private static final int REMOTE_INPUT_KEPT_ENTRY_AUTO_CANCEL_DELAY = 200;
-
- /**
- * Notifications that are already removed but are kept around because we want to show the
- * remote input history. See {@link RemoteInputHistoryExtender} and
- * {@link SmartReplyHistoryExtender}.
- */
- protected final ArraySet<String> mKeysKeptForRemoteInputHistory = new ArraySet<>();
-
- /**
- * Notifications that are already removed but are kept around because the remote input is
- * actively being used (i.e. user is typing in it). See {@link RemoteInputActiveExtender}.
- */
- protected final ArraySet<NotificationEntry> mEntriesKeptForRemoteInputActive =
- new ArraySet<>();
-
- protected NotificationLifetimeExtender.NotificationSafeToRemoveCallback
- mNotificationLifetimeFinishedCallback;
-
- protected final ArrayList<NotificationLifetimeExtender> mLifetimeExtenders =
- new ArrayList<>();
- private RemoteInputController mRemoteInputController;
-
- LegacyRemoteInputLifetimeExtender() {
- addLifetimeExtenders();
- }
-
- /**
- * Adds all the notification lifetime extenders. Each extender represents a reason for the
- * NotificationRemoteInputManager to keep a notification lifetime extended.
- */
- protected void addLifetimeExtenders() {
- mLifetimeExtenders.add(new RemoteInputHistoryExtender());
- mLifetimeExtenders.add(new SmartReplyHistoryExtender());
- mLifetimeExtenders.add(new RemoteInputActiveExtender());
- }
-
- @Override
- public void setRemoteInputController(@NonNull RemoteInputController remoteInputController) {
- mRemoteInputController= remoteInputController;
- }
-
- @Override
- public void onRemoteInputSent(@NonNull NotificationEntry entry) {
- if (FORCE_REMOTE_INPUT_HISTORY
- && isNotificationKeptForRemoteInputHistory(entry.getKey())) {
- mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.getKey());
- } else if (mEntriesKeptForRemoteInputActive.contains(entry)) {
- // We're currently holding onto this notification, but from the apps point of
- // view it is already canceled, so we'll need to cancel it on the apps behalf
- // after sending - unless the app posts an update in the mean time, so wait a
- // bit.
- mMainHandler.postDelayed(() -> {
- if (mEntriesKeptForRemoteInputActive.remove(entry)) {
- mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.getKey());
- }
- }, REMOTE_INPUT_KEPT_ENTRY_AUTO_CANCEL_DELAY);
- }
- }
-
- @Override
- public void onPanelCollapsed() {
- for (int i = 0; i < mEntriesKeptForRemoteInputActive.size(); i++) {
- NotificationEntry entry = mEntriesKeptForRemoteInputActive.valueAt(i);
- if (mRemoteInputController != null) {
- mRemoteInputController.removeRemoteInput(entry, null);
- }
- if (mNotificationLifetimeFinishedCallback != null) {
- mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.getKey());
- }
- }
- mEntriesKeptForRemoteInputActive.clear();
- }
-
- @Override
- public boolean isNotificationKeptForRemoteInputHistory(@NonNull String key) {
- return mKeysKeptForRemoteInputHistory.contains(key);
- }
-
- @Override
- public void releaseNotificationIfKeptForRemoteInputHistory(
- @NonNull NotificationEntry entry) {
- final String key = entry.getKey();
- if (isNotificationKeptForRemoteInputHistory(key)) {
- mMainHandler.postDelayed(() -> {
- if (isNotificationKeptForRemoteInputHistory(key)) {
- mNotificationLifetimeFinishedCallback.onSafeToRemove(key);
- }
- }, REMOTE_INPUT_KEPT_ENTRY_AUTO_CANCEL_DELAY);
- }
- }
-
- @VisibleForTesting
- public Set<NotificationEntry> getEntriesKeptForRemoteInputActive() {
- return mEntriesKeptForRemoteInputActive;
- }
-
- @Override
- public void dump(@NonNull PrintWriter pw,
- @NonNull String[] args) {
- pw.println("LegacyRemoteInputLifetimeExtender:");
- pw.print(" mKeysKeptForRemoteInputHistory: ");
- pw.println(mKeysKeptForRemoteInputHistory);
- pw.print(" mEntriesKeptForRemoteInputActive: ");
- pw.println(mEntriesKeptForRemoteInputActive);
- }
-
- /**
- * NotificationRemoteInputManager has multiple reasons to keep notification lifetime
- * extended so we implement multiple NotificationLifetimeExtenders
- */
- protected abstract class RemoteInputExtender implements NotificationLifetimeExtender {
- @Override
- public void setCallback(NotificationSafeToRemoveCallback callback) {
- if (mNotificationLifetimeFinishedCallback == null) {
- mNotificationLifetimeFinishedCallback = callback;
- }
- }
- }
-
- /**
- * Notification is kept alive as it was cancelled in response to a remote input interaction.
- * This allows us to show what you replied and allows you to continue typing into it.
- */
- protected class RemoteInputHistoryExtender extends RemoteInputExtender {
- @Override
- public boolean shouldExtendLifetime(@NonNull NotificationEntry entry) {
- return shouldKeepForRemoteInputHistory(entry);
- }
-
- @Override
- public void setShouldManageLifetime(NotificationEntry entry,
- boolean shouldExtend) {
- if (shouldExtend) {
- StatusBarNotification newSbn = mRebuilder.rebuildForRemoteInputReply(entry);
- entry.onRemoteInputInserted();
-
- if (newSbn == null) {
- return;
- }
-
- mEntryManager.updateNotification(newSbn, null);
-
- // Ensure the entry hasn't already been removed. This can happen if there is an
- // inflation exception while updating the remote history
- if (entry.isRemoved()) {
- return;
- }
-
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Keeping notification around after sending remote input "
- + entry.getKey());
- }
-
- mKeysKeptForRemoteInputHistory.add(entry.getKey());
- } else {
- mKeysKeptForRemoteInputHistory.remove(entry.getKey());
- }
- }
- }
-
- /**
- * Notification is kept alive for smart reply history. Similar to REMOTE_INPUT_HISTORY but
- * with {@link SmartReplyController} specific logic
- */
- protected class SmartReplyHistoryExtender extends RemoteInputExtender {
- @Override
- public boolean shouldExtendLifetime(@NonNull NotificationEntry entry) {
- return shouldKeepForSmartReplyHistory(entry);
- }
-
- @Override
- public void setShouldManageLifetime(NotificationEntry entry,
- boolean shouldExtend) {
- if (shouldExtend) {
- StatusBarNotification newSbn = mRebuilder.rebuildForCanceledSmartReplies(entry);
-
- if (newSbn == null) {
- return;
- }
-
- mEntryManager.updateNotification(newSbn, null);
-
- if (entry.isRemoved()) {
- return;
- }
-
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Keeping notification around after sending smart reply "
- + entry.getKey());
- }
-
- mKeysKeptForRemoteInputHistory.add(entry.getKey());
- } else {
- mKeysKeptForRemoteInputHistory.remove(entry.getKey());
- mSmartReplyController.stopSending(entry);
- }
- }
- }
-
- /**
- * Notification is kept alive because the user is still using the remote input
- */
- protected class RemoteInputActiveExtender extends RemoteInputExtender {
- @Override
- public boolean shouldExtendLifetime(@NonNull NotificationEntry entry) {
- return isRemoteInputActive(entry);
- }
-
- @Override
- public void setShouldManageLifetime(NotificationEntry entry,
- boolean shouldExtend) {
- if (shouldExtend) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Keeping notification around while remote input active "
- + entry.getKey());
- }
- mEntriesKeptForRemoteInputActive.add(entry);
- } else {
- mEntriesKeptForRemoteInputActive.remove(entry);
- }
- }
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index 0951e821cdc2..8752f92145db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -115,10 +115,8 @@ public interface CentralSurfacesDependenciesModule {
smartReplyController,
visibilityProvider,
notificationEntryManager,
- rebuilder,
centralSurfacesOptionalLazy,
statusBarStateController,
- mainHandler,
remoteInputUriController,
clickNotifier,
actionClickLogger,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 7583a98df308..e2f87b6f4ffe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -15,66 +15,22 @@
*/
package com.android.systemui.statusbar.notification;
-import static android.service.notification.NotificationListenerService.REASON_ERROR;
-
-import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_UNKNOWN;
-import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback;
-
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.Trace;
-import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
-import android.service.notification.NotificationListenerService.Ranking;
-import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Log;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.statusbar.NotificationVisibility;
-import com.android.systemui.Dumpable;
-import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.statusbar.NotificationLifetimeExtender;
-import com.android.systemui.statusbar.NotificationListener;
-import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationRemoveInterceptor;
-import com.android.systemui.statusbar.NotificationUiAdjustment;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
-import com.android.systemui.statusbar.notification.collection.legacy.LegacyNotificationRanker;
-import com.android.systemui.statusbar.notification.collection.legacy.LegacyNotificationRankerStub;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
-import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.util.Assert;
-import com.android.systemui.util.Compile;
-import com.android.systemui.util.leak.LeakDetector;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Executor;
-
-import dagger.Lazy;
/**
* NotificationEntryManager is responsible for the adding, removing, and updating of
@@ -90,33 +46,10 @@ import dagger.Lazy;
* inflated, an entry moves into the active state, where it _could_ potentially be shown to the
* user. After an entry makes its way into the active state, we sort and filter the entire set to
* repopulate the visible set.
- *
- * There are a few different things that other classes may be interested in, and most of them
- * involve the current set of notifications. Here's a brief overview of things you may want to know:
- * @see #getVisibleNotifications() for the visible set
- * @see #getActiveNotificationUnfiltered(String) to check if a key exists
- * @see #getPendingNotificationsIterator() for an iterator over the pending notifications
- * @see #getPendingOrActiveNotif(String) to find a notification exists for that key in any list
- * @see #getActiveNotificationsForCurrentUser() to see every notification that the current user owns
*/
-public class NotificationEntryManager implements
- CommonNotifCollection,
- Dumpable,
- VisualStabilityManager.Callback {
+public class NotificationEntryManager implements VisualStabilityManager.Callback {
private final NotificationEntryManagerLogger mLogger;
- private final NotificationGroupManagerLegacy mGroupManager;
- private final NotifPipelineFlags mNotifPipelineFlags;
- private final Lazy<NotificationRowBinder> mNotificationRowBinderLazy;
- private final Lazy<NotificationRemoteInputManager> mRemoteInputManagerLazy;
- private final LeakDetector mLeakDetector;
- private final IStatusBarService mStatusBarService;
- private final DumpManager mDumpManager;
- private final Executor mBgExecutor;
-
- private final Set<NotificationEntry> mAllNotifications = new ArraySet<>();
- private final Set<NotificationEntry> mReadOnlyAllNotifications =
- Collections.unmodifiableSet(mAllNotifications);
/** Pending notifications are ones awaiting inflation */
@VisibleForTesting
@@ -129,96 +62,14 @@ public class NotificationEntryManager implements
/** This is the list of "active notifications for this user in this context" */
@VisibleForTesting
protected final ArrayList<NotificationEntry> mSortedAndFiltered = new ArrayList<>();
- private final List<NotificationEntry> mReadOnlyNotifications =
- Collections.unmodifiableList(mSortedAndFiltered);
-
- private final Map<NotificationEntry, NotificationLifetimeExtender> mRetainedNotifications =
- new ArrayMap<>();
-
private final List<NotifCollectionListener> mNotifCollectionListeners = new ArrayList<>();
-
- private LegacyNotificationRanker mRanker = new LegacyNotificationRankerStub();
- private RankingMap mLatestRankingMap;
-
- @VisibleForTesting
- final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
- = new ArrayList<>();
private final List<NotificationEntryListener> mNotificationEntryListeners = new ArrayList<>();
- private final List<NotificationRemoveInterceptor> mRemoveInterceptors = new ArrayList<>();
/**
* Injected constructor. See {@link NotificationsModule}.
*/
- public NotificationEntryManager(
- NotificationEntryManagerLogger logger,
- NotificationGroupManagerLegacy groupManager,
- NotifPipelineFlags notifPipelineFlags,
- Lazy<NotificationRowBinder> notificationRowBinderLazy,
- Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
- LeakDetector leakDetector,
- IStatusBarService statusBarService,
- DumpManager dumpManager,
- @Background Executor bgExecutor
- ) {
+ public NotificationEntryManager(NotificationEntryManagerLogger logger) {
mLogger = logger;
- mGroupManager = groupManager;
- mNotifPipelineFlags = notifPipelineFlags;
- mNotificationRowBinderLazy = notificationRowBinderLazy;
- mRemoteInputManagerLazy = notificationRemoteInputManagerLazy;
- mLeakDetector = leakDetector;
- mStatusBarService = statusBarService;
- mDumpManager = dumpManager;
- mBgExecutor = bgExecutor;
- }
-
- /** Once called, the NEM will start processing notification events from system server. */
- public void initialize(
- NotificationListener notificationListener,
- LegacyNotificationRanker ranker) {
- mRanker = ranker;
- notificationListener.addNotificationHandler(mNotifListener);
- mDumpManager.registerDumpable(this);
- }
-
- @Override
- public void dump(PrintWriter pw, String[] args) {
- pw.println("NotificationEntryManager state:");
- pw.println(" mAllNotifications=");
- if (mAllNotifications.size() == 0) {
- pw.println("null");
- } else {
- int i = 0;
- for (NotificationEntry entry : mAllNotifications) {
- dumpEntry(pw, " ", i, entry);
- i++;
- }
- }
- pw.print(" mPendingNotifications=");
- if (mPendingNotifications.size() == 0) {
- pw.println("null");
- } else {
- for (NotificationEntry entry : mPendingNotifications.values()) {
- pw.println(entry.getSbn());
- }
- }
- pw.println(" Remove interceptors registered:");
- for (NotificationRemoveInterceptor interceptor : mRemoveInterceptors) {
- pw.println(" " + interceptor.getClass().getSimpleName());
- }
- pw.println(" Lifetime extenders registered:");
- for (NotificationLifetimeExtender extender : mNotificationLifetimeExtenders) {
- pw.println(" " + extender.getClass().getSimpleName());
- }
- pw.println(" Lifetime-extended notifications:");
- if (mRetainedNotifications.isEmpty()) {
- pw.println(" None");
- } else {
- for (Map.Entry<NotificationEntry, NotificationLifetimeExtender> entry
- : mRetainedNotifications.entrySet()) {
- pw.println(" " + entry.getKey().getSbn() + " retained by "
- + entry.getValue().getClass().getName());
- }
- }
}
/** Adds a {@link NotificationEntryListener}. */
@@ -234,501 +85,12 @@ public class NotificationEntryManager implements
mNotificationEntryListeners.remove(listener);
}
- /** Add a {@link NotificationRemoveInterceptor}. */
- public void addNotificationRemoveInterceptor(NotificationRemoveInterceptor interceptor) {
- mRemoveInterceptors.add(interceptor);
- }
-
- /** Remove a {@link NotificationRemoveInterceptor} */
- public void removeNotificationRemoveInterceptor(NotificationRemoveInterceptor interceptor) {
- mRemoveInterceptors.remove(interceptor);
- }
-
- /** Adds multiple {@link NotificationLifetimeExtender}s. */
- public void addNotificationLifetimeExtenders(List<NotificationLifetimeExtender> extenders) {
- for (NotificationLifetimeExtender extender : extenders) {
- addNotificationLifetimeExtender(extender);
- }
- }
-
- /** Adds a {@link NotificationLifetimeExtender}. */
- public void addNotificationLifetimeExtender(NotificationLifetimeExtender extender) {
- mNotificationLifetimeExtenders.add(extender);
- extender.setCallback(key -> removeNotification(key, mLatestRankingMap,
- UNDEFINED_DISMISS_REASON));
- }
-
@Override
public void onChangeAllowed() {
updateNotifications("reordering is now allowed");
}
/**
- * User requests a notification to be removed.
- *
- * @param n the notification to remove.
- * @param reason why it is being removed e.g. {@link NotificationListenerService#REASON_CANCEL},
- * or 0 if unknown.
- */
- public void performRemoveNotification(
- StatusBarNotification n,
- @NonNull DismissedByUserStats stats,
- int reason
- ) {
- removeNotificationInternal(
- n.getKey(),
- null,
- stats.notificationVisibility,
- false /* forceRemove */,
- stats,
- reason);
- }
-
- private NotificationVisibility obtainVisibility(String key) {
- NotificationEntry e = mActiveNotifications.get(key);
- final int rank;
- if (e != null) {
- rank = e.getRanking().getRank();
- } else {
- rank = 0;
- }
-
- final int count = mActiveNotifications.size();
- NotificationVisibility.NotificationLocation location =
- NotificationLogger.getNotificationLocation(getActiveNotificationUnfiltered(key));
- return NotificationVisibility.obtain(key, rank, count, true, location);
- }
-
- private void abortExistingInflation(String key, String reason) {
- if (mPendingNotifications.containsKey(key)) {
- NotificationEntry entry = mPendingNotifications.get(key);
- entry.abortTask();
- mPendingNotifications.remove(key);
- mLogger.logInflationAborted(key, "pending", reason);
- }
- NotificationEntry addedEntry = getActiveNotificationUnfiltered(key);
- if (addedEntry != null) {
- addedEntry.abortTask();
- mLogger.logInflationAborted(key, "active", reason);
- }
- }
-
- /**
- * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
- * about the failure.
- *
- * WARNING: this will call back into us. Don't hold any locks.
- */
- private void handleInflationException(StatusBarNotification n, Exception e) {
- removeNotificationInternal(
- n.getKey(),
- null,
- null,
- true /* forceRemove */,
- null /* dismissedByUserStats */,
- REASON_ERROR);
- for (NotificationEntryListener listener : mNotificationEntryListeners) {
- listener.onInflationError(n, e);
- }
- }
-
- private final InflationCallback mInflationCallback = new InflationCallback() {
- @Override
- public void handleInflationException(NotificationEntry entry, Exception e) {
- Trace.beginSection("NotificationEntryManager.handleInflationException");
- NotificationEntryManager.this.handleInflationException(entry.getSbn(), e);
- Trace.endSection();
- }
-
- @Override
- public void onAsyncInflationFinished(NotificationEntry entry) {
- Trace.beginSection("NotificationEntryManager.onAsyncInflationFinished");
- mPendingNotifications.remove(entry.getKey());
- // If there was an async task started after the removal, we don't want to add it back to
- // the list, otherwise we might get leaks.
- if (!entry.isRowRemoved()) {
- boolean isNew = getActiveNotificationUnfiltered(entry.getKey()) == null;
- mLogger.logNotifInflated(entry.getKey(), isNew);
- if (isNew) {
- for (NotificationEntryListener listener : mNotificationEntryListeners) {
- listener.onEntryInflated(entry);
- }
- addActiveNotification(entry);
- updateNotifications("onAsyncInflationFinished");
- for (NotificationEntryListener listener : mNotificationEntryListeners) {
- listener.onNotificationAdded(entry);
- }
- } else {
- for (NotificationEntryListener listener : mNotificationEntryListeners) {
- listener.onEntryReinflated(entry);
- }
- }
- }
- Trace.endSection();
- }
- };
-
- private final NotificationHandler mNotifListener = new NotificationHandler() {
- @Override
- public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
- final boolean isUpdateToInflatedNotif = mActiveNotifications.containsKey(sbn.getKey());
- if (isUpdateToInflatedNotif) {
- updateNotification(sbn, rankingMap);
- } else {
- addNotification(sbn, rankingMap);
- }
- }
-
- @Override
- public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) {
- removeNotification(sbn.getKey(), rankingMap, UNDEFINED_DISMISS_REASON);
- }
-
- @Override
- public void onNotificationRemoved(
- StatusBarNotification sbn,
- RankingMap rankingMap,
- int reason) {
- removeNotification(sbn.getKey(), rankingMap, reason);
- }
-
- @Override
- public void onNotificationRankingUpdate(RankingMap rankingMap) {
- updateNotificationRanking(rankingMap);
- }
-
- @Override
- public void onNotificationsInitialized() {
- }
-
- @Override
- public void onNotificationChannelModified(
- String pkgName,
- UserHandle user,
- NotificationChannel channel,
- int modificationType) {
- notifyChannelModified(pkgName, user, channel, modificationType);
- }
- };
-
- /**
- * Equivalent to the old NotificationData#add
- * @param entry - an entry which is prepared for display
- */
- private void addActiveNotification(NotificationEntry entry) {
- Assert.isMainThread();
-
- mActiveNotifications.put(entry.getKey(), entry);
- mGroupManager.onEntryAdded(entry);
- updateRankingAndSort(mRanker.getRankingMap(), "addEntryInternalInternal");
- }
-
- /**
- * Available so that tests can directly manipulate the list of active notifications easily
- *
- * @param entry the entry to add directly to the visible notification map
- */
- @VisibleForTesting
- public void addActiveNotificationForTest(NotificationEntry entry) {
- mActiveNotifications.put(entry.getKey(), entry);
- mGroupManager.onEntryAdded(entry);
-
- reapplyFilterAndSort("addVisibleNotification");
- }
-
- @VisibleForTesting
- protected void removeNotification(String key, RankingMap ranking, int reason) {
- removeNotificationInternal(
- key,
- ranking,
- obtainVisibility(key),
- false /* forceRemove */,
- null /* dismissedByUserStats */,
- reason);
- }
-
- /**
- * Internally remove a notification because system server has reported the notification
- * should be removed OR the user has manually dismissed the notification
- * @param dismissedByUserStats non-null if the user manually dismissed the notification
- */
- private void removeNotificationInternal(
- String key,
- @Nullable RankingMap ranking,
- @Nullable NotificationVisibility visibility,
- boolean forceRemove,
- DismissedByUserStats dismissedByUserStats,
- int reason) {
- Trace.beginSection("NotificationEntryManager.removeNotificationInternal");
-
- final NotificationEntry entry = getActiveNotificationUnfiltered(key);
-
- for (NotificationRemoveInterceptor interceptor : mRemoveInterceptors) {
- if (interceptor.onNotificationRemoveRequested(key, entry, reason)) {
- // Remove intercepted; log and skip
- mLogger.logRemovalIntercepted(key);
- Trace.endSection();
- return;
- }
- }
-
- boolean lifetimeExtended = false;
-
- // Notification was canceled before it got inflated
- if (entry == null) {
- NotificationEntry pendingEntry = mPendingNotifications.get(key);
- if (pendingEntry != null) {
- for (NotificationLifetimeExtender extender : mNotificationLifetimeExtenders) {
- if (extender.shouldExtendLifetimeForPendingNotification(pendingEntry)) {
- extendLifetime(pendingEntry, extender);
- lifetimeExtended = true;
- mLogger.logLifetimeExtended(key, extender.getClass().getName(), "pending");
- }
- }
- if (!lifetimeExtended) {
- // At this point, we are guaranteed the notification will be removed
- abortExistingInflation(key, "removeNotification");
- // Fix for b/201097913: NotifCollectionListener#onEntryRemoved specifies that
- // #onEntryRemoved should be called when a notification is cancelled,
- // regardless of whether the notification was pending or active.
- // Note that mNotificationEntryListeners are NOT notified of #onEntryRemoved
- // because for that interface, #onEntryRemoved should only be called for
- // active entries, NOT pending ones.
- for (NotifCollectionListener listener : mNotifCollectionListeners) {
- listener.onEntryRemoved(pendingEntry, REASON_UNKNOWN);
- }
- for (NotifCollectionListener listener : mNotifCollectionListeners) {
- listener.onEntryCleanUp(pendingEntry);
- }
- mAllNotifications.remove(pendingEntry);
- mLeakDetector.trackGarbage(pendingEntry);
- }
- }
- } else {
- // If a manager needs to keep the notification around for whatever reason, we
- // keep the notification
- boolean entryDismissed = entry.isRowDismissed();
- if (!forceRemove && !entryDismissed) {
- for (NotificationLifetimeExtender extender : mNotificationLifetimeExtenders) {
- if (extender.shouldExtendLifetime(entry)) {
- mLatestRankingMap = ranking;
- extendLifetime(entry, extender);
- lifetimeExtended = true;
- mLogger.logLifetimeExtended(key, extender.getClass().getName(), "active");
- break;
- }
- }
- }
-
- if (!lifetimeExtended) {
- // At this point, we are guaranteed the notification will be removed
- abortExistingInflation(key, "removeNotification");
- mAllNotifications.remove(entry);
-
- // Ensure any managers keeping the lifetime extended stop managing the entry
- cancelLifetimeExtension(entry);
-
- if (entry.rowExists()) {
- entry.removeRow();
- }
-
- // Let's remove the children if this was a summary
- handleGroupSummaryRemoved(key);
- removeVisibleNotification(key);
- updateNotifications("removeNotificationInternal");
- final boolean removedByUser = dismissedByUserStats != null;
-
- mLogger.logNotifRemoved(entry.getKey(), removedByUser);
- if (removedByUser && visibility != null) {
- sendNotificationRemovalToServer(entry.getSbn(), dismissedByUserStats);
- }
- for (NotificationEntryListener listener : mNotificationEntryListeners) {
- listener.onEntryRemoved(entry, visibility, removedByUser, reason);
- }
- for (NotifCollectionListener listener : mNotifCollectionListeners) {
- // NEM doesn't have a good knowledge of reasons so defaulting to unknown.
- listener.onEntryRemoved(entry, REASON_UNKNOWN);
- }
- for (NotifCollectionListener listener : mNotifCollectionListeners) {
- listener.onEntryCleanUp(entry);
- }
- mLeakDetector.trackGarbage(entry);
- }
- }
- Trace.endSection();
- }
-
- private void sendNotificationRemovalToServer(
- StatusBarNotification notification,
- DismissedByUserStats dismissedByUserStats) {
- mBgExecutor.execute(() -> {
- try {
- mStatusBarService.onNotificationClear(
- notification.getPackageName(),
- notification.getUser().getIdentifier(),
- notification.getKey(),
- dismissedByUserStats.dismissalSurface,
- dismissedByUserStats.dismissalSentiment,
- dismissedByUserStats.notificationVisibility);
- } catch (RemoteException ex) {
- // system process is dead if we're here.
- }
- });
- }
-
- /**
- * Ensures that the group children are cancelled immediately when the group summary is cancelled
- * instead of waiting for the notification manager to send all cancels. Otherwise this could
- * lead to flickers.
- *
- * This also ensures that the animation looks nice and only consists of a single disappear
- * animation instead of multiple.
- * @param key the key of the notification was removed
- *
- */
- private void handleGroupSummaryRemoved(String key) {
- NotificationEntry entry = getActiveNotificationUnfiltered(key);
- if (entry != null && entry.rowExists() && entry.isSummaryWithChildren()) {
- if (entry.getSbn().getOverrideGroupKey() != null && !entry.isRowDismissed()) {
- // We don't want to remove children for autobundled notifications as they are not
- // always cancelled. We only remove them if they were dismissed by the user.
- return;
- }
- List<NotificationEntry> childEntries = entry.getAttachedNotifChildren();
- if (childEntries == null) {
- return;
- }
- for (int i = 0; i < childEntries.size(); i++) {
- NotificationEntry childEntry = childEntries.get(i);
- boolean isForeground = (entry.getSbn().getNotification().flags
- & Notification.FLAG_FOREGROUND_SERVICE) != 0;
- boolean keepForReply =
- mRemoteInputManagerLazy.get().shouldKeepForRemoteInputHistory(childEntry)
- || mRemoteInputManagerLazy.get().shouldKeepForSmartReplyHistory(childEntry);
- if (isForeground || keepForReply) {
- // the child is a foreground service notification which we can't remove or it's
- // a child we're keeping around for reply!
- continue;
- }
- childEntry.setKeepInParent(true);
- // we need to set this state earlier as otherwise we might generate some weird
- // animations
- childEntry.removeRow();
- }
- }
- }
-
- private void addNotificationInternal(
- StatusBarNotification notification,
- RankingMap rankingMap) throws InflationException {
- Trace.beginSection("NotificationEntryManager.addNotificationInternal");
- String key = notification.getKey();
- if (DEBUG) {
- Log.d(TAG, "addNotification key=" + key);
- }
-
- updateRankingAndSort(rankingMap, "addNotificationInternal");
-
- Ranking ranking = new Ranking();
- rankingMap.getRanking(key, ranking);
-
- NotificationEntry entry = mPendingNotifications.get(key);
- if (entry != null) {
- entry.setSbn(notification);
- entry.setRanking(ranking);
- } else {
- entry = new NotificationEntry(
- notification,
- ranking,
- SystemClock.uptimeMillis());
- mAllNotifications.add(entry);
- mLeakDetector.trackInstance(entry);
-
- for (NotifCollectionListener listener : mNotifCollectionListeners) {
- listener.onEntryInit(entry);
- }
- }
-
- for (NotifCollectionListener listener : mNotifCollectionListeners) {
- listener.onEntryBind(entry, notification);
- }
-
- mPendingNotifications.put(key, entry);
- mLogger.logNotifAdded(entry.getKey());
- for (NotificationEntryListener listener : mNotificationEntryListeners) {
- listener.onPendingEntryAdded(entry);
- }
- for (NotifCollectionListener listener : mNotifCollectionListeners) {
- listener.onEntryAdded(entry);
- }
- for (NotifCollectionListener listener : mNotifCollectionListeners) {
- listener.onRankingApplied();
- }
- Trace.endSection();
- }
-
- public void addNotification(StatusBarNotification notification, RankingMap ranking) {
- try {
- addNotificationInternal(notification, ranking);
- } catch (InflationException e) {
- handleInflationException(notification, e);
- }
- }
-
- private void updateNotificationInternal(StatusBarNotification notification,
- RankingMap ranking) throws InflationException {
- Trace.beginSection("NotificationEntryManager.updateNotificationInternal");
- if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
-
- final String key = notification.getKey();
- abortExistingInflation(key, "updateNotification");
- final NotificationEntry entry = getActiveNotificationUnfiltered(key);
- if (entry == null) {
- Trace.endSection();
- return;
- }
-
- // Notification is updated so it is essentially re-added and thus alive again. Don't need
- // to keep its lifetime extended.
- cancelLifetimeExtension(entry);
-
- updateRankingAndSort(ranking, "updateNotificationInternal");
- StatusBarNotification oldSbn = entry.getSbn();
- entry.setSbn(notification);
- for (NotifCollectionListener listener : mNotifCollectionListeners) {
- listener.onEntryBind(entry, notification);
- }
- mGroupManager.onEntryUpdated(entry, oldSbn);
-
- mLogger.logNotifUpdated(entry.getKey());
- for (NotificationEntryListener listener : mNotificationEntryListeners) {
- listener.onPreEntryUpdated(entry);
- }
- final boolean fromSystem = ranking != null;
- for (NotifCollectionListener listener : mNotifCollectionListeners) {
- listener.onEntryUpdated(entry, fromSystem);
- }
-
- updateNotifications("updateNotificationInternal");
-
- for (NotificationEntryListener listener : mNotificationEntryListeners) {
- listener.onPostEntryUpdated(entry);
- }
- for (NotifCollectionListener listener : mNotifCollectionListeners) {
- listener.onRankingApplied();
- }
- Trace.endSection();
- }
-
- public void updateNotification(StatusBarNotification notification, RankingMap ranking) {
- try {
- updateNotificationInternal(notification, ranking);
- } catch (InflationException e) {
- handleInflationException(notification, e);
- }
- }
-
- /**
* Update the notifications
* @param reason why the notifications are updating
*/
@@ -736,128 +98,6 @@ public class NotificationEntryManager implements
mLogger.logUseWhileNewPipelineActive("updateNotifications", reason);
}
- public void updateNotificationRanking(RankingMap rankingMap) {
- Trace.beginSection("NotificationEntryManager.updateNotificationRanking");
- List<NotificationEntry> entries = new ArrayList<>();
- entries.addAll(getVisibleNotifications());
- entries.addAll(mPendingNotifications.values());
-
- // Has a copy of the current UI adjustments.
- ArrayMap<String, NotificationUiAdjustment> oldAdjustments = new ArrayMap<>();
- ArrayMap<String, Integer> oldImportances = new ArrayMap<>();
- for (NotificationEntry entry : entries) {
- NotificationUiAdjustment adjustment =
- NotificationUiAdjustment.extractFromNotificationEntry(entry);
- oldAdjustments.put(entry.getKey(), adjustment);
- oldImportances.put(entry.getKey(), entry.getImportance());
- }
-
- // Populate notification entries from the new rankings.
- updateRankingAndSort(rankingMap, "updateNotificationRanking");
- updateRankingOfPendingNotifications(rankingMap);
-
- // By comparing the old and new UI adjustments, reinflate the view accordingly.
- for (NotificationEntry entry : entries) {
- mNotificationRowBinderLazy.get()
- .onNotificationRankingUpdated(
- entry,
- oldImportances.get(entry.getKey()),
- oldAdjustments.get(entry.getKey()),
- NotificationUiAdjustment.extractFromNotificationEntry(entry),
- mInflationCallback);
- }
-
- updateNotifications("updateNotificationRanking");
-
- for (NotificationEntryListener listener : mNotificationEntryListeners) {
- listener.onNotificationRankingUpdated(rankingMap);
- }
- for (NotifCollectionListener listener : mNotifCollectionListeners) {
- listener.onRankingUpdate(rankingMap);
- }
- for (NotifCollectionListener listener : mNotifCollectionListeners) {
- listener.onRankingApplied();
- }
- Trace.endSection();
- }
-
- void notifyChannelModified(
- String pkgName,
- UserHandle user,
- NotificationChannel channel,
- int modificationType) {
- for (NotifCollectionListener listener : mNotifCollectionListeners) {
- listener.onNotificationChannelModified(pkgName, user, channel, modificationType);
- }
- for (NotificationEntryListener listener : mNotificationEntryListeners) {
- listener.onNotificationChannelModified(pkgName, user, channel, modificationType);
- }
- }
-
- private void updateRankingOfPendingNotifications(@Nullable RankingMap rankingMap) {
- if (rankingMap == null) {
- return;
- }
- for (NotificationEntry pendingNotification : mPendingNotifications.values()) {
- Ranking ranking = new Ranking();
- if (rankingMap.getRanking(pendingNotification.getKey(), ranking)) {
- pendingNotification.setRanking(ranking);
- }
- }
- }
-
- /**
- * @return An iterator for all "pending" notifications. Pending notifications are newly-posted
- * notifications whose views have not yet been inflated. In general, the system pretends like
- * these don't exist, although there are a couple exceptions.
- */
- public Iterable<NotificationEntry> getPendingNotificationsIterator() {
- mNotifPipelineFlags.checkLegacyPipelineEnabled();
- return mPendingNotifications.values();
- }
-
- /**
- * Use this method to retrieve a notification entry that has been prepared for presentation.
- * Note that the notification may be filtered out and never shown to the user.
- *
- * @see #getVisibleNotifications() for the currently sorted and filtered list
- *
- * @return a {@link NotificationEntry} if it has been prepared, else null
- */
- public NotificationEntry getActiveNotificationUnfiltered(String key) {
- mNotifPipelineFlags.checkLegacyPipelineEnabled();
- return mActiveNotifications.get(key);
- }
-
- /**
- * Gets the pending or visible notification entry with the given key. Returns null if
- * notification doesn't exist.
- */
- public NotificationEntry getPendingOrActiveNotif(String key) {
- mNotifPipelineFlags.checkLegacyPipelineEnabled();
- NotificationEntry entry = mPendingNotifications.get(key);
- if (entry != null) {
- return entry;
- }
- return mActiveNotifications.get(key);
- }
-
- private void extendLifetime(NotificationEntry entry, NotificationLifetimeExtender extender) {
- NotificationLifetimeExtender activeExtender = mRetainedNotifications.get(entry);
- if (activeExtender != null && activeExtender != extender) {
- activeExtender.setShouldManageLifetime(entry, false);
- }
- mRetainedNotifications.put(entry, extender);
- extender.setShouldManageLifetime(entry, true);
- }
-
- private void cancelLifetimeExtension(NotificationEntry entry) {
- NotificationLifetimeExtender activeExtender = mRetainedNotifications.remove(entry);
- if (activeExtender != null) {
- activeExtender.setShouldManageLifetime(entry, false);
- }
- }
-
/*
* -----
* Annexed from NotificationData below:
@@ -865,59 +105,11 @@ public class NotificationEntryManager implements
* we'll try to keep the behavior the same and can simplify these interfaces in another pass
*/
- /** Internalization of NotificationData#remove */
- private void removeVisibleNotification(String key) {
- // no need to synchronize if we're on the main thread dawg
- Assert.isMainThread();
-
- NotificationEntry removed = mActiveNotifications.remove(key);
-
- if (removed == null) return;
- mGroupManager.onEntryRemoved(removed);
- }
-
- /** @return list of active notifications filtered for the current user */
- public List<NotificationEntry> getActiveNotificationsForCurrentUser() {
- Trace.beginSection("NotificationEntryManager.getActiveNotificationsForCurrentUser");
- Assert.isMainThread();
- ArrayList<NotificationEntry> filtered = new ArrayList<>();
-
- final int len = mActiveNotifications.size();
- for (int i = 0; i < len; i++) {
- NotificationEntry entry = mActiveNotifications.valueAt(i);
- if (!mRanker.isNotificationForCurrentProfiles(entry)) {
- continue;
- }
- filtered.add(entry);
- }
- Trace.endSection();
- return filtered;
- }
-
- //TODO: Get rid of this in favor of NotificationUpdateHandler#updateNotificationRanking
- /**
- * @param rankingMap the {@link RankingMap} to apply to the current notification list
- * @param reason the reason for calling this method, which will be logged
- */
- public void updateRanking(RankingMap rankingMap, String reason) {
- Trace.beginSection("NotificationEntryManager.updateRanking");
- updateRankingAndSort(rankingMap, reason);
- for (NotifCollectionListener listener : mNotifCollectionListeners) {
- listener.onRankingApplied();
- }
- Trace.endSection();
- }
-
/** Resorts / filters the current notification set with the current RankingMap */
public void reapplyFilterAndSort(String reason) {
mLogger.logUseWhileNewPipelineActive("reapplyFilterAndSort", reason);
}
- /** Calls to NotificationRankingManager and updates mSortedAndFiltered */
- private void updateRankingAndSort(RankingMap rankingMap, String reason) {
- mLogger.logUseWhileNewPipelineActive("updateRankingAndSort", reason);
- }
-
/** dump the current active notification list. Called from CentralSurfaces */
public void dump(PrintWriter pw, String indent) {
pw.println("NotificationEntryManager (Legacy)");
@@ -955,55 +147,10 @@ public class NotificationEntryManager implements
pw.println(" notification=" + n.getNotification());
}
- /**
- * This is the answer to the question "what notifications should the user be seeing right now?"
- * These are sorted and filtered, and directly inform the notification shade what to show
- *
- * @return A read-only list of the currently active notifications
- */
- public List<NotificationEntry> getVisibleNotifications() {
- mNotifPipelineFlags.checkLegacyPipelineEnabled();
- return mReadOnlyNotifications;
- }
-
- /**
- * Returns a collections containing ALL notifications we know about, including ones that are
- * hidden or for other users. See {@link CommonNotifCollection#getAllNotifs()}.
- */
- @NonNull
- @Override
- public Collection<NotificationEntry> getAllNotifs() {
- mNotifPipelineFlags.checkLegacyPipelineEnabled();
- return mReadOnlyAllNotifications;
- }
-
- @Nullable
- @Override
- public NotificationEntry getEntry(@NonNull String key) {
- mNotifPipelineFlags.checkLegacyPipelineEnabled();
- return getPendingOrActiveNotif(key);
- }
-
- /** @return A count of the active notifications */
- public int getActiveNotificationsCount() {
- mNotifPipelineFlags.checkLegacyPipelineEnabled();
- return mReadOnlyNotifications.size();
- }
-
- /**
- * @return {@code true} if there is at least one notification that should be visible right now
- */
- public boolean hasActiveNotifications() {
- mNotifPipelineFlags.checkLegacyPipelineEnabled();
- return mReadOnlyNotifications.size() != 0;
- }
-
- @Override
public void addCollectionListener(@NonNull NotifCollectionListener listener) {
mNotifCollectionListeners.add(listener);
}
- @Override
public void removeCollectionListener(@NonNull NotifCollectionListener listener) {
mNotifCollectionListeners.remove(listener);
}
@@ -1024,9 +171,6 @@ public class NotificationEntryManager implements
boolean isNotificationForCurrentProfiles(StatusBarNotification sbn);
}
- private static final String TAG = "NotificationEntryMgr";
- private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
-
/**
* Used when a notification is removed and it doesn't have a reason that maps to one of the
* reasons defined in NotificationListenerService
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
deleted file mode 100644
index 54f13808cdad..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification;
-
-import static com.android.systemui.media.MediaDataManagerKt.isMediaNotification;
-
-import android.Manifest;
-import android.app.AppGlobals;
-import android.app.Notification;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
-import android.os.RemoteException;
-import android.service.notification.StatusBarNotification;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.ForegroundServiceController;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.media.MediaFeatureFlag;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.provider.DebugModeFilterProvider;
-
-import javax.inject.Inject;
-
-/** Component which manages the various reasons a notification might be filtered out.*/
-// TODO: delete NotificationFilter.java after migrating to new NotifPipeline b/145659174.
-// Notification filtering is taken care of across the different Coordinators (mostly
-// KeyguardCoordinator.java)
-@SysUISingleton
-public class NotificationFilter {
-
- private final DebugModeFilterProvider mDebugNotificationFilter;
- private final StatusBarStateController mStatusBarStateController;
- private final KeyguardEnvironment mKeyguardEnvironment;
- private final ForegroundServiceController mForegroundServiceController;
- private final NotificationLockscreenUserManager mUserManager;
- private final Boolean mIsMediaFlagEnabled;
-
- @Inject
- public NotificationFilter(
- DebugModeFilterProvider debugNotificationFilter,
- StatusBarStateController statusBarStateController,
- KeyguardEnvironment keyguardEnvironment,
- ForegroundServiceController foregroundServiceController,
- NotificationLockscreenUserManager userManager,
- MediaFeatureFlag mediaFeatureFlag) {
- mDebugNotificationFilter = debugNotificationFilter;
- mStatusBarStateController = statusBarStateController;
- mKeyguardEnvironment = keyguardEnvironment;
- mForegroundServiceController = foregroundServiceController;
- mUserManager = userManager;
- mIsMediaFlagEnabled = mediaFeatureFlag.getEnabled();
- }
-
- /**
- * @return true if the provided notification should NOT be shown right now.
- */
- public boolean shouldFilterOut(NotificationEntry entry) {
- final StatusBarNotification sbn = entry.getSbn();
- if (mDebugNotificationFilter.shouldFilterOut(entry)) {
- return true;
- }
-
- if (!(mKeyguardEnvironment.isDeviceProvisioned()
- || showNotificationEvenIfUnprovisioned(sbn))) {
- return true;
- }
-
- if (!mKeyguardEnvironment.isNotificationForCurrentProfiles(sbn)) {
- return true;
- }
-
- if (mUserManager.isLockscreenPublicMode(sbn.getUserId())
- && (sbn.getNotification().visibility == Notification.VISIBILITY_SECRET
- || mUserManager.shouldHideNotifications(sbn.getUserId())
- || mUserManager.shouldHideNotifications(sbn.getKey()))) {
- return true;
- }
-
- if (mStatusBarStateController.isDozing() && entry.shouldSuppressAmbient()) {
- return true;
- }
-
- if (!mStatusBarStateController.isDozing() && entry.shouldSuppressNotificationList()) {
- return true;
- }
-
- if (entry.getRanking().isSuspended()) {
- return true;
- }
-
- if (mForegroundServiceController.isDisclosureNotification(sbn)
- && !mForegroundServiceController.isDisclosureNeededForUser(sbn.getUserId())) {
- // this is a foreground-service disclosure for a user that does not need to show one
- return true;
- }
-
- if (mIsMediaFlagEnabled && isMediaNotification(sbn)) {
- return true;
- }
- return false;
- }
-
- // Q: What kinds of notifications should show during setup?
- // A: Almost none! Only things coming from packages with permission
- // android.permission.NOTIFICATION_DURING_SETUP that also have special "kind" tags marking them
- // as relevant for setup (see below).
- private static boolean showNotificationEvenIfUnprovisioned(StatusBarNotification sbn) {
- return showNotificationEvenIfUnprovisioned(AppGlobals.getPackageManager(), sbn);
- }
-
- @VisibleForTesting
- static boolean showNotificationEvenIfUnprovisioned(IPackageManager packageManager,
- StatusBarNotification sbn) {
- return checkUidPermission(packageManager, Manifest.permission.NOTIFICATION_DURING_SETUP,
- sbn.getUid()) == PackageManager.PERMISSION_GRANTED
- && sbn.getNotification().extras.getBoolean(Notification.EXTRA_ALLOW_DURING_SETUP);
- }
-
- private static int checkUidPermission(IPackageManager packageManager, String permission,
- int uid) {
- try {
- return packageManager.checkUidPermission(permission, uid);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index b6392f705c49..585d871bad4e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -61,11 +61,11 @@ import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.statusbar.InflationTask;
import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.icon.IconPack;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
@@ -421,7 +421,7 @@ public final class NotificationEntry extends ListEntry {
* Get the children that are actually attached to this notification's row.
*
* TODO: Seems like most callers here should probably be using
- * {@link NotificationGroupManagerLegacy#getChildren}
+ * {@link GroupMembershipManager#getChildren(ListEntry)}
*/
public @Nullable List<NotificationEntry> getAttachedNotifChildren() {
if (row == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
deleted file mode 100644
index a92cff886b53..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.collection
-
-import android.app.Notification
-import android.app.NotificationManager.IMPORTANCE_HIGH
-import android.app.NotificationManager.IMPORTANCE_MIN
-import android.service.notification.NotificationListenerService.Ranking
-import android.service.notification.NotificationListenerService.RankingMap
-import android.service.notification.StatusBarNotification
-import com.android.systemui.statusbar.NotificationMediaManager
-import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment
-import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger
-import com.android.systemui.statusbar.notification.NotificationFilter
-import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
-import com.android.systemui.statusbar.notification.collection.legacy.LegacyNotificationRanker
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
-import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
-import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
-import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON
-import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING
-import com.android.systemui.statusbar.notification.stack.BUCKET_FOREGROUND_SERVICE
-import com.android.systemui.statusbar.notification.stack.BUCKET_PEOPLE
-import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
-import com.android.systemui.statusbar.notification.stack.PriorityBucket
-import com.android.systemui.statusbar.policy.HeadsUpManager
-import dagger.Lazy
-import java.util.Objects
-import javax.inject.Inject
-
-private const val TAG = "NotifRankingManager"
-
-/**
- * NotificationRankingManager is responsible for holding on to the most recent [RankingMap], and
- * updating SystemUI's set of [NotificationEntry]s with their own ranking. It also sorts and filters
- * a set of entries (but retains none of them). We also set buckets on the entries here since
- * bucketing is tied closely to sorting.
- *
- * For the curious: this class is one iteration closer to null of what used to be called
- * NotificationData.java.
- */
-open class NotificationRankingManager @Inject constructor(
- private val mediaManagerLazy: Lazy<NotificationMediaManager>,
- private val groupManager: NotificationGroupManagerLegacy,
- private val headsUpManager: HeadsUpManager,
- private val notifFilter: NotificationFilter,
- private val logger: NotificationEntryManagerLogger,
- private val sectionsFeatureManager: NotificationSectionsFeatureManager,
- private val peopleNotificationIdentifier: PeopleNotificationIdentifier,
- private val highPriorityProvider: HighPriorityProvider,
- private val keyguardEnvironment: KeyguardEnvironment
-) : LegacyNotificationRanker {
-
- override var rankingMap: RankingMap? = null
- protected set
- private val mediaManager by lazy {
- mediaManagerLazy.get()
- }
- private val usePeopleFiltering: Boolean
- get() = sectionsFeatureManager.isFilteringEnabled()
- private val rankingComparator: Comparator<NotificationEntry> = Comparator { a, b ->
- val na = a.sbn
- val nb = b.sbn
- val aRank = a.ranking.rank
- val bRank = b.ranking.rank
-
- val aIsFsn = a.isColorizedForegroundService()
- val bIsFsn = b.isColorizedForegroundService()
-
- val aCall = a.isImportantCall()
- val bCall = b.isImportantCall()
-
- val aPersonType = a.getPeopleNotificationType()
- val bPersonType = b.getPeopleNotificationType()
-
- val aMedia = a.isImportantMedia()
- val bMedia = b.isImportantMedia()
-
- val aSystemMax = a.isSystemMax()
- val bSystemMax = b.isSystemMax()
-
- val aHeadsUp = a.isRowHeadsUp
- val bHeadsUp = b.isRowHeadsUp
-
- val aIsHighPriority = a.isHighPriority()
- val bIsHighPriority = b.isHighPriority()
- when {
- aHeadsUp != bHeadsUp -> if (aHeadsUp) -1 else 1
- // Provide consistent ranking with headsUpManager
- aHeadsUp -> headsUpManager.compare(a, b)
- aIsFsn != bIsFsn -> if (aIsFsn) -1 else 1
- aCall != bCall -> if (aCall) -1 else 1
- usePeopleFiltering && aPersonType != bPersonType ->
- peopleNotificationIdentifier.compareTo(aPersonType, bPersonType)
- // Upsort current media notification.
- aMedia != bMedia -> if (aMedia) -1 else 1
- // Upsort PRIORITY_MAX system notifications
- aSystemMax != bSystemMax -> if (aSystemMax) -1 else 1
- aIsHighPriority != bIsHighPriority ->
- -1 * aIsHighPriority.compareTo(bIsHighPriority)
- aRank != bRank -> aRank - bRank
- else -> nb.notification.`when`.compareTo(na.notification.`when`)
- }
- }
-
- override fun updateRanking(
- newRankingMap: RankingMap?,
- entries: Collection<NotificationEntry>,
- reason: String
- ): List<NotificationEntry> {
- // TODO: may not be ideal to guard on null here, but this code is implementing exactly what
- // NotificationData used to do
- if (newRankingMap != null) {
- rankingMap = newRankingMap
- updateRankingForEntries(entries)
- }
- return synchronized(this) {
- filterAndSortLocked(entries, reason)
- }
- }
-
- override fun isNotificationForCurrentProfiles(
- entry: NotificationEntry
- ): Boolean {
- return keyguardEnvironment.isNotificationForCurrentProfiles(entry.sbn)
- }
-
- /** Uses the [rankingComparator] to sort notifications which aren't filtered */
- private fun filterAndSortLocked(
- entries: Collection<NotificationEntry>,
- reason: String
- ): List<NotificationEntry> {
- logger.logFilterAndSort(reason)
- val filtered = entries.asSequence()
- .filterNot(this::filter)
- .sortedWith(rankingComparator)
- .toList()
- entries.forEach { it.bucket = getBucketForEntry(it) }
- return filtered
- }
-
- private fun filter(entry: NotificationEntry): Boolean {
- val filtered = notifFilter.shouldFilterOut(entry)
- if (filtered) {
- // notification is removed from the list, so we reset its initialization time
- entry.resetInitializationTime()
- }
- return filtered
- }
-
- @PriorityBucket
- private fun getBucketForEntry(entry: NotificationEntry): Int {
- val isImportantCall = entry.isImportantCall()
- val isHeadsUp = entry.isRowHeadsUp
- val isMedia = entry.isImportantMedia()
- val isSystemMax = entry.isSystemMax()
- return when {
- entry.isColorizedForegroundService() || isImportantCall -> BUCKET_FOREGROUND_SERVICE
- usePeopleFiltering && entry.isConversation() -> BUCKET_PEOPLE
- isHeadsUp || isMedia || isSystemMax || entry.isHighPriority() -> BUCKET_ALERTING
- else -> BUCKET_SILENT
- }
- }
-
- private fun updateRankingForEntries(entries: Iterable<NotificationEntry>) {
- rankingMap?.let { rankingMap ->
- synchronized(entries) {
- for (entry in entries) {
- val newRanking = Ranking()
- if (!rankingMap.getRanking(entry.key, newRanking)) {
- continue
- }
- entry.ranking = newRanking
-
- val newOverrideGroupKey = newRanking.overrideGroupKey
- if (!Objects.equals(entry.sbn.overrideGroupKey, newOverrideGroupKey)) {
- val oldGroupKey = entry.sbn.groupKey
- val oldIsGroup = entry.sbn.isGroup
- val oldIsGroupSummary = entry.sbn.notification.isGroupSummary
- entry.sbn.overrideGroupKey = newOverrideGroupKey
- groupManager.onEntryUpdated(entry, oldGroupKey, oldIsGroup,
- oldIsGroupSummary)
- }
- }
- }
- }
- }
-
- private fun NotificationEntry.isImportantMedia() =
- key == mediaManager.mediaNotificationKey && importance > IMPORTANCE_MIN
-
- private fun NotificationEntry.isConversation() = getPeopleNotificationType() != TYPE_NON_PERSON
-
- private fun NotificationEntry.getPeopleNotificationType() =
- peopleNotificationIdentifier.getPeopleNotificationType(this)
-
- private fun NotificationEntry.isHighPriority() =
- highPriorityProvider.isHighPriority(this)
-}
-
-// Convenience functions
-private fun NotificationEntry.isSystemMax() =
- importance >= IMPORTANCE_HIGH && sbn.isSystemNotification()
-
-private fun StatusBarNotification.isSystemNotification() =
- "android" == packageName || "com.android.systemui" == packageName
-
-private fun NotificationEntry.isImportantCall() =
- sbn.notification.isStyle(Notification.CallStyle::class.java) && importance > IMPORTANCE_MIN
-
-private fun NotificationEntry.isColorizedForegroundService() = sbn.notification.run {
- isForegroundService && isColorized && importance > IMPORTANCE_MIN
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 420f21db2c73..14cc6bf1ea41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -38,6 +38,7 @@ import android.os.Trace;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
@@ -126,6 +127,9 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
private List<ListEntry> mReadOnlyNewNotifList = Collections.unmodifiableList(mNewNotifList);
private final NotifPipelineChoreographer mChoreographer;
+ private int mConsecutiveReentrantRebuilds = 0;
+ @VisibleForTesting public static final int MAX_CONSECUTIVE_REENTRANT_REBUILDS = 3;
+
@Inject
public ShadeListBuilder(
DumpManager dumpManager,
@@ -310,7 +314,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
mLogger.logOnBuildList(reason);
mAllEntries = entries;
- mChoreographer.schedule();
+ scheduleRebuild(/* reentrant = */ false);
}
};
@@ -1332,11 +1336,64 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
throw new RuntimeException("Missing default sectioner!");
}
- private void rebuildListIfBefore(@PipelineState.StateName int state) {
- mPipelineState.requireIsBefore(state);
- if (mPipelineState.is(STATE_IDLE)) {
+ private void rebuildListIfBefore(@PipelineState.StateName int rebuildState) {
+ final @PipelineState.StateName int currentState = mPipelineState.getState();
+
+ // If the pipeline is idle, requesting an invalidation is always okay, and starts a new run.
+ if (currentState == STATE_IDLE) {
+ scheduleRebuild(/* reentrant = */ false, rebuildState);
+ return;
+ }
+
+ // If the pipeline is running, it is okay to request an invalidation of a *later* stage.
+ // Since the current pipeline run hasn't run it yet, no new pipeline run is needed.
+ if (rebuildState > currentState) {
+ return;
+ }
+
+ // If the pipeline is running, it is bad to request an invalidation of *earlier* stages or
+ // the *current* stage; this will run the pipeline more often than needed, and may even
+ // cause an infinite loop of pipeline runs.
+ //
+ // Unfortunately, there are some unfixed bugs that cause reentrant pipeline runs, so we keep
+ // a counter and allow a few reentrant runs in a row between any two non-reentrant runs.
+ //
+ // It is technically possible for a *pair* of invalidations, one reentrant and one not, to
+ // trigger *each other*, alternating responsibility for pipeline runs in an infinite loop
+ // but constantly resetting the reentrant run counter. Hopefully that doesn't happen.
+ scheduleRebuild(/* reentrant = */ true, rebuildState);
+ }
+
+ private void scheduleRebuild(boolean reentrant) {
+ scheduleRebuild(reentrant, STATE_IDLE);
+ }
+
+ private void scheduleRebuild(boolean reentrant, @PipelineState.StateName int rebuildState) {
+ if (!reentrant) {
+ mConsecutiveReentrantRebuilds = 0;
mChoreographer.schedule();
+ return;
}
+
+ final @PipelineState.StateName int currentState = mPipelineState.getState();
+
+ final String rebuildStateName = PipelineState.getStateName(rebuildState);
+ final String currentStateName = PipelineState.getStateName(currentState);
+ final IllegalStateException exception = new IllegalStateException(
+ "Reentrant notification pipeline rebuild of state " + rebuildStateName
+ + " while pipeline in state " + currentStateName + ".");
+
+ mConsecutiveReentrantRebuilds++;
+
+ if (mConsecutiveReentrantRebuilds > MAX_CONSECUTIVE_REENTRANT_REBUILDS) {
+ Log.e(TAG, "Crashing after more than " + MAX_CONSECUTIVE_REENTRANT_REBUILDS
+ + " consecutive reentrant notification pipeline rebuilds.", exception);
+ throw exception;
+ }
+
+ Log.e(TAG, "Allowing " + mConsecutiveReentrantRebuilds
+ + " consecutive reentrant notification pipeline rebuild(s).", exception);
+ mChoreographer.schedule();
}
private static int countChildren(List<ListEntry> entries) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index ef1e57b4cd3b..6e76691ae1b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -286,7 +286,7 @@ public class PreparationCoordinator implements Coordinator {
if (isInflated(child)) {
// TODO: May want to put an animation hint here so view manager knows to treat
// this differently from a regular removal animation
- freeNotifViews(child);
+ freeNotifViews(child, "Past last visible group child");
}
}
}
@@ -379,7 +379,8 @@ public class PreparationCoordinator implements Coordinator {
mNotifInflatingFilter.invalidateList("onInflationFinished for " + logKey(entry));
}
- private void freeNotifViews(NotificationEntry entry) {
+ private void freeNotifViews(NotificationEntry entry, String reason) {
+ mLogger.logFreeNotifViews(entry, reason);
mViewBarn.removeViewForEntry(entry);
mNotifInflater.releaseViews(entry);
// TODO: clear the entry's row here, or even better, stop setting the row on the entry!
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt
index 30f13152126c..c4f4ed54e2fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt
@@ -31,7 +31,7 @@ class PreparationCoordinatorLogger @Inject constructor(
buffer.log(TAG, LogLevel.DEBUG, {
str1 = entry.logKey
}, {
- "NOTIF INFLATED $str1"
+ "Inflation completed for notif $str1"
})
}
@@ -40,7 +40,16 @@ class PreparationCoordinatorLogger @Inject constructor(
str1 = entry.logKey
str2 = reason
}, {
- "NOTIF INFLATION ABORTED $str1 reason=$str2"
+ "Infation aborted for notif $str1 reason=$str2"
+ })
+ }
+
+ fun logFreeNotifViews(entry: NotificationEntry, reason: String) {
+ buffer.log(TAG, LogLevel.DEBUG, {
+ str1 = entry.logKey
+ str2 = reason
+ }, {
+ "Freeing content views for notif $str1 reason=$str2"
})
}
@@ -70,4 +79,4 @@ class PreparationCoordinatorLogger @Inject constructor(
}
}
-private const val TAG = "PreparationCoordinator" \ No newline at end of file
+private const val TAG = "PreparationCoordinator"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/BindEventManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/BindEventManagerImpl.kt
index 9d5b859ef29c..496266f8831e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/BindEventManagerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/BindEventManagerImpl.kt
@@ -17,8 +17,6 @@
package com.android.systemui.statusbar.notification.collection.inflation
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.notification.NotificationEntryListener
-import com.android.systemui.statusbar.notification.NotificationEntryManager
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.inflation.BindEventManager.Listener
import javax.inject.Inject
@@ -31,12 +29,4 @@ class BindEventManagerImpl @Inject constructor() : BindEventManager() {
/** Emit the [Listener.onViewBound] event to all registered listeners. */
fun notifyViewBound(entry: NotificationEntry) =
listeners.forEach { listener -> listener.onViewBound(entry) }
-
- /** Initialize this for the legacy pipeline. */
- fun attachToLegacyPipeline(notificationEntryManager: NotificationEntryManager) {
- notificationEntryManager.addNotificationEntryListener(object : NotificationEntryListener {
- override fun onEntryInflated(entry: NotificationEntry) = notifyViewBound(entry)
- override fun onEntryReinflated(entry: NotificationEntry) = notifyViewBound(entry)
- })
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationRanker.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationRanker.kt
deleted file mode 100644
index 49bc48efda00..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationRanker.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.collection.legacy
-
-import android.service.notification.NotificationListenerService
-import com.android.systemui.statusbar.notification.collection.NotificationEntry
-
-interface LegacyNotificationRanker {
- val rankingMap: NotificationListenerService.RankingMap?
-
- fun updateRanking(
- newRankingMap: NotificationListenerService.RankingMap?,
- entries: Collection<NotificationEntry>,
- reason: String
- ): List<NotificationEntry>
-
- fun isNotificationForCurrentProfiles(
- entry: NotificationEntry
- ): Boolean
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationRankerStub.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationRankerStub.java
deleted file mode 100644
index 12353f80c0d1..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationRankerStub.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.collection.legacy;
-
-import android.service.notification.NotificationListenerService.Ranking;
-import android.service.notification.NotificationListenerService.RankingMap;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.List;
-
-/**
- * Stub implementation that we use until we get passed the "real" one in the form of
- * {@link com.android.systemui.statusbar.notification.collection.NotificationRankingManager}
- */
-public class LegacyNotificationRankerStub implements LegacyNotificationRanker {
- private RankingMap mRankingMap = new RankingMap(new Ranking[] {});
-
- @NonNull
- @Override
- public List<NotificationEntry> updateRanking(
- @Nullable RankingMap newRankingMap,
- @NonNull Collection<NotificationEntry> entries,
- @NonNull String reason) {
- if (newRankingMap != null) {
- mRankingMap = newRankingMap;
- }
- List<NotificationEntry> ranked = new ArrayList<>(entries);
- ranked.sort(mEntryComparator);
- return ranked;
- }
-
- @Nullable
- @Override
- public RankingMap getRankingMap() {
- return mRankingMap;
- }
-
- private final Comparator<NotificationEntry> mEntryComparator = Comparator.comparingLong(
- o -> o.getSbn().getNotification().when);
-
- @Override
- public boolean isNotificationForCurrentProfiles(@NonNull NotificationEntry entry) {
- return true;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LowPriorityInflationHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LowPriorityInflationHelper.java
index ae4f2bbc0453..89445a5c6467 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LowPriorityInflationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LowPriorityInflationHelper.java
@@ -19,9 +19,6 @@ package com.android.systemui.statusbar.notification.collection.legacy;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.RowContentBindParams;
-import com.android.systemui.statusbar.notification.row.RowContentBindStage;
import javax.inject.Inject;
@@ -32,44 +29,17 @@ import javax.inject.Inject;
@SysUISingleton
public class LowPriorityInflationHelper {
private final NotificationGroupManagerLegacy mGroupManager;
- private final RowContentBindStage mRowContentBindStage;
private final NotifPipelineFlags mNotifPipelineFlags;
@Inject
LowPriorityInflationHelper(
NotificationGroupManagerLegacy groupManager,
- RowContentBindStage rowContentBindStage,
NotifPipelineFlags notifPipelineFlags) {
mGroupManager = groupManager;
- mRowContentBindStage = rowContentBindStage;
mNotifPipelineFlags = notifPipelineFlags;
}
/**
- * Check if we inflated the wrong version of the view and if we need to reinflate the
- * content views to be their low priority version or not.
- *
- * Whether we inflate the low priority view or not depends on the notification being visually
- * part of a group. Since group membership is determined AFTER inflation, we're forced to check
- * again at a later point in the pipeline to see if we inflated the wrong view and reinflate
- * the correct one here.
- *
- * TODO: The group manager should run before inflation so that we don't deal with this
- */
- public void recheckLowPriorityViewAndInflate(
- NotificationEntry entry,
- ExpandableNotificationRow row) {
- mNotifPipelineFlags.checkLegacyPipelineEnabled();
- RowContentBindParams params = mRowContentBindStage.getStageParams(entry);
- final boolean shouldBeLowPriority = shouldUseLowPriorityView(entry);
- if (!row.isRemoved() && row.isLowPriority() != shouldBeLowPriority) {
- params.setUseLowPriority(shouldBeLowPriority);
- mRowContentBindStage.requestRebind(entry,
- en -> row.setIsLowPriority(shouldBeLowPriority));
- }
- }
-
- /**
* Whether the notification should inflate a low priority version of its content views.
*/
public boolean shouldUseLowPriorityView(NotificationEntry entry) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
index b8da9a83da3f..d41f6fe7bdde 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
@@ -33,13 +33,9 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
-import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
+import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager.OnGroupExpansionChangeListener;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.util.Compile;
import com.android.wm.shell.bubbles.Bubbles;
@@ -47,7 +43,6 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
@@ -65,12 +60,7 @@ import dagger.Lazy;
* 2. Tracking group expansion states
*/
@SysUISingleton
-public class NotificationGroupManagerLegacy implements
- OnHeadsUpChangedListener,
- StateListener,
- GroupMembershipManager,
- GroupExpansionManager,
- Dumpable {
+public class NotificationGroupManagerLegacy implements StateListener, Dumpable {
private static final String TAG = "LegacyNotifGroupManager";
private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
@@ -81,14 +71,10 @@ public class NotificationGroupManagerLegacy implements
*/
private static final long POST_BATCH_MAX_AGE = 5000;
private final HashMap<String, NotificationGroup> mGroupMap = new HashMap<>();
- private final ArraySet<OnGroupExpansionChangeListener> mExpansionChangeListeners =
- new ArraySet<>();
private final Lazy<PeopleNotificationIdentifier> mPeopleNotificationIdentifier;
private final Optional<Bubbles> mBubblesOptional;
private final GroupEventDispatcher mEventDispatcher = new GroupEventDispatcher(mGroupMap::get);
- private int mBarState = -1;
- private HashMap<String, StatusBarNotification> mIsolatedEntries = new HashMap<>();
- private HeadsUpManager mHeadsUpManager;
+ private final HashMap<String, StatusBarNotification> mIsolatedEntries = new HashMap<>();
private boolean mIsUpdatingUnchangedGroup;
@Inject
@@ -111,47 +97,8 @@ public class NotificationGroupManagerLegacy implements
mEventDispatcher.registerGroupChangeListener(listener);
}
- @Override
- public void registerGroupExpansionChangeListener(OnGroupExpansionChangeListener listener) {
- mExpansionChangeListeners.add(listener);
- }
-
- @Override
- public boolean isGroupExpanded(NotificationEntry entry) {
- NotificationGroup group = mGroupMap.get(getGroupKey(entry.getSbn()));
- if (group == null) {
- return false;
- }
- return group.expanded;
- }
-
- /**
- * @return if the group that this notification is associated with logically is expanded
- */
- public boolean isLogicalGroupExpanded(StatusBarNotification sbn) {
- NotificationGroup group = mGroupMap.get(sbn.getGroupKey());
- if (group == null) {
- return false;
- }
- return group.expanded;
- }
-
- @Override
- public void setGroupExpanded(NotificationEntry entry, boolean expanded) {
- NotificationGroup group = mGroupMap.get(getGroupKey(entry.getSbn()));
- if (group == null) {
- return;
- }
- setGroupExpanded(group, expanded);
- }
-
private void setGroupExpanded(NotificationGroup group, boolean expanded) {
group.expanded = expanded;
- if (group.summary != null) {
- for (OnGroupExpansionChangeListener listener : mExpansionChangeListeners) {
- listener.onGroupExpansionChange(group.summary.getRow(), expanded);
- }
- }
}
/**
@@ -212,19 +159,6 @@ public class NotificationGroupManagerLegacy implements
}
}
- /**
- * Notify the group manager that a new entry was added
- */
- public void onEntryAdded(final NotificationEntry added) {
- if (SPEW) {
- Log.d(TAG, "onEntryAdded: entry=" + logKey(added));
- }
- mEventDispatcher.openBufferScope();
- updateIsolation(added);
- onEntryAddedInternal(added);
- mEventDispatcher.closeBufferScope();
- }
-
private void onEntryAddedInternal(final NotificationEntry added) {
if (added.isRowRemoved()) {
added.setDebugThrowable(new Throwable());
@@ -499,106 +433,13 @@ public class NotificationGroupManagerLegacy implements
return result;
}
- /**
- * Update an entry's group information
- * @param entry notification entry to update
- * @param oldNotification previous notification info before this update
- */
- public void onEntryUpdated(NotificationEntry entry, StatusBarNotification oldNotification) {
- if (SPEW) {
- Log.d(TAG, "onEntryUpdated: entry=" + logKey(entry));
- }
- onEntryUpdated(entry, oldNotification.getGroupKey(), oldNotification.isGroup(),
- oldNotification.getNotification().isGroupSummary());
- }
-
- /**
- * Updates an entry's group information
- * @param entry notification entry to update
- * @param oldGroupKey the notification's previous group key before this update
- * @param oldIsGroup whether this notification was a group before this update
- * @param oldIsGroupSummary whether this notification was a group summary before this update
- */
- public void onEntryUpdated(NotificationEntry entry, String oldGroupKey, boolean oldIsGroup,
- boolean oldIsGroupSummary) {
- String newGroupKey = entry.getSbn().getGroupKey();
- boolean groupKeysChanged = !oldGroupKey.equals(newGroupKey);
- boolean wasGroupChild = isGroupChild(entry.getKey(), oldIsGroup, oldIsGroupSummary);
- boolean isGroupChild = isGroupChild(entry.getSbn());
- mEventDispatcher.openBufferScope();
- mIsUpdatingUnchangedGroup = !groupKeysChanged && wasGroupChild == isGroupChild;
- if (mGroupMap.get(getGroupKey(entry.getKey(), oldGroupKey)) != null) {
- onEntryRemovedInternal(entry, oldGroupKey, oldIsGroup, oldIsGroupSummary);
- }
- onEntryAddedInternal(entry);
- mIsUpdatingUnchangedGroup = false;
- if (isIsolated(entry.getSbn().getKey())) {
- mIsolatedEntries.put(entry.getKey(), entry.getSbn());
- if (groupKeysChanged) {
- updateSuppression(mGroupMap.get(oldGroupKey));
- }
- // Always update the suppression of the group from which you're isolated, in case
- // this entry was or now is the alertOverride for that group.
- updateSuppression(mGroupMap.get(newGroupKey));
- } else if (!wasGroupChild && isGroupChild) {
- onEntryBecomingChild(entry);
- }
- mEventDispatcher.closeBufferScope();
- }
-
- /**
- * Whether the given notification is the summary of a group that is being suppressed
- */
- public boolean isSummaryOfSuppressedGroup(StatusBarNotification sbn) {
- return sbn.getNotification().isGroupSummary() && isGroupSuppressed(getGroupKey(sbn));
- }
-
- /**
- * If the given notification is a summary, get the group for it.
- */
- public NotificationGroup getGroupForSummary(StatusBarNotification sbn) {
- if (sbn.getNotification().isGroupSummary()) {
- return mGroupMap.get(getGroupKey(sbn));
- }
- return null;
- }
-
- private boolean isOnlyChild(StatusBarNotification sbn) {
- return !sbn.getNotification().isGroupSummary()
- && getTotalNumberOfChildren(sbn) == 1;
- }
-
- @Override
- public boolean isOnlyChildInGroup(NotificationEntry entry) {
- final StatusBarNotification sbn = entry.getSbn();
- if (!isOnlyChild(sbn)) {
- return false;
- }
- NotificationEntry logicalGroupSummary = getLogicalGroupSummary(entry);
- return logicalGroupSummary != null && !logicalGroupSummary.getSbn().equals(sbn);
- }
-
- private int getTotalNumberOfChildren(StatusBarNotification sbn) {
- int isolatedChildren = getNumberOfIsolatedChildren(sbn.getGroupKey());
- NotificationGroup group = mGroupMap.get(sbn.getGroupKey());
- int realChildren = group != null ? group.children.size() : 0;
- return isolatedChildren + realChildren;
- }
-
- private boolean isGroupSuppressed(String groupKey) {
- NotificationGroup group = mGroupMap.get(groupKey);
- return group != null && group.suppressed;
- }
-
private void setStatusBarState(int newState) {
- mBarState = newState;
- if (mBarState == StatusBarState.KEYGUARD) {
+ if (newState == StatusBarState.KEYGUARD) {
collapseGroups();
}
}
- @Override
- public void collapseGroups() {
+ private void collapseGroups() {
// Because notifications can become isolated when the group becomes suppressed it can
// lead to concurrent modifications while looping. We need to make a copy.
ArrayList<NotificationGroup> groupCopy = new ArrayList<>(mGroupMap.values());
@@ -612,7 +453,6 @@ public class NotificationGroupManagerLegacy implements
}
}
- @Override
public boolean isChildInGroup(NotificationEntry entry) {
final StatusBarNotification sbn = entry.getSbn();
if (!isGroupChild(sbn)) {
@@ -631,67 +471,6 @@ public class NotificationGroupManagerLegacy implements
return true;
}
- @Override
- public boolean isGroupSummary(NotificationEntry entry) {
- final StatusBarNotification sbn = entry.getSbn();
- if (!isGroupSummary(sbn)) {
- return false;
- }
- NotificationGroup group = mGroupMap.get(getGroupKey(sbn));
- if (group == null || group.summary == null) {
- return false;
- }
- return !group.children.isEmpty() && Objects.equals(group.summary.getSbn(), sbn);
- }
-
- @Override
- public NotificationEntry getGroupSummary(NotificationEntry entry) {
- return getGroupSummary(getGroupKey(entry.getSbn()));
- }
-
- @Override
- public NotificationEntry getLogicalGroupSummary(NotificationEntry entry) {
- return getGroupSummary(entry.getSbn().getGroupKey());
- }
-
- @Nullable
- private NotificationEntry getGroupSummary(String groupKey) {
- NotificationGroup group = mGroupMap.get(groupKey);
- //TODO: see if this can become an Entry
- return group == null ? null
- : group.summary;
- }
-
- /**
- * Get the children that are logically in the summary's group, whether or not they are isolated.
- *
- * @param summary summary of a group
- * @return list of the children
- */
- public ArrayList<NotificationEntry> getLogicalChildren(StatusBarNotification summary) {
- NotificationGroup group = mGroupMap.get(summary.getGroupKey());
- if (group == null) {
- return null;
- }
- ArrayList<NotificationEntry> children = new ArrayList<>(group.children.values());
- for (StatusBarNotification sbn : mIsolatedEntries.values()) {
- if (sbn.getGroupKey().equals(summary.getGroupKey())) {
- children.add(mGroupMap.get(sbn.getKey()).summary);
- }
- }
- return children;
- }
-
- @Override
- public @Nullable List<NotificationEntry> getChildren(ListEntry listEntrySummary) {
- NotificationEntry summary = listEntrySummary.getRepresentativeEntry();
- NotificationGroup group = mGroupMap.get(summary.getSbn().getGroupKey());
- if (group == null) {
- return null;
- }
- return new ArrayList<>(group.children.values());
- }
-
/**
* If there is a {@link NotificationGroup} associated with the provided entry, this method
* will update the suppression of that group.
@@ -710,7 +489,7 @@ public class NotificationGroupManagerLegacy implements
* @param sbn notification to check
* @return the key of the notification
*/
- public String getGroupKey(StatusBarNotification sbn) {
+ private String getGroupKey(StatusBarNotification sbn) {
return getGroupKey(sbn.getKey(), sbn.getGroupKey());
}
@@ -721,37 +500,17 @@ public class NotificationGroupManagerLegacy implements
return groupKey;
}
- @Override
- public boolean toggleGroupExpansion(NotificationEntry entry) {
- NotificationGroup group = mGroupMap.get(getGroupKey(entry.getSbn()));
- if (group == null) {
- return false;
- }
- setGroupExpanded(group, !group.expanded);
- return group.expanded;
- }
-
private boolean isIsolated(String sbnKey) {
return mIsolatedEntries.containsKey(sbnKey);
}
/**
- * Is this notification the summary of a group?
- */
- public boolean isGroupSummary(StatusBarNotification sbn) {
- if (isIsolated(sbn.getKey())) {
- return true;
- }
- return sbn.getNotification().isGroupSummary();
- }
-
- /**
* Whether a notification is visually a group child.
*
* @param sbn notification to check
* @return true if it is visually a group child
*/
- public boolean isGroupChild(StatusBarNotification sbn) {
+ private boolean isGroupChild(StatusBarNotification sbn) {
return isGroupChild(sbn.getKey(), sbn.isGroup(), sbn.getNotification().isGroupSummary());
}
@@ -762,11 +521,6 @@ public class NotificationGroupManagerLegacy implements
return isGroup && !isGroupSummary;
}
- @Override
- public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
- updateIsolation(entry);
- }
-
/**
* Whether a notification that is normally part of a group should be temporarily isolated from
* the group and put in their own group visually. This generally happens when the notification
@@ -783,9 +537,6 @@ public class NotificationGroupManagerLegacy implements
if (isImportantConversation(entry)) {
return true;
}
- if (mHeadsUpManager != null && !mHeadsUpManager.isAlerting(entry.getKey())) {
- return false;
- }
NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey());
return (sbn.getNotification().fullScreenIntent != null
|| notificationGroup == null
@@ -825,7 +576,7 @@ public class NotificationGroupManagerLegacy implements
/**
* Update the isolation of an entry, splitting it from the group.
*/
- public void updateIsolation(NotificationEntry entry) {
+ private void updateIsolation(NotificationEntry entry) {
// We need to buffer a few events because we do isolation changes in 3 steps:
// removeInternal, update mIsolatedEntries, addInternal. This means that often the
// alertOverride will update on the removal, however processing the event in that case can
@@ -866,13 +617,6 @@ public class NotificationGroupManagerLegacy implements
|| notificationGroup.summary.isGroupNotFullyVisible();
}
- /**
- * Directly set the heads up manager to avoid circular dependencies
- */
- public void setHeadsUpManager(HeadsUpManager headsUpManager) {
- mHeadsUpManager = headsUpManager;
- }
-
@Override
public void dump(PrintWriter pw, String[] args) {
pw.println("GroupManagerLegacy state:");
@@ -893,7 +637,7 @@ public class NotificationGroupManagerLegacy implements
}
/** Get the group key, reformatted for logging, for the (optional) group */
- public static String logGroupKey(NotificationGroup group) {
+ private static String logGroupKey(NotificationGroup group) {
if (group == null) {
return "null";
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/VisualStabilityManager.java
index 456bf5148f7c..bb8c0e0d3b77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/VisualStabilityManager.java
@@ -16,84 +16,32 @@
package com.android.systemui.statusbar.notification.collection.legacy;
-import android.os.Handler;
-import android.os.SystemClock;
-import android.view.View;
-
-import androidx.collection.ArraySet;
-
-import com.android.systemui.Dumpable;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
/**
* A manager that ensures that notifications are visually stable. It will suppress reorderings
* and reorder at the right time when they are out of view.
*/
-public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpable {
+public class VisualStabilityManager {
- private static final long TEMPORARY_REORDERING_ALLOWED_DURATION = 1000;
-
- private final ArrayList<Callback> mReorderingAllowedCallbacks = new ArrayList<>();
- private final ArraySet<Callback> mPersistentReorderingCallbacks = new ArraySet<>();
- private final ArrayList<Callback> mGroupChangesAllowedCallbacks = new ArrayList<>();
- private final ArraySet<Callback> mPersistentGroupCallbacks = new ArraySet<>();
- private final Handler mHandler;
private final VisualStabilityProvider mVisualStabilityProvider;
private boolean mPanelExpanded;
private boolean mScreenOn;
- private boolean mReorderingAllowed;
- private boolean mGroupChangedAllowed;
- private boolean mIsTemporaryReorderingAllowed;
- private long mTemporaryReorderingStart;
- private VisibilityLocationProvider mVisibilityLocationProvider;
- private ArraySet<View> mAllowedReorderViews = new ArraySet<>();
- private ArraySet<NotificationEntry> mLowPriorityReorderingViews = new ArraySet<>();
- private ArraySet<View> mAddedChildren = new ArraySet<>();
private boolean mPulsing;
/**
* Injected constructor. See {@link NotificationsModule}.
*/
public VisualStabilityManager(
- NotificationEntryManager notificationEntryManager,
VisualStabilityProvider visualStabilityProvider,
- @Main Handler handler,
StatusBarStateController statusBarStateController,
- WakefulnessLifecycle wakefulnessLifecycle,
- DumpManager dumpManager) {
+ WakefulnessLifecycle wakefulnessLifecycle) {
mVisualStabilityProvider = visualStabilityProvider;
- mHandler = handler;
- dumpManager.registerDumpable(this);
-
- if (notificationEntryManager != null) {
- notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
- @Override
- public void onPreEntryUpdated(NotificationEntry entry) {
- final boolean ambientStateHasChanged =
- entry.isAmbient() != entry.getRow().isLowPriority();
- if (ambientStateHasChanged) {
- // note: entries are removed in onReorderingFinished
- mLowPriorityReorderingViews.add(entry);
- }
- }
- });
- }
if (statusBarStateController != null) {
setPulsing(statusBarStateController.isPulsing());
@@ -116,40 +64,6 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl
}
/**
- * Add a callback to invoke when reordering is allowed again.
- *
- * @param callback the callback to add
- * @param persistent {@code true} if this callback should this callback be persisted, otherwise
- * it will be removed after a single invocation
- */
- public void addReorderingAllowedCallback(Callback callback, boolean persistent) {
- if (persistent) {
- mPersistentReorderingCallbacks.add(callback);
- }
- if (mReorderingAllowedCallbacks.contains(callback)) {
- return;
- }
- mReorderingAllowedCallbacks.add(callback);
- }
-
- /**
- * Add a callback to invoke when group changes are allowed again.
- *
- * @param callback the callback to add
- * @param persistent {@code true} if this callback should this callback be persisted, otherwise
- * it will be removed after a single invocation
- */
- public void addGroupChangesAllowedCallback(Callback callback, boolean persistent) {
- if (persistent) {
- mPersistentGroupCallbacks.add(callback);
- }
- if (mGroupChangesAllowedCallbacks.contains(callback)) {
- return;
- }
- mGroupChangesAllowedCallbacks.add(callback);
- }
-
- /**
* @param screenOn whether the screen is on
*/
private void setScreenOn(boolean screenOn) {
@@ -177,133 +91,8 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl
}
private void updateAllowedStates() {
- boolean reorderingAllowed =
- (!mScreenOn || !mPanelExpanded || mIsTemporaryReorderingAllowed) && !mPulsing;
- boolean changedToTrue = reorderingAllowed && !mReorderingAllowed;
- mReorderingAllowed = reorderingAllowed;
- if (changedToTrue) {
- notifyChangeAllowed(mReorderingAllowedCallbacks, mPersistentReorderingCallbacks);
- }
+ boolean reorderingAllowed = (!mScreenOn || !mPanelExpanded) && !mPulsing;
mVisualStabilityProvider.setReorderingAllowed(reorderingAllowed);
- boolean groupChangesAllowed = (!mScreenOn || !mPanelExpanded) && !mPulsing;
- changedToTrue = groupChangesAllowed && !mGroupChangedAllowed;
- mGroupChangedAllowed = groupChangesAllowed;
- if (changedToTrue) {
- notifyChangeAllowed(mGroupChangesAllowedCallbacks, mPersistentGroupCallbacks);
- }
- }
-
- private void notifyChangeAllowed(ArrayList<Callback> callbacks,
- ArraySet<Callback> persistentCallbacks) {
- for (int i = 0; i < callbacks.size(); i++) {
- Callback callback = callbacks.get(i);
- callback.onChangeAllowed();
- if (!persistentCallbacks.contains(callback)) {
- callbacks.remove(callback);
- i--;
- }
- }
- }
-
- /**
- * @return whether reordering is currently allowed in general.
- */
- public boolean isReorderingAllowed() {
- return mReorderingAllowed;
- }
-
- /**
- * @return whether changes in the grouping should be allowed right now.
- */
- public boolean areGroupChangesAllowed() {
- return mGroupChangedAllowed;
- }
-
- /**
- * @return whether a specific notification is allowed to reorder. Certain notifications are
- * allowed to reorder even if {@link #isReorderingAllowed()} returns false, like newly added
- * notifications or heads-up notifications that are out of view.
- */
- public boolean canReorderNotification(ExpandableNotificationRow row) {
- if (mReorderingAllowed) {
- return true;
- }
- if (mAddedChildren.contains(row)) {
- return true;
- }
- if (mLowPriorityReorderingViews.contains(row.getEntry())) {
- return true;
- }
- if (mAllowedReorderViews.contains(row)
- && !mVisibilityLocationProvider.isInVisibleLocation(row.getEntry())) {
- return true;
- }
- return false;
- }
-
- public void setVisibilityLocationProvider(
- VisibilityLocationProvider visibilityLocationProvider) {
- mVisibilityLocationProvider = visibilityLocationProvider;
- }
-
- /**
- * Notifications have been reordered, so reset all the allowed list of views that are allowed
- * to reorder.
- */
- public void onReorderingFinished() {
- mAllowedReorderViews.clear();
- mAddedChildren.clear();
- mLowPriorityReorderingViews.clear();
- }
-
- @Override
- public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
- if (isHeadsUp) {
- // Heads up notifications should in general be allowed to reorder if they are out of
- // view and stay at the current location if they aren't.
- mAllowedReorderViews.add(entry.getRow());
- }
- }
-
- /**
- * Temporarily allows reordering of the entire shade for a period of 1000ms. Subsequent calls
- * to this method will extend the timer.
- */
- public void temporarilyAllowReordering() {
- mHandler.removeCallbacks(mOnTemporaryReorderingExpired);
- mHandler.postDelayed(mOnTemporaryReorderingExpired, TEMPORARY_REORDERING_ALLOWED_DURATION);
- if (!mIsTemporaryReorderingAllowed) {
- mTemporaryReorderingStart = SystemClock.elapsedRealtime();
- }
- mIsTemporaryReorderingAllowed = true;
- updateAllowedStates();
- }
-
- private final Runnable mOnTemporaryReorderingExpired = () -> {
- mIsTemporaryReorderingAllowed = false;
- updateAllowedStates();
- };
-
- /**
- * Notify the visual stability manager that a new view was added and should be allowed to
- * reorder next time.
- */
- public void notifyViewAddition(View view) {
- mAddedChildren.add(view);
- }
-
- @Override
- public void dump(PrintWriter pw, String[] args) {
- pw.println("VisualStabilityManager state:");
- pw.print(" mIsTemporaryReorderingAllowed="); pw.println(mIsTemporaryReorderingAllowed);
- pw.print(" mTemporaryReorderingStart="); pw.println(mTemporaryReorderingStart);
-
- long now = SystemClock.elapsedRealtime();
- pw.print(" Temporary reordering window has been open for ");
- pw.print(now - (mIsTemporaryReorderingAllowed ? mTemporaryReorderingStart : now));
- pw.println("ms");
-
- pw.println();
}
final WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index fac234c58850..eda2eeca0620 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -30,7 +30,6 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
-import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -53,7 +52,6 @@ import com.android.systemui.statusbar.notification.collection.coordinator.dagger
import com.android.systemui.statusbar.notification.collection.inflation.BindEventManager;
import com.android.systemui.statusbar.notification.collection.inflation.BindEventManagerImpl;
import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
-import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.inflation.OnUserInteractionCallbackImpl;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
@@ -123,22 +121,13 @@ public interface NotificationsModule {
NotificationEntryManagerLogger logger,
NotificationGroupManagerLegacy groupManager,
NotifPipelineFlags notifPipelineFlags,
- Lazy<NotificationRowBinder> notificationRowBinderLazy,
Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
LeakDetector leakDetector,
IStatusBarService statusBarService,
- DumpManager dumpManager,
@Background Executor bgExecutor) {
return new NotificationEntryManager(
- logger,
- groupManager,
- notifPipelineFlags,
- notificationRowBinderLazy,
- notificationRemoteInputManagerLazy,
- leakDetector,
- statusBarService,
- dumpManager,
- bgExecutor);
+ logger
+ );
}
/** Provides an instance of {@link NotificationGutsManager} */
@@ -162,8 +151,7 @@ public interface NotificationsModule {
Optional<BubblesManager> bubblesManagerOptional,
UiEventLogger uiEventLogger,
OnUserInteractionCallback onUserInteractionCallback,
- ShadeController shadeController,
- DumpManager dumpManager) {
+ ShadeController shadeController) {
return new NotificationGutsManager(
context,
centralSurfacesOptionalLazy,
@@ -181,8 +169,8 @@ public interface NotificationsModule {
bubblesManagerOptional,
uiEventLogger,
onUserInteractionCallback,
- shadeController,
- dumpManager);
+ shadeController
+ );
}
/** Provides an instance of {@link NotifGutsViewManager} */
@@ -193,19 +181,14 @@ public interface NotificationsModule {
@SysUISingleton
@Provides
static VisualStabilityManager provideVisualStabilityManager(
- NotificationEntryManager notificationEntryManager,
VisualStabilityProvider visualStabilityProvider,
- @Main Handler handler,
StatusBarStateController statusBarStateController,
- WakefulnessLifecycle wakefulnessLifecycle,
- DumpManager dumpManager) {
+ WakefulnessLifecycle wakefulnessLifecycle) {
return new VisualStabilityManager(
- notificationEntryManager,
visualStabilityProvider,
- handler,
statusBarStateController,
- wakefulnessLifecycle,
- dumpManager);
+ wakefulnessLifecycle
+ );
}
/** Provides an instance of {@link NotificationLogger} */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpController.java
deleted file mode 100644
index 74fb3f7f9f66..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpController.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.systemui.statusbar.notification.interruption;
-
-import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY;
-
-import android.app.Notification;
-import android.service.notification.StatusBarNotification;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.NotificationListener;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
-
-import javax.inject.Inject;
-
-/**
- * Controller class for old pipeline heads up logic. It listens to {@link NotificationEntryManager}
- * entry events and appropriately binds or unbinds the heads up view and promotes it to the top
- * of the screen.
- */
-@SysUISingleton
-public class HeadsUpController {
- private final HeadsUpViewBinder mHeadsUpViewBinder;
- private final NotificationInterruptStateProvider mInterruptStateProvider;
- private final NotificationRemoteInputManager mRemoteInputManager;
- private final VisualStabilityManager mVisualStabilityManager;
- private final StatusBarStateController mStatusBarStateController;
- private final NotificationListener mNotificationListener;
- private final HeadsUpManager mHeadsUpManager;
-
- @Inject
- HeadsUpController(
- HeadsUpViewBinder headsUpViewBinder,
- NotificationInterruptStateProvider notificationInterruptStateProvider,
- HeadsUpManager headsUpManager,
- NotificationRemoteInputManager remoteInputManager,
- StatusBarStateController statusBarStateController,
- VisualStabilityManager visualStabilityManager,
- NotificationListener notificationListener) {
- mHeadsUpViewBinder = headsUpViewBinder;
- mHeadsUpManager = headsUpManager;
- mInterruptStateProvider = notificationInterruptStateProvider;
- mRemoteInputManager = remoteInputManager;
- mStatusBarStateController = statusBarStateController;
- mVisualStabilityManager = visualStabilityManager;
- mNotificationListener = notificationListener;
- }
-
- /**
- * Attach this controller and add its listeners.
- */
- public void attach(
- NotificationEntryManager entryManager,
- HeadsUpManager headsUpManager) {
- entryManager.addCollectionListener(mCollectionListener);
- headsUpManager.addListener(mOnHeadsUpChangedListener);
- }
-
- private NotifCollectionListener mCollectionListener = new NotifCollectionListener() {
- @Override
- public void onEntryAdded(NotificationEntry entry) {
- if (mInterruptStateProvider.shouldHeadsUp(entry)) {
- mHeadsUpViewBinder.bindHeadsUpView(
- entry, HeadsUpController.this::showAlertingView);
- }
- }
-
- @Override
- public void onEntryUpdated(NotificationEntry entry) {
- updateHunState(entry);
- }
-
- @Override
- public void onEntryRemoved(NotificationEntry entry, int reason) {
- stopAlerting(entry);
- }
-
- @Override
- public void onEntryCleanUp(NotificationEntry entry) {
- mHeadsUpViewBinder.abortBindCallback(entry);
- }
- };
-
- /**
- * Adds the entry to the HUN manager and show it for the first time.
- */
- private void showAlertingView(NotificationEntry entry) {
- mHeadsUpManager.showNotification(entry);
- if (!mStatusBarStateController.isDozing()) {
- // Mark as seen immediately
- setNotificationShown(entry.getSbn());
- }
- }
-
- private void updateHunState(NotificationEntry entry) {
- boolean hunAgain = alertAgain(entry, entry.getSbn().getNotification());
- // includes check for whether this notification should be filtered:
- boolean shouldHeadsUp = mInterruptStateProvider.shouldHeadsUp(entry);
- final boolean wasHeadsUp = mHeadsUpManager.isAlerting(entry.getKey());
- if (wasHeadsUp) {
- if (shouldHeadsUp) {
- mHeadsUpManager.updateNotification(entry.getKey(), hunAgain);
- } else {
- // We don't want this to be interrupting anymore, let's remove it
- mHeadsUpManager.removeNotification(entry.getKey(), false /* removeImmediately */);
- }
- } else if (shouldHeadsUp && hunAgain) {
- mHeadsUpViewBinder.bindHeadsUpView(entry, mHeadsUpManager::showNotification);
- }
- }
-
- private void setNotificationShown(StatusBarNotification n) {
- try {
- mNotificationListener.setNotificationsShown(new String[]{n.getKey()});
- } catch (RuntimeException e) {
- Log.d(TAG, "failed setNotificationsShown: ", e);
- }
- }
-
- private void stopAlerting(NotificationEntry entry) {
- // Attempt to remove notifications from their HUN manager.
- // Though the remove itself may fail, it lets the manager know to remove as soon as
- // possible.
- String key = entry.getKey();
- if (mHeadsUpManager.isAlerting(key)) {
- // A cancel() in response to a remote input shouldn't be delayed, as it makes the
- // sending look longer than it takes.
- // Also we should not defer the removal if reordering isn't allowed since otherwise
- // some notifications can't disappear before the panel is closed.
- boolean ignoreEarliestRemovalTime =
- mRemoteInputManager.isSpinning(key)
- && !FORCE_REMOTE_INPUT_HISTORY
- || !mVisualStabilityManager.isReorderingAllowed();
- mHeadsUpManager.removeNotification(key, ignoreEarliestRemovalTime);
- }
- }
-
- /**
- * Checks whether an update for a notification warrants an alert for the user.
- *
- * @param oldEntry the entry for this notification.
- * @param newNotification the new notification for this entry.
- * @return whether this notification should alert the user.
- */
- public static boolean alertAgain(
- NotificationEntry oldEntry, Notification newNotification) {
- return oldEntry == null || !oldEntry.hasInterrupted()
- || (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0;
- }
-
- private OnHeadsUpChangedListener mOnHeadsUpChangedListener = new OnHeadsUpChangedListener() {
- @Override
- public void onHeadsUpStateChanged(@NonNull NotificationEntry entry, boolean isHeadsUp) {
- if (!isHeadsUp && !entry.getRow().isRemoved()) {
- mHeadsUpViewBinder.unbindHeadsUpView(entry);
- }
- }
- };
-
- private static final String TAG = "HeadsUpBindController";
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinder.java
index 5ef2b9e55d9e..6f41425b506d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinder.java
@@ -41,8 +41,7 @@ import javax.inject.Inject;
* figuring out the right heads up inflation parameters and inflating/freeing the heads up
* content view.
*
- * TODO: This should be moved into {@link HeadsUpCoordinator} when the old pipeline is deprecated
- * (i.e. when {@link HeadsUpController} is removed).
+ * TODO: This should be moved into {@link HeadsUpCoordinator} when the old pipeline is deprecated.
*/
@SysUISingleton
public class HeadsUpViewBinder {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 3c018022085d..9ad906c83e10 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -94,7 +94,6 @@ import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorController;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.logging.NotificationCounters;
@@ -904,21 +903,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return mChildrenContainer == null ? null : mChildrenContainer.getAttachedChildren();
}
- /**
- * Apply the order given in the list to the children.
- *
- * @param childOrder the new list order
- * @param visualStabilityManager
- * @param callback the callback to invoked in case it is not allowed
- * @return whether the list order has changed
- */
- public boolean applyChildOrder(List<ExpandableNotificationRow> childOrder,
- VisualStabilityManager visualStabilityManager,
- VisualStabilityManager.Callback callback) {
- return mChildrenContainer != null && mChildrenContainer.applyChildOrder(childOrder,
- visualStabilityManager, callback);
- }
-
/** Updates states of all children. */
public void updateChildrenStates(AmbientState ambientState) {
if (mIsSummaryWithChildren) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index c4ff2599c2ce..7b0b0ce3a691 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -55,7 +55,6 @@ import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.shade.ShadeController;
-import com.android.systemui.statusbar.NotificationLifetimeExtender;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.StatusBarState;
@@ -81,8 +80,7 @@ import dagger.Lazy;
* Handles various NotificationGuts related tasks, such as binding guts to a row, opening and
* closing guts, and keeping track of the currently exposed notification guts.
*/
-public class NotificationGutsManager implements Dumpable, NotificationLifetimeExtender,
- NotifGutsViewManager {
+public class NotificationGutsManager implements NotifGutsViewManager {
private static final String TAG = "NotificationGutsManager";
// Must match constant in Settings. Used to highlight preferences when linking to Settings.
@@ -107,13 +105,10 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
// which notification is currently being longpress-examined by the user
private NotificationGuts mNotificationGutsExposed;
private NotificationMenuRowPlugin.MenuItem mGutsMenuItem;
- private NotificationSafeToRemoveCallback mNotificationLifetimeFinishedCallback;
private NotificationPresenter mPresenter;
private NotificationActivityStarter mNotificationActivityStarter;
private NotificationListContainer mListContainer;
private OnSettingsClickListener mOnSettingsClickListener;
- @VisibleForTesting
- protected String mKeyToRemoveOnGutsClosed;
private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
private final Handler mMainHandler;
@@ -148,8 +143,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
Optional<BubblesManager> bubblesManagerOptional,
UiEventLogger uiEventLogger,
OnUserInteractionCallback onUserInteractionCallback,
- ShadeController shadeController,
- DumpManager dumpManager) {
+ ShadeController shadeController) {
mContext = context;
mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mMainHandler = mainHandler;
@@ -167,8 +161,6 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
mUiEventLogger = uiEventLogger;
mOnUserInteractionCallback = onUserInteractionCallback;
mShadeController = shadeController;
-
- dumpManager.registerDumpable(this);
}
public void setUpWithPresenter(NotificationPresenter presenter,
@@ -266,12 +258,6 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
mGutsListener.onGutsClose(entry);
}
String key = entry.getKey();
- if (key.equals(mKeyToRemoveOnGutsClosed)) {
- mKeyToRemoveOnGutsClosed = null;
- if (mNotificationLifetimeFinishedCallback != null) {
- mNotificationLifetimeFinishedCallback.onSafeToRemove(key);
- }
- }
});
View gutsView = item.getGutsView();
@@ -658,46 +644,6 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
return true;
}
- @Override
- public void setCallback(NotificationSafeToRemoveCallback callback) {
- mNotificationLifetimeFinishedCallback = callback;
- }
-
- @Override
- public boolean shouldExtendLifetime(NotificationEntry entry) {
- return entry != null
- &&(mNotificationGutsExposed != null
- && entry.getGuts() != null
- && mNotificationGutsExposed == entry.getGuts()
- && !mNotificationGutsExposed.isLeavebehind());
- }
-
- @Override
- public void setShouldManageLifetime(NotificationEntry entry, boolean shouldExtend) {
- if (shouldExtend) {
- mKeyToRemoveOnGutsClosed = entry.getKey();
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Keeping notification because it's showing guts. " + entry.getKey());
- }
- } else {
- if (mKeyToRemoveOnGutsClosed != null
- && mKeyToRemoveOnGutsClosed.equals(entry.getKey())) {
- mKeyToRemoveOnGutsClosed = null;
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Notification that was kept for guts was updated. "
- + entry.getKey());
- }
- }
- }
- }
-
- @Override
- public void dump(PrintWriter pw, String[] args) {
- pw.println("NotificationGutsManager state:");
- pw.print(" mKeyToRemoveOnGutsClosed (legacy): ");
- pw.println(mKeyToRemoveOnGutsClosed);
- }
-
/**
* @param gutsListener the listener for open and close guts events
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index a76f0827fc18..d77e03fd043d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -43,7 +43,6 @@ import com.android.systemui.statusbar.NotificationGroupingUtil;
import com.android.systemui.statusbar.notification.FeedbackIcon;
import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.NotificationUtils;
-import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.HybridGroupManager;
@@ -462,39 +461,6 @@ public class NotificationChildrenContainer extends ViewGroup
return mAttachedChildren;
}
- /**
- * Apply the order given in the list to the children.
- *
- * @param childOrder the new list order
- * @param visualStabilityManager
- * @param callback
- * @return whether the list order has changed
- */
- public boolean applyChildOrder(List<ExpandableNotificationRow> childOrder,
- VisualStabilityManager visualStabilityManager,
- VisualStabilityManager.Callback callback) {
- if (childOrder == null) {
- return false;
- }
- boolean result = false;
- for (int i = 0; i < mAttachedChildren.size() && i < childOrder.size(); i++) {
- ExpandableNotificationRow child = mAttachedChildren.get(i);
- ExpandableNotificationRow desiredChild = childOrder.get(i);
- if (child != desiredChild) {
- if (visualStabilityManager.canReorderNotification(desiredChild)) {
- mAttachedChildren.remove(desiredChild);
- mAttachedChildren.add(i, desiredChild);
- result = true;
- } else {
- visualStabilityManager.addReorderingAllowedCallback(callback,
- false /* persistent */);
- }
- }
- }
- updateExpansionStates();
- return result;
- }
-
/** To be called any time the rows have been updated */
public void updateExpansionStates() {
if (mChildrenExpanded || mUserLocked) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index 54e26c34522d..91a28139c775 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -20,9 +20,6 @@ import android.util.Log
import android.view.View
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.media.KeyguardMediaController
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.statusbar.StatusBarState
-import com.android.systemui.statusbar.notification.NotifPipelineFlags
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.collection.render.MediaContainerController
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController
@@ -33,32 +30,20 @@ import com.android.systemui.statusbar.notification.dagger.PeopleHeader
import com.android.systemui.statusbar.notification.dagger.SilentHeader
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
-import com.android.systemui.statusbar.notification.row.StackScrollerDecorView
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.SectionProvider
import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.util.children
import com.android.systemui.util.foldToSparseArray
-import com.android.systemui.util.takeUntil
-import com.android.systemui.util.traceSection
import javax.inject.Inject
/**
- * Manages the boundaries of the notification sections (incoming, conversations, high priority, and
- * low priority).
- *
- * In the legacy notification pipeline, this is responsible for correctly positioning all section
- * headers after the [NotificationStackScrollLayout] has had notifications added/removed/changed. In
- * the new pipeline, this is handled as part of the [ShadeViewManager].
+ * Manages section headers in the NSSL.
*
* TODO: Move remaining sections logic from NSSL into this class.
*/
class NotificationSectionsManager @Inject internal constructor(
- private val statusBarStateController: StatusBarStateController,
private val configurationController: ConfigurationController,
private val keyguardMediaController: KeyguardMediaController,
private val sectionsFeatureManager: NotificationSectionsFeatureManager,
- private val logger: NotificationSectionsLogger,
- private val notifPipelineFlags: NotifPipelineFlags,
private val mediaContainerController: MediaContainerController,
@IncomingHeader private val incomingHeaderController: SectionHeaderController,
@PeopleHeader private val peopleHeaderController: SectionHeaderController,
@@ -139,205 +124,6 @@ class NotificationSectionsManager @Inject internal constructor(
else -> null
}
- private fun logShadeChild(i: Int, child: View) {
- when {
- child === incomingHeaderView -> logger.logIncomingHeader(i)
- child === mediaControlsView -> logger.logMediaControls(i)
- child === peopleHeaderView -> logger.logConversationsHeader(i)
- child === alertingHeaderView -> logger.logAlertingHeader(i)
- child === silentHeaderView -> logger.logSilentHeader(i)
- child !is ExpandableNotificationRow -> logger.logOther(i, child.javaClass)
- else -> {
- val isHeadsUp = child.isHeadsUp
- when (child.entry.bucket) {
- BUCKET_HEADS_UP -> logger.logHeadsUp(i, isHeadsUp)
- BUCKET_PEOPLE -> logger.logConversation(i, isHeadsUp)
- BUCKET_ALERTING -> logger.logAlerting(i, isHeadsUp)
- BUCKET_SILENT -> logger.logSilent(i, isHeadsUp)
- }
- }
- }
- }
- private fun logShadeContents() = traceSection("NotifSectionsManager.logShadeContents") {
- parent.children.forEachIndexed(::logShadeChild)
- }
-
- private val isUsingMultipleSections: Boolean
- get() = sectionsFeatureManager.getNumberOfBuckets() > 1
-
- @VisibleForTesting
- fun updateSectionBoundaries() = updateSectionBoundaries("test")
-
- private interface SectionUpdateState<out T : ExpandableView> {
- val header: T
- var currentPosition: Int?
- var targetPosition: Int?
- fun adjustViewPosition()
- }
-
- private fun <T : ExpandableView> expandableViewHeaderState(header: T): SectionUpdateState<T> =
- object : SectionUpdateState<T> {
- override val header = header
- override var currentPosition: Int? = null
- override var targetPosition: Int? = null
-
- override fun adjustViewPosition() {
- notifPipelineFlags.checkLegacyPipelineEnabled()
- val target = targetPosition
- val current = currentPosition
- if (target == null) {
- if (current != null) {
- parent.removeView(header)
- }
- } else {
- if (current == null) {
- // If the header is animating away, it will still have a parent, so
- // detach it first
- // TODO: We should really cancel the active animations here. This will
- // happen automatically when the view's intro animation starts, but
- // it's a fragile link.
- header.removeFromTransientContainer()
- parent.addView(header, target)
- } else {
- parent.changeViewPosition(header, target)
- }
- }
- }
- }
-
- private fun <T : StackScrollerDecorView> decorViewHeaderState(
- header: T
- ): SectionUpdateState<T> {
- notifPipelineFlags.checkLegacyPipelineEnabled()
- val inner = expandableViewHeaderState(header)
- return object : SectionUpdateState<T> by inner {
- override fun adjustViewPosition() {
- inner.adjustViewPosition()
- if (targetPosition != null && currentPosition == null) {
- header.isContentVisible = true
- }
- }
- }
- }
-
- /**
- * Should be called whenever notifs are added, removed, or updated. Updates section boundary
- * bookkeeping and adds/moves/removes section headers if appropriate.
- */
- fun updateSectionBoundaries(reason: String) = traceSection("NotifSectionsManager.update") {
- notifPipelineFlags.checkLegacyPipelineEnabled()
- if (!isUsingMultipleSections) {
- return@traceSection
- }
- logger.logStartSectionUpdate(reason)
-
- // The overall strategy here is to iterate over the current children of mParent, looking
- // for where the sections headers are currently positioned, and where each section begins.
- // Then, once we find the start of a new section, we track that position as the "target" for
- // the section header, adjusted for the case where existing headers are in front of that
- // target, but won't be once they are moved / removed after the pass has completed.
-
- val showHeaders = statusBarStateController.state != StatusBarState.KEYGUARD
- val usingMediaControls = sectionsFeatureManager.isMediaControlsEnabled()
-
- val mediaState = mediaControlsView?.let(::expandableViewHeaderState)
- val incomingState = incomingHeaderView?.let(::decorViewHeaderState)
- val peopleState = peopleHeaderView?.let(::decorViewHeaderState)
- val alertingState = alertingHeaderView?.let(::decorViewHeaderState)
- val gentleState = silentHeaderView?.let(::decorViewHeaderState)
-
- fun getSectionState(view: View): SectionUpdateState<ExpandableView>? = when {
- view === mediaControlsView -> mediaState
- view === incomingHeaderView -> incomingState
- view === peopleHeaderView -> peopleState
- view === alertingHeaderView -> alertingState
- view === silentHeaderView -> gentleState
- else -> null
- }
-
- val headersOrdered = sequenceOf(
- mediaState, incomingState, peopleState, alertingState, gentleState
- ).filterNotNull()
-
- var peopleNotifsPresent = false
- var nextBucket: Int? = null
- var inIncomingSection = false
-
- // Iterating backwards allows for easier construction of the Incoming section, as opposed
- // to backtracking when a discontinuity in the sections is discovered.
- // Iterating to -1 in order to support the case where a header is at the very top of the
- // shade.
- for (i in parent.childCount - 1 downTo -1) {
- val child: View? = parent.getChildAt(i)
-
- child?.let {
- logShadeChild(i, child)
- // If this child is a header, update the tracked positions
- getSectionState(child)?.let { state ->
- state.currentPosition = i
- // If headers that should appear above this one in the shade already have a
- // target index, then we need to decrement them in order to account for this one
- // being either removed, or moved below them.
- headersOrdered.takeUntil { it === state }
- .forEach { it.targetPosition = it.targetPosition?.minus(1) }
- }
- }
-
- val row = (child as? ExpandableNotificationRow)
- ?.takeUnless { it.visibility == View.GONE }
-
- // Is there a section discontinuity? This usually occurs due to HUNs
- inIncomingSection = inIncomingSection || nextBucket?.let { next ->
- row?.entry?.bucket?.let { curr -> next < curr }
- } == true
-
- if (inIncomingSection) {
- // Update the bucket to reflect that it's being placed in the Incoming section
- row?.entry?.bucket = BUCKET_HEADS_UP
- }
-
- // Insert a header in front of the next row, if there's a boundary between it and this
- // row, or if it is the topmost row.
- val isSectionBoundary = nextBucket != null &&
- (child == null || row != null && nextBucket != row.entry.bucket)
- if (isSectionBoundary && showHeaders) {
- when (nextBucket) {
- BUCKET_SILENT -> gentleState?.targetPosition = i + 1
- }
- }
-
- row ?: continue
-
- // Check if there are any people notifications
- peopleNotifsPresent = peopleNotifsPresent || row.entry.bucket == BUCKET_PEOPLE
- nextBucket = row.entry.bucket
- }
-
- mediaState?.targetPosition = if (usingMediaControls) 0 else null
-
- logger.logStr("New header target positions:")
- logger.logMediaControls(mediaState?.targetPosition ?: -1)
- logger.logIncomingHeader(incomingState?.targetPosition ?: -1)
- logger.logConversationsHeader(peopleState?.targetPosition ?: -1)
- logger.logAlertingHeader(alertingState?.targetPosition ?: -1)
- logger.logSilentHeader(gentleState?.targetPosition ?: -1)
-
- // Update headers in reverse order to preserve indices, otherwise movements earlier in the
- // list will affect the target indices of the headers later in the list.
- headersOrdered.asIterable().reversed().forEach { it.adjustViewPosition() }
-
- logger.logStr("Final order:")
- logShadeContents()
- logger.logStr("Section boundary update complete")
-
- // Update headers to reflect state of section contents
- silentHeaderView?.run {
- val hasActiveClearableNotifications = this@NotificationSectionsManager.parent
- .hasActiveClearableNotifications(NotificationStackScrollLayout.ROWS_GENTLE)
- setClearSectionButtonEnabled(hasActiveClearableNotifications)
- }
- }
-
private sealed class SectionBounds {
data class Many(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 427004e71425..952bafbe6eb3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -5822,12 +5822,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mSpeedBumpIndexDirty = true;
}
- /** Updates the indices of the boundaries between sections. */
- @ShadeViewRefactor(RefactorComponent.INPUT)
- public void updateSectionBoundaries(String reason) {
- mSectionsManager.updateSectionBoundaries(reason);
- }
-
void updateContinuousBackgroundDrawing() {
boolean continuousBackground = !mAmbientState.isFullyAwake()
&& mSwipeHelper.isSwiping();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index cc539b01b894..9998fe41b775 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -90,7 +90,6 @@ import com.android.systemui.statusbar.notification.collection.PipelineDumpable;
import com.android.systemui.statusbar.notification.collection.PipelineDumper;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.OnGroupChangeListener;
-import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
@@ -162,7 +161,6 @@ public class NotificationStackScrollLayoutController {
private final NotificationEntryManager mNotificationEntryManager;
private final UiEventLogger mUiEventLogger;
private final NotificationRemoteInputManager mRemoteInputManager;
- private final VisualStabilityManager mVisualStabilityManager;
private final ShadeController mShadeController;
private final KeyguardMediaController mKeyguardMediaController;
private final SysuiStatusBarStateController mStatusBarStateController;
@@ -646,7 +644,6 @@ public class NotificationStackScrollLayoutController {
ShadeTransitionController shadeTransitionController,
UiEventLogger uiEventLogger,
NotificationRemoteInputManager remoteInputManager,
- VisualStabilityManager visualStabilityManager,
ShadeController shadeController,
InteractionJankMonitor jankMonitor,
StackStateLogger stackLogger,
@@ -693,7 +690,6 @@ public class NotificationStackScrollLayoutController {
mNotificationEntryManager = notificationEntryManager;
mUiEventLogger = uiEventLogger;
mRemoteInputManager = remoteInputManager;
- mVisualStabilityManager = visualStabilityManager;
mShadeController = shadeController;
updateResources();
}
@@ -765,8 +761,6 @@ public class NotificationStackScrollLayoutController {
mNotificationRoundnessManager.setOnRoundingChangedCallback(mView::invalidate);
mView.addOnExpandedHeightChangedListener(mNotificationRoundnessManager::setExpanded);
- mVisualStabilityManager.setVisibilityLocationProvider(this::isInVisibleLocation);
-
mTunerService.addTunable(
(key, newValue) -> {
switch (key) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index a5bf88e1770b..b0a5adb62339 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -206,7 +206,6 @@ import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
-import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
@@ -689,7 +688,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
WakefulnessLifecycle wakefulnessLifecycle,
SysuiStatusBarStateController statusBarStateController,
Optional<Bubbles> bubblesOptional,
- VisualStabilityManager visualStabilityManager,
DeviceProvisionedController deviceProvisionedController,
NavigationBarController navigationBarController,
AccessibilityFloatingMenuController accessibilityFloatingMenuController,
@@ -777,7 +775,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
mWakefulnessLifecycle = wakefulnessLifecycle;
mStatusBarStateController = statusBarStateController;
mBubblesOptional = bubblesOptional;
- mVisualStabilityManager = visualStabilityManager;
mDeviceProvisionedController = deviceProvisionedController;
mNavigationBarController = navigationBarController;
mAccessibilityFloatingMenuController = accessibilityFloatingMenuController;
@@ -3914,9 +3911,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
// all notifications
protected NotificationStackScrollLayout mStackScroller;
- // handling reordering
- private final VisualStabilityManager mVisualStabilityManager;
-
protected AccessibilityManager mAccessibilityManager;
protected boolean mDeviceInteractive;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index b99b4ab16812..4c9c75baf0e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -330,14 +330,6 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
dumpInternal(pw, args);
}
- @Override
- public boolean shouldExtendLifetime(NotificationEntry entry) {
- // We should not defer the removal if reordering isn't allowed since otherwise
- // these won't disappear until reordering is allowed again, which happens only once
- // the notification panel is collapsed again.
- return mVisualStabilityProvider.isReorderingAllowed() && super.shouldExtendLifetime(entry);
- }
-
///////////////////////////////////////////////////////////////////////////////////////////////
// OnReorderingAllowedListener:
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/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProvider.kt
index 96b9aca9c64c..276375004f76 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProvider.kt
@@ -17,11 +17,14 @@
package com.android.systemui.statusbar.phone
import android.annotation.ColorInt
+import android.app.WallpaperManager
import android.graphics.Color
+import android.os.Handler
import android.os.RemoteException
import android.view.IWindowManager
import com.android.systemui.Dumpable
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
@@ -37,6 +40,8 @@ constructor(
private val windowManager: IWindowManager,
@Background private val backgroundExecutor: Executor,
private val dumpManager: DumpManager,
+ private val wallpaperManager: WallpaperManager,
+ @Main private val mainHandler: Handler,
) : CentralSurfacesComponent.Startable, Dumpable {
@ColorInt
@@ -46,9 +51,18 @@ constructor(
var isLetterboxBackgroundMultiColored: Boolean = false
private set
+ private val wallpaperColorsListener =
+ WallpaperManager.OnColorsChangedListener { _, _ ->
+ fetchBackgroundColorInfo()
+ }
+
override fun start() {
dumpManager.registerDumpable(javaClass.simpleName, this)
+ fetchBackgroundColorInfo()
+ wallpaperManager.addOnColorsChangedListener(wallpaperColorsListener, mainHandler)
+ }
+ private fun fetchBackgroundColorInfo() {
// Using a background executor, as binder calls to IWindowManager are blocking
backgroundExecutor.execute {
try {
@@ -62,6 +76,7 @@ constructor(
override fun stop() {
dumpManager.unregisterDumpable(javaClass.simpleName)
+ wallpaperManager.removeOnColorsChangedListener(wallpaperColorsListener)
}
override fun dump(pw: PrintWriter, args: Array<out String>) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
deleted file mode 100644
index ca6e67ec4a83..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ /dev/null
@@ -1,717 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import static com.android.systemui.statusbar.notification.NotificationUtils.logKey;
-import static com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.logGroupKey;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.Notification;
-import android.os.SystemClock;
-import android.service.notification.StatusBarNotification;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import com.android.internal.statusbar.NotificationVisibility;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.NotificationGroup;
-import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
-import com.android.systemui.statusbar.notification.row.RowContentBindParams;
-import com.android.systemui.statusbar.notification.row.RowContentBindStage;
-import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
-import com.android.systemui.util.Compile;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-import javax.inject.Inject;
-
-/**
- * A helper class dealing with the alert interactions between {@link NotificationGroupManagerLegacy}
- * and {@link HeadsUpManager}. In particular, this class deals with keeping
- * the correct notification in a group alerting based off the group suppression and alertOverride.
- */
-@SysUISingleton
-public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedListener,
- StateListener {
-
- private static final long ALERT_TRANSFER_TIMEOUT = 300;
- private static final String TAG = "NotifGroupAlertTransfer";
- private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
- private static final boolean SPEW = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE);
-
- /**
- * The list of entries containing group alert metadata for each group. Keyed by group key.
- */
- private final ArrayMap<String, GroupAlertEntry> mGroupAlertEntries = new ArrayMap<>();
-
- /**
- * The list of entries currently inflating that should alert after inflation. Keyed by
- * notification key.
- */
- private final ArrayMap<String, PendingAlertInfo> mPendingAlerts = new ArrayMap<>();
-
- private HeadsUpManager mHeadsUpManager;
- private final RowContentBindStage mRowContentBindStage;
- private final NotificationGroupManagerLegacy mGroupManager;
-
- private NotificationEntryManager mEntryManager;
-
- private boolean mIsDozing;
-
- /**
- * Injected constructor. See {@link StatusBarPhoneModule}.
- */
- @Inject
- public NotificationGroupAlertTransferHelper(
- RowContentBindStage bindStage,
- StatusBarStateController statusBarStateController,
- NotificationGroupManagerLegacy notificationGroupManagerLegacy) {
- mRowContentBindStage = bindStage;
- mGroupManager = notificationGroupManagerLegacy;
- statusBarStateController.addCallback(this);
- }
-
- /** Causes the TransferHelper to register itself as a listener to the appropriate classes. */
- public void bind(NotificationEntryManager entryManager,
- NotificationGroupManagerLegacy groupManager) {
- if (mEntryManager != null) {
- throw new IllegalStateException("Already bound.");
- }
-
- // TODO(b/119637830): It would be good if GroupManager already had all pending notifications
- // as normal children (i.e. add notifications to GroupManager before inflation) so that we
- // don't have to have this dependency. We'd also have to worry less about the suppression
- // not being up to date.
- mEntryManager = entryManager;
-
- mEntryManager.addNotificationEntryListener(mNotificationEntryListener);
- groupManager.registerGroupChangeListener(mOnGroupChangeListener);
- }
-
- /**
- * Whether or not a notification has transferred its alert state to the notification and
- * the notification should alert after inflating.
- *
- * @param entry notification to check
- * @return true if the entry was transferred to and should inflate + alert
- */
- public boolean isAlertTransferPending(@NonNull NotificationEntry entry) {
- PendingAlertInfo alertInfo = mPendingAlerts.get(entry.getKey());
- return alertInfo != null && alertInfo.isStillValid();
- }
-
- public void setHeadsUpManager(HeadsUpManager headsUpManager) {
- mHeadsUpManager = headsUpManager;
- }
-
- @Override
- public void onStateChanged(int newState) {}
-
- @Override
- public void onDozingChanged(boolean isDozing) {
- if (mIsDozing != isDozing) {
- for (GroupAlertEntry groupAlertEntry : mGroupAlertEntries.values()) {
- groupAlertEntry.mLastAlertTransferTime = 0;
- groupAlertEntry.mAlertSummaryOnNextAddition = false;
- }
- }
- mIsDozing = isDozing;
- }
-
- private final NotificationGroupManagerLegacy.OnGroupChangeListener mOnGroupChangeListener =
- new NotificationGroupManagerLegacy.OnGroupChangeListener() {
- @Override
- public void onGroupCreated(NotificationGroup group, String groupKey) {
- mGroupAlertEntries.put(groupKey, new GroupAlertEntry(group));
- }
-
- @Override
- public void onGroupRemoved(NotificationGroup group, String groupKey) {
- mGroupAlertEntries.remove(groupKey);
- }
-
- @Override
- public void onGroupSuppressionChanged(NotificationGroup group, boolean suppressed) {
- if (DEBUG) {
- Log.d(TAG, "!! onGroupSuppressionChanged:"
- + " group=" + logGroupKey(group)
- + " group.summary=" + logKey(group.summary)
- + " suppressed=" + suppressed);
- }
- NotificationEntry oldAlertOverride = group.alertOverride;
- onGroupChanged(group, oldAlertOverride);
- }
-
- @Override
- public void onGroupAlertOverrideChanged(NotificationGroup group,
- @Nullable NotificationEntry oldAlertOverride,
- @Nullable NotificationEntry newAlertOverride) {
- if (DEBUG) {
- Log.d(TAG, "!! onGroupAlertOverrideChanged:"
- + " group=" + logGroupKey(group)
- + " group.summary=" + logKey(group.summary)
- + " oldAlertOverride=" + logKey(oldAlertOverride)
- + " newAlertOverride=" + logKey(newAlertOverride));
- }
- onGroupChanged(group, oldAlertOverride);
- }
- };
-
- /**
- * Called when either the suppressed or alertOverride fields of the group changed
- *
- * @param group the group which changed
- * @param oldAlertOverride the previous value of group.alertOverride
- */
- private void onGroupChanged(NotificationGroup group,
- NotificationEntry oldAlertOverride) {
- // Group summary can be null if we are no longer suppressed because the summary was
- // removed. In that case, we don't need to alert the summary.
- if (group.summary == null) {
- if (DEBUG) {
- Log.d(TAG, "onGroupChanged: summary is null");
- }
- return;
- }
- if (group.suppressed || group.alertOverride != null) {
- checkForForwardAlertTransfer(group.summary, oldAlertOverride);
- } else {
- if (DEBUG) {
- Log.d(TAG, "onGroupChanged: maybe transfer back");
- }
- GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(mGroupManager.getGroupKey(
- group.summary.getSbn()));
- // Group is no longer suppressed or overridden.
- // We should check if we need to transfer the alert back to the summary.
- if (groupAlertEntry.mAlertSummaryOnNextAddition) {
- if (!mHeadsUpManager.isAlerting(group.summary.getKey())) {
- alertNotificationWhenPossible(group.summary);
- }
- groupAlertEntry.mAlertSummaryOnNextAddition = false;
- } else {
- checkShouldTransferBack(groupAlertEntry);
- }
- }
- }
-
- @Override
- public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
- if (DEBUG) {
- Log.d(TAG, "!! onHeadsUpStateChanged:"
- + " entry=" + logKey(entry)
- + " isHeadsUp=" + isHeadsUp);
- }
- if (isHeadsUp && entry.getSbn().getNotification().isGroupSummary()) {
- // a group summary is alerting; trigger the forward transfer checks
- checkForForwardAlertTransfer(entry, /* oldAlertOverride */ null);
- }
- }
-
- /**
- * Handles changes in a group's suppression or alertOverride, but where at least one of those
- * conditions is still true (either the group is suppressed, the group has an alertOverride,
- * or both). The method determined which kind of child needs to receive the alert, finds the
- * entry currently alerting, and makes the transfer.
- *
- * Internally, this is handled with two main cases: the override needs the alert, or there is
- * no override but the summary is suppressed (so an isolated child needs the alert).
- *
- * @param summary the notification entry of the summary of the logical group.
- * @param oldAlertOverride the former value of group.alertOverride, before whatever event
- * required us to check for for a transfer condition.
- */
- private void checkForForwardAlertTransfer(NotificationEntry summary,
- NotificationEntry oldAlertOverride) {
- if (DEBUG) {
- Log.d(TAG, "checkForForwardAlertTransfer: enter");
- }
- NotificationGroup group = mGroupManager.getGroupForSummary(summary.getSbn());
- if (group != null && group.alertOverride != null) {
- handleOverriddenSummaryAlerted(summary);
- } else if (mGroupManager.isSummaryOfSuppressedGroup(summary.getSbn())) {
- handleSuppressedSummaryAlerted(summary, oldAlertOverride);
- }
- if (DEBUG) {
- Log.d(TAG, "checkForForwardAlertTransfer: done");
- }
- }
-
- private final NotificationEntryListener mNotificationEntryListener =
- new NotificationEntryListener() {
- // Called when a new notification has been posted but is not inflated yet. We use this to
- // see as early as we can if we need to abort a transfer.
- @Override
- public void onPendingEntryAdded(NotificationEntry entry) {
- if (DEBUG) {
- Log.d(TAG, "!! onPendingEntryAdded: entry=" + logKey(entry));
- }
- String groupKey = mGroupManager.getGroupKey(entry.getSbn());
- GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(groupKey);
- if (groupAlertEntry != null && groupAlertEntry.mGroup.alertOverride == null) {
- // new pending group entries require us to transfer back from the child to the
- // group, but alertOverrides are only present in very limited circumstances, so
- // while it's possible the group should ALSO alert, the previous detection which set
- // this alertOverride won't be invalidated by this notification added to this group.
- checkShouldTransferBack(groupAlertEntry);
- }
- }
-
- @Override
- public void onEntryRemoved(
- @Nullable NotificationEntry entry,
- NotificationVisibility visibility,
- boolean removedByUser,
- int reason) {
- // Removes any alerts pending on this entry. Note that this will not stop any inflation
- // tasks started by a transfer, so this should only be used as clean-up for when
- // inflation is stopped and the pending alert no longer needs to happen.
- mPendingAlerts.remove(entry.getKey());
- }
- };
-
- /**
- * Gets the number of new notifications pending inflation that will be added to the group
- * but currently aren't and should not alert.
- *
- * @param group group to check
- * @return the number of new notifications that will be added to the group
- */
- private int getPendingChildrenNotAlerting(@NonNull NotificationGroup group) {
- if (mEntryManager == null) {
- return 0;
- }
- int number = 0;
- Iterable<NotificationEntry> values = mEntryManager.getPendingNotificationsIterator();
- for (NotificationEntry entry : values) {
- if (isPendingNotificationInGroup(entry, group) && onlySummaryAlerts(entry)) {
- number++;
- }
- }
- return number;
- }
-
- /**
- * Checks if the pending inflations will add children to this group.
- *
- * @param group group to check
- * @return true if a pending notification will add to this group
- */
- private boolean pendingInflationsWillAddChildren(@NonNull NotificationGroup group) {
- if (mEntryManager == null) {
- return false;
- }
- Iterable<NotificationEntry> values = mEntryManager.getPendingNotificationsIterator();
- for (NotificationEntry entry : values) {
- if (isPendingNotificationInGroup(entry, group)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Checks if a new pending notification will be added to the group.
- *
- * @param entry pending notification
- * @param group group to check
- * @return true if the notification will add to the group, false o/w
- */
- private boolean isPendingNotificationInGroup(@NonNull NotificationEntry entry,
- @NonNull NotificationGroup group) {
- String groupKey = mGroupManager.getGroupKey(group.summary.getSbn());
- return mGroupManager.isGroupChild(entry.getSbn())
- && Objects.equals(mGroupManager.getGroupKey(entry.getSbn()), groupKey)
- && !group.children.containsKey(entry.getKey());
- }
-
- /**
- * Handles the scenario where a summary that has been suppressed is itself, or has a former
- * alertOverride (in the form of an isolated logical child) which was alerted. A suppressed
- * summary should for all intents and purposes be invisible to the user and as a result should
- * not alert. When this is the case, it is our responsibility to pass the alert to the
- * appropriate child which will be the representative notification alerting for the group.
- *
- * @param summary the summary that is suppressed and (potentially) alerting
- * @param oldAlertOverride the alertOverride before whatever event triggered this method. If
- * the alert override was removed, this will be the entry that should
- * be transferred back from.
- */
- private void handleSuppressedSummaryAlerted(@NonNull NotificationEntry summary,
- NotificationEntry oldAlertOverride) {
- if (DEBUG) {
- Log.d(TAG, "handleSuppressedSummaryAlerted: summary=" + logKey(summary));
- }
- GroupAlertEntry groupAlertEntry =
- mGroupAlertEntries.get(mGroupManager.getGroupKey(summary.getSbn()));
-
- if (!mGroupManager.isSummaryOfSuppressedGroup(summary.getSbn())
- || groupAlertEntry == null) {
- if (DEBUG) {
- Log.d(TAG, "handleSuppressedSummaryAlerted: invalid state");
- }
- return;
- }
- boolean summaryIsAlerting = mHeadsUpManager.isAlerting(summary.getKey());
- boolean priorityIsAlerting = oldAlertOverride != null
- && mHeadsUpManager.isAlerting(oldAlertOverride.getKey());
- if (!summaryIsAlerting && !priorityIsAlerting) {
- if (DEBUG) {
- Log.d(TAG, "handleSuppressedSummaryAlerted: no summary or override alerting");
- }
- return;
- }
-
- if (pendingInflationsWillAddChildren(groupAlertEntry.mGroup)) {
- // New children will actually be added to this group, let's not transfer the alert.
- if (DEBUG) {
- Log.d(TAG, "handleSuppressedSummaryAlerted: pending inflations");
- }
- return;
- }
-
- NotificationEntry child =
- mGroupManager.getLogicalChildren(summary.getSbn()).iterator().next();
- if (summaryIsAlerting) {
- if (DEBUG) {
- Log.d(TAG, "handleSuppressedSummaryAlerted: transfer summary -> child");
- }
- tryTransferAlertState(summary, /*from*/ summary, /*to*/ child, groupAlertEntry);
- return;
- }
- // Summary didn't have the alert, so we're in "transfer back" territory. First, make sure
- // it's not too late to transfer back, then transfer the alert from the oldAlertOverride to
- // the isolated child which should receive the alert.
- if (!canStillTransferBack(groupAlertEntry)) {
- if (DEBUG) {
- Log.d(TAG, "handleSuppressedSummaryAlerted: transfer from override: too late");
- }
- return;
- }
-
- if (DEBUG) {
- Log.d(TAG, "handleSuppressedSummaryAlerted: transfer override -> child");
- }
- tryTransferAlertState(summary, /*from*/ oldAlertOverride, /*to*/ child, groupAlertEntry);
- }
-
- /**
- * Checks for and handles the scenario where the given entry is the summary of a group which
- * has an alertOverride, and either the summary itself or one of its logical isolated children
- * is currently alerting (which happens if the summary is suppressed).
- */
- private void handleOverriddenSummaryAlerted(NotificationEntry summary) {
- if (DEBUG) {
- Log.d(TAG, "handleOverriddenSummaryAlerted: summary=" + logKey(summary));
- }
- GroupAlertEntry groupAlertEntry =
- mGroupAlertEntries.get(mGroupManager.getGroupKey(summary.getSbn()));
- NotificationGroup group = mGroupManager.getGroupForSummary(summary.getSbn());
- if (group == null || group.alertOverride == null || groupAlertEntry == null) {
- if (DEBUG) {
- Log.d(TAG, "handleOverriddenSummaryAlerted: invalid state");
- }
- return;
- }
- boolean summaryIsAlerting = mHeadsUpManager.isAlerting(summary.getKey());
- if (summaryIsAlerting) {
- if (DEBUG) {
- Log.d(TAG, "handleOverriddenSummaryAlerted: transfer summary -> override");
- }
- tryTransferAlertState(summary, /*from*/ summary, group.alertOverride, groupAlertEntry);
- return;
- }
- // Summary didn't have the alert, so we're in "transfer back" territory. First, make sure
- // it's not too late to transfer back, then remove the alert from any of the logical
- // children, and if one of them was alerting, we can alert the override.
- if (!canStillTransferBack(groupAlertEntry)) {
- if (DEBUG) {
- Log.d(TAG, "handleOverriddenSummaryAlerted: transfer from child: too late");
- }
- return;
- }
- List<NotificationEntry> children = mGroupManager.getLogicalChildren(summary.getSbn());
- if (children == null) {
- if (DEBUG) {
- Log.d(TAG, "handleOverriddenSummaryAlerted: no children");
- }
- return;
- }
- children.remove(group.alertOverride); // do not release the alert on our desired destination
- boolean releasedChild = releaseChildAlerts(children);
- if (releasedChild) {
- if (DEBUG) {
- Log.d(TAG, "handleOverriddenSummaryAlerted: transfer child -> override");
- }
- tryTransferAlertState(summary, /*from*/ null, group.alertOverride, groupAlertEntry);
- } else {
- if (DEBUG) {
- Log.d(TAG, "handleOverriddenSummaryAlerted: no child alert released");
- }
- }
- }
-
- /**
- * Transfers the alert state one entry to another. We remove the alert from the first entry
- * immediately to have the incorrect one up as short as possible. The second should alert
- * when possible.
- *
- * @param summary entry of the summary
- * @param fromEntry entry to transfer alert from
- * @param toEntry entry to transfer to
- */
- private void tryTransferAlertState(
- NotificationEntry summary,
- NotificationEntry fromEntry,
- NotificationEntry toEntry,
- GroupAlertEntry groupAlertEntry) {
- if (toEntry != null) {
- if (toEntry.getRow().keepInParent()
- || toEntry.isRowRemoved()
- || toEntry.isRowDismissed()) {
- // The notification is actually already removed. No need to alert it.
- return;
- }
- if (!mHeadsUpManager.isAlerting(toEntry.getKey()) && onlySummaryAlerts(summary)) {
- groupAlertEntry.mLastAlertTransferTime = SystemClock.elapsedRealtime();
- }
- if (DEBUG) {
- Log.d(TAG, "transferAlertState:"
- + " fromEntry=" + logKey(fromEntry)
- + " toEntry=" + logKey(toEntry));
- }
- transferAlertState(fromEntry, toEntry);
- }
- }
- private void transferAlertState(@Nullable NotificationEntry fromEntry,
- @NonNull NotificationEntry toEntry) {
- if (fromEntry != null) {
- mHeadsUpManager.removeNotification(fromEntry.getKey(), true /* releaseImmediately */);
- }
- alertNotificationWhenPossible(toEntry);
- }
-
- /**
- * Determines if we need to transfer the alert back to the summary from the child and does
- * so if needed.
- *
- * This can happen since notification groups are not delivered as a whole unit and it is
- * possible we erroneously transfer the alert from the summary to the child even though
- * more children are coming. Thus, if a child is added within a certain timeframe after we
- * transfer, we back out and alert the summary again.
- *
- * An alert can only transfer back within a small window of time after a transfer away from the
- * summary to a child happened.
- *
- * @param groupAlertEntry group alert entry to check
- */
- private void checkShouldTransferBack(@NonNull GroupAlertEntry groupAlertEntry) {
- if (canStillTransferBack(groupAlertEntry)) {
- NotificationEntry summary = groupAlertEntry.mGroup.summary;
-
- if (!onlySummaryAlerts(summary)) {
- return;
- }
- ArrayList<NotificationEntry> children = mGroupManager.getLogicalChildren(
- summary.getSbn());
- int numActiveChildren = children.size();
- int numPendingChildren = getPendingChildrenNotAlerting(groupAlertEntry.mGroup);
- int numChildren = numActiveChildren + numPendingChildren;
- if (numChildren <= 1) {
- return;
- }
- boolean releasedChild = releaseChildAlerts(children);
- if (releasedChild && !mHeadsUpManager.isAlerting(summary.getKey())) {
- boolean notifyImmediately = numActiveChildren > 1;
- if (notifyImmediately) {
- alertNotificationWhenPossible(summary);
- } else {
- // Should wait until the pending child inflates before alerting.
- groupAlertEntry.mAlertSummaryOnNextAddition = true;
- }
- groupAlertEntry.mLastAlertTransferTime = 0;
- }
- }
- }
-
- private boolean canStillTransferBack(@NonNull GroupAlertEntry groupAlertEntry) {
- return SystemClock.elapsedRealtime() - groupAlertEntry.mLastAlertTransferTime
- < ALERT_TRANSFER_TIMEOUT;
- }
-
- private boolean releaseChildAlerts(List<NotificationEntry> children) {
- boolean releasedChild = false;
- if (SPEW) {
- Log.d(TAG, "releaseChildAlerts: numChildren=" + children.size());
- }
- for (int i = 0; i < children.size(); i++) {
- NotificationEntry entry = children.get(i);
- if (SPEW) {
- Log.d(TAG, "releaseChildAlerts: checking i=" + i + " entry=" + entry
- + " onlySummaryAlerts=" + onlySummaryAlerts(entry)
- + " isAlerting=" + mHeadsUpManager.isAlerting(entry.getKey())
- + " isPendingAlert=" + mPendingAlerts.containsKey(entry.getKey()));
- }
- if (onlySummaryAlerts(entry) && mHeadsUpManager.isAlerting(entry.getKey())) {
- releasedChild = true;
- mHeadsUpManager.removeNotification(
- entry.getKey(), true /* releaseImmediately */);
- }
- if (mPendingAlerts.containsKey(entry.getKey())) {
- // This is the child that would've been removed if it was inflated.
- releasedChild = true;
- mPendingAlerts.get(entry.getKey()).mAbortOnInflation = true;
- }
- }
- if (SPEW) {
- Log.d(TAG, "releaseChildAlerts: didRelease=" + releasedChild);
- }
- return releasedChild;
- }
-
- /**
- * Tries to alert the notification. If its content view is not inflated, we inflate and continue
- * when the entry finishes inflating the view.
- *
- * @param entry entry to show
- */
- private void alertNotificationWhenPossible(@NonNull NotificationEntry entry) {
- @InflationFlag int contentFlag = mHeadsUpManager.getContentFlag();
- final RowContentBindParams params = mRowContentBindStage.getStageParams(entry);
- if ((params.getContentViews() & contentFlag) == 0) {
- if (DEBUG) {
- Log.d(TAG, "alertNotificationWhenPossible:"
- + " async requestRebind entry=" + logKey(entry));
- }
- mPendingAlerts.put(entry.getKey(), new PendingAlertInfo(entry));
- params.requireContentViews(contentFlag);
- mRowContentBindStage.requestRebind(entry, en -> {
- PendingAlertInfo alertInfo = mPendingAlerts.remove(entry.getKey());
- if (alertInfo != null) {
- if (alertInfo.isStillValid()) {
- alertNotificationWhenPossible(entry);
- } else {
- if (DEBUG) {
- Log.d(TAG, "alertNotificationWhenPossible:"
- + " markContentViewsFreeable entry=" + logKey(entry));
- }
- // The transfer is no longer valid. Free the content.
- mRowContentBindStage.getStageParams(entry).markContentViewsFreeable(
- contentFlag);
- mRowContentBindStage.requestRebind(entry, null);
- }
- }
- });
- return;
- }
- if (mHeadsUpManager.isAlerting(entry.getKey())) {
- if (DEBUG) {
- Log.d(TAG, "alertNotificationWhenPossible:"
- + " continue alerting entry=" + logKey(entry));
- }
- mHeadsUpManager.updateNotification(entry.getKey(), true /* alert */);
- } else {
- if (DEBUG) {
- Log.d(TAG, "alertNotificationWhenPossible:"
- + " start alerting entry=" + logKey(entry));
- }
- mHeadsUpManager.showNotification(entry);
- }
- }
-
- private boolean onlySummaryAlerts(NotificationEntry entry) {
- return entry.getSbn().getNotification().getGroupAlertBehavior()
- == Notification.GROUP_ALERT_SUMMARY;
- }
-
- /**
- * Information about a pending alert used to determine if the alert is still needed when
- * inflation completes.
- */
- private class PendingAlertInfo {
-
- /**
- * The original notification when the transfer is initiated. This is used to determine if
- * the transfer is still valid if the notification is updated.
- */
- final StatusBarNotification mOriginalNotification;
- final NotificationEntry mEntry;
-
- /**
- * The notification is still pending inflation but we've decided that we no longer need
- * the content view (e.g. suppression might have changed and we decided we need to transfer
- * back).
- *
- * TODO: Replace this entire structure with {@link RowContentBindStage#requestRebind)}.
- */
- boolean mAbortOnInflation;
-
- PendingAlertInfo(NotificationEntry entry) {
- mOriginalNotification = entry.getSbn();
- mEntry = entry;
- }
-
- /**
- * Whether or not the pending alert is still valid and should still alert after inflation.
- *
- * @return true if the pending alert should still occur, false o/w
- */
- private boolean isStillValid() {
- if (mAbortOnInflation) {
- // Notification is aborted due to the transfer being explicitly cancelled
- return false;
- }
- if (!mEntry.getSbn().getGroupKey().equals(mOriginalNotification.getGroupKey())) {
- // Groups have changed
- return false;
- }
- if (mEntry.getSbn().getNotification().isGroupSummary()
- != mOriginalNotification.getNotification().isGroupSummary()) {
- // Notification has changed from group summary to not or vice versa
- return false;
- }
- return true;
- }
- }
-
- /**
- * Contains alert metadata for the notification group used to determine when/how the alert
- * should be transferred.
- */
- private static class GroupAlertEntry {
- /**
- * The time when the last alert transfer from summary to child happened.
- */
- long mLastAlertTransferTime;
- boolean mAlertSummaryOnNextAddition;
- final NotificationGroup mGroup;
-
- GroupAlertEntry(NotificationGroup group) {
- this.mGroup = group;
- }
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUserSwitcherAnchorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUserSwitcherAnchorTest.kt
new file mode 100644
index 000000000000..08185af1238e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUserSwitcherAnchorTest.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.keyguard
+
+import android.testing.AndroidTestingRunner
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class KeyguardUserSwitcherAnchorTest : SysuiTestCase() {
+
+ private lateinit var keyguardUserSwitcherAnchor: KeyguardUserSwitcherAnchor
+
+ @Before
+ fun setUp() {
+ keyguardUserSwitcherAnchor = KeyguardUserSwitcherAnchor(context)
+ }
+
+ @Test
+ fun roleDescription_is_set_to_pulldown_menu() {
+ // GIVEN
+ val roleDescriptionString =
+ context.getString(R.string.accessibility_multi_user_list_switcher)
+
+ // WHEN
+ val result = keyguardUserSwitcherAnchor.createAccessibilityNodeInfo()
+
+ // THEN
+ assertThat(
+ AccessibilityNodeInfoCompat.wrap(result).roleDescription
+ ).isEqualTo(roleDescriptionString)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java
new file mode 100644
index 000000000000..a78886f8d504
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DreamOverlayStatusBarItemsProviderTest extends SysuiTestCase {
+ @Mock
+ DreamOverlayStatusBarItemsProvider.Callback mCallback;
+ @Mock
+ DreamOverlayStatusBarItemsProvider.StatusBarItem mStatusBarItem;
+
+ private final Executor mMainExecutor = Runnable::run;
+
+ DreamOverlayStatusBarItemsProvider mProvider;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mProvider = new DreamOverlayStatusBarItemsProvider(mMainExecutor);
+ }
+
+ @Test
+ public void addingCallbackCallsOnStatusBarItemsChanged() {
+ mProvider.addStatusBarItem(mStatusBarItem);
+ mProvider.addCallback(mCallback);
+ verify(mCallback).onStatusBarItemsChanged(List.of(mStatusBarItem));
+ }
+
+ @Test
+ public void addingStatusBarItemCallsOnStatusBarItemsChanged() {
+ mProvider.addCallback(mCallback);
+ mProvider.addStatusBarItem(mStatusBarItem);
+ verify(mCallback).onStatusBarItemsChanged(List.of(mStatusBarItem));
+ }
+
+ @Test
+ public void addingDuplicateStatusBarItemDoesNotCallOnStatusBarItemsChanged() {
+ mProvider.addCallback(mCallback);
+ mProvider.addStatusBarItem(mStatusBarItem);
+ mProvider.addStatusBarItem(mStatusBarItem);
+ // Called only once for addStatusBarItem.
+ verify(mCallback, times(1))
+ .onStatusBarItemsChanged(List.of(mStatusBarItem));
+ }
+
+ @Test
+ public void removingStatusBarItemCallsOnStatusBarItemsChanged() {
+ mProvider.addCallback(mCallback);
+ mProvider.addStatusBarItem(mStatusBarItem);
+ mProvider.removeStatusBarItem(mStatusBarItem);
+ // Called once for addStatusBarItem and once for removeStatusBarItem.
+ verify(mCallback, times(2)).onStatusBarItemsChanged(any());
+ }
+
+ @Test
+ public void removingNonexistentStatusBarItemDoesNotCallOnStatusBarItemsChanged() {
+ mProvider.addCallback(mCallback);
+ mProvider.removeStatusBarItem(mStatusBarItem);
+ verify(mCallback, never()).onStatusBarItemsChanged(any());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
index 60e5a9423c61..01309f86a137 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
@@ -57,6 +57,7 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.List;
import java.util.Optional;
import java.util.concurrent.Executor;
@@ -94,6 +95,12 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
DreamOverlayNotificationCountProvider mDreamOverlayNotificationCountProvider;
@Mock
StatusBarWindowStateController mStatusBarWindowStateController;
+ @Mock
+ DreamOverlayStatusBarItemsProvider mDreamOverlayStatusBarItemsProvider;
+ @Mock
+ DreamOverlayStatusBarItemsProvider.StatusBarItem mStatusBarItem;
+ @Mock
+ View mStatusBarItemView;
private final Executor mMainExecutor = Runnable::run;
@@ -118,7 +125,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
mSensorPrivacyController,
Optional.of(mDreamOverlayNotificationCountProvider),
mZenModeController,
- mStatusBarWindowStateController);
+ mStatusBarWindowStateController,
+ mDreamOverlayStatusBarItemsProvider);
}
@Test
@@ -128,6 +136,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
verify(mSensorPrivacyController).addCallback(any());
verify(mZenModeController).addCallback(any());
verify(mDreamOverlayNotificationCountProvider).addCallback(any());
+ verify(mDreamOverlayStatusBarItemsProvider).addCallback(any());
}
@Test
@@ -256,7 +265,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
mSensorPrivacyController,
Optional.empty(),
mZenModeController,
- mStatusBarWindowStateController);
+ mStatusBarWindowStateController,
+ mDreamOverlayStatusBarItemsProvider);
controller.onViewAttached();
verify(mView, never()).showIcon(
eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any());
@@ -294,6 +304,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
verify(mSensorPrivacyController).removeCallback(any());
verify(mZenModeController).removeCallback(any());
verify(mDreamOverlayNotificationCountProvider).removeCallback(any());
+ verify(mDreamOverlayStatusBarItemsProvider).removeCallback(any());
}
@Test
@@ -462,4 +473,18 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
verify(mView, never()).setVisibility(anyInt());
}
+
+ @Test
+ public void testExtraStatusBarItemSetWhenItemsChange() {
+ mController.onViewAttached();
+ when(mStatusBarItem.getView()).thenReturn(mStatusBarItemView);
+
+ final ArgumentCaptor<DreamOverlayStatusBarItemsProvider.Callback>
+ callbackCapture = ArgumentCaptor.forClass(
+ DreamOverlayStatusBarItemsProvider.Callback.class);
+ verify(mDreamOverlayStatusBarItemsProvider).addCallback(callbackCapture.capture());
+ callbackCapture.getValue().onStatusBarItemsChanged(List.of(mStatusBarItem));
+
+ verify(mView).setExtraStatusBarItemViews(List.of(mStatusBarItemView));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardQuickAffordanceConfigs.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardQuickAffordanceConfigs.kt
deleted file mode 100644
index a24fc93fedc2..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardQuickAffordanceConfigs.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyguard.data.repository
-
-import com.android.systemui.keyguard.data.config.KeyguardQuickAffordanceConfigs
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
-import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePosition
-import kotlin.reflect.KClass
-
-/** Fake implementation of [KeyguardQuickAffordanceConfigs], for tests. */
-class FakeKeyguardQuickAffordanceConfigs(
- private val configsByPosition:
- Map<KeyguardQuickAffordancePosition, List<KeyguardQuickAffordanceConfig>>,
-) : KeyguardQuickAffordanceConfigs {
-
- override fun getAll(
- position: KeyguardQuickAffordancePosition
- ): List<KeyguardQuickAffordanceConfig> {
- return configsByPosition.getValue(position)
- }
-
- override fun get(
- configClass: KClass<out KeyguardQuickAffordanceConfig>
- ): KeyguardQuickAffordanceConfig {
- return configsByPosition.values
- .flatten()
- .associateBy { config -> config::class }
- .getValue(configClass)
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardQuickAffordanceRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardQuickAffordanceRepository.kt
deleted file mode 100644
index 10d2e4de631d..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardQuickAffordanceRepository.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyguard.data.repository
-
-import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordanceModel
-import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePosition
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.yield
-
-/** Fake implementation of [KeyguardQuickAffordanceRepository], for tests. */
-class FakeKeyguardQuickAffordanceRepository : KeyguardQuickAffordanceRepository {
-
- private val modelByPosition =
- mutableMapOf<
- KeyguardQuickAffordancePosition, MutableStateFlow<KeyguardQuickAffordanceModel>>()
-
- init {
- KeyguardQuickAffordancePosition.values().forEach { value ->
- modelByPosition[value] = MutableStateFlow(KeyguardQuickAffordanceModel.Hidden)
- }
- }
-
- override fun affordance(
- position: KeyguardQuickAffordancePosition
- ): Flow<KeyguardQuickAffordanceModel> {
- return modelByPosition.getValue(position)
- }
-
- suspend fun setModel(
- position: KeyguardQuickAffordancePosition,
- model: KeyguardQuickAffordanceModel
- ) {
- modelByPosition.getValue(position).value = model
- // Yield to allow the test's collection coroutine to "catch up" and collect this value
- // before the test continues to the next line.
- // TODO(b/239834928): once coroutines.test is updated, switch to the approach described in
- // https://developer.android.com/kotlin/flow/test#continuous-collection and remove this.
- yield()
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index d40b985f64de..38a337563165 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -20,6 +20,7 @@ import com.android.systemui.common.data.model.Position
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.yield
/** Fake implementation of [KeyguardRepository] */
class FakeKeyguardRepository : KeyguardRepository {
@@ -43,11 +44,6 @@ class FakeKeyguardRepository : KeyguardRepository {
private val _dozeAmount = MutableStateFlow(0f)
override val dozeAmount: Flow<Float> = _dozeAmount
- init {
- setDozeAmount(0f)
- setDozing(false)
- }
-
override fun setAnimateDozingTransitions(animate: Boolean) {
_animateBottomAreaDozingTransitions.tryEmit(animate)
}
@@ -60,15 +56,30 @@ class FakeKeyguardRepository : KeyguardRepository {
_clockPosition.value = Position(x, y)
}
- fun setKeyguardShowing(isShowing: Boolean) {
+ suspend fun setKeyguardShowing(isShowing: Boolean) {
_isKeyguardShowing.value = isShowing
+ // Yield to allow the test's collection coroutine to "catch up" and collect this value
+ // before the test continues to the next line.
+ // TODO(b/239834928): once coroutines.test is updated, switch to the approach described in
+ // https://developer.android.com/kotlin/flow/test#continuous-collection and remove this.
+ yield()
}
- fun setDozing(isDozing: Boolean) {
+ suspend fun setDozing(isDozing: Boolean) {
_isDozing.value = isDozing
+ // Yield to allow the test's collection coroutine to "catch up" and collect this value
+ // before the test continues to the next line.
+ // TODO(b/239834928): once coroutines.test is updated, switch to the approach described in
+ // https://developer.android.com/kotlin/flow/test#continuous-collection and remove this.
+ yield()
}
- fun setDozeAmount(dozeAmount: Float) {
+ suspend fun setDozeAmount(dozeAmount: Float) {
_dozeAmount.value = dozeAmount
+ // Yield to allow the test's collection coroutine to "catch up" and collect this value
+ // before the test continues to the next line.
+ // TODO(b/239834928): once coroutines.test is updated, switch to the approach described in
+ // https://developer.android.com/kotlin/flow/test#continuous-collection and remove this.
+ yield()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryImplTest.kt
deleted file mode 100644
index dc0e6f7663ff..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryImplTest.kt
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyguard.data.repository
-
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
-import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordanceModel
-import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePosition
-import com.android.systemui.util.mockito.mock
-import com.google.common.truth.Truth.assertThat
-import kotlin.reflect.KClass
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.test.runBlockingTest
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@RunWith(JUnit4::class)
-class KeyguardQuickAffordanceRepositoryImplTest : SysuiTestCase() {
-
- private lateinit var underTest: KeyguardQuickAffordanceRepository
-
- private lateinit var homeControls: FakeKeyguardQuickAffordanceConfig
- private lateinit var quickAccessWallet: FakeKeyguardQuickAffordanceConfig
- private lateinit var qrCodeScanner: FakeKeyguardQuickAffordanceConfig
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
-
- homeControls = object : FakeKeyguardQuickAffordanceConfig() {}
- quickAccessWallet = object : FakeKeyguardQuickAffordanceConfig() {}
- qrCodeScanner = object : FakeKeyguardQuickAffordanceConfig() {}
-
- underTest =
- KeyguardQuickAffordanceRepositoryImpl(
- configs =
- FakeKeyguardQuickAffordanceConfigs(
- mapOf(
- KeyguardQuickAffordancePosition.BOTTOM_START to
- listOf(
- homeControls,
- ),
- KeyguardQuickAffordancePosition.BOTTOM_END to
- listOf(
- quickAccessWallet,
- qrCodeScanner,
- ),
- ),
- ),
- )
- }
-
- @Test
- fun `bottom start affordance - none`() = runBlockingTest {
- // TODO(b/239834928): once coroutines.test is updated, switch to the approach described in
- // https://developer.android.com/kotlin/flow/test#continuous-collection
- var latest: KeyguardQuickAffordanceModel? = null
- val job =
- underTest
- .affordance(KeyguardQuickAffordancePosition.BOTTOM_START)
- .onEach { latest = it }
- .launchIn(this)
-
- assertThat(latest).isEqualTo(KeyguardQuickAffordanceModel.Hidden)
- job.cancel()
- }
-
- @Test
- fun `bottom start affordance - home controls`() = runBlockingTest {
- // TODO(b/239834928): once coroutines.test is updated, switch to the approach described in
- // https://developer.android.com/kotlin/flow/test#continuous-collection
- var latest: KeyguardQuickAffordanceModel? = null
- val job =
- underTest
- .affordance(KeyguardQuickAffordancePosition.BOTTOM_START)
- .onEach { latest = it }
- .launchIn(this)
-
- val state =
- KeyguardQuickAffordanceConfig.State.Visible(
- icon = mock(),
- contentDescriptionResourceId = CONTENT_DESCRIPTION_RESOURCE_ID,
- )
- homeControls.setState(state)
-
- assertThat(latest).isEqualTo(state.toModel(homeControls::class))
- job.cancel()
- }
-
- @Test
- fun `bottom end affordance - none`() = runBlockingTest {
- // TODO(b/239834928): once coroutines.test is updated, switch to the approach described in
- // https://developer.android.com/kotlin/flow/test#continuous-collection
- var latest: KeyguardQuickAffordanceModel? = null
- val job =
- underTest
- .affordance(KeyguardQuickAffordancePosition.BOTTOM_END)
- .onEach { latest = it }
- .launchIn(this)
-
- assertThat(latest).isEqualTo(KeyguardQuickAffordanceModel.Hidden)
- job.cancel()
- }
-
- @Test
- fun `bottom end affordance - quick access wallet`() = runBlockingTest {
- // TODO(b/239834928): once coroutines.test is updated, switch to the approach described in
- // https://developer.android.com/kotlin/flow/test#continuous-collection
- var latest: KeyguardQuickAffordanceModel? = null
- val job =
- underTest
- .affordance(KeyguardQuickAffordancePosition.BOTTOM_END)
- .onEach { latest = it }
- .launchIn(this)
-
- val quickAccessWalletState =
- KeyguardQuickAffordanceConfig.State.Visible(
- icon = mock(),
- contentDescriptionResourceId = CONTENT_DESCRIPTION_RESOURCE_ID,
- )
- quickAccessWallet.setState(quickAccessWalletState)
- val qrCodeScannerState =
- KeyguardQuickAffordanceConfig.State.Visible(
- icon = mock(),
- contentDescriptionResourceId = CONTENT_DESCRIPTION_RESOURCE_ID,
- )
- qrCodeScanner.setState(qrCodeScannerState)
-
- assertThat(latest).isEqualTo(quickAccessWalletState.toModel(quickAccessWallet::class))
- job.cancel()
- }
-
- @Test
- fun `bottom end affordance - qr code scanner`() = runBlockingTest {
- // TODO(b/239834928): once coroutines.test is updated, switch to the approach described in
- // https://developer.android.com/kotlin/flow/test#continuous-collection
- var latest: KeyguardQuickAffordanceModel? = null
- val job =
- underTest
- .affordance(KeyguardQuickAffordancePosition.BOTTOM_END)
- .onEach { latest = it }
- .launchIn(this)
-
- val state =
- KeyguardQuickAffordanceConfig.State.Visible(
- icon = mock(),
- contentDescriptionResourceId = CONTENT_DESCRIPTION_RESOURCE_ID,
- )
- qrCodeScanner.setState(state)
-
- assertThat(latest).isEqualTo(state.toModel(qrCodeScanner::class))
- job.cancel()
- }
-
- private fun KeyguardQuickAffordanceConfig.State?.toModel(
- configKey: KClass<out KeyguardQuickAffordanceConfig>,
- ): KeyguardQuickAffordanceModel? {
- return when (this) {
- is KeyguardQuickAffordanceConfig.State.Visible ->
- KeyguardQuickAffordanceModel.Visible(
- configKey = configKey,
- icon = icon,
- contentDescriptionResourceId = CONTENT_DESCRIPTION_RESOURCE_ID,
- )
- is KeyguardQuickAffordanceConfig.State.Hidden -> KeyguardQuickAffordanceModel.Hidden
- null -> null
- }
- }
-
- companion object {
- private const val CONTENT_DESCRIPTION_RESOURCE_ID = 1337
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
index 6fff440ec2fa..6ea1daa7704f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
@@ -1,24 +1,24 @@
/*
- * Copyright (C) 2022 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.
- * You may obtain a copy of the License at
+ * 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
+ * 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.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
*/
-package com.android.systemui.keyguard.data.repository
+package com.android.systemui.keyguard.domain.quickaffordance
import com.android.systemui.animation.ActivityLaunchAnimator
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnClickedResult
+import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig.OnClickedResult
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.yield
@@ -31,9 +31,6 @@ import kotlinx.coroutines.yield
*/
abstract class FakeKeyguardQuickAffordanceConfig : KeyguardQuickAffordanceConfig {
- private val _onClickedInvocations = mutableListOf<ActivityLaunchAnimator.Controller?>()
- val onClickedInvocations: List<ActivityLaunchAnimator.Controller?> = _onClickedInvocations
-
var onClickedResult: OnClickedResult = OnClickedResult.Handled
private val _state =
@@ -45,7 +42,6 @@ abstract class FakeKeyguardQuickAffordanceConfig : KeyguardQuickAffordanceConfig
override fun onQuickAffordanceClicked(
animationController: ActivityLaunchAnimator.Controller?,
): OnClickedResult {
- _onClickedInvocations.add(animationController)
return onClickedResult
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/FakeKeyguardQuickAffordanceRegistry.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/FakeKeyguardQuickAffordanceRegistry.kt
new file mode 100644
index 000000000000..1c9902b36517
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/FakeKeyguardQuickAffordanceRegistry.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.domain.quickaffordance
+
+import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordancePosition
+import kotlin.reflect.KClass
+
+/** Fake implementation of [FakeKeyguardQuickAffordanceRegistry], for tests. */
+class FakeKeyguardQuickAffordanceRegistry(
+ private val configsByPosition:
+ Map<KeyguardQuickAffordancePosition, List<KeyguardQuickAffordanceConfig>>,
+) : KeyguardQuickAffordanceRegistry {
+
+ override fun getAll(
+ position: KeyguardQuickAffordancePosition
+ ): List<KeyguardQuickAffordanceConfig> {
+ return configsByPosition.getValue(position)
+ }
+
+ override fun get(
+ configClass: KClass<out KeyguardQuickAffordanceConfig>
+ ): KeyguardQuickAffordanceConfig {
+ return configsByPosition.values
+ .flatten()
+ .associateBy { config -> config::class }
+ .getValue(configClass)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
index 810c6dc4776d..9acd21cc6398 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
@@ -1,20 +1,21 @@
/*
- * Copyright (C) 2022 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.
- * You may obtain a copy of the License at
+ * 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
+ * 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.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
*/
-package com.android.systemui.keyguard.data.quickaffordance
+package com.android.systemui.keyguard.domain.quickaffordance
import androidx.test.filters.SmallTest
import com.android.systemui.R
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
index ef588f5ce255..059487dfdbc8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
@@ -15,7 +15,7 @@
*
*/
-package com.android.systemui.keyguard.data.quickaffordance
+package com.android.systemui.keyguard.domain.quickaffordance
import androidx.test.filters.SmallTest
import com.android.systemui.R
@@ -23,7 +23,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.controls.dagger.ControlsComponent
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnClickedResult
+import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig.OnClickedResult
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import java.util.Optional
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
index 6fd04de8c9ac..d4fba4126127 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
@@ -15,12 +15,12 @@
*
*/
-package com.android.systemui.keyguard.data.quickaffordance
+package com.android.systemui.keyguard.domain.quickaffordance
import android.content.Intent
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnClickedResult
+import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig.OnClickedResult
import com.android.systemui.qrcodescanner.controller.QRCodeScannerController
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
index 345c51f6f760..5a3a78e9cb04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
@@ -15,7 +15,7 @@
*
*/
-package com.android.systemui.keyguard.data.quickaffordance
+package com.android.systemui.keyguard.domain.quickaffordance
import android.graphics.drawable.Drawable
import android.service.quickaccesswallet.GetWalletCardsResponse
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/FakeObserveKeyguardQuickAffordanceUseCase.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/FakeObserveKeyguardQuickAffordanceUseCase.kt
new file mode 100644
index 000000000000..8982752c9fcc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/FakeObserveKeyguardQuickAffordanceUseCase.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.domain.usecase
+
+import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
+import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordancePosition
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class FakeObserveKeyguardQuickAffordanceUseCase : ObserveKeyguardQuickAffordanceUseCase {
+
+ private val affordanceByPosition =
+ mutableMapOf<
+ KeyguardQuickAffordancePosition, MutableStateFlow<KeyguardQuickAffordanceModel>>()
+
+ init {
+ KeyguardQuickAffordancePosition.values().forEach { position ->
+ affordanceByPosition[position] = MutableStateFlow(KeyguardQuickAffordanceModel.Hidden)
+ }
+ }
+
+ override fun invoke(
+ position: KeyguardQuickAffordancePosition
+ ): Flow<KeyguardQuickAffordanceModel> {
+ return affordanceByPosition[position] ?: error("Flow unexpectedly missing!")
+ }
+
+ fun setModel(position: KeyguardQuickAffordancePosition, model: KeyguardQuickAffordanceModel) {
+ affordanceByPosition[position]?.value = model
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/ObserveKeyguardQuickAffordanceUseCaseTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/ObserveKeyguardQuickAffordanceUseCaseImplTest.kt
index b90400be16d8..63eb68f423ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/ObserveKeyguardQuickAffordanceUseCaseTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/ObserveKeyguardQuickAffordanceUseCaseImplTest.kt
@@ -19,11 +19,12 @@ package com.android.systemui.keyguard.domain.usecase
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.containeddrawable.ContainedDrawable
-import com.android.systemui.keyguard.data.quickaffordance.HomeControlsKeyguardQuickAffordanceConfig
-import com.android.systemui.keyguard.data.repository.FakeKeyguardQuickAffordanceRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordanceModel
-import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePosition
+import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
+import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordancePosition
+import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
+import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.launchIn
@@ -36,48 +37,62 @@ import org.junit.runners.JUnit4
@SmallTest
@RunWith(JUnit4::class)
-class ObserveKeyguardQuickAffordanceUseCaseTest : SysuiTestCase() {
+class ObserveKeyguardQuickAffordanceUseCaseImplTest : SysuiTestCase() {
private lateinit var underTest: ObserveKeyguardQuickAffordanceUseCase
private lateinit var repository: FakeKeyguardRepository
- private lateinit var quickAffordanceRepository: FakeKeyguardQuickAffordanceRepository
private lateinit var isDozingUseCase: ObserveIsDozingUseCase
private lateinit var isKeyguardShowingUseCase: ObserveIsKeyguardShowingUseCase
+ private lateinit var homeControls: FakeKeyguardQuickAffordanceConfig
+ private lateinit var quickAccessWallet: FakeKeyguardQuickAffordanceConfig
+ private lateinit var qrCodeScanner: FakeKeyguardQuickAffordanceConfig
@Before
- fun setUp() {
+ fun setUp() = runBlockingTest {
repository = FakeKeyguardRepository()
repository.setKeyguardShowing(true)
isDozingUseCase = ObserveIsDozingUseCase(repository)
isKeyguardShowingUseCase = ObserveIsKeyguardShowingUseCase(repository)
- quickAffordanceRepository = FakeKeyguardQuickAffordanceRepository()
+
+ homeControls = object : FakeKeyguardQuickAffordanceConfig() {}
+ quickAccessWallet = object : FakeKeyguardQuickAffordanceConfig() {}
+ qrCodeScanner = object : FakeKeyguardQuickAffordanceConfig() {}
underTest =
- ObserveKeyguardQuickAffordanceUseCase(
- repository = quickAffordanceRepository,
+ ObserveKeyguardQuickAffordanceUseCaseImpl(
+ registry =
+ FakeKeyguardQuickAffordanceRegistry(
+ mapOf(
+ KeyguardQuickAffordancePosition.BOTTOM_START to
+ listOf(
+ homeControls,
+ ),
+ KeyguardQuickAffordancePosition.BOTTOM_END to
+ listOf(
+ quickAccessWallet,
+ qrCodeScanner,
+ ),
+ ),
+ ),
isDozingUseCase = isDozingUseCase,
isKeyguardShowingUseCase = isKeyguardShowingUseCase,
)
}
@Test
- fun `invoke - affordance is visible`() = runBlockingTest {
- val configKey = HomeControlsKeyguardQuickAffordanceConfig::class
- val model =
- KeyguardQuickAffordanceModel.Visible(
- configKey = configKey,
+ fun `invoke - bottom start affordance is visible`() = runBlockingTest {
+ val configKey = homeControls::class
+ homeControls.setState(
+ KeyguardQuickAffordanceConfig.State.Visible(
icon = ICON,
contentDescriptionResourceId = CONTENT_DESCRIPTION_RESOURCE_ID,
)
- quickAffordanceRepository.setModel(
- KeyguardQuickAffordancePosition.BOTTOM_END,
- model,
)
var latest: KeyguardQuickAffordanceModel? = null
val job =
- underTest(KeyguardQuickAffordancePosition.BOTTOM_END)
+ underTest(KeyguardQuickAffordancePosition.BOTTOM_START)
.onEach { latest = it }
.launchIn(this)
@@ -91,18 +106,13 @@ class ObserveKeyguardQuickAffordanceUseCaseTest : SysuiTestCase() {
}
@Test
- fun `invoke - affordance not visible while dozing`() = runBlockingTest {
- repository.setDozing(true)
- val configKey = HomeControlsKeyguardQuickAffordanceConfig::class
- val model =
- KeyguardQuickAffordanceModel.Visible(
- configKey = configKey,
+ fun `invoke - bottom end affordance is visible`() = runBlockingTest {
+ val configKey = quickAccessWallet::class
+ quickAccessWallet.setState(
+ KeyguardQuickAffordanceConfig.State.Visible(
icon = ICON,
contentDescriptionResourceId = CONTENT_DESCRIPTION_RESOURCE_ID,
)
- quickAffordanceRepository.setModel(
- KeyguardQuickAffordancePosition.BOTTOM_END,
- model,
)
var latest: KeyguardQuickAffordanceModel? = null
@@ -110,28 +120,29 @@ class ObserveKeyguardQuickAffordanceUseCaseTest : SysuiTestCase() {
underTest(KeyguardQuickAffordancePosition.BOTTOM_END)
.onEach { latest = it }
.launchIn(this)
- assertThat(latest).isEqualTo(KeyguardQuickAffordanceModel.Hidden)
+
+ assertThat(latest).isInstanceOf(KeyguardQuickAffordanceModel.Visible::class.java)
+ val visibleModel = latest as KeyguardQuickAffordanceModel.Visible
+ assertThat(visibleModel.configKey).isEqualTo(configKey)
+ assertThat(visibleModel.icon).isEqualTo(ICON)
+ assertThat(visibleModel.contentDescriptionResourceId)
+ .isEqualTo(CONTENT_DESCRIPTION_RESOURCE_ID)
job.cancel()
}
@Test
- fun `invoke - affordance not visible when lockscreen is not showing`() = runBlockingTest {
- repository.setKeyguardShowing(false)
- val configKey = HomeControlsKeyguardQuickAffordanceConfig::class
- val model =
- KeyguardQuickAffordanceModel.Visible(
- configKey = configKey,
+ fun `invoke - bottom start affordance hidden while dozing`() = runBlockingTest {
+ repository.setDozing(true)
+ homeControls.setState(
+ KeyguardQuickAffordanceConfig.State.Visible(
icon = ICON,
contentDescriptionResourceId = CONTENT_DESCRIPTION_RESOURCE_ID,
)
- quickAffordanceRepository.setModel(
- KeyguardQuickAffordancePosition.BOTTOM_END,
- model,
)
var latest: KeyguardQuickAffordanceModel? = null
val job =
- underTest(KeyguardQuickAffordancePosition.BOTTOM_END)
+ underTest(KeyguardQuickAffordancePosition.BOTTOM_START)
.onEach { latest = it }
.launchIn(this)
assertThat(latest).isEqualTo(KeyguardQuickAffordanceModel.Hidden)
@@ -139,20 +150,24 @@ class ObserveKeyguardQuickAffordanceUseCaseTest : SysuiTestCase() {
}
@Test
- fun `invoke - affordance is none`() = runBlockingTest {
- quickAffordanceRepository.setModel(
- KeyguardQuickAffordancePosition.BOTTOM_START,
- KeyguardQuickAffordanceModel.Hidden,
- )
+ fun `invoke - bottom start affordance hidden when lockscreen is not showing`() =
+ runBlockingTest {
+ repository.setKeyguardShowing(false)
+ homeControls.setState(
+ KeyguardQuickAffordanceConfig.State.Visible(
+ icon = ICON,
+ contentDescriptionResourceId = CONTENT_DESCRIPTION_RESOURCE_ID,
+ )
+ )
- var latest: KeyguardQuickAffordanceModel? = null
- val job =
- underTest(KeyguardQuickAffordancePosition.BOTTOM_START)
- .onEach { latest = it }
- .launchIn(this)
- assertThat(latest).isEqualTo(KeyguardQuickAffordanceModel.Hidden)
- job.cancel()
- }
+ var latest: KeyguardQuickAffordanceModel? = null
+ val job =
+ underTest(KeyguardQuickAffordancePosition.BOTTOM_START)
+ .onEach { latest = it }
+ .launchIn(this)
+ assertThat(latest).isEqualTo(KeyguardQuickAffordanceModel.Hidden)
+ job.cancel()
+ }
companion object {
private val ICON: ContainedDrawable = mock()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 00dd58e19142..8758ce5eade6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -22,22 +22,20 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.containeddrawable.ContainedDrawable
import com.android.systemui.doze.util.BurnInHelperWrapper
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
-import com.android.systemui.keyguard.data.repository.FakeKeyguardQuickAffordanceConfig
-import com.android.systemui.keyguard.data.repository.FakeKeyguardQuickAffordanceConfigs
-import com.android.systemui.keyguard.data.repository.FakeKeyguardQuickAffordanceRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
+import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordancePosition
+import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceConfig
+import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
+import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.domain.usecase.FakeLaunchKeyguardQuickAffordanceUseCase
+import com.android.systemui.keyguard.domain.usecase.FakeObserveKeyguardQuickAffordanceUseCase
import com.android.systemui.keyguard.domain.usecase.ObserveAnimateBottomAreaTransitionsUseCase
import com.android.systemui.keyguard.domain.usecase.ObserveBottomAreaAlphaUseCase
import com.android.systemui.keyguard.domain.usecase.ObserveClockPositionUseCase
import com.android.systemui.keyguard.domain.usecase.ObserveDozeAmountUseCase
import com.android.systemui.keyguard.domain.usecase.ObserveIsDozingUseCase
-import com.android.systemui.keyguard.domain.usecase.ObserveIsKeyguardShowingUseCase
-import com.android.systemui.keyguard.domain.usecase.ObserveKeyguardQuickAffordanceUseCase
import com.android.systemui.keyguard.domain.usecase.OnKeyguardQuickAffordanceClickedUseCase
-import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordanceModel
-import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePosition
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
@@ -63,14 +61,14 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
private lateinit var underTest: KeyguardBottomAreaViewModel
- private lateinit var affordanceRepository: FakeKeyguardQuickAffordanceRepository
private lateinit var repository: FakeKeyguardRepository
+ private lateinit var registry: FakeKeyguardQuickAffordanceRegistry
private lateinit var isDozingUseCase: ObserveIsDozingUseCase
- private lateinit var isKeyguardShowingUseCase: ObserveIsKeyguardShowingUseCase
private lateinit var launchQuickAffordanceUseCase: FakeLaunchKeyguardQuickAffordanceUseCase
private lateinit var homeControlsQuickAffordanceConfig: FakeKeyguardQuickAffordanceConfig
private lateinit var quickAccessWalletAffordanceConfig: FakeKeyguardQuickAffordanceConfig
private lateinit var qrCodeScannerAffordanceConfig: FakeKeyguardQuickAffordanceConfig
+ private lateinit var observeQuickAffordanceUseCase: FakeObserveKeyguardQuickAffordanceUseCase
@Before
fun setUp() {
@@ -78,33 +76,38 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
whenever(burnInHelperWrapper.burnInOffset(anyInt(), any()))
.thenReturn(RETURNED_BURN_IN_OFFSET)
- affordanceRepository = FakeKeyguardQuickAffordanceRepository()
+ homeControlsQuickAffordanceConfig = object : FakeKeyguardQuickAffordanceConfig() {}
+ quickAccessWalletAffordanceConfig = object : FakeKeyguardQuickAffordanceConfig() {}
+ qrCodeScannerAffordanceConfig = object : FakeKeyguardQuickAffordanceConfig() {}
+ registry =
+ FakeKeyguardQuickAffordanceRegistry(
+ mapOf(
+ KeyguardQuickAffordancePosition.BOTTOM_START to
+ listOf(
+ homeControlsQuickAffordanceConfig,
+ ),
+ KeyguardQuickAffordancePosition.BOTTOM_END to
+ listOf(
+ quickAccessWalletAffordanceConfig,
+ qrCodeScannerAffordanceConfig,
+ ),
+ ),
+ )
repository = FakeKeyguardRepository()
isDozingUseCase =
ObserveIsDozingUseCase(
repository = repository,
)
- isKeyguardShowingUseCase =
- ObserveIsKeyguardShowingUseCase(
- repository = repository,
- )
launchQuickAffordanceUseCase = FakeLaunchKeyguardQuickAffordanceUseCase()
- homeControlsQuickAffordanceConfig = object : FakeKeyguardQuickAffordanceConfig() {}
- quickAccessWalletAffordanceConfig = object : FakeKeyguardQuickAffordanceConfig() {}
- qrCodeScannerAffordanceConfig = object : FakeKeyguardQuickAffordanceConfig() {}
+ observeQuickAffordanceUseCase = FakeObserveKeyguardQuickAffordanceUseCase()
underTest =
KeyguardBottomAreaViewModel(
- observeQuickAffordanceUseCase =
- ObserveKeyguardQuickAffordanceUseCase(
- repository = affordanceRepository,
- isDozingUseCase = isDozingUseCase,
- isKeyguardShowingUseCase = isKeyguardShowingUseCase,
- ),
+ observeQuickAffordanceUseCase = observeQuickAffordanceUseCase,
onQuickAffordanceClickedUseCase =
OnKeyguardQuickAffordanceClickedUseCase(
- configs =
- FakeKeyguardQuickAffordanceConfigs(
+ registry =
+ FakeKeyguardQuickAffordanceRegistry(
mapOf(
KeyguardQuickAffordancePosition.BOTTOM_START to
listOf(
@@ -141,146 +144,79 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
}
@Test
- fun `startButton - present - not dozing - lockscreen showing - visible model - starts activity on click`() = // ktlint-disable max-line-length
- runBlockingTest {
- var latest: KeyguardQuickAffordanceViewModel? = null
- val job = underTest.startButton.onEach { latest = it }.launchIn(this)
-
- repository.setDozing(false)
- repository.setKeyguardShowing(true)
- val testConfig =
- TestConfig(
- isVisible = true,
- icon = mock(),
- canShowWhileLocked = false,
- intent = Intent("action"),
- )
- val configKey =
- setUpQuickAffordanceModel(
- position = KeyguardQuickAffordancePosition.BOTTOM_START,
- testConfig = testConfig,
- )
+ fun `startButton - present - visible model - starts activity on click`() = runBlockingTest {
+ var latest: KeyguardQuickAffordanceViewModel? = null
+ val job = underTest.startButton.onEach { latest = it }.launchIn(this)
- assertQuickAffordanceViewModel(
- viewModel = latest,
- testConfig = testConfig,
- configKey = configKey,
+ val testConfig =
+ TestConfig(
+ isVisible = true,
+ icon = mock(),
+ canShowWhileLocked = false,
+ intent = Intent("action"),
)
- job.cancel()
- }
-
- @Test
- fun `endButton - present - not dozing - lockscreen showing - visible model - do nothing on click`() = // ktlint-disable max-line-length
- runBlockingTest {
- var latest: KeyguardQuickAffordanceViewModel? = null
- val job = underTest.endButton.onEach { latest = it }.launchIn(this)
-
- repository.setDozing(false)
- repository.setKeyguardShowing(true)
- val config =
- TestConfig(
- isVisible = true,
- icon = mock(),
- canShowWhileLocked = false,
- intent =
- null, // This will cause it to tell the system that the click was handled.
- )
- val configKey =
- setUpQuickAffordanceModel(
- position = KeyguardQuickAffordancePosition.BOTTOM_END,
- testConfig = config,
- )
-
- assertQuickAffordanceViewModel(
- viewModel = latest,
- testConfig = config,
- configKey = configKey,
+ val configKey =
+ setUpQuickAffordanceModel(
+ position = KeyguardQuickAffordancePosition.BOTTOM_START,
+ testConfig = testConfig,
)
- job.cancel()
- }
- @Test
- fun `startButton - not present - not dozing - lockscreen showing - model is none`() =
- runBlockingTest {
- var latest: KeyguardQuickAffordanceViewModel? = null
- val job = underTest.startButton.onEach { latest = it }.launchIn(this)
-
- repository.setDozing(false)
- repository.setKeyguardShowing(true)
- val config =
- TestConfig(
- isVisible = false,
- )
- val configKey =
- setUpQuickAffordanceModel(
- position = KeyguardQuickAffordancePosition.BOTTOM_START,
- testConfig = config,
- )
-
- assertQuickAffordanceViewModel(
- viewModel = latest,
- testConfig = config,
- configKey = configKey,
- )
- job.cancel()
- }
+ assertQuickAffordanceViewModel(
+ viewModel = latest,
+ testConfig = testConfig,
+ configKey = configKey,
+ )
+ job.cancel()
+ }
@Test
- fun `startButton - present - dozing - lockscreen showing - model is none`() = runBlockingTest {
+ fun `endButton - present - visible model - do nothing on click`() = runBlockingTest {
var latest: KeyguardQuickAffordanceViewModel? = null
- val job = underTest.startButton.onEach { latest = it }.launchIn(this)
+ val job = underTest.endButton.onEach { latest = it }.launchIn(this)
- repository.setDozing(true)
- repository.setKeyguardShowing(true)
val config =
TestConfig(
isVisible = true,
icon = mock(),
canShowWhileLocked = false,
- intent = Intent("action"),
+ intent = null, // This will cause it to tell the system that the click was handled.
)
val configKey =
setUpQuickAffordanceModel(
- position = KeyguardQuickAffordancePosition.BOTTOM_START,
+ position = KeyguardQuickAffordancePosition.BOTTOM_END,
testConfig = config,
)
assertQuickAffordanceViewModel(
viewModel = latest,
- testConfig = TestConfig(isVisible = false),
+ testConfig = config,
configKey = configKey,
)
job.cancel()
}
@Test
- fun `startButton - present - not dozing - lockscreen not showing - model is none`() =
- runBlockingTest {
- var latest: KeyguardQuickAffordanceViewModel? = null
- val job = underTest.startButton.onEach { latest = it }.launchIn(this)
-
- repository.setDozing(false)
- repository.setKeyguardShowing(false)
- val config =
- TestConfig(
- isVisible = true,
- icon = mock(),
- canShowWhileLocked = false,
- intent = Intent("action"),
- )
- val configKey =
- setUpQuickAffordanceModel(
- position = KeyguardQuickAffordancePosition.BOTTOM_START,
- testConfig = config,
- )
+ fun `startButton - not present - model is hidden`() = runBlockingTest {
+ var latest: KeyguardQuickAffordanceViewModel? = null
+ val job = underTest.startButton.onEach { latest = it }.launchIn(this)
- assertQuickAffordanceViewModel(
- viewModel = latest,
- testConfig = TestConfig(isVisible = false),
- configKey = configKey,
+ val config =
+ TestConfig(
+ isVisible = false,
+ )
+ val configKey =
+ setUpQuickAffordanceModel(
+ position = KeyguardQuickAffordancePosition.BOTTOM_START,
+ testConfig = config,
)
- job.cancel()
- }
+
+ assertQuickAffordanceViewModel(
+ viewModel = latest,
+ testConfig = config,
+ configKey = configKey,
+ )
+ job.cancel()
+ }
@Test
fun animateButtonReveal() = runBlockingTest {
@@ -413,7 +349,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
job.cancel()
}
- private fun setDozeAmountAndCalculateExpectedTranslationY(dozeAmount: Float): Float {
+ private suspend fun setDozeAmountAndCalculateExpectedTranslationY(dozeAmount: Float): Float {
repository.setDozeAmount(dozeAmount)
return dozeAmount * (RETURNED_BURN_IN_OFFSET - DEFAULT_BURN_IN_OFFSET)
}
@@ -428,27 +364,31 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
KeyguardQuickAffordancePosition.BOTTOM_END -> quickAccessWalletAffordanceConfig
}
- affordanceRepository.setModel(
- position = position,
- model =
- if (testConfig.isVisible) {
- if (testConfig.intent != null) {
- config.onClickedResult =
- KeyguardQuickAffordanceConfig.OnClickedResult.StartActivity(
- intent = testConfig.intent,
- canShowWhileLocked = testConfig.canShowWhileLocked,
- )
- }
- KeyguardQuickAffordanceModel.Visible(
- configKey = config::class,
- icon = testConfig.icon ?: error("Icon is unexpectedly null!"),
- contentDescriptionResourceId = CONTENT_DESCRIPTION_RESOURCE_ID,
- )
- } else {
- KeyguardQuickAffordanceModel.Hidden
+ val state =
+ if (testConfig.isVisible) {
+ if (testConfig.intent != null) {
+ config.onClickedResult =
+ KeyguardQuickAffordanceConfig.OnClickedResult.StartActivity(
+ intent = testConfig.intent,
+ canShowWhileLocked = testConfig.canShowWhileLocked,
+ )
}
+ KeyguardQuickAffordanceConfig.State.Visible(
+ icon = testConfig.icon ?: error("Icon is unexpectedly null!"),
+ contentDescriptionResourceId = CONTENT_DESCRIPTION_RESOURCE_ID,
+ )
+ } else {
+ KeyguardQuickAffordanceConfig.State.Hidden
+ }
+ config.setState(state)
+
+ val configKey = config::class
+ observeQuickAffordanceUseCase.setModel(
+ position,
+ KeyguardQuickAffordanceModel.from(state, configKey)
)
- return config::class
+
+ return configKey
}
private fun assertQuickAffordanceViewModel(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index 533c231f68a9..314997d8d38a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -43,7 +43,6 @@ import android.widget.TextView;
import androidx.core.graphics.drawable.IconCompat;
import androidx.test.filters.SmallTest;
-import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -54,7 +53,7 @@ import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import org.junit.Before;
import org.junit.Test;
@@ -83,8 +82,7 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase {
LocalBluetoothLeBroadcast.class);
private ActivityStarter mStarter = mock(ActivityStarter.class);
private BroadcastSender mBroadcastSender = mock(BroadcastSender.class);
- private NotificationEntryManager mNotificationEntryManager =
- mock(NotificationEntryManager.class);
+ private final CommonNotifCollection mNotifCollection = mock(CommonNotifCollection.class);
private NearbyMediaDevicesManager mNearbyMediaDevicesManager = mock(
NearbyMediaDevicesManager.class);
private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
@@ -120,7 +118,7 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase {
mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
mMediaSessionManager, mLocalBluetoothManager, mStarter,
- mNotificationEntryManager, mDialogLaunchAnimator,
+ mNotifCollection, mDialogLaunchAnimator,
Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager);
mMediaOutputBaseDialogImpl = new MediaOutputBaseDialogImpl(mContext, mBroadcastSender,
mMediaOutputController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
index 6afed1a846b0..4779d322a90e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
@@ -47,7 +47,7 @@ import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import org.junit.After;
import org.junit.Before;
@@ -78,8 +78,7 @@ public class MediaOutputDialogTest extends SysuiTestCase {
private final BroadcastSender mBroadcastSender = mock(BroadcastSender.class);
private final LocalMediaManager mLocalMediaManager = mock(LocalMediaManager.class);
private final MediaDevice mMediaDevice = mock(MediaDevice.class);
- private final NotificationEntryManager mNotificationEntryManager =
- mock(NotificationEntryManager.class);
+ private final CommonNotifCollection mNotifCollection = mock(CommonNotifCollection.class);
private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
private final NearbyMediaDevicesManager mNearbyMediaDevicesManager = mock(
@@ -104,7 +103,7 @@ public class MediaOutputDialogTest extends SysuiTestCase {
mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
mMediaSessionManager, mLocalBluetoothManager, mStarter,
- mNotificationEntryManager, mDialogLaunchAnimator,
+ mNotifCollection, mDialogLaunchAnimator,
Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
mMediaOutputDialog = new MediaOutputDialog(mContext, false, mBroadcastSender,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
index fba19861b006..bd913bab6f65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
@@ -63,7 +63,6 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
import com.android.systemui.people.widget.PeopleTileKey;
import com.android.systemui.statusbar.NotificationListener;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
@@ -196,8 +195,6 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
@Mock
private PackageManager mPackageManager;
@Mock
- private NotificationEntryManager mNotificationEntryManager;
- @Mock
private PeopleSpaceWidgetManager mPeopleSpaceWidgetManager;
@Mock
private BackupManager mBackupManager;
@@ -234,8 +231,6 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
when(mMockContext.getString(R.string.over_two_weeks_timestamp)).thenReturn(
mContext.getString(R.string.over_two_weeks_timestamp));
when(mPackageManager.getApplicationIcon(anyString())).thenReturn(null);
- when(mNotificationEntryManager.getVisibleNotifications())
- .thenReturn(List.of(mNotificationEntry1, mNotificationEntry2, mNotificationEntry3));
}
@After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
index 642e29b364d3..2ba8782c6d02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
@@ -22,13 +22,17 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.MultiUserSwitchController
import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.statusbar.policy.FakeConfigurationController
import com.android.systemui.statusbar.policy.UserInfoController
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.utils.leaks.LeakCheckedTest
+import com.google.common.truth.Expect
import com.google.common.truth.Truth.assertThat
+import javax.inject.Provider
import org.junit.After
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
@@ -42,47 +46,38 @@ import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-import javax.inject.Provider
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@RunWith(AndroidTestingRunner::class)
class FooterActionsControllerTest : LeakCheckedTest() {
- @Mock
- private lateinit var userManager: UserManager
- @Mock
- private lateinit var userTracker: UserTracker
- @Mock
- private lateinit var activityStarter: ActivityStarter
- @Mock
- private lateinit var deviceProvisionedController: DeviceProvisionedController
- @Mock
- private lateinit var userInfoController: UserInfoController
- @Mock
- private lateinit var multiUserSwitchControllerFactory: MultiUserSwitchController.Factory
- @Mock
- private lateinit var multiUserSwitchController: MultiUserSwitchController
- @Mock
- private lateinit var globalActionsDialogProvider: Provider<GlobalActionsDialogLite>
- @Mock
- private lateinit var globalActionsDialog: GlobalActionsDialogLite
- @Mock
- private lateinit var uiEventLogger: UiEventLogger
- @Mock
- private lateinit var securityFooterController: QSSecurityFooter
- @Mock
- private lateinit var fgsManagerController: QSFgsManagerFooter
+
+ @get:Rule var expect: Expect = Expect.create()
+
+ @Mock private lateinit var userManager: UserManager
+ @Mock private lateinit var userTracker: UserTracker
+ @Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
+ @Mock private lateinit var userInfoController: UserInfoController
+ @Mock private lateinit var multiUserSwitchControllerFactory: MultiUserSwitchController.Factory
+ @Mock private lateinit var multiUserSwitchController: MultiUserSwitchController
+ @Mock private lateinit var globalActionsDialogProvider: Provider<GlobalActionsDialogLite>
+ @Mock private lateinit var globalActionsDialog: GlobalActionsDialogLite
+ @Mock private lateinit var uiEventLogger: UiEventLogger
+ @Mock private lateinit var securityFooterController: QSSecurityFooter
+ @Mock private lateinit var fgsManagerController: QSFgsManagerFooter
@Captor
private lateinit var visibilityChangedCaptor:
ArgumentCaptor<VisibilityChangedDispatcher.OnVisibilityChangedListener>
private lateinit var controller: FooterActionsController
+ private val configurationController = FakeConfigurationController()
private val metricsLogger: MetricsLogger = FakeMetricsLogger()
- private lateinit var view: FooterActionsView
private val falsingManager: FalsingManagerFake = FalsingManagerFake()
+ private lateinit var view: FooterActionsView
private lateinit var testableLooper: TestableLooper
private lateinit var fakeSettings: FakeSettings
private lateinit var securityFooter: View
@@ -90,12 +85,15 @@ class FooterActionsControllerTest : LeakCheckedTest() {
@Before
fun setUp() {
+ // We want to make sure testable resources are always used
+ context.ensureTestableResources()
+
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
fakeSettings = FakeSettings()
whenever(multiUserSwitchControllerFactory.create(any()))
- .thenReturn(multiUserSwitchController)
+ .thenReturn(multiUserSwitchController)
whenever(globalActionsDialogProvider.get()).thenReturn(globalActionsDialog)
securityFooter = View(mContext)
@@ -135,7 +133,7 @@ class FooterActionsControllerTest : LeakCheckedTest() {
view.findViewById<View>(R.id.pm_lite).performClick()
// Verify clicks are logged
verify(uiEventLogger, Mockito.times(1))
- .log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS)
+ .log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS)
}
@Test
@@ -299,6 +297,86 @@ class FooterActionsControllerTest : LeakCheckedTest() {
assertThat(booleanCaptor.allValues.last()).isTrue()
}
+ @Test
+ fun setExpansion_inSplitShade_alphaFollowsExpansion() {
+ enableSplitShade()
+
+ controller.setExpansion(0f)
+ expect.that(view.alpha).isEqualTo(0f)
+
+ controller.setExpansion(0.25f)
+ expect.that(view.alpha).isEqualTo(0.25f)
+
+ controller.setExpansion(0.5f)
+ expect.that(view.alpha).isEqualTo(0.5f)
+
+ controller.setExpansion(0.75f)
+ expect.that(view.alpha).isEqualTo(0.75f)
+
+ controller.setExpansion(1f)
+ expect.that(view.alpha).isEqualTo(1f)
+ }
+
+ @Test
+ fun setExpansion_inSplitShade_backgroundAlphaFollowsExpansion_with_0_9_delay() {
+ enableSplitShade()
+
+ controller.setExpansion(0f)
+ expect.that(view.backgroundAlphaFraction).isEqualTo(0f)
+
+ controller.setExpansion(0.5f)
+ expect.that(view.backgroundAlphaFraction).isEqualTo(0f)
+
+ controller.setExpansion(0.9f)
+ expect.that(view.backgroundAlphaFraction).isEqualTo(0f)
+
+ controller.setExpansion(0.91f)
+ expect.that(view.backgroundAlphaFraction).isWithin(FLOAT_TOLERANCE).of(0.1f)
+
+ controller.setExpansion(0.95f)
+ expect.that(view.backgroundAlphaFraction).isWithin(FLOAT_TOLERANCE).of(0.5f)
+
+ controller.setExpansion(1f)
+ expect.that(view.backgroundAlphaFraction).isEqualTo(1f)
+ }
+
+ @Test
+ fun setExpansion_inSingleShade_alphaFollowsExpansion_with_0_9_delay() {
+ disableSplitShade()
+
+ controller.setExpansion(0f)
+ expect.that(view.alpha).isEqualTo(0f)
+
+ controller.setExpansion(0.5f)
+ expect.that(view.alpha).isEqualTo(0f)
+
+ controller.setExpansion(0.9f)
+ expect.that(view.alpha).isEqualTo(0f)
+
+ controller.setExpansion(0.91f)
+ expect.that(view.alpha).isWithin(FLOAT_TOLERANCE).of(0.1f)
+
+ controller.setExpansion(0.95f)
+ expect.that(view.alpha).isWithin(FLOAT_TOLERANCE).of(0.5f)
+
+ controller.setExpansion(1f)
+ expect.that(view.alpha).isEqualTo(1f)
+ }
+
+ @Test
+ fun setExpansion_inSingleShade_backgroundAlphaAlways1() {
+ disableSplitShade()
+
+ controller.setExpansion(0f)
+ expect.that(view.backgroundAlphaFraction).isEqualTo(1f)
+
+ controller.setExpansion(0.5f)
+ expect.that(view.backgroundAlphaFraction).isEqualTo(1f)
+
+ controller.setExpansion(1f)
+ expect.that(view.backgroundAlphaFraction).isEqualTo(1f)
+ }
+
private fun setVisibilities(
securityFooterVisible: Boolean,
fgsFooterVisible: Boolean,
@@ -311,15 +389,52 @@ class FooterActionsControllerTest : LeakCheckedTest() {
}
private fun inflateView(): FooterActionsView {
- return LayoutInflater.from(context)
- .inflate(R.layout.footer_actions, null) as FooterActionsView
+ return LayoutInflater.from(context).inflate(R.layout.footer_actions, null)
+ as FooterActionsView
}
private fun constructFooterActionsController(view: FooterActionsView): FooterActionsController {
- return FooterActionsController(view, multiUserSwitchControllerFactory,
- activityStarter, userManager, userTracker, userInfoController,
- deviceProvisionedController, securityFooterController, fgsManagerController,
- falsingManager, metricsLogger, globalActionsDialogProvider, uiEventLogger,
- showPMLiteButton = true, fakeSettings, Handler(testableLooper.looper))
+ return FooterActionsController(
+ view,
+ multiUserSwitchControllerFactory,
+ activityStarter,
+ userManager,
+ userTracker,
+ userInfoController,
+ deviceProvisionedController,
+ securityFooterController,
+ fgsManagerController,
+ falsingManager,
+ metricsLogger,
+ globalActionsDialogProvider,
+ uiEventLogger,
+ showPMLiteButton = true,
+ fakeSettings,
+ Handler(testableLooper.looper),
+ configurationController)
+ }
+
+ private fun enableSplitShade() {
+ setSplitShadeEnabled(true)
+ }
+
+ private fun disableSplitShade() {
+ setSplitShadeEnabled(false)
+ }
+
+ private fun setSplitShadeEnabled(enabled: Boolean) {
+ overrideResource(R.bool.config_use_split_notification_shade, enabled)
+ configurationController.notifyConfigurationChanged()
+ }
+}
+
+private const val FLOAT_TOLERANCE = 0.01f
+
+private val View.backgroundAlphaFraction: Float?
+ get() {
+ return if (background != null) {
+ background.alpha / 255f
+ } else {
+ null
+ }
}
-} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index 32c66d25d4aa..10f6ce8c0ec9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -205,6 +205,40 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
}
@Test
+ public void setQsExpansion_inSplitShade_setsFooterActionsExpansion_basedOnPanelExpFraction() {
+ // Random test values without any meaning. They just have to be different from each other.
+ float expansion = 0.123f;
+ float panelExpansionFraction = 0.321f;
+ float proposedTranslation = 456f;
+ float squishinessFraction = 0.987f;
+
+ QSFragment fragment = resumeAndGetFragment();
+ enableSplitShade();
+
+ fragment.setQsExpansion(expansion, panelExpansionFraction, proposedTranslation,
+ squishinessFraction);
+
+ verify(mQSFooterActionController).setExpansion(panelExpansionFraction);
+ }
+
+ @Test
+ public void setQsExpansion_notInSplitShade_setsFooterActionsExpansion_basedOnExpansion() {
+ // Random test values without any meaning. They just have to be different from each other.
+ float expansion = 0.123f;
+ float panelExpansionFraction = 0.321f;
+ float proposedTranslation = 456f;
+ float squishinessFraction = 0.987f;
+
+ QSFragment fragment = resumeAndGetFragment();
+ disableSplitShade();
+
+ fragment.setQsExpansion(expansion, panelExpansionFraction, proposedTranslation,
+ squishinessFraction);
+
+ verify(mQSFooterActionController).setExpansion(expansion);
+ }
+
+ @Test
public void getQsMinExpansionHeight_notInSplitShade_returnsHeaderHeight() {
QSFragment fragment = resumeAndGetFragment();
disableSplitShade();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index 7dbc561fbfc1..3c58b6fc1354 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -22,6 +22,7 @@ import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.mock;
@@ -32,11 +33,13 @@ import static org.mockito.Mockito.when;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.database.ContentObserver;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.testing.AndroidTestingRunner;
+import android.util.SparseArray;
import android.view.View;
import androidx.annotation.Nullable;
@@ -60,12 +63,14 @@ import com.android.systemui.qs.external.TileServiceKey;
import com.android.systemui.qs.external.TileServiceRequestController;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.settings.UserFileManager;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.phone.AutoTileManager;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.FakeSharedPreferences;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.settings.SecureSettings;
@@ -76,6 +81,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -130,6 +136,10 @@ public class QSTileHostTest extends SysuiTestCase {
private TileLifecycleManager.Factory mTileLifecycleManagerFactory;
@Mock
private TileLifecycleManager mTileLifecycleManager;
+ @Mock
+ private UserFileManager mUserFileManager;
+
+ private SparseArray<SharedPreferences> mSharedPreferencesByUser;
private FakeExecutor mMainExecutor;
@@ -140,17 +150,29 @@ public class QSTileHostTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mMainExecutor = new FakeExecutor(new FakeSystemClock());
+ mSharedPreferencesByUser = new SparseArray<>();
+
when(mTileServiceRequestControllerBuilder.create(any()))
.thenReturn(mTileServiceRequestController);
when(mTileLifecycleManagerFactory.create(any(Intent.class), any(UserHandle.class)))
.thenReturn(mTileLifecycleManager);
+ when(mUserFileManager.getSharedPreferences(anyString(), anyInt(), anyInt()))
+ .thenAnswer((Answer<SharedPreferences>) invocation -> {
+ assertEquals(QSTileHost.TILES, invocation.getArgument(0));
+ int userId = invocation.getArgument(2);
+ if (!mSharedPreferencesByUser.contains(userId)) {
+ mSharedPreferencesByUser.put(userId, new FakeSharedPreferences());
+ }
+ return mSharedPreferencesByUser.get(userId);
+ });
mSecureSettings = new FakeSettings();
saveSetting("");
mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mMainExecutor,
mPluginManager, mTunerService, mAutoTiles, mDumpManager, mCentralSurfaces,
mQSLogger, mUiEventLogger, mUserTracker, mSecureSettings, mCustomTileStatePersister,
- mTileServiceRequestControllerBuilder, mTileLifecycleManagerFactory);
+ mTileServiceRequestControllerBuilder, mTileLifecycleManagerFactory,
+ mUserFileManager);
mSecureSettings.registerContentObserverForUser(SETTING, new ContentObserver(null) {
@Override
@@ -528,6 +550,118 @@ public class QSTileHostTest extends SysuiTestCase {
assertEquals("spec1", getSetting());
}
+ @Test
+ public void testIsTileAdded_true() {
+ int user = mUserTracker.getUserId();
+ getSharedPreferenecesForUser(user)
+ .edit()
+ .putBoolean(CUSTOM_TILE.flattenToString(), true)
+ .apply();
+
+ assertTrue(mQSTileHost.isTileAdded(CUSTOM_TILE, user));
+ }
+
+ @Test
+ public void testIsTileAdded_false() {
+ int user = mUserTracker.getUserId();
+ getSharedPreferenecesForUser(user)
+ .edit()
+ .putBoolean(CUSTOM_TILE.flattenToString(), false)
+ .apply();
+
+ assertFalse(mQSTileHost.isTileAdded(CUSTOM_TILE, user));
+ }
+
+ @Test
+ public void testIsTileAdded_notSet() {
+ int user = mUserTracker.getUserId();
+
+ assertFalse(mQSTileHost.isTileAdded(CUSTOM_TILE, user));
+ }
+
+ @Test
+ public void testIsTileAdded_differentUser() {
+ int user = mUserTracker.getUserId();
+ mUserFileManager.getSharedPreferences(QSTileHost.TILES, 0, user)
+ .edit()
+ .putBoolean(CUSTOM_TILE.flattenToString(), true)
+ .apply();
+
+ assertFalse(mQSTileHost.isTileAdded(CUSTOM_TILE, user + 1));
+ }
+
+ @Test
+ public void testSetTileAdded_true() {
+ int user = mUserTracker.getUserId();
+ mQSTileHost.setTileAdded(CUSTOM_TILE, user, true);
+
+ assertTrue(getSharedPreferenecesForUser(user)
+ .getBoolean(CUSTOM_TILE.flattenToString(), false));
+ }
+
+ @Test
+ public void testSetTileAdded_false() {
+ int user = mUserTracker.getUserId();
+ mQSTileHost.setTileAdded(CUSTOM_TILE, user, false);
+
+ assertFalse(getSharedPreferenecesForUser(user)
+ .getBoolean(CUSTOM_TILE.flattenToString(), false));
+ }
+
+ @Test
+ public void testSetTileAdded_differentUser() {
+ int user = mUserTracker.getUserId();
+ mQSTileHost.setTileAdded(CUSTOM_TILE, user, true);
+
+ assertFalse(getSharedPreferenecesForUser(user + 1)
+ .getBoolean(CUSTOM_TILE.flattenToString(), false));
+ }
+
+ @Test
+ public void testSetTileRemoved_afterCustomTileChangedByUser() {
+ int user = mUserTracker.getUserId();
+ saveSetting(CUSTOM_TILE_SPEC);
+
+ // This will be done by TileServiceManager
+ mQSTileHost.setTileAdded(CUSTOM_TILE, user, true);
+
+ mQSTileHost.changeTilesByUser(mQSTileHost.mTileSpecs, List.of("spec1"));
+ assertFalse(getSharedPreferenecesForUser(user)
+ .getBoolean(CUSTOM_TILE.flattenToString(), false));
+ }
+
+ @Test
+ public void testSetTileRemoved_removedByUser() {
+ int user = mUserTracker.getUserId();
+ saveSetting(CUSTOM_TILE_SPEC);
+
+ // This will be done by TileServiceManager
+ mQSTileHost.setTileAdded(CUSTOM_TILE, user, true);
+
+ mQSTileHost.removeTileByUser(CUSTOM_TILE);
+ mMainExecutor.runAllReady();
+ assertFalse(getSharedPreferenecesForUser(user)
+ .getBoolean(CUSTOM_TILE.flattenToString(), false));
+ }
+
+ @Test
+ public void testSetTileRemoved_removedBySystem() {
+ int user = mUserTracker.getUserId();
+ saveSetting("spec1" + CUSTOM_TILE_SPEC);
+
+ // This will be done by TileServiceManager
+ mQSTileHost.setTileAdded(CUSTOM_TILE, user, true);
+
+ mQSTileHost.removeTile(CUSTOM_TILE_SPEC);
+ mMainExecutor.runAllReady();
+ assertFalse(getSharedPreferenecesForUser(user)
+ .getBoolean(CUSTOM_TILE.flattenToString(), false));
+ }
+
+ private SharedPreferences getSharedPreferenecesForUser(int user) {
+ return mUserFileManager.getSharedPreferences(QSTileHost.TILES, 0, user);
+ }
+
private class TestQSTileHost extends QSTileHost {
TestQSTileHost(Context context, StatusBarIconController iconController,
QSFactory defaultFactory, Executor mainExecutor,
@@ -537,11 +671,13 @@ public class QSTileHostTest extends SysuiTestCase {
UserTracker userTracker, SecureSettings secureSettings,
CustomTileStatePersister customTileStatePersister,
TileServiceRequestController.Builder tileServiceRequestControllerBuilder,
- TileLifecycleManager.Factory tileLifecycleManagerFactory) {
+ TileLifecycleManager.Factory tileLifecycleManagerFactory,
+ UserFileManager userFileManager) {
super(context, iconController, defaultFactory, mainExecutor, pluginManager,
tunerService, autoTiles, dumpManager, Optional.of(centralSurfaces), qsLogger,
uiEventLogger, userTracker, secureSettings, customTileStatePersister,
- tileServiceRequestControllerBuilder, tileLifecycleManagerFactory);
+ tileServiceRequestControllerBuilder, tileLifecycleManagerFactory,
+ userFileManager);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTest.java
index 573980d015a9..8aa625a7ea20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTest.java
@@ -19,6 +19,14 @@ import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -31,6 +39,7 @@ import android.test.suitebuilder.annotation.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.qs.QSTileHost;
import com.android.systemui.settings.UserTracker;
import org.junit.After;
@@ -38,37 +47,45 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-import org.mockito.Mockito;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class TileServiceManagerTest extends SysuiTestCase {
+ @Mock
private TileServices mTileServices;
+ @Mock
private TileLifecycleManager mTileLifecycle;
+ @Mock
+ private UserTracker mUserTracker;
+ @Mock
+ private QSTileHost mQSTileHost;
+ @Mock
+ private Context mMockContext;
+
private HandlerThread mThread;
private Handler mHandler;
private TileServiceManager mTileServiceManager;
- private UserTracker mUserTracker;
- private Context mMockContext;
+ private ComponentName mComponentName;
@Before
public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
mThread = new HandlerThread("TestThread");
mThread.start();
mHandler = Handler.createAsync(mThread.getLooper());
- mTileServices = Mockito.mock(TileServices.class);
- mUserTracker = Mockito.mock(UserTracker.class);
- Mockito.when(mUserTracker.getUserId()).thenReturn(UserHandle.USER_SYSTEM);
- Mockito.when(mUserTracker.getUserHandle()).thenReturn(UserHandle.SYSTEM);
-
- mMockContext = Mockito.mock(Context.class);
- Mockito.when(mTileServices.getContext()).thenReturn(mMockContext);
- mTileLifecycle = Mockito.mock(TileLifecycleManager.class);
- Mockito.when(mTileLifecycle.isActiveTile()).thenReturn(false);
- ComponentName componentName = new ComponentName(mContext,
- TileServiceManagerTest.class);
- Mockito.when(mTileLifecycle.getComponent()).thenReturn(componentName);
+ when(mUserTracker.getUserId()).thenReturn(UserHandle.USER_SYSTEM);
+ when(mUserTracker.getUserHandle()).thenReturn(UserHandle.SYSTEM);
+
+ when(mTileServices.getContext()).thenReturn(mMockContext);
+ when(mTileServices.getHost()).thenReturn(mQSTileHost);
+ when(mTileLifecycle.getUserId()).thenAnswer(invocation -> mUserTracker.getUserId());
+ when(mTileLifecycle.isActiveTile()).thenReturn(false);
+
+ mComponentName = new ComponentName(mContext, TileServiceManagerTest.class);
+ when(mTileLifecycle.getComponent()).thenReturn(mComponentName);
mTileServiceManager = new TileServiceManager(mTileServices, mHandler, mUserTracker,
mTileLifecycle);
}
@@ -80,17 +97,44 @@ public class TileServiceManagerTest extends SysuiTestCase {
}
@Test
+ public void testSetTileAddedIfNotAdded() {
+ when(mQSTileHost.isTileAdded(eq(mComponentName), anyInt())).thenReturn(false);
+ mTileServiceManager.startLifecycleManagerAndAddTile();
+
+ verify(mQSTileHost).setTileAdded(mComponentName, mUserTracker.getUserId(), true);
+ }
+
+ @Test
+ public void testNotSetTileAddedIfAdded() {
+ when(mQSTileHost.isTileAdded(eq(mComponentName), anyInt())).thenReturn(true);
+ mTileServiceManager.startLifecycleManagerAndAddTile();
+
+ verify(mQSTileHost, never()).setTileAdded(eq(mComponentName), anyInt(), eq(true));
+ }
+
+ @Test
+ public void testSetTileAddedCorrectUser() {
+ int user = 10;
+ when(mUserTracker.getUserId()).thenReturn(user);
+ when(mQSTileHost.isTileAdded(eq(mComponentName), anyInt())).thenReturn(false);
+ mTileServiceManager.startLifecycleManagerAndAddTile();
+
+ verify(mQSTileHost).setTileAdded(mComponentName, user, true);
+ }
+
+ @Test
public void testUninstallReceiverExported() {
+ mTileServiceManager.startLifecycleManagerAndAddTile();
ArgumentCaptor<IntentFilter> intentFilterCaptor =
ArgumentCaptor.forClass(IntentFilter.class);
- Mockito.verify(mMockContext).registerReceiverAsUser(
- Mockito.any(),
- Mockito.any(),
+ verify(mMockContext).registerReceiverAsUser(
+ any(),
+ any(),
intentFilterCaptor.capture(),
- Mockito.any(),
- Mockito.any(),
- Mockito.eq(Context.RECEIVER_EXPORTED)
+ any(),
+ any(),
+ eq(Context.RECEIVER_EXPORTED)
);
IntentFilter filter = intentFilterCaptor.getValue();
assertTrue(filter.hasAction(Intent.ACTION_PACKAGE_REMOVED));
@@ -99,38 +143,41 @@ public class TileServiceManagerTest extends SysuiTestCase {
@Test
public void testSetBindRequested() {
+ mTileServiceManager.startLifecycleManagerAndAddTile();
// Request binding.
mTileServiceManager.setBindRequested(true);
mTileServiceManager.setLastUpdate(0);
mTileServiceManager.calculateBindPriority(5);
- Mockito.verify(mTileServices, Mockito.times(2)).recalculateBindAllowance();
+ verify(mTileServices, times(2)).recalculateBindAllowance();
assertEquals(5, mTileServiceManager.getBindPriority());
// Verify same state doesn't trigger recalculating for no reason.
mTileServiceManager.setBindRequested(true);
- Mockito.verify(mTileServices, Mockito.times(2)).recalculateBindAllowance();
+ verify(mTileServices, times(2)).recalculateBindAllowance();
mTileServiceManager.setBindRequested(false);
mTileServiceManager.calculateBindPriority(5);
- Mockito.verify(mTileServices, Mockito.times(3)).recalculateBindAllowance();
+ verify(mTileServices, times(3)).recalculateBindAllowance();
assertEquals(Integer.MIN_VALUE, mTileServiceManager.getBindPriority());
}
@Test
public void testPendingClickPriority() {
- Mockito.when(mTileLifecycle.hasPendingClick()).thenReturn(true);
+ mTileServiceManager.startLifecycleManagerAndAddTile();
+ when(mTileLifecycle.hasPendingClick()).thenReturn(true);
mTileServiceManager.calculateBindPriority(0);
assertEquals(Integer.MAX_VALUE, mTileServiceManager.getBindPriority());
}
@Test
public void testBind() {
+ mTileServiceManager.startLifecycleManagerAndAddTile();
// Trigger binding requested and allowed.
mTileServiceManager.setBindRequested(true);
mTileServiceManager.setBindAllowed(true);
ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class);
- Mockito.verify(mTileLifecycle, Mockito.times(1)).setBindService(captor.capture());
+ verify(mTileLifecycle, times(1)).setBindService(captor.capture());
assertTrue((boolean) captor.getValue());
mTileServiceManager.setBindRequested(false);
@@ -141,7 +188,7 @@ public class TileServiceManagerTest extends SysuiTestCase {
mTileServiceManager.setBindAllowed(false);
captor = ArgumentCaptor.forClass(Boolean.class);
- Mockito.verify(mTileLifecycle, Mockito.times(2)).setBindService(captor.capture());
+ verify(mTileLifecycle, times(2)).setBindService(captor.capture());
assertFalse((boolean) captor.getValue());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
index 471ddfd3f224..213eca895eb9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
@@ -45,6 +45,7 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSFactoryImpl;
+import com.android.systemui.settings.UserFileManager;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.CommandQueue;
@@ -118,6 +119,8 @@ public class TileServicesTest extends SysuiTestCase {
private TileLifecycleManager.Factory mTileLifecycleManagerFactory;
@Mock
private TileLifecycleManager mTileLifecycleManager;
+ @Mock
+ private UserFileManager mUserFileManager;
@Before
public void setUp() throws Exception {
@@ -149,7 +152,8 @@ public class TileServicesTest extends SysuiTestCase {
mSecureSettings,
mock(CustomTileStatePersister.class),
mTileServiceRequestControllerBuilder,
- mTileLifecycleManagerFactory);
+ mTileLifecycleManagerFactory,
+ mUserFileManager);
mTileService = new TestTileServices(host, provider, mBroadcastDispatcher,
mUserTracker, mKeyguardStateController, mCommandQueue);
}
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/shared/system/RemoteTransitionTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/system/RemoteTransitionTest.java
index 360eef9d214f..cf5fa87272c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/system/RemoteTransitionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/system/RemoteTransitionTest.java
@@ -19,12 +19,14 @@ package com.android.systemui.shared.system;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.window.TransitionInfo.FLAG_FIRST_CUSTOM;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_STANDARD;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CHANGING;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
@@ -64,12 +66,15 @@ public class RemoteTransitionTest extends SysuiTestCase {
@Test
public void testLegacyTargetExtract() {
TransitionInfo combined = new TransitionInfoBuilder(TRANSIT_CLOSE)
- .addChange(TRANSIT_CHANGE, FLAG_SHOW_WALLPAPER)
- .addChange(TRANSIT_CLOSE, 0 /* flags */)
- .addChange(TRANSIT_OPEN, FLAG_IS_WALLPAPER).build();
- // Check non-wallpaper extraction
- RemoteAnimationTargetCompat[] wrapped = RemoteAnimationTargetCompat.wrap(combined,
- false /* wallpapers */, mock(SurfaceControl.Transaction.class), null /* leashes */);
+ .addChange(TRANSIT_CHANGE, FLAG_SHOW_WALLPAPER,
+ createTaskInfo(1 /* taskId */, ACTIVITY_TYPE_STANDARD))
+ .addChange(TRANSIT_CLOSE, 0 /* flags */,
+ createTaskInfo(2 /* taskId */, ACTIVITY_TYPE_STANDARD))
+ .addChange(TRANSIT_OPEN, FLAG_IS_WALLPAPER, null /* taskInfo */)
+ .addChange(TRANSIT_CHANGE, FLAG_FIRST_CUSTOM, null /* taskInfo */).build();
+ // Check apps extraction
+ RemoteAnimationTargetCompat[] wrapped = RemoteAnimationTargetCompat.wrapApps(combined,
+ mock(SurfaceControl.Transaction.class), null /* leashes */);
assertEquals(2, wrapped.length);
int changeLayer = -1;
int closeLayer = -1;
@@ -86,17 +91,25 @@ public class RemoteTransitionTest extends SysuiTestCase {
assertTrue(closeLayer < changeLayer);
// Check wallpaper extraction
- RemoteAnimationTargetCompat[] wallps = RemoteAnimationTargetCompat.wrap(combined,
+ RemoteAnimationTargetCompat[] wallps = RemoteAnimationTargetCompat.wrapNonApps(combined,
true /* wallpapers */, mock(SurfaceControl.Transaction.class), null /* leashes */);
assertEquals(1, wallps.length);
assertTrue(wallps[0].prefixOrderIndex < closeLayer);
assertEquals(MODE_OPENING, wallps[0].mode);
+
+ // Check non-apps extraction
+ RemoteAnimationTargetCompat[] nonApps = RemoteAnimationTargetCompat.wrapNonApps(combined,
+ false /* wallpapers */, mock(SurfaceControl.Transaction.class), null /* leashes */);
+ assertEquals(1, nonApps.length);
+ assertTrue(nonApps[0].prefixOrderIndex < closeLayer);
+ assertEquals(MODE_CHANGING, nonApps[0].mode);
}
@Test
public void testLegacyTargetWrapper() {
TransitionInfo tinfo = new TransitionInfoBuilder(TRANSIT_CLOSE)
- .addChange(TRANSIT_CHANGE, FLAG_TRANSLUCENT).build();
+ .addChange(TRANSIT_CHANGE, FLAG_TRANSLUCENT,
+ createTaskInfo(1 /* taskId */, ACTIVITY_TYPE_STANDARD)).build();
final TransitionInfo.Change change = tinfo.getChanges().get(0);
final Rect endBounds = new Rect(40, 60, 140, 200);
change.setTaskInfo(createTaskInfo(1 /* taskId */, ACTIVITY_TYPE_HOME));
@@ -119,11 +132,12 @@ public class RemoteTransitionTest extends SysuiTestCase {
}
TransitionInfoBuilder addChange(@WindowManager.TransitionType int mode,
- @TransitionInfo.ChangeFlags int flags) {
+ @TransitionInfo.ChangeFlags int flags, ActivityManager.RunningTaskInfo taskInfo) {
final TransitionInfo.Change change =
new TransitionInfo.Change(null /* token */, createMockSurface(true));
change.setMode(mode);
change.setFlags(flags);
+ change.setTaskInfo(taskInfo);
mInfo.addChange(change);
return this;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index 65d0adc99739..c6207e53beed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -25,7 +25,6 @@ import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
import android.app.ActivityManager;
import android.app.Notification;
@@ -216,41 +215,4 @@ public class AlertingNotificationManagerTest extends SysuiTestCase {
// The entry has just been added so we should not remove immediately.
assertFalse(mAlertingNotificationManager.canRemoveImmediately(mEntry.getKey()));
}
-
- @Test
- public void testShouldExtendLifetime() {
- mAlertingNotificationManager.showNotification(mEntry);
-
- // While the entry is alerting, it should not be removable.
- assertTrue(mAlertingNotificationManager.shouldExtendLifetime(mEntry));
- }
-
- @Test
- public void testSetShouldManageLifetime_setShouldManage() {
- mAlertingNotificationManager.showNotification(mEntry);
-
- mAlertingNotificationManager.setShouldManageLifetime(mEntry, true /* shouldManage */);
-
- assertTrue(mAlertingNotificationManager.mExtendedLifetimeAlertEntries.contains(mEntry));
- }
-
- @Test
- public void testSetShouldManageLifetime_setShouldManageCallsRemoval() {
- mAlertingNotificationManager.showNotification(mEntry);
- mAlertingNotificationManager.setShouldManageLifetime(mEntry, true /* shouldManage */);
- if (mAlertingNotificationManager instanceof TestableAlertingNotificationManager) {
- TestableAlertingNotificationManager testableManager =
- (TestableAlertingNotificationManager) mAlertingNotificationManager;
- verify(testableManager.mLastCreatedEntry).removeAsSoonAsPossible();
- }
- }
-
- @Test
- public void testSetShouldManageLifetime_setShouldNotManage() {
- mAlertingNotificationManager.mExtendedLifetimeAlertEntries.add(mEntry);
-
- mAlertingNotificationManager.setShouldManageLifetime(mEntry, false /* shouldManage */);
-
- assertFalse(mAlertingNotificationManager.mExtendedLifetimeAlertEntries.contains(mEntry));
- }
}
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/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index 34d13c76399a..6e29669a227b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -161,10 +161,8 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase {
smartReplyController,
visibilityProvider,
notificationEntryManager,
- rebuilder,
centralSurfacesOptionalLazy,
statusBarStateController,
- mainHandler,
remoteInputUriController,
clickNotifier,
actionClickLogger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
deleted file mode 100644
index 842f057b96fc..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ /dev/null
@@ -1,684 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.notification;
-
-import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
-import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
-import static android.service.notification.NotificationListenerService.REASON_CANCEL;
-import static android.service.notification.NotificationStats.DISMISSAL_SHADE;
-import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
-
-import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertTrue;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import android.app.ActivityManager;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.graphics.drawable.Icon;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.service.notification.NotificationListenerService.Ranking;
-import android.service.notification.NotificationListenerService.RankingMap;
-import android.service.notification.StatusBarNotification;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.util.ArraySet;
-
-import androidx.annotation.NonNull;
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.statusbar.NotificationVisibility;
-import com.android.systemui.Dependency;
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.statusbar.NotificationLifetimeExtender;
-import com.android.systemui.statusbar.NotificationListener;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.NotificationPresenter;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationRemoveInterceptor;
-import com.android.systemui.statusbar.SmartReplyController;
-import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
-import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
-import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
-import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
-import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
-import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.RowInflaterTask;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.leak.LeakDetector;
-import com.android.systemui.util.time.FakeSystemClock;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.ArgumentMatcher;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Unit tests for {@link NotificationEntryManager}.
- */
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper()
-public class NotificationEntryManagerTest extends SysuiTestCase {
- private static final String TEST_PACKAGE_NAME = "test";
- private static final int TEST_UID = 0;
-
- @Mock private NotificationPresenter mPresenter;
- @Mock private KeyguardEnvironment mEnvironment;
- @Mock private ExpandableNotificationRow mRow;
- @Mock private NotificationEntryListener mEntryListener;
- @Mock private NotifCollectionListener mNotifCollectionListener;
- @Mock private NotificationRemoveInterceptor mRemoveInterceptor;
- @Mock private HeadsUpManager mHeadsUpManager;
- @Mock private RankingMap mRankingMap;
- @Mock private NotificationGroupManagerLegacy mGroupManager;
- @Mock private NotificationRemoteInputManager mRemoteInputManager;
- @Mock private DeviceProvisionedController mDeviceProvisionedController;
- @Mock private RowInflaterTask mAsyncInflationTask;
- @Mock private NotificationEntryManagerLogger mLogger;
- @Mock private NotifPipelineFlags mNotifPipelineFlags;
- @Mock private LeakDetector mLeakDetector;
- @Mock private NotificationMediaManager mNotificationMediaManager;
- @Mock private NotificationRowBinder mNotificationRowBinder;
- @Mock private NotificationListener mNotificationListener;
- @Mock private IStatusBarService mStatusBarService;
-
- private FakeSystemClock mFakeSystemClock = new FakeSystemClock();
- private FakeExecutor mBgExecutor = new FakeExecutor(mFakeSystemClock);
-
- private int mId;
- private NotificationEntry mEntry;
- private DismissedByUserStats mStats;
- private StatusBarNotification mSbn;
- private NotificationEntryManager mEntryManager;
-
- private void setUserSentiment(String key, int sentiment) {
- doAnswer(invocationOnMock -> {
- Ranking ranking = (Ranking)
- invocationOnMock.getArguments()[1];
- ranking.populate(
- key,
- 0,
- false,
- 0,
- 0,
- IMPORTANCE_DEFAULT,
- null, null,
- null, null, null, true, sentiment, false, -1, false, null, null, false, false,
- false, null, 0, false);
- return true;
- }).when(mRankingMap).getRanking(eq(key), any(Ranking.class));
- }
-
- private void setSmartActions(String key, ArrayList<Notification.Action> smartActions) {
- doAnswer(invocationOnMock -> {
- Ranking ranking = (Ranking)
- invocationOnMock.getArguments()[1];
- ranking.populate(
- key,
- 0,
- false,
- 0,
- 0,
- IMPORTANCE_DEFAULT,
- null, null,
- null, null, null, true,
- Ranking.USER_SENTIMENT_NEUTRAL, false, -1,
- false, smartActions, null, false, false, false, null, 0, false);
- return true;
- }).when(mRankingMap).getRanking(eq(key), any(Ranking.class));
- }
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mDependency.injectMockDependency(SmartReplyController.class);
-
- allowTestableLooperAsMainThread();
- mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
- Handler.createAsync(TestableLooper.get(this).getLooper()));
-
- mEntry = createNotification();
- mStats = defaultStats(mEntry);
- mSbn = mEntry.getSbn();
-
- mEntryManager = new NotificationEntryManager(
- mLogger,
- mGroupManager,
- mNotifPipelineFlags,
- () -> mNotificationRowBinder,
- () -> mRemoteInputManager,
- mLeakDetector,
- mStatusBarService,
- mock(DumpManager.class),
- mBgExecutor
- );
- mEntryManager.initialize(
- mNotificationListener,
- new NotificationRankingManager(
- () -> mNotificationMediaManager,
- mGroupManager,
- mHeadsUpManager,
- mock(NotificationFilter.class),
- mLogger,
- mock(NotificationSectionsFeatureManager.class),
- mock(PeopleNotificationIdentifier.class),
- mock(HighPriorityProvider.class),
- mEnvironment));
- mEntryManager.addNotificationEntryListener(mEntryListener);
- mEntryManager.addCollectionListener(mNotifCollectionListener);
- mEntryManager.addNotificationRemoveInterceptor(mRemoveInterceptor);
-
- setUserSentiment(mSbn.getKey(), Ranking.USER_SENTIMENT_NEUTRAL);
- }
-
- @Test
- public void testAddNotification_noDuplicateEntriesCreated() {
- // GIVEN a notification has been added
- mEntryManager.addNotification(mSbn, mRankingMap);
-
- // WHEN the same notification is added multiple times before the previous entry (with
- // the same key) didn't finish inflating
- mEntryManager.addNotification(mSbn, mRankingMap);
- mEntryManager.addNotification(mSbn, mRankingMap);
- mEntryManager.addNotification(mSbn, mRankingMap);
-
- // THEN getAllNotifs() only contains exactly one notification with this key
- int count = 0;
- for (NotificationEntry entry : mEntryManager.getAllNotifs()) {
- if (entry.getKey().equals(mSbn.getKey())) {
- count++;
- }
- }
- assertEquals("Should only be one entry with key=" + mSbn.getKey() + " in mAllNotifs. "
- + "Instead there are " + count, 1, count);
- }
-
- @Test
- public void testAddNotification_setsUserSentiment() {
- mEntryManager.addNotification(mSbn, mRankingMap);
-
- ArgumentCaptor<NotificationEntry> entryCaptor = ArgumentCaptor.forClass(
- NotificationEntry.class);
- verify(mEntryListener).onPendingEntryAdded(entryCaptor.capture());
- NotificationEntry entry = entryCaptor.getValue();
-
- assertEquals(entry.getUserSentiment(), Ranking.USER_SENTIMENT_NEUTRAL);
- }
-
- @Test
- public void testUpdateNotification_prePostEntryOrder() throws Exception {
- TestableLooper.get(this).processAllMessages();
-
- mEntryManager.addActiveNotificationForTest(mEntry);
-
- mEntryManager.updateNotification(mSbn, mRankingMap);
-
- // Ensure that update callbacks happen in correct order
- InOrder order = inOrder(mEntryListener, mPresenter, mEntryListener);
- order.verify(mEntryListener).onPreEntryUpdated(mEntry);
- order.verify(mEntryListener).onPostEntryUpdated(mEntry);
- }
-
- @Test
- public void testRemoveNotification() {
- mEntry.setRow(mRow);
- mEntryManager.addActiveNotificationForTest(mEntry);
-
- mEntryManager.removeNotification(mSbn.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
-
- verify(mEntryListener).onEntryRemoved(
- argThat(matchEntryOnKey()), any(),
- eq(false) /* removedByUser */, eq(UNDEFINED_DISMISS_REASON));
- verify(mRow).setRemoved();
-
- assertNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()));
- }
-
- @Test
- public void testRemoveUninflatedNotification_removesNotificationFromAllNotifsList() {
- // GIVEN an uninflated entry is added
- mEntryManager.addNotification(mSbn, mRankingMap);
- assertTrue(entriesContainKey(mEntryManager.getAllNotifs(), mSbn.getKey()));
-
- // WHEN the uninflated entry is removed
- mEntryManager.performRemoveNotification(mSbn, mock(DismissedByUserStats.class),
- UNDEFINED_DISMISS_REASON);
-
- // THEN the entry is still removed from the allNotifications list
- assertFalse(entriesContainKey(mEntryManager.getAllNotifs(), mSbn.getKey()));
- }
-
- @Test
- public void testPerformRemoveNotification_sendRemovalToServer() throws RemoteException {
- // GIVEN an entry manager with a notification
- mEntryManager.addActiveNotificationForTest(mEntry);
-
- // GIVEN interceptor that doesn't intercept
- when(mRemoveInterceptor.onNotificationRemoveRequested(
- eq(mEntry.getKey()), argThat(matchEntryOnKey()), anyInt()))
- .thenReturn(false);
-
- // WHEN the notification entry is removed
- mEntryManager.performRemoveNotification(mSbn, mStats, REASON_CANCEL);
-
- // THEN notification removal is sent to the server
- FakeExecutor.exhaustExecutors(mBgExecutor);
- verify(mStatusBarService).onNotificationClear(
- mSbn.getPackageName(),
- mSbn.getUser().getIdentifier(),
- mSbn.getKey(),
- mStats.dismissalSurface,
- mStats.dismissalSentiment,
- mStats.notificationVisibility);
- verifyNoMoreInteractions(mStatusBarService);
- }
-
- @Test
- public void testRemoveNotification_onEntryRemoveNotFiredIfEntryDoesntExist() {
-
- mEntryManager.removeNotification("not_a_real_key", mRankingMap, UNDEFINED_DISMISS_REASON);
-
- verify(mEntryListener, never()).onEntryRemoved(argThat(matchEntryOnKey()), any(),
- eq(false) /* removedByUser */, eq(UNDEFINED_DISMISS_REASON));
- }
-
- /** Regression test for b/201097913. */
- @Test
- public void testRemoveNotification_whilePending_onlyCollectionListenerNotified() {
- // Add and then remove a pending entry (entry that hasn't been inflated).
- mEntryManager.addNotification(mSbn, mRankingMap);
- mEntryManager.removeNotification(mSbn.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
-
- // Verify that only the listener for the NEW pipeline is notified.
- // Old pipeline:
- verify(mEntryListener, never()).onEntryRemoved(
- argThat(matchEntryOnKey()), any(), anyBoolean(), anyInt());
- // New pipeline:
- verify(mNotifCollectionListener).onEntryRemoved(
- argThat(matchEntryOnKey()), anyInt());
- }
-
- @Test
- public void testUpdateNotificationRanking_noChange() {
- when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
- when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
-
- mEntry.setRow(mRow);
- mEntryManager.addActiveNotificationForTest(mEntry);
- setSmartActions(mEntry.getKey(), null);
-
- mEntryManager.updateNotificationRanking(mRankingMap);
- assertThat(mEntry.getSmartActions()).isEmpty();
- }
-
- @Test
- public void testUpdateNotificationRanking_pendingNotification() {
- when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
- when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
-
- mEntry.setRow(null);
- mEntryManager.mPendingNotifications.put(mEntry.getKey(), mEntry);
- setSmartActions(mEntry.getKey(), new ArrayList<>(Arrays.asList(createAction())));
-
- mEntryManager.updateNotificationRanking(mRankingMap);
- assertEquals(1, mEntry.getSmartActions().size());
- assertEquals("action", mEntry.getSmartActions().get(0).title);
- }
-
- @Test
- public void testUpdatePendingNotification_rankingUpdated() {
- // GIVEN a notification with ranking is pending
- final Ranking originalRanking = mEntry.getRanking();
- mEntryManager.mPendingNotifications.put(mEntry.getKey(), mEntry);
-
- // WHEN the same notification has been updated with a new ranking
- final int newRank = 2345;
- doAnswer(invocationOnMock -> {
- Ranking ranking = (Ranking)
- invocationOnMock.getArguments()[1];
- ranking.populate(
- mEntry.getKey(),
- newRank, /* this changed!! */
- false,
- 0,
- 0,
- IMPORTANCE_DEFAULT,
- null, null,
- null, null, null, true,
- Ranking.USER_SENTIMENT_NEUTRAL, false, -1,
- false, null, null, false, false, false, null, 0, false);
- return true;
- }).when(mRankingMap).getRanking(eq(mEntry.getKey()), any(Ranking.class));
- mEntryManager.addNotification(mSbn, mRankingMap);
-
- // THEN ranking for the entry has been updated with new ranking
- assertEquals(newRank, mEntry.getRanking().getRank());
- }
-
- @Test
- public void testNotifyChannelModified_notifiesListeners() {
- NotificationChannel channel = mock(NotificationChannel.class);
- String pkg = "PKG";
- mEntryManager.notifyChannelModified(pkg, UserHandle.CURRENT, channel,
- NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
- verify(mNotifCollectionListener).onNotificationChannelModified(eq(pkg),
- eq(UserHandle.CURRENT), eq(channel), eq(NOTIFICATION_CHANNEL_OR_GROUP_UPDATED));
- verify(mEntryListener).onNotificationChannelModified(eq(pkg),
- eq(UserHandle.CURRENT), eq(channel), eq(NOTIFICATION_CHANNEL_OR_GROUP_UPDATED));
- }
-
- @Test
- public void testLifetimeExtenders_ifNotificationIsRetainedItIsntRemoved() {
- // GIVEN an entry manager with a notification
- mEntryManager.addActiveNotificationForTest(mEntry);
-
- // GIVEN a lifetime extender that always tries to extend lifetime
- NotificationLifetimeExtender extender = mock(NotificationLifetimeExtender.class);
- when(extender.shouldExtendLifetime(mEntry)).thenReturn(true);
- mEntryManager.addNotificationLifetimeExtender(extender);
-
- // WHEN the notification is removed
- mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
-
- // THEN the extender is asked to manage the lifetime
- verify(extender).setShouldManageLifetime(mEntry, true);
- // THEN the notification is retained
- assertNotNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()));
- verify(mEntryListener, never()).onEntryRemoved(
- argThat(matchEntryOnKey()), any(), eq(false), eq(UNDEFINED_DISMISS_REASON));
- }
-
- @Test
- public void testLifetimeExtenders_whenRetentionEndsNotificationIsRemoved() {
- // GIVEN an entry manager with a notification whose life has been extended
- mEntryManager.addActiveNotificationForTest(mEntry);
- final FakeNotificationLifetimeExtender extender = new FakeNotificationLifetimeExtender();
- mEntryManager.addNotificationLifetimeExtender(extender);
- mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
- assertTrue(extender.isManaging(mEntry.getKey()));
-
- // WHEN the extender finishes its extension
- extender.setExtendLifetimes(false);
- extender.getCallback().onSafeToRemove(mEntry.getKey());
-
- // THEN the notification is removed
- assertNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()));
- verify(mEntryListener).onEntryRemoved(
- argThat(matchEntryOnKey()), any(), eq(false), eq(UNDEFINED_DISMISS_REASON));
- }
-
- @Test
- public void testLifetimeExtenders_whenNotificationUpdatedRetainersAreCanceled() {
- // GIVEN an entry manager with a notification whose life has been extended
- mEntryManager.addActiveNotificationForTest(mEntry);
- NotificationLifetimeExtender extender = mock(NotificationLifetimeExtender.class);
- when(extender.shouldExtendLifetime(mEntry)).thenReturn(true);
- mEntryManager.addNotificationLifetimeExtender(extender);
- mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
-
- // WHEN the notification is updated
- mEntryManager.updateNotification(mEntry.getSbn(), mRankingMap);
-
- // THEN the lifetime extension is canceled
- verify(extender).setShouldManageLifetime(mEntry, false);
- }
-
- @Test
- public void testLifetimeExtenders_whenNewExtenderTakesPrecedenceOldExtenderIsCanceled() {
- // GIVEN an entry manager with a notification
- mEntryManager.addActiveNotificationForTest(mEntry);
-
- // GIVEN two lifetime extenders, the first which never extends and the second which
- // always extends
- NotificationLifetimeExtender extender1 = mock(NotificationLifetimeExtender.class);
- when(extender1.shouldExtendLifetime(mEntry)).thenReturn(false);
- NotificationLifetimeExtender extender2 = mock(NotificationLifetimeExtender.class);
- when(extender2.shouldExtendLifetime(mEntry)).thenReturn(true);
- mEntryManager.addNotificationLifetimeExtender(extender1);
- mEntryManager.addNotificationLifetimeExtender(extender2);
-
- // GIVEN a notification was lifetime-extended and extender2 is managing it
- mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
- verify(extender1, never()).setShouldManageLifetime(mEntry, true);
- verify(extender2).setShouldManageLifetime(mEntry, true);
-
- // WHEN the extender1 changes its mind and wants to extend the lifetime of the notif
- when(extender1.shouldExtendLifetime(mEntry)).thenReturn(true);
- mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
-
- // THEN extender2 stops managing the notif and extender1 starts managing it
- verify(extender1).setShouldManageLifetime(mEntry, true);
- verify(extender2).setShouldManageLifetime(mEntry, false);
- }
-
- /**
- * Ensure that calling NotificationEntryManager.performRemoveNotification() doesn't crash when
- * given a notification that has already been removed from NotificationData.
- */
- @Test
- public void testPerformRemoveNotification_removedEntry() {
- mEntryManager.removeNotification(mSbn.getKey(), null, 0);
- mEntryManager.performRemoveNotification(mSbn, mock(DismissedByUserStats.class),
- REASON_CANCEL);
- }
-
- @Test
- public void testRemoveInterceptor_interceptsDontGetRemoved() throws InterruptedException {
- // GIVEN an entry manager with a notification
- mEntryManager.addActiveNotificationForTest(mEntry);
-
- // GIVEN interceptor that intercepts that entry
- when(mRemoveInterceptor.onNotificationRemoveRequested(
- eq(mEntry.getKey()), argThat(matchEntryOnKey()), anyInt()))
- .thenReturn(true);
-
- // WHEN the notification is removed
- mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
-
- // THEN the interceptor intercepts & the entry is not removed & no listeners are called
- assertNotNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()));
- verify(mEntryListener, never()).onEntryRemoved(argThat(matchEntryOnKey()),
- any(NotificationVisibility.class), anyBoolean(), eq(UNDEFINED_DISMISS_REASON));
- }
-
- @Test
- public void testRemoveInterceptor_notInterceptedGetsRemoved() {
- // GIVEN an entry manager with a notification
- mEntryManager.addActiveNotificationForTest(mEntry);
-
- // GIVEN interceptor that doesn't intercept
- when(mRemoveInterceptor.onNotificationRemoveRequested(
- eq(mEntry.getKey()), argThat(matchEntryOnKey()), anyInt()))
- .thenReturn(false);
-
- // WHEN the notification is removed
- mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
-
- // THEN the interceptor intercepts & the entry is not removed & no listeners are called
- assertNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()));
- verify(mEntryListener, atLeastOnce()).onEntryRemoved(argThat(matchEntryOnKey()),
- any(NotificationVisibility.class), anyBoolean(), eq(UNDEFINED_DISMISS_REASON));
- }
-
- /* Tests annexed from NotificationDataTest go here */
-
- @Test
- public void testGetNotificationsForCurrentUser_shouldFilterNonCurrentUserNotifications() {
- Notification.Builder n = new Notification.Builder(mContext, "di")
- .setSmallIcon(R.drawable.ic_person)
- .setContentTitle("Title")
- .setContentText("Text");
-
- NotificationEntry e2 = new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_NAME)
- .setOpPkg(TEST_PACKAGE_NAME)
- .setUid(TEST_UID)
- .setId(mId++)
- .setNotification(n.build())
- .setUser(new UserHandle(ActivityManager.getCurrentUser()))
- .setChannel(new NotificationChannel("id", "", IMPORTANCE_DEFAULT))
- .build();
-
- mEntryManager.addActiveNotificationForTest(mEntry);
- mEntryManager.addActiveNotificationForTest(e2);
-
- when(mEnvironment.isNotificationForCurrentProfiles(mEntry.getSbn())).thenReturn(false);
- when(mEnvironment.isNotificationForCurrentProfiles(e2.getSbn())).thenReturn(true);
-
- List<NotificationEntry> result = mEntryManager.getActiveNotificationsForCurrentUser();
- assertEquals(result.size(), 1);
- junit.framework.Assert.assertEquals(result.get(0), e2);
- }
-
- /* End annex */
-
- private boolean entriesContainKey(Collection<NotificationEntry> entries, String key) {
- for (NotificationEntry entry : entries) {
- if (entry.getSbn().getKey().equals(key)) {
- return true;
- }
- }
- return false;
- }
-
- private Notification.Action createAction() {
- return new Notification.Action.Builder(
- Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
- "action",
- PendingIntent.getBroadcast(getContext(), 0, new Intent("Action"),
- PendingIntent.FLAG_IMMUTABLE)).build();
- }
-
- private ArgumentMatcher<NotificationEntry> matchEntryOnKey() {
- return e -> e.getKey().equals(mEntry.getKey());
- }
-
- private static class FakeNotificationLifetimeExtender implements NotificationLifetimeExtender {
- private NotificationSafeToRemoveCallback mCallback;
- private boolean mExtendLifetimes = true;
- private Set<String> mManagedNotifs = new ArraySet<>();
-
- @Override
- public void setCallback(@NonNull NotificationSafeToRemoveCallback callback) {
- mCallback = callback;
- }
-
- @Override
- public boolean shouldExtendLifetime(@NonNull NotificationEntry entry) {
- return mExtendLifetimes;
- }
-
- @Override
- public void setShouldManageLifetime(
- @NonNull NotificationEntry entry,
- boolean shouldManage) {
- final boolean hasEntry = mManagedNotifs.contains(entry.getKey());
- if (shouldManage) {
- if (hasEntry) {
- throw new RuntimeException("Already managing this entry: " + entry.getKey());
- }
- mManagedNotifs.add(entry.getKey());
- } else {
- if (!hasEntry) {
- throw new RuntimeException("Not managing this entry: " + entry.getKey());
- }
- mManagedNotifs.remove(entry.getKey());
- }
- }
-
- public void setExtendLifetimes(boolean extendLifetimes) {
- mExtendLifetimes = extendLifetimes;
- }
-
- public NotificationSafeToRemoveCallback getCallback() {
- return mCallback;
- }
-
- public boolean isManaging(String notificationKey) {
- return mManagedNotifs.contains(notificationKey);
- }
- }
-
- private NotificationEntry createNotification() {
- Notification.Builder n = new Notification.Builder(mContext, "id")
- .setSmallIcon(R.drawable.ic_person)
- .setContentTitle("Title")
- .setContentText("Text");
-
- return new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_NAME)
- .setOpPkg(TEST_PACKAGE_NAME)
- .setUid(TEST_UID)
- .setId(mId++)
- .setNotification(n.build())
- .setChannel(new NotificationChannel("id", "", IMPORTANCE_DEFAULT))
- .setUser(new UserHandle(ActivityManager.getCurrentUser()))
- .build();
- }
-
- private static DismissedByUserStats defaultStats(NotificationEntry entry) {
- return new DismissedByUserStats(
- DISMISSAL_SHADE,
- DISMISS_SENTIMENT_NEUTRAL,
- NotificationVisibility.obtain(entry.getKey(), 7, 2, true));
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
deleted file mode 100644
index 2cacaf78479d..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.Manifest;
-import android.app.Notification;
-import android.app.Notification.MediaStyle;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
-import android.media.session.MediaSession;
-import android.os.Bundle;
-import android.service.notification.StatusBarNotification;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
-
-import androidx.annotation.NonNull;
-import androidx.test.annotation.UiThreadTest;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.ForegroundServiceController;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.media.MediaFeatureFlag;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shade.ShadeController;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
-import com.android.systemui.statusbar.notification.collection.provider.DebugModeFilterProvider;
-import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
-import com.android.wm.shell.bubbles.Bubbles;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Optional;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
-public class NotificationFilterTest extends SysuiTestCase {
-
- private static final int UID_NORMAL = 123;
- private static final int UID_ALLOW_DURING_SETUP = 456;
- private static final String TEST_HIDDEN_NOTIFICATION_KEY = "testHiddenNotificationKey";
-
- private final StatusBarNotification mMockStatusBarNotification =
- mock(StatusBarNotification.class);
-
- @Mock
- DebugModeFilterProvider mDebugModeFilterProvider;
- @Mock
- StatusBarStateController mStatusBarStateController;
- @Mock
- KeyguardEnvironment mEnvironment;
- @Mock
- ForegroundServiceController mFsc;
- @Mock
- NotificationLockscreenUserManager mUserManager;
- @Mock
- MediaFeatureFlag mMediaFeatureFlag;
-
- private final IPackageManager mMockPackageManager = mock(IPackageManager.class);
-
- private NotificationFilter mNotificationFilter;
- private ExpandableNotificationRow mRow;
- private NotificationEntry mMediaEntry;
- private MediaSession mMediaSession;
-
- @Before
- public void setUp() throws Exception {
- allowTestableLooperAsMainThread();
- MockitoAnnotations.initMocks(this);
- when(mMockStatusBarNotification.getUid()).thenReturn(UID_NORMAL);
-
- mMediaSession = new MediaSession(mContext, "TEST_MEDIA_SESSION");
- NotificationEntryBuilder builder = new NotificationEntryBuilder();
- builder.modifyNotification(mContext).setStyle(
- new MediaStyle().setMediaSession(mMediaSession.getSessionToken()));
- mMediaEntry = builder.build();
-
- when(mMockPackageManager.checkUidPermission(
- eq(Manifest.permission.NOTIFICATION_DURING_SETUP),
- eq(UID_NORMAL)))
- .thenReturn(PackageManager.PERMISSION_DENIED);
- when(mMockPackageManager.checkUidPermission(
- eq(Manifest.permission.NOTIFICATION_DURING_SETUP),
- eq(UID_ALLOW_DURING_SETUP)))
- .thenReturn(PackageManager.PERMISSION_GRANTED);
- mDependency.injectTestDependency(ForegroundServiceController.class, mFsc);
- mDependency.injectTestDependency(NotificationGroupManagerLegacy.class,
- new NotificationGroupManagerLegacy(
- mock(StatusBarStateController.class),
- () -> mock(PeopleNotificationIdentifier.class),
- Optional.of(mock(Bubbles.class)),
- mock(DumpManager.class)));
- mDependency.injectMockDependency(ShadeController.class);
- mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
- mDependency.injectTestDependency(KeyguardEnvironment.class, mEnvironment);
- when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
- when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
- NotificationTestHelper testHelper = new NotificationTestHelper(
- mContext,
- mDependency,
- TestableLooper.get(this));
- mRow = testHelper.createRow();
- mNotificationFilter = newNotificationFilter();
- }
-
- @NonNull
- private NotificationFilter newNotificationFilter() {
- return new NotificationFilter(
- mDebugModeFilterProvider,
- mStatusBarStateController,
- mEnvironment,
- mFsc,
- mUserManager,
- mMediaFeatureFlag);
- }
-
- @After
- public void tearDown() {
- mMediaSession.release();
- }
-
- @Test
- @UiThreadTest
- public void testShowNotificationEvenIfUnprovisioned_FalseIfNoExtra() {
- initStatusBarNotification(false);
- when(mMockStatusBarNotification.getUid()).thenReturn(UID_ALLOW_DURING_SETUP);
-
- assertFalse(
- NotificationFilter.showNotificationEvenIfUnprovisioned(
- mMockPackageManager,
- mMockStatusBarNotification));
- }
-
- @Test
- @UiThreadTest
- public void testShowNotificationEvenIfUnprovisioned_FalseIfNoPermission() {
- initStatusBarNotification(true);
-
- assertFalse(
- NotificationFilter.showNotificationEvenIfUnprovisioned(
- mMockPackageManager,
- mMockStatusBarNotification));
- }
-
- @Test
- @UiThreadTest
- public void testShowNotificationEvenIfUnprovisioned_TrueIfHasPermissionAndExtra() {
- initStatusBarNotification(true);
- when(mMockStatusBarNotification.getUid()).thenReturn(UID_ALLOW_DURING_SETUP);
-
- assertTrue(
- NotificationFilter.showNotificationEvenIfUnprovisioned(
- mMockPackageManager,
- mMockStatusBarNotification));
- }
-
- @Test
- public void testShouldFilterHiddenNotifications() {
- initStatusBarNotification(false);
- // setup
- when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
-
- // test should filter out hidden notifications:
- // hidden
- NotificationEntry entry = new NotificationEntryBuilder()
- .setSuspended(true)
- .build();
-
- assertTrue(mNotificationFilter.shouldFilterOut(entry));
-
- // not hidden
- entry = new NotificationEntryBuilder()
- .setSuspended(false)
- .build();
- assertFalse(mNotificationFilter.shouldFilterOut(entry));
- }
-
- @Test
- public void shouldFilterOtherNotificationWhenDisabled() {
- // GIVEN that the media feature is disabled
- when(mMediaFeatureFlag.getEnabled()).thenReturn(false);
- NotificationFilter filter = newNotificationFilter();
- // WHEN the media filter is asked about an entry
- NotificationEntry otherEntry = new NotificationEntryBuilder().build();
- final boolean shouldFilter = filter.shouldFilterOut(otherEntry);
- // THEN it shouldn't be filtered
- assertFalse(shouldFilter);
- }
-
- @Test
- public void shouldFilterOtherNotificationWhenEnabled() {
- // GIVEN that the media feature is enabled
- when(mMediaFeatureFlag.getEnabled()).thenReturn(true);
- NotificationFilter filter = newNotificationFilter();
- // WHEN the media filter is asked about an entry
- NotificationEntry otherEntry = new NotificationEntryBuilder().build();
- final boolean shouldFilter = filter.shouldFilterOut(otherEntry);
- // THEN it shouldn't be filtered
- assertFalse(shouldFilter);
- }
-
- @Test
- public void shouldFilterMediaNotificationWhenDisabled() {
- // GIVEN that the media feature is disabled
- when(mMediaFeatureFlag.getEnabled()).thenReturn(false);
- NotificationFilter filter = newNotificationFilter();
- // WHEN the media filter is asked about a media entry
- final boolean shouldFilter = filter.shouldFilterOut(mMediaEntry);
- // THEN it shouldn't be filtered
- assertFalse(shouldFilter);
- }
-
- @Test
- public void shouldFilterMediaNotificationWhenEnabled() {
- // GIVEN that the media feature is enabled
- when(mMediaFeatureFlag.getEnabled()).thenReturn(true);
- NotificationFilter filter = newNotificationFilter();
- // WHEN the media filter is asked about a media entry
- final boolean shouldFilter = filter.shouldFilterOut(mMediaEntry);
- // THEN it should be filtered
- assertTrue(shouldFilter);
- }
-
- private void initStatusBarNotification(boolean allowDuringSetup) {
- Bundle bundle = new Bundle();
- bundle.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, allowDuringSetup);
- Notification notification = new Notification.Builder(mContext, "test")
- .addExtras(bundle)
- .build();
- when(mMockStatusBarNotification.getNotification()).thenReturn(notification);
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
deleted file mode 100644
index 58abbf2ddef7..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.notification;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.os.Handler;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper()
-public class VisualStabilityManagerTest extends SysuiTestCase {
-
- private TestableLooper mTestableLooper;
-
- private VisualStabilityManager mVisualStabilityManager;
- private VisualStabilityProvider mVisualStabilityProvider = mock(VisualStabilityProvider.class);
- private VisualStabilityManager.Callback mCallback = mock(VisualStabilityManager.Callback.class);
- private VisibilityLocationProvider mLocationProvider = mock(VisibilityLocationProvider.class);
- private ExpandableNotificationRow mRow = mock(ExpandableNotificationRow.class);
- private NotificationEntry mEntry;
-
- private StatusBarStateController.StateListener mStatusBarStateListener;
- private WakefulnessLifecycle.Observer mWakefulnessObserver;
-
- @Before
- public void setUp() {
- StatusBarStateController statusBarStateController = mock(StatusBarStateController.class);
- WakefulnessLifecycle wakefulnessLifecycle = mock(WakefulnessLifecycle.class);
-
- mTestableLooper = TestableLooper.get(this);
- mVisualStabilityManager = new VisualStabilityManager(
- mock(NotificationEntryManager.class),
- mVisualStabilityProvider,
- new Handler(mTestableLooper.getLooper()),
- statusBarStateController,
- wakefulnessLifecycle,
- mock(DumpManager.class));
-
- mVisualStabilityManager.setVisibilityLocationProvider(mLocationProvider);
- mEntry = new NotificationEntryBuilder().build();
- mEntry.setRow(mRow);
-
- when(mRow.getEntry()).thenReturn(mEntry);
-
- ArgumentCaptor<StatusBarStateController.StateListener> stateListenerCaptor =
- ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
- verify(statusBarStateController).addCallback(stateListenerCaptor.capture());
- mStatusBarStateListener = stateListenerCaptor.getValue();
-
- ArgumentCaptor<WakefulnessLifecycle.Observer> wakefulnessObserverCaptor =
- ArgumentCaptor.forClass(WakefulnessLifecycle.Observer.class);
- verify(wakefulnessLifecycle).addObserver(wakefulnessObserverCaptor.capture());
- mWakefulnessObserver = wakefulnessObserverCaptor.getValue();
- }
-
- @Test
- public void testPanelExpansion() {
- setPanelExpanded(true);
- setScreenOn(true);
- assertFalse(mVisualStabilityManager.canReorderNotification(mRow));
- setPanelExpanded(false);
- assertTrue(mVisualStabilityManager.canReorderNotification(mRow));
- }
-
- @Test
- public void testScreenOn() {
- setPanelExpanded(true);
- setScreenOn(true);
- assertFalse(mVisualStabilityManager.canReorderNotification(mRow));
- setScreenOn(false);
- assertTrue(mVisualStabilityManager.canReorderNotification(mRow));
- }
-
- @Test
- public void testReorderingAllowedChangesScreenOn() {
- setPanelExpanded(true);
- setScreenOn(true);
- assertFalse(mVisualStabilityManager.isReorderingAllowed());
- setScreenOn(false);
- assertTrue(mVisualStabilityManager.isReorderingAllowed());
- }
-
- @Test
- public void testReorderingAllowedChangesPanel() {
- setPanelExpanded(true);
- setScreenOn(true);
- assertFalse(mVisualStabilityManager.isReorderingAllowed());
- setPanelExpanded(false);
- assertTrue(mVisualStabilityManager.isReorderingAllowed());
- }
-
- @Test
- public void testCallBackCalledScreenOn() {
- setPanelExpanded(true);
- setScreenOn(true);
- mVisualStabilityManager.addReorderingAllowedCallback(mCallback, false /* persistent */);
- setScreenOn(false);
- verify(mCallback).onChangeAllowed();
- }
-
- @Test
- public void testCallBackCalledPanelExpanded() {
- setPanelExpanded(true);
- setScreenOn(true);
- mVisualStabilityManager.addReorderingAllowedCallback(mCallback, false /* persistent */);
- setPanelExpanded(false);
- verify(mCallback).onChangeAllowed();
- }
-
- @Test
- public void testCallBackExactlyOnce() {
- setPanelExpanded(true);
- setScreenOn(true);
- mVisualStabilityManager.addReorderingAllowedCallback(mCallback, false /* persistent */);
- setScreenOn(false);
- setScreenOn(true);
- setScreenOn(false);
- verify(mCallback).onChangeAllowed();
- }
-
- @Test
- public void testCallBackCalledContinuouslyWhenRequested() {
- setPanelExpanded(true);
- setScreenOn(true);
- mVisualStabilityManager.addReorderingAllowedCallback(mCallback, true /* persistent */);
- setScreenOn(false);
- setScreenOn(true);
- setScreenOn(false);
- verify(mCallback, times(2)).onChangeAllowed();
- }
-
- @Test
- public void testAddedCanReorder() {
- setPanelExpanded(true);
- setScreenOn(true);
- mVisualStabilityManager.notifyViewAddition(mRow);
- assertTrue(mVisualStabilityManager.canReorderNotification(mRow));
- }
-
- @Test
- public void testReorderingVisibleHeadsUpNotAllowed() {
- setPanelExpanded(true);
- setScreenOn(true);
- when(mLocationProvider.isInVisibleLocation(any(NotificationEntry.class))).thenReturn(true);
- mVisualStabilityManager.onHeadsUpStateChanged(mEntry, true);
- assertFalse(mVisualStabilityManager.canReorderNotification(mRow));
- }
-
- @Test
- public void testReorderingVisibleHeadsUpAllowed() {
- setPanelExpanded(true);
- setScreenOn(true);
- when(mLocationProvider.isInVisibleLocation(any(NotificationEntry.class))).thenReturn(false);
- mVisualStabilityManager.onHeadsUpStateChanged(mEntry, true);
- assertTrue(mVisualStabilityManager.canReorderNotification(mRow));
- }
-
- @Test
- public void testReorderingVisibleHeadsUpAllowedOnce() {
- setPanelExpanded(true);
- setScreenOn(true);
- when(mLocationProvider.isInVisibleLocation(any(NotificationEntry.class))).thenReturn(false);
- mVisualStabilityManager.onHeadsUpStateChanged(mEntry, true);
- mVisualStabilityManager.onReorderingFinished();
- assertFalse(mVisualStabilityManager.canReorderNotification(mRow));
- }
-
- @Test
- public void testPulsing() {
- setPulsing(true);
- assertFalse(mVisualStabilityManager.canReorderNotification(mRow));
- setPulsing(false);
- assertTrue(mVisualStabilityManager.canReorderNotification(mRow));
- }
-
- @Test
- public void testReorderingAllowedChanges_Pulsing() {
- setPulsing(true);
- assertFalse(mVisualStabilityManager.isReorderingAllowed());
- setPulsing(false);
- assertTrue(mVisualStabilityManager.isReorderingAllowed());
- }
-
- @Test
- public void testCallBackCalled_Pulsing() {
- setPulsing(true);
- mVisualStabilityManager.addReorderingAllowedCallback(mCallback, false /* persistent */);
- setPulsing(false);
- verify(mCallback).onChangeAllowed();
- }
-
- @Test
- public void testTemporarilyAllowReorderingNotifiesCallbacks() {
- // GIVEN having the panel open (which would block reordering)
- setScreenOn(true);
- setPanelExpanded(true);
- mVisualStabilityManager.addReorderingAllowedCallback(mCallback, false /* persistent */);
-
- // WHEN we temprarily allow reordering
- mVisualStabilityManager.temporarilyAllowReordering();
-
- // THEN callbacks are notified that reordering is allowed
- verify(mCallback).onChangeAllowed();
- assertTrue(mVisualStabilityManager.isReorderingAllowed());
- }
-
- @Test
- public void testTemporarilyAllowReorderingDoesntOverridePulsing() {
- // GIVEN we are in a pulsing state
- setPulsing(true);
- mVisualStabilityManager.addReorderingAllowedCallback(mCallback, false /* persistent */);
-
- // WHEN we temprarily allow reordering
- mVisualStabilityManager.temporarilyAllowReordering();
-
- // THEN reordering is still not allowed
- verify(mCallback, never()).onChangeAllowed();
- assertFalse(mVisualStabilityManager.isReorderingAllowed());
- }
-
- @Test
- public void testTemporarilyAllowReorderingExpires() {
- // GIVEN having the panel open (which would block reordering)
- setScreenOn(true);
- setPanelExpanded(true);
- mVisualStabilityManager.addReorderingAllowedCallback(mCallback, false /* persistent */);
-
- // WHEN we temprarily allow reordering and then wait until the window expires
- mVisualStabilityManager.temporarilyAllowReordering();
- assertTrue(mVisualStabilityManager.isReorderingAllowed());
- mTestableLooper.processMessages(1);
-
- // THEN reordering is no longer allowed
- assertFalse(mVisualStabilityManager.isReorderingAllowed());
- }
-
- private void setPanelExpanded(boolean expanded) {
- mStatusBarStateListener.onExpandedChanged(expanded);
- }
-
- private void setPulsing(boolean pulsing) {
- mStatusBarStateListener.onPulsingChanged(pulsing);
- }
-
- private void setScreenOn(boolean screenOn) {
- if (screenOn) {
- mWakefulnessObserver.onStartedWakingUp();
- } else {
- mWakefulnessObserver.onFinishedGoingToSleep();
- }
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
deleted file mode 100644
index c51c628c5457..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
+++ /dev/null
@@ -1,509 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.collection
-
-import android.app.Notification
-import android.app.NotificationChannel
-import android.app.NotificationManager.IMPORTANCE_DEFAULT
-import android.app.NotificationManager.IMPORTANCE_HIGH
-import android.app.NotificationManager.IMPORTANCE_LOW
-import android.app.PendingIntent
-import android.app.Person
-import android.os.SystemClock
-import android.service.notification.NotificationListenerService.RankingMap
-import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking
-import com.android.systemui.statusbar.NotificationMediaManager
-import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment
-import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger
-import com.android.systemui.statusbar.notification.NotificationFilter
-import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
-import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
-import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
-import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_FULL_PERSON
-import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_IMPORTANT_PERSON
-import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_PERSON
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
-import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING
-import com.android.systemui.statusbar.notification.stack.BUCKET_FOREGROUND_SERVICE
-import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
-import com.android.systemui.statusbar.policy.HeadsUpManager
-import com.google.common.truth.Truth.assertThat
-import dagger.Lazy
-import junit.framework.Assert.assertEquals
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.`when` as whenever
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class NotificationRankingManagerTest : SysuiTestCase() {
-
- private val lazyMedia: Lazy<NotificationMediaManager> = Lazy {
- mock(NotificationMediaManager::class.java)
- }
- private lateinit var personNotificationIdentifier: PeopleNotificationIdentifier
- private lateinit var rankingManager: TestableNotificationRankingManager
- private lateinit var sectionsManager: NotificationSectionsFeatureManager
- private lateinit var notificationFilter: NotificationFilter
-
- @Before
- fun setup() {
- personNotificationIdentifier =
- mock(PeopleNotificationIdentifier::class.java)
- sectionsManager = mock(NotificationSectionsFeatureManager::class.java)
- notificationFilter = mock(NotificationFilter::class.java)
- rankingManager = TestableNotificationRankingManager(
- lazyMedia,
- mock(NotificationGroupManagerLegacy::class.java),
- mock(HeadsUpManager::class.java),
- notificationFilter,
- mock(NotificationEntryManagerLogger::class.java),
- sectionsManager,
- personNotificationIdentifier,
- HighPriorityProvider(
- personNotificationIdentifier,
- mock(NotificationGroupManagerLegacy::class.java)),
- mock(KeyguardEnvironment::class.java)
- )
- }
-
- @Test
- fun testSort_highPriorityTrumpsNMSRank() {
- // NMS rank says A and then B. But A is not high priority and B is, so B should sort in
- // front
- val a = NotificationEntryBuilder()
- .setImportance(IMPORTANCE_LOW) // low priority
- .setRank(1) // NMS says rank first
- .setPkg("pkg")
- .setOpPkg("pkg")
- .setTag("tag")
- .setNotification(
- Notification.Builder(mContext, "test")
- .build())
- .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
- .setUser(mContext.getUser())
- .setOverrideGroupKey("")
- .build()
-
- val b = NotificationEntryBuilder()
- .setImportance(IMPORTANCE_HIGH) // high priority
- .setRank(2) // NMS says rank second
- .setPkg("pkg2")
- .setOpPkg("pkg2")
- .setTag("tag")
- .setNotification(
- Notification.Builder(mContext, "test")
- .build())
- .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
- .setUser(mContext.getUser())
- .setOverrideGroupKey("")
- .build()
-
- assertEquals(
- listOf(b, a),
- rankingManager.updateRanking(null, listOf(a, b), "test"))
- }
-
- @Test
- fun testSort_samePriorityUsesNMSRank() {
- // NMS rank says A and then B, and they are the same priority so use that rank
- val aN = Notification.Builder(mContext, "test")
- .setStyle(Notification.MessagingStyle(""))
- .build()
- val a = NotificationEntryBuilder()
- .setRank(1)
- .setImportance(IMPORTANCE_HIGH)
- .setPkg("pkg")
- .setOpPkg("pkg")
- .setTag("tag")
- .setNotification(aN)
- .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
- .setUser(mContext.getUser())
- .setOverrideGroupKey("")
- .build()
-
- val bN = Notification.Builder(mContext, "test")
- .setStyle(Notification.MessagingStyle(""))
- .build()
- val b = NotificationEntryBuilder()
- .setRank(2)
- .setImportance(IMPORTANCE_HIGH)
- .setPkg("pkg2")
- .setOpPkg("pkg2")
- .setTag("tag")
- .setNotification(bN)
- .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
- .setUser(mContext.getUser())
- .setOverrideGroupKey("")
- .build()
-
- assertEquals(
- listOf(a, b),
- rankingManager.updateRanking(null, listOf(a, b), "test"))
- }
-
- @Test
- fun testSort_headsUp_trumpsPeople() {
- whenever(sectionsManager.isFilteringEnabled()).thenReturn(true)
- val aN = Notification.Builder(mContext, "test")
- .setStyle(Notification.MessagingStyle(""))
- .build()
- val a = NotificationEntryBuilder()
- .setImportance(IMPORTANCE_HIGH)
- .setPkg("pkg")
- .setOpPkg("pkg")
- .setTag("tag")
- .setNotification(aN)
- .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
- .setUser(mContext.getUser())
- .setOverrideGroupKey("")
- .build()
-
- whenever(personNotificationIdentifier.getPeopleNotificationType(a))
- .thenReturn(TYPE_IMPORTANT_PERSON)
-
- val bN = Notification.Builder(mContext, "test")
- .setStyle(Notification.MessagingStyle(""))
- .build()
- val b = NotificationEntryBuilder()
- .setImportance(IMPORTANCE_HIGH)
- .setPkg("pkg2")
- .setOpPkg("pkg2")
- .setTag("tag")
- .setNotification(bN)
- .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
- .setUser(mContext.getUser())
- .setOverrideGroupKey("")
- .build()
- b.row = mock(ExpandableNotificationRow::class.java).also {
- whenever(it.isHeadsUp).thenReturn(true)
- }
-
- whenever(personNotificationIdentifier.getPeopleNotificationType(a))
- .thenReturn(TYPE_PERSON)
-
- assertEquals(listOf(b, a), rankingManager.updateRanking(null, listOf(a, b), "test"))
- }
-
- @Test
- fun testSort_importantPeople() {
- whenever(sectionsManager.isFilteringEnabled()).thenReturn(true)
- val aN = Notification.Builder(mContext, "test")
- .setStyle(Notification.MessagingStyle(""))
- .build()
- val a = NotificationEntryBuilder()
- .setImportance(IMPORTANCE_HIGH)
- .setPkg("pkg")
- .setOpPkg("pkg")
- .setTag("tag")
- .setNotification(aN)
- .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
- .setUser(mContext.user)
- .setOverrideGroupKey("")
- .build()
- whenever(personNotificationIdentifier.getPeopleNotificationType(a))
- .thenReturn(TYPE_PERSON)
-
- val bN = Notification.Builder(mContext, "test")
- .setStyle(Notification.MessagingStyle(""))
- .build()
- val b = NotificationEntryBuilder()
- .setImportance(IMPORTANCE_HIGH)
- .setPkg("pkg2")
- .setOpPkg("pkg2")
- .setTag("tag")
- .setNotification(bN)
- .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
- .setUser(mContext.user)
- .setOverrideGroupKey("")
- .build()
- whenever(personNotificationIdentifier.getPeopleNotificationType(b))
- .thenReturn(TYPE_IMPORTANT_PERSON)
-
- whenever(personNotificationIdentifier.compareTo(TYPE_PERSON, TYPE_IMPORTANT_PERSON))
- .thenReturn(TYPE_IMPORTANT_PERSON.compareTo(TYPE_PERSON))
- whenever(personNotificationIdentifier.compareTo(TYPE_IMPORTANT_PERSON, TYPE_PERSON))
- .thenReturn(TYPE_PERSON.compareTo(TYPE_IMPORTANT_PERSON))
-
- assertEquals(
- listOf(b, a),
- rankingManager.updateRanking(null, listOf(a, b), "test"))
- }
-
- @Test
- fun testSort_fullPeople() {
- whenever(sectionsManager.isFilteringEnabled()).thenReturn(true)
- val aN = Notification.Builder(mContext, "test")
- .setStyle(Notification.MessagingStyle(""))
- .build()
- val a = NotificationEntryBuilder()
- .setImportance(IMPORTANCE_HIGH)
- .setPkg("pkg")
- .setOpPkg("pkg")
- .setTag("tag")
- .setNotification(aN)
- .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
- .setUser(mContext.user)
- .setOverrideGroupKey("")
- .build()
- whenever(personNotificationIdentifier.getPeopleNotificationType(a))
- .thenReturn(TYPE_PERSON)
-
- val bN = Notification.Builder(mContext, "test")
- .setStyle(Notification.MessagingStyle(""))
- .build()
- val b = NotificationEntryBuilder()
- .setImportance(IMPORTANCE_HIGH)
- .setPkg("pkg2")
- .setOpPkg("pkg2")
- .setTag("tag")
- .setNotification(bN)
- .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
- .setUser(mContext.user)
- .setOverrideGroupKey("")
- .build()
- whenever(personNotificationIdentifier.getPeopleNotificationType(b))
- .thenReturn(TYPE_FULL_PERSON)
-
- whenever(personNotificationIdentifier.compareTo(TYPE_PERSON, TYPE_FULL_PERSON))
- .thenReturn(TYPE_FULL_PERSON.compareTo(TYPE_PERSON))
- whenever(personNotificationIdentifier.compareTo(TYPE_FULL_PERSON, TYPE_PERSON))
- .thenReturn(TYPE_PERSON.compareTo(TYPE_FULL_PERSON))
-
- assertEquals(
- listOf(b, a),
- rankingManager.updateRanking(null, listOf(a, b), "test"))
- }
-
- @Test
- fun testSort_properlySetsAlertingBucket() {
- val notif = Notification.Builder(mContext, "test") .build()
-
- val e = NotificationEntryBuilder()
- .setPkg("pkg")
- .setOpPkg("pkg")
- .setTag("tag")
- .setNotification(notif)
- .setUser(mContext.user)
- .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
- .setOverrideGroupKey("")
- .build()
-
- modifyRanking(e).setImportance(IMPORTANCE_DEFAULT).build()
-
- rankingManager.updateRanking(RankingMap(arrayOf(e.ranking)), listOf(e), "test")
- assertEquals(e.bucket, BUCKET_ALERTING)
- }
-
- @Test
- fun testSort_properlySetsSilentBucket() {
- val notif = Notification.Builder(mContext, "test") .build()
-
- val e = NotificationEntryBuilder()
- .setPkg("pkg")
- .setOpPkg("pkg")
- .setTag("tag")
- .setNotification(notif)
- .setUser(mContext.user)
- .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
- .setOverrideGroupKey("")
- .build()
-
- modifyRanking(e).setImportance(IMPORTANCE_LOW).build()
-
- rankingManager.updateRanking(RankingMap(arrayOf(e.ranking)), listOf(e), "test")
- assertEquals(e.bucket, BUCKET_SILENT)
- }
-
- @Test
- fun testFilter_resetsInitalizationTime() {
- // GIVEN an entry that was initialized 1 second ago
- val notif = Notification.Builder(mContext, "test") .build()
-
- val e = NotificationEntryBuilder()
- .setPkg("pkg")
- .setOpPkg("pkg")
- .setTag("tag")
- .setNotification(notif)
- .setUser(mContext.user)
- .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
- .setOverrideGroupKey("")
- .build()
-
- e.setInitializationTime(SystemClock.elapsedRealtime() - 1000)
- assertEquals(true, e.hasFinishedInitialization())
-
- // WHEN we update ranking and filter out the notification entry
- whenever(notificationFilter.shouldFilterOut(e)).thenReturn(true)
- rankingManager.updateRanking(RankingMap(arrayOf(e.ranking)), listOf(e), "test")
-
- // THEN the initialization time for the entry is reset
- assertEquals(false, e.hasFinishedInitialization())
- }
-
- @Test
- fun testSort_colorizedForegroundService() {
- whenever(sectionsManager.isFilteringEnabled()).thenReturn(true)
-
- val a = NotificationEntryBuilder()
- .setImportance(IMPORTANCE_HIGH)
- .setPkg("pkg")
- .setOpPkg("pkg")
- .setTag("tag")
- .setNotification(
- Notification.Builder(mContext, "test")
- .build())
- .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
- .setUser(mContext.getUser())
- .setOverrideGroupKey("")
- .build()
-
- val b = NotificationEntryBuilder()
- .setImportance(IMPORTANCE_DEFAULT) // high priority
- .setPkg("pkg2")
- .setOpPkg("pkg2")
- .setTag("tag")
- .setNotification(mock(Notification::class.java).also { notif ->
- whenever(notif.isForegroundService).thenReturn(true)
- whenever(notif.isColorized).thenReturn(true)
- })
- .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
- .setUser(mContext.getUser())
- .setOverrideGroupKey("")
- .build()
-
- val cN = Notification.Builder(mContext, "test")
- .setStyle(Notification.MessagingStyle(""))
- .build()
- val c = NotificationEntryBuilder()
- .setImportance(IMPORTANCE_HIGH)
- .setPkg("pkg")
- .setOpPkg("pkg")
- .setTag("tag")
- .setNotification(cN)
- .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
- .setUser(mContext.user)
- .setOverrideGroupKey("")
- .build()
- whenever(personNotificationIdentifier.getPeopleNotificationType(a))
- .thenReturn(TYPE_IMPORTANT_PERSON)
-
- assertThat(rankingManager.updateRanking(null, listOf(a, b, c), "test"))
- .containsExactly(b, c, a)
- assertThat(b.bucket).isEqualTo(BUCKET_FOREGROUND_SERVICE)
- }
-
- @Test
- fun testSort_importantCall() {
- whenever(sectionsManager.isFilteringEnabled()).thenReturn(true)
-
- val a = NotificationEntryBuilder()
- .setImportance(IMPORTANCE_HIGH)
- .setPkg("pkg")
- .setOpPkg("pkg")
- .setTag("tag")
- .setNotification(
- Notification.Builder(mContext, "test")
- .build())
- .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
- .setUser(mContext.getUser())
- .setOverrideGroupKey("")
- .build()
-
- val b = NotificationEntryBuilder()
- .setImportance(IMPORTANCE_DEFAULT) // high priority
- .setPkg("pkg2")
- .setOpPkg("pkg2")
- .setTag("tag")
- .setNotification(mock(Notification::class.java).also { notif ->
- whenever(notif.isForegroundService).thenReturn(true)
- whenever(notif.isColorized).thenReturn(true)
- })
- .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
- .setUser(mContext.getUser())
- .setOverrideGroupKey("")
- .build()
-
- val cN = Notification.Builder(mContext, "test")
- .setStyle(Notification.MessagingStyle(""))
- .build()
- val c = NotificationEntryBuilder()
- .setImportance(IMPORTANCE_HIGH)
- .setPkg("pkg")
- .setOpPkg("pkg")
- .setTag("tag")
- .setNotification(cN)
- .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
- .setUser(mContext.user)
- .setOverrideGroupKey("")
- .build()
-
- val dN = Notification.Builder(mContext, "test")
- .setStyle(Notification.CallStyle.forOngoingCall(
- Person.Builder().setName("caller").build(),
- mock(PendingIntent::class.java)))
- .build()
- val d = NotificationEntryBuilder()
- .setImportance(IMPORTANCE_DEFAULT) // high priority
- .setPkg("pkg2")
- .setOpPkg("pkg2")
- .setTag("tag")
- .setNotification(dN)
- .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
- .setUser(mContext.user)
- .setOverrideGroupKey("")
- .build()
- whenever(personNotificationIdentifier.getPeopleNotificationType(a))
- .thenReturn(TYPE_IMPORTANT_PERSON)
-
- assertThat(rankingManager.updateRanking(null, listOf(a, b, c, d), "test"))
- .containsExactly(b, d, c, a)
- assertThat(d.bucket).isEqualTo(BUCKET_FOREGROUND_SERVICE)
- }
-
- internal class TestableNotificationRankingManager(
- mediaManager: Lazy<NotificationMediaManager>,
- groupManager: NotificationGroupManagerLegacy,
- headsUpManager: HeadsUpManager,
- filter: NotificationFilter,
- logger: NotificationEntryManagerLogger,
- sectionsFeatureManager: NotificationSectionsFeatureManager,
- peopleNotificationIdentifier: PeopleNotificationIdentifier,
- highPriorityProvider: HighPriorityProvider,
- keyguardEnvironment: KeyguardEnvironment
- ) : NotificationRankingManager(
- mediaManager,
- groupManager,
- headsUpManager,
- filter,
- logger,
- sectionsFeatureManager,
- peopleNotificationIdentifier,
- highPriorityProvider,
- keyguardEnvironment
- ) {
- fun applyTestRankingMap(r: RankingMap) {
- rankingMap = r
- }
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index dfa38abc1ff8..9f214097ea55 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -68,6 +68,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener;
import com.android.systemui.util.time.FakeSystemClock;
@@ -1715,66 +1716,201 @@ public class ShadeListBuilderTest extends SysuiTestCase {
assertEquals(GroupEntry.ROOT_ENTRY, group.getPreviousParent());
}
+ static class CountingInvalidator {
+ CountingInvalidator(Pluggable pluggableToInvalidate) {
+ mPluggableToInvalidate = pluggableToInvalidate;
+ mInvalidationCount = 0;
+ }
+
+ public void setInvalidationCount(int invalidationCount) {
+ mInvalidationCount = invalidationCount;
+ }
+
+ public void maybeInvalidate() {
+ if (mInvalidationCount > 0) {
+ mPluggableToInvalidate.invalidateList("test invalidation");
+ mInvalidationCount--;
+ }
+ }
+
+ private Pluggable mPluggableToInvalidate;
+ private int mInvalidationCount;
+
+ private static final String TAG = "ShadeListBuilderTestCountingInvalidator";
+ }
+
+ @Test
+ public void testOutOfOrderPreGroupFilterInvalidationDoesNotThrowBeforeTooManyRuns() {
+ // GIVEN a PreGroupNotifFilter that gets invalidated during the grouping stage,
+ NotifFilter filter = new PackageFilter(PACKAGE_1);
+ CountingInvalidator invalidator = new CountingInvalidator(filter);
+ OnBeforeTransformGroupsListener listener = (list) -> invalidator.maybeInvalidate();
+ mListBuilder.addPreGroupFilter(filter);
+ mListBuilder.addOnBeforeTransformGroupsListener(listener);
+
+ // WHEN we try to run the pipeline and the filter is invalidated exactly
+ // MAX_CONSECUTIVE_REENTRANT_REBUILDS times,
+ addNotif(0, PACKAGE_2);
+ invalidator.setInvalidationCount(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS);
+ dispatchBuild();
+ runWhileScheduledUpTo(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
+
+ // THEN an exception is NOT thrown.
+ }
+
@Test(expected = IllegalStateException.class)
- public void testOutOfOrderPreGroupFilterInvalidationThrows() {
- // GIVEN a PreGroupNotifFilter that gets invalidated during the grouping stage
- NotifFilter filter = new PackageFilter(PACKAGE_5);
- OnBeforeTransformGroupsListener listener = (list) -> filter.invalidateList(null);
+ public void testOutOfOrderPreGroupFilterInvalidationThrowsAfterTooManyRuns() {
+ // GIVEN a PreGroupNotifFilter that gets invalidated during the grouping stage,
+ NotifFilter filter = new PackageFilter(PACKAGE_1);
+ CountingInvalidator invalidator = new CountingInvalidator(filter);
+ OnBeforeTransformGroupsListener listener = (list) -> invalidator.maybeInvalidate();
mListBuilder.addPreGroupFilter(filter);
mListBuilder.addOnBeforeTransformGroupsListener(listener);
- // WHEN we try to run the pipeline and the filter is invalidated
+ // WHEN we try to run the pipeline and the filter is invalidated more than
+ // MAX_CONSECUTIVE_REENTRANT_REBUILDS times,
+ addNotif(0, PACKAGE_2);
+ invalidator.setInvalidationCount(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS + 1);
+ dispatchBuild();
+ runWhileScheduledUpTo(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
+
+ // THEN an exception IS thrown.
+ }
+
+ @Test
+ public void testNonConsecutiveOutOfOrderInvalidationDontThrowAfterTooManyRuns() {
+ // GIVEN a PreGroupNotifFilter that gets invalidated during the grouping stage,
+ NotifFilter filter = new PackageFilter(PACKAGE_1);
+ CountingInvalidator invalidator = new CountingInvalidator(filter);
+ OnBeforeTransformGroupsListener listener = (list) -> invalidator.maybeInvalidate();
+ mListBuilder.addPreGroupFilter(filter);
+ mListBuilder.addOnBeforeTransformGroupsListener(listener);
+
+ // WHEN we try to run the pipeline and the filter is invalidated at least
+ // MAX_CONSECUTIVE_REENTRANT_REBUILDS times,
+ addNotif(0, PACKAGE_2);
+ invalidator.setInvalidationCount(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS);
+ dispatchBuild();
+ runWhileScheduledUpTo(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
+ invalidator.setInvalidationCount(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS);
+ dispatchBuild();
+ runWhileScheduledUpTo(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
+
+ // THEN an exception is NOT thrown.
+ }
+
+ @Test
+ public void testOutOfOrderPrompterInvalidationDoesNotThrowBeforeTooManyRuns() {
+ // GIVEN a NotifPromoter that gets invalidated during the sorting stage,
+ NotifPromoter promoter = new IdPromoter(47);
+ CountingInvalidator invalidator = new CountingInvalidator(promoter);
+ OnBeforeSortListener listener = (list) -> invalidator.maybeInvalidate();
+ mListBuilder.addPromoter(promoter);
+ mListBuilder.addOnBeforeSortListener(listener);
+
+ // WHEN we try to run the pipeline and the promoter is invalidated exactly
+ // MAX_CONSECUTIVE_REENTRANT_REBUILDS times,
addNotif(0, PACKAGE_1);
+ invalidator.setInvalidationCount(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS);
dispatchBuild();
+ runWhileScheduledUpTo(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
- // THEN an exception is thrown
+ // THEN an exception is NOT thrown.
}
@Test(expected = IllegalStateException.class)
- public void testOutOfOrderPrompterInvalidationThrows() {
- // GIVEN a NotifPromoter that gets invalidated during the sorting stage
+ public void testOutOfOrderPrompterInvalidationThrowsAfterTooManyRuns() {
+ // GIVEN a NotifPromoter that gets invalidated during the sorting stage,
NotifPromoter promoter = new IdPromoter(47);
- OnBeforeSortListener listener =
- (list) -> promoter.invalidateList(null);
+ CountingInvalidator invalidator = new CountingInvalidator(promoter);
+ OnBeforeSortListener listener = (list) -> invalidator.maybeInvalidate();
mListBuilder.addPromoter(promoter);
mListBuilder.addOnBeforeSortListener(listener);
- // WHEN we try to run the pipeline and the promoter is invalidated
+ // WHEN we try to run the pipeline and the promoter is invalidated more than
+ // MAX_CONSECUTIVE_REENTRANT_REBUILDS times,
addNotif(0, PACKAGE_1);
+ invalidator.setInvalidationCount(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS + 1);
dispatchBuild();
+ runWhileScheduledUpTo(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
- // THEN an exception is thrown
+ // THEN an exception IS thrown.
+ }
+
+ @Test
+ public void testOutOfOrderComparatorInvalidationDoesNotThrowBeforeTooManyRuns() {
+ // GIVEN a NotifComparator that gets invalidated during the finalizing stage,
+ NotifComparator comparator = new HypeComparator(PACKAGE_1);
+ CountingInvalidator invalidator = new CountingInvalidator(comparator);
+ OnBeforeRenderListListener listener = (list) -> invalidator.maybeInvalidate();
+ mListBuilder.setComparators(singletonList(comparator));
+ mListBuilder.addOnBeforeRenderListListener(listener);
+
+ // WHEN we try to run the pipeline and the comparator is invalidated exactly
+ // MAX_CONSECUTIVE_REENTRANT_REBUILDS times,
+ addNotif(0, PACKAGE_2);
+ invalidator.setInvalidationCount(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS);
+ dispatchBuild();
+ runWhileScheduledUpTo(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
+
+ // THEN an exception is NOT thrown.
}
@Test(expected = IllegalStateException.class)
- public void testOutOfOrderComparatorInvalidationThrows() {
- // GIVEN a NotifComparator that gets invalidated during the finalizing stage
- NotifComparator comparator = new HypeComparator(PACKAGE_5);
- OnBeforeRenderListListener listener =
- (list) -> comparator.invalidateList(null);
+ public void testOutOfOrderComparatorInvalidationThrowsAfterTooManyRuns() {
+ // GIVEN a NotifComparator that gets invalidated during the finalizing stage,
+ NotifComparator comparator = new HypeComparator(PACKAGE_1);
+ CountingInvalidator invalidator = new CountingInvalidator(comparator);
+ OnBeforeRenderListListener listener = (list) -> invalidator.maybeInvalidate();
mListBuilder.setComparators(singletonList(comparator));
mListBuilder.addOnBeforeRenderListListener(listener);
- // WHEN we try to run the pipeline and the comparator is invalidated
- addNotif(0, PACKAGE_1);
+ // WHEN we try to run the pipeline and the comparator is invalidated more than
+ // MAX_CONSECUTIVE_REENTRANT_REBUILDS times,
+ addNotif(0, PACKAGE_2);
+ invalidator.setInvalidationCount(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS + 1);
dispatchBuild();
+ runWhileScheduledUpTo(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
- // THEN an exception is thrown
+ // THEN an exception IS thrown.
+ }
+
+ @Test
+ public void testOutOfOrderPreRenderFilterInvalidationDoesNotThrowBeforeTooManyRuns() {
+ // GIVEN a PreRenderNotifFilter that gets invalidated during the finalizing stage,
+ NotifFilter filter = new PackageFilter(PACKAGE_1);
+ CountingInvalidator invalidator = new CountingInvalidator(filter);
+ OnBeforeRenderListListener listener = (list) -> invalidator.maybeInvalidate();
+ mListBuilder.addFinalizeFilter(filter);
+ mListBuilder.addOnBeforeRenderListListener(listener);
+
+ // WHEN we try to run the pipeline and the PreRenderFilter is invalidated exactly
+ // MAX_CONSECUTIVE_REENTRANT_REBUILDS times,
+ addNotif(0, PACKAGE_2);
+ invalidator.setInvalidationCount(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS);
+ dispatchBuild();
+ runWhileScheduledUpTo(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
+
+ // THEN an exception is NOT thrown.
}
@Test(expected = IllegalStateException.class)
- public void testOutOfOrderPreRenderFilterInvalidationThrows() {
- // GIVEN a PreRenderNotifFilter that gets invalidated during the finalizing stage
- NotifFilter filter = new PackageFilter(PACKAGE_5);
- OnBeforeRenderListListener listener = (list) -> filter.invalidateList(null);
+ public void testOutOfOrderPreRenderFilterInvalidationThrowsAfterTooManyRuns() {
+ // GIVEN a PreRenderNotifFilter that gets invalidated during the finalizing stage,
+ NotifFilter filter = new PackageFilter(PACKAGE_1);
+ CountingInvalidator invalidator = new CountingInvalidator(filter);
+ OnBeforeRenderListListener listener = (list) -> invalidator.maybeInvalidate();
mListBuilder.addFinalizeFilter(filter);
mListBuilder.addOnBeforeRenderListListener(listener);
- // WHEN we try to run the pipeline and the PreRenderFilter is invalidated
- addNotif(0, PACKAGE_1);
+ // WHEN we try to run the pipeline and the PreRenderFilter is invalidated more than
+ // MAX_CONSECUTIVE_REENTRANT_REBUILDS times,
+ addNotif(0, PACKAGE_2);
+ invalidator.setInvalidationCount(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS + 1);
dispatchBuild();
+ runWhileScheduledUpTo(ShadeListBuilder.MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2);
- // THEN an exception is thrown
+ // THEN an exception IS thrown.
}
@Test
@@ -2096,6 +2232,18 @@ public class ShadeListBuilderTest extends SysuiTestCase {
mPipelineChoreographer.runIfScheduled();
}
+ private void runWhileScheduledUpTo(int maxRuns) {
+ int runs = 0;
+ while (mPipelineChoreographer.isScheduled()) {
+ if (runs > maxRuns) {
+ throw new IndexOutOfBoundsException(
+ "Pipeline scheduled itself more than " + maxRuns + "times");
+ }
+ runs++;
+ mPipelineChoreographer.runIfScheduled();
+ }
+ }
+
private void verifyBuiltList(ExpectedEntry ...expectedEntries) {
try {
assertEquals(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
index e00e20f0e46b..46f630b7db63 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
@@ -33,9 +33,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -57,7 +55,6 @@ import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
-import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -85,8 +82,6 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
@Mock
AmbientDisplayConfiguration mAmbientDisplayConfiguration;
@Mock
- NotificationFilter mNotificationFilter;
- @Mock
StatusBarStateController mStatusBarStateController;
@Mock
KeyguardStateController mKeyguardStateController;
@@ -131,20 +126,10 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
/**
* Sets up the state such that any requests to
- * {@link NotificationInterruptStateProviderImpl#canAlertCommon(NotificationEntry)} will
- * pass as long its provided NotificationEntry fulfills group suppression check.
- */
- private void ensureStateForAlertCommon() {
- when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
- }
-
- /**
- * Sets up the state such that any requests to
* {@link NotificationInterruptStateProviderImpl#shouldHeadsUp(NotificationEntry)} will
* pass as long its provided NotificationEntry fulfills importance & DND checks.
*/
private void ensureStateForHeadsUpWhenAwake() throws RemoteException {
- ensureStateForAlertCommon();
when(mHeadsUpManager.isSnoozed(any())).thenReturn(false);
when(mStatusBarStateController.isDozing()).thenReturn(false);
@@ -158,21 +143,10 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
* pass as long its provided NotificationEntry fulfills importance & DND checks.
*/
private void ensureStateForHeadsUpWhenDozing() {
- ensureStateForAlertCommon();
-
when(mStatusBarStateController.isDozing()).thenReturn(true);
when(mAmbientDisplayConfiguration.pulseOnNotificationEnabled(anyInt())).thenReturn(true);
}
- /**
- * Sets up the state such that any requests to
- * {@link NotificationInterruptStateProviderImpl#shouldBubbleUp(NotificationEntry)} will
- * pass as long its provided NotificationEntry fulfills importance & bubble checks.
- */
- private void ensureStateForBubbleUp() {
- ensureStateForAlertCommon();
- }
-
@Test
public void testDefaultSuppressorDoesNotSuppress() {
// GIVEN a suppressor without any overrides
@@ -201,27 +175,6 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
}
@Test
- public void testShouldNotHeadsUpAwake_flteredOut() throws RemoteException {
- // GIVEN state for "heads up when awake" is true
- ensureStateForHeadsUpWhenAwake();
-
- // WHEN this entry should be filtered out
- NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
- when(mNotificationFilter.shouldFilterOut(entry)).thenReturn(true);
-
- // THEN we shouldn't heads up this entry
- assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
- }
-
- @Test
- public void testDoNotRunFilterOnNewPipeline() {
- // WHEN this entry should be filtered out
- NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
- mNotifInterruptionStateProvider.shouldHeadsUp(entry);
- verify(mNotificationFilter, times(0)).shouldFilterOut(eq(entry));
- }
-
- @Test
public void testShouldNotHeadsUp_suppressedForGroups() throws RemoteException {
// GIVEN state for "heads up when awake" is true
ensureStateForHeadsUpWhenAwake();
@@ -654,7 +607,6 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
*/
@Test
public void testShouldBubbleUp() {
- ensureStateForBubbleUp();
assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(createBubble())).isTrue();
}
@@ -664,7 +616,6 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
*/
@Test
public void testShouldBubbleUp_notifInGroupWithOnlySummaryAlerts() {
- ensureStateForBubbleUp();
NotificationEntry bubble = createBubble("testgroup", GROUP_ALERT_SUMMARY);
assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(bubble)).isTrue();
}
@@ -674,8 +625,6 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
*/
@Test
public void shouldNotBubbleUp_notAllowedToBubble() {
- ensureStateForBubbleUp();
-
NotificationEntry entry = createBubble();
modifyRanking(entry)
.setCanBubble(false)
@@ -689,8 +638,6 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
*/
@Test
public void shouldNotBubbleUp_notABubble() {
- ensureStateForBubbleUp();
-
NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
modifyRanking(entry)
.setCanBubble(true)
@@ -704,8 +651,6 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
*/
@Test
public void shouldNotBubbleUp_invalidMetadata() {
- ensureStateForBubbleUp();
-
NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
modifyRanking(entry)
.setCanBubble(true)
@@ -717,8 +662,6 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
@Test
public void shouldNotBubbleUp_suppressedInterruptions() {
- ensureStateForBubbleUp();
-
// If the notification can't heads up in general, it shouldn't bubble.
mNotifInterruptionStateProvider.addSuppressor(mSuppressInterruptions);
@@ -727,8 +670,6 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
@Test
public void shouldNotBubbleUp_filteredOut() {
- ensureStateForBubbleUp();
-
// Make canAlertCommon false by saying it's filtered out
when(mKeyguardNotificationVisibilityProvider.shouldHideNotification(any()))
.thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index f57c40958b24..b6a1bb3b27d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -26,7 +26,6 @@ import static android.service.notification.NotificationListenerService.Ranking.U
import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
@@ -37,7 +36,6 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anySet;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -77,7 +75,6 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
@@ -88,7 +85,6 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.wmshell.BubblesManager;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -128,7 +124,6 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
@Mock private AccessibilityManager mAccessibilityManager;
@Mock private HighPriorityProvider mHighPriorityProvider;
@Mock private INotificationManager mINotificationManager;
- @Mock private NotificationEntryManager mNotificationEntryManager;
@Mock private LauncherApps mLauncherApps;
@Mock private ShortcutManager mShortcutManager;
@Mock private ChannelEditorDialogController mChannelEditorDialogController;
@@ -160,7 +155,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
mPeopleSpaceWidgetManager, mLauncherApps, mShortcutManager,
mChannelEditorDialogController, mContextTracker, mAssistantFeedbackController,
Optional.of(mBubblesManager), new UiEventLoggerFake(), mOnUserInteractionCallback,
- mShadeController, mock(DumpManager.class));
+ mShadeController);
mGutsManager.setUpWithPresenter(mPresenter, mNotificationListContainer,
mOnSettingsClickListener);
mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
@@ -446,36 +441,6 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
eq(mAssistantFeedbackController));
}
- @Test
- public void testShouldExtendLifetime() {
- NotificationGuts guts = new NotificationGuts(mContext);
- ExpandableNotificationRow row = spy(createTestNotificationRow());
- doReturn(guts).when(row).getGuts();
- NotificationEntry entry = row.getEntry();
- entry.setRow(row);
- mGutsManager.setExposedGuts(guts);
-
- assertTrue(mGutsManager.shouldExtendLifetime(entry));
- }
-
- @Test
- @Ignore
- public void testSetShouldManageLifetime_setShouldManage() {
- NotificationEntry entry = createTestNotificationRow().getEntry();
- mGutsManager.setShouldManageLifetime(entry, true /* shouldManage */);
-
- assertTrue(entry.getKey().equals(mGutsManager.mKeyToRemoveOnGutsClosed));
- }
-
- @Test
- public void testSetShouldManageLifetime_setShouldNotManage() {
- NotificationEntry entry = createTestNotificationRow().getEntry();
- mGutsManager.mKeyToRemoveOnGutsClosed = entry.getKey();
- mGutsManager.setShouldManageLifetime(entry, false /* shouldManage */);
-
- assertNull(mGutsManager.mKeyToRemoveOnGutsClosed);
- }
-
////////////////////////////////////////////////////////////////////////////////////////////////
// Utility methods:
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 820dea0e788e..c25737d24948 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -51,7 +51,6 @@ import com.android.internal.logging.MetricsLogger;
import com.android.systemui.TestableDependency;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
-import com.android.systemui.dump.DumpManager;
import com.android.systemui.media.MediaFeatureFlag;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -62,10 +61,11 @@ import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
+import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.icon.IconBuilder;
import com.android.systemui.statusbar.notification.icon.IconManager;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
@@ -84,7 +84,6 @@ import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent;
import com.android.systemui.tests.R;
import com.android.systemui.wmshell.BubblesManager;
import com.android.systemui.wmshell.BubblesTestActivity;
-import com.android.wm.shell.bubbles.Bubbles;
import org.mockito.ArgumentCaptor;
@@ -112,8 +111,8 @@ public class NotificationTestHelper {
private final Context mContext;
private final TestableLooper mTestLooper;
private int mId;
- private final NotificationGroupManagerLegacy mGroupMembershipManager;
- private final NotificationGroupManagerLegacy mGroupExpansionManager;
+ private final GroupMembershipManager mGroupMembershipManager;
+ private final GroupExpansionManager mGroupExpansionManager;
private ExpandableNotificationRow mRow;
private HeadsUpManagerPhone mHeadsUpManager;
private final NotifBindPipeline mBindPipeline;
@@ -136,23 +135,18 @@ public class NotificationTestHelper {
dependency.injectMockDependency(NotificationShadeWindowController.class);
dependency.injectMockDependency(MediaOutputDialogFactory.class);
mStatusBarStateController = mock(StatusBarStateController.class);
- mGroupMembershipManager = new NotificationGroupManagerLegacy(
- mStatusBarStateController,
- () -> mock(PeopleNotificationIdentifier.class),
- Optional.of((mock(Bubbles.class))),
- mock(DumpManager.class));
- mGroupExpansionManager = mGroupMembershipManager;
+ mGroupMembershipManager = mock(GroupMembershipManager.class);
+ mGroupExpansionManager = mock(GroupExpansionManager.class);
mHeadsUpManager = new HeadsUpManagerPhone(
mContext,
mock(HeadsUpManagerLogger.class),
mStatusBarStateController,
mock(KeyguardBypassController.class),
- mock(NotificationGroupManagerLegacy.class),
+ mock(GroupMembershipManager.class),
mock(VisualStabilityProvider.class),
mock(ConfigurationControllerImpl.class),
new Handler(mTestLooper.getLooper())
);
- mGroupMembershipManager.setHeadsUpManager(mHeadsUpManager);
mIconManager = new IconManager(
mock(CommonNotifCollection.class),
mock(LauncherApps.class),
@@ -528,10 +522,6 @@ public class NotificationTestHelper {
mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags);
inflateAndWait(entry);
- // This would be done as part of onAsyncInflationFinished, but we skip large amounts of
- // the callback chain, so we need to make up for not adding it to the group manager
- // here.
- mGroupMembershipManager.onEntryAdded(entry);
return row;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index ac9fcc064375..9d848e87b0a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -18,24 +18,7 @@ package com.android.systemui.statusbar.notification.stack;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_ALERTING;
-import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_FOREGROUND_SERVICE;
-import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_HEADS_UP;
-import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_PEOPLE;
-import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_SILENT;
-
-import static com.google.common.truth.Truth.assertThat;
-
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
-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.never;
-import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.testing.AndroidTestingRunner;
@@ -50,28 +33,19 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.media.KeyguardMediaController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.render.MediaContainerController;
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
-import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
import com.android.systemui.statusbar.policy.ConfigurationController;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
-import java.util.ArrayList;
-import java.util.List;
-
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -84,15 +58,11 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
@Mock private ConfigurationController mConfigurationController;
@Mock private KeyguardMediaController mKeyguardMediaController;
@Mock private NotificationSectionsFeatureManager mSectionsFeatureManager;
- @Mock private NotificationRowComponent mNotificationRowComponent;
- @Mock private ActivatableNotificationViewController mActivatableNotificationViewController;
- @Mock private NotificationSectionsLogger mLogger;
@Mock private MediaContainerController mMediaContainerController;
@Mock private SectionHeaderController mIncomingHeaderController;
@Mock private SectionHeaderController mPeopleHeaderController;
@Mock private SectionHeaderController mAlertingHeaderController;
@Mock private SectionHeaderController mSilentHeaderController;
- @Mock private NotifPipelineFlags mNotifPipelineFlags;
private NotificationSectionsManager mSectionsManager;
@@ -113,22 +83,11 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
}
return count;
});
- when(mNotificationRowComponent.getActivatableNotificationViewController())
- .thenReturn(mActivatableNotificationViewController);
- when(mMediaContainerController.getMediaContainerView())
- .thenReturn(mock(MediaContainerView.class));
- when(mIncomingHeaderController.getHeaderView()).thenReturn(mock(SectionHeaderView.class));
- when(mPeopleHeaderController.getHeaderView()).thenReturn(mock(SectionHeaderView.class));
- when(mAlertingHeaderController.getHeaderView()).thenReturn(mock(SectionHeaderView.class));
- when(mSilentHeaderController.getHeaderView()).thenReturn(mock(SectionHeaderView.class));
mSectionsManager =
new NotificationSectionsManager(
- mStatusBarStateController,
mConfigurationController,
mKeyguardMediaController,
mSectionsFeatureManager,
- mLogger,
- mNotifPipelineFlags,
mMediaContainerController,
mIncomingHeaderController,
mPeopleHeaderController,
@@ -141,7 +100,6 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
mSectionsManager.initialize(mNssl);
when(mNssl.indexOfChild(any(View.class))).thenReturn(-1);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
-
}
@Test(expected = IllegalStateException.class)
@@ -149,641 +107,4 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
mSectionsManager.initialize(mNssl);
}
- @Test
- public void testInsertHeader() {
- // GIVEN a stack with HI and LO rows but no section headers
- setStackState(
- ALERTING,
- ALERTING,
- ALERTING,
- GENTLE);
-
- // WHEN we update the section headers
- mSectionsManager.updateSectionBoundaries();
-
- // THEN a LO section header is added
- verify(mNssl).addView(mSectionsManager.getSilentHeaderView(), 3);
- }
-
- @Test
- public void testRemoveHeader() {
- // GIVEN a stack that originally had a header between the HI and LO sections
- setStackState(
- ALERTING,
- ALERTING,
- GENTLE);
- mSectionsManager.updateSectionBoundaries();
-
- // WHEN the last LO row is replaced with a HI row
- setStackState(
- ALERTING,
- ALERTING,
- GENTLE_HEADER,
- ALERTING);
- clearInvocations(mNssl);
- mSectionsManager.updateSectionBoundaries();
-
- // THEN the LO section header is removed
- verify(mNssl).removeView(mSectionsManager.getSilentHeaderView());
- }
-
- @Test
- public void testDoNothingIfHeaderAlreadyRemoved() {
- // GIVEN a stack with only HI rows
- setStackState(
- ALERTING,
- ALERTING,
- ALERTING);
-
- // WHEN we update the sections headers
- mSectionsManager.updateSectionBoundaries();
-
- // THEN we don't add any section headers
- verify(mNssl, never()).addView(eq(mSectionsManager.getSilentHeaderView()), anyInt());
- }
-
- @Test
- public void testMoveHeaderForward() {
- // GIVEN a stack that originally had a header between the HI and LO sections
- setStackState(
- ALERTING,
- ALERTING,
- ALERTING,
- GENTLE);
- mSectionsManager.updateSectionBoundaries();
-
- // WHEN the LO section moves forward
- setStackState(
- ALERTING,
- ALERTING,
- GENTLE,
- GENTLE_HEADER,
- GENTLE);
- mSectionsManager.updateSectionBoundaries();
-
- // THEN the LO section header is also moved forward
- verify(mNssl).changeViewPosition(mSectionsManager.getSilentHeaderView(), 2);
- }
-
- @Test
- public void testMoveHeaderBackward() {
- // GIVEN a stack that originally had a header between the HI and LO sections
- setStackState(
- ALERTING,
- GENTLE,
- GENTLE,
- GENTLE);
- mSectionsManager.updateSectionBoundaries();
-
- // WHEN the LO section moves backward
- setStackState(
- ALERTING,
- GENTLE_HEADER,
- ALERTING,
- ALERTING,
- GENTLE);
- mSectionsManager.updateSectionBoundaries();
-
- // THEN the LO section header is also moved backward (with appropriate index shifting)
- verify(mNssl).changeViewPosition(mSectionsManager.getSilentHeaderView(), 3);
- }
-
- @Test
- public void testHeaderRemovedFromTransientParent() {
- // GIVEN a stack where the header is animating away
- setStackState(
- ALERTING,
- GENTLE_HEADER);
- mSectionsManager.updateSectionBoundaries();
- clearInvocations(mNssl);
-
- SectionHeaderView silentHeaderView = mSectionsManager.getSilentHeaderView();
- ViewGroup transientParent = mock(ViewGroup.class);
- when(silentHeaderView.getTransientContainer()).thenReturn(transientParent);
-
- // WHEN the LO section reappears
- setStackState(
- ALERTING,
- GENTLE);
- mSectionsManager.updateSectionBoundaries();
-
- // THEN the header is first removed from the transient parent before being added to the
- // NSSL.
- final InOrder inOrder = inOrder(silentHeaderView, mNssl);
- inOrder.verify(silentHeaderView).removeFromTransientContainer();
- inOrder.verify(mNssl).addView(eq(silentHeaderView), eq(1));
- }
-
- @Test
- public void testHeaderNotShownOnLockscreen() {
- // GIVEN a stack of HI and LO notifs on the lockscreen
- when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
- setStackState(
- ALERTING,
- ALERTING,
- ALERTING,
- GENTLE);
-
- // WHEN we update the section headers
- mSectionsManager.updateSectionBoundaries();
-
- // Then the section header is not added
- verify(mNssl, never()).addView(eq(mSectionsManager.getSilentHeaderView()), anyInt());
- }
-
- @Test
- public void testHeaderShownWhenEnterLockscreen() {
- // GIVEN a stack of HI and LO notifs on the lockscreen
- when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
- setStackState(
- ALERTING,
- ALERTING,
- ALERTING,
- GENTLE);
- mSectionsManager.updateSectionBoundaries();
-
- // WHEN we unlock
- when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
- mSectionsManager.updateSectionBoundaries();
-
- // Then the section header is added
- verify(mNssl).addView(mSectionsManager.getSilentHeaderView(), 3);
- }
-
- @Test
- public void testHeaderHiddenWhenEnterLockscreen() {
- // GIVEN a stack of HI and LO notifs on the shade
- setStackState(
- ALERTING,
- GENTLE_HEADER,
- GENTLE);
-
- // WHEN we go back to the keyguard
- when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
- mSectionsManager.updateSectionBoundaries();
-
- // Then the section header is removed
- verify(mNssl).removeView(mSectionsManager.getSilentHeaderView());
- }
-
- @Test
- public void testPeopleFiltering_onlyAddSilentHeader() {
- enablePeopleFiltering();
-
- setStackState(
- PERSON,
- ALERTING,
- GENTLE);
- mSectionsManager.updateSectionBoundaries();
-
- verify(mNssl).addView(mSectionsManager.getSilentHeaderView(), 2);
- }
-
- @Test
- public void testPeopleFiltering_AlertingHunWhilePeopleVisible() {
- enablePeopleFiltering();
-
- setupMockStack(
- PEOPLE_HEADER,
- ALERTING,
- PERSON,
- ALERTING_HEADER,
- GENTLE_HEADER,
- GENTLE
- );
- mSectionsManager.updateSectionBoundaries();
-
- verifyMockStack(
- ChildType.HEADS_UP,
- ChildType.PERSON,
- ChildType.GENTLE_HEADER,
- ChildType.GENTLE
- );
- }
-
- @Test
- public void testPeopleFiltering_PersonHunWhileAlertingHunVisible() {
- enablePeopleFiltering();
-
- setupMockStack(
- PERSON,
- INCOMING_HEADER,
- ALERTING,
- PEOPLE_HEADER,
- PERSON
- );
- mSectionsManager.updateSectionBoundaries();
-
- verifyMockStack(
- ChildType.HEADS_UP,
- ChildType.HEADS_UP,
- ChildType.PERSON
- );
- }
-
- @Test
- public void testPeopleFiltering_PersonHun() {
- enablePeopleFiltering();
-
- setupMockStack(
- PERSON,
- PEOPLE_HEADER,
- PERSON
- );
- mSectionsManager.updateSectionBoundaries();
-
- verifyMockStack(
- ChildType.PERSON,
- ChildType.PERSON
- );
- }
-
- @Test
- public void testPeopleFiltering_AlertingHunWhilePersonHunning() {
- enablePeopleFiltering();
-
- setupMockStack(
- ALERTING,
- PERSON
- );
- mSectionsManager.updateSectionBoundaries();
- verifyMockStack(
- ChildType.HEADS_UP,
- ChildType.PERSON
- );
- }
-
- @Test
- public void testPeopleFiltering_Fsn() {
- enablePeopleFiltering();
-
- setupMockStack(
- INCOMING_HEADER,
- ALERTING,
- PEOPLE_HEADER,
- FSN,
- PERSON,
- ALERTING,
- GENTLE
- );
- mSectionsManager.updateSectionBoundaries();
-
- verifyMockStack(
- ChildType.HEADS_UP,
- ChildType.FSN,
- ChildType.PERSON,
- ChildType.ALERTING,
- ChildType.GENTLE_HEADER,
- ChildType.GENTLE
- );
- }
-
- @Test
- public void testMediaControls_AddWhenEnterKeyguard() {
- enableMediaControls();
-
- // GIVEN a stack that doesn't include media controls
- setStackState(ALERTING, GENTLE_HEADER, GENTLE);
-
- // WHEN we go back to the keyguard
- when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
- mSectionsManager.updateSectionBoundaries();
-
- // Then the media controls are added
- verify(mNssl).addView(mSectionsManager.getMediaControlsView(), 0);
- }
-
- @Test
- public void testMediaControls_AddWhenEnterKeyguardWithHeadsUp() {
- enableMediaControls();
-
- // GIVEN a stack that doesn't include media
- setupMockStack(
- ALERTING,
- ALERTING,
- GENTLE_HEADER,
- GENTLE);
-
- // WHEN we go back to the keyguard
- when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
- mSectionsManager.updateSectionBoundaries();
-
- verifyMockStack(
- ChildType.MEDIA_CONTROLS,
- ChildType.ALERTING,
- ChildType.ALERTING,
- ChildType.GENTLE);
- }
-
- @Test
- public void testRemoveNonSilentHeader() {
- enablePeopleFiltering();
- enableMediaControls();
-
- setupMockStack(
- MEDIA_CONTROLS,
- INCOMING_HEADER,
- PERSON,
- ALERTING,
- PEOPLE_HEADER,
- ALERTING_HEADER,
- ALERTING,
- ALERTING,
- GENTLE_HEADER,
- GENTLE,
- GENTLE
- );
-
- mSectionsManager.updateSectionBoundaries();
-
- verifyMockStack(
- ChildType.MEDIA_CONTROLS,
- ChildType.PERSON,
- ChildType.ALERTING,
- ChildType.ALERTING,
- ChildType.ALERTING,
- ChildType.GENTLE_HEADER,
- ChildType.GENTLE,
- ChildType.GENTLE
- );
- }
-
- @Test
- public void testExpandIncomingSection() {
- enablePeopleFiltering();
-
- setupMockStack(
- INCOMING_HEADER,
- PERSON,
- ALERTING,
- PEOPLE_HEADER,
- ALERTING,
- PERSON,
- ALERTING_HEADER,
- ALERTING
- );
-
- mSectionsManager.updateSectionBoundaries();
-
- verifyMockStack(
- ChildType.HEADS_UP,
- ChildType.HEADS_UP,
- ChildType.HEADS_UP,
- ChildType.PERSON,
- ChildType.ALERTING
- );
- }
-
- @Test
- public void testIgnoreGoneView() {
- enablePeopleFiltering();
-
- setupMockStack(
- PERSON.gone(),
- ALERTING,
- GENTLE
- );
-
- mSectionsManager.updateSectionBoundaries();
-
- verifyMockStack(
- ChildType.PERSON,
- ChildType.ALERTING,
- ChildType.GENTLE_HEADER,
- ChildType.GENTLE
- );
- }
-
- private void enablePeopleFiltering() {
- when(mSectionsFeatureManager.isFilteringEnabled()).thenReturn(true);
- }
-
- private void enableMediaControls() {
- when(mSectionsFeatureManager.isMediaControlsEnabled()).thenReturn(true);
- }
-
- private enum ChildType {
- INCOMING_HEADER, MEDIA_CONTROLS, PEOPLE_HEADER, ALERTING_HEADER, GENTLE_HEADER, HEADS_UP,
- FSN, PERSON, ALERTING, GENTLE, OTHER
- }
-
- private void setStackState(StackEntry... children) {
- when(mNssl.getChildCount()).thenReturn(children.length);
- for (int i = 0; i < children.length; i++) {
- View child;
- StackEntry entry = children[i];
- switch (entry.mChildType) {
- case INCOMING_HEADER:
- child = mSectionsManager.getIncomingHeaderView();
- break;
- case MEDIA_CONTROLS:
- child = mSectionsManager.getMediaControlsView();
- break;
- case PEOPLE_HEADER:
- child = mSectionsManager.getPeopleHeaderView();
- break;
- case ALERTING_HEADER:
- child = mSectionsManager.getAlertingHeaderView();
- break;
- case GENTLE_HEADER:
- child = mSectionsManager.getSilentHeaderView();
- break;
- case FSN:
- child = mockNotification(BUCKET_FOREGROUND_SERVICE, entry.mIsGone);
- break;
- case PERSON:
- child = mockNotification(BUCKET_PEOPLE, entry.mIsGone);
- break;
- case ALERTING:
- child = mockNotification(BUCKET_ALERTING, entry.mIsGone);
- break;
- case GENTLE:
- child = mockNotification(BUCKET_SILENT, entry.mIsGone);
- break;
- case OTHER:
- child = mock(View.class);
- when(child.getVisibility()).thenReturn(View.VISIBLE);
- when(child.getParent()).thenReturn(mNssl);
- break;
- default:
- throw new RuntimeException("Unknown ChildType: " + children[i]);
- }
- when(mNssl.getChildAt(i)).thenReturn(child);
- when(mNssl.indexOfChild(child)).thenReturn(i);
- }
- }
-
- private View mockNotification(@PriorityBucket int bucket, boolean isGone) {
- ExpandableNotificationRow notifRow =
- mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS);
- when(notifRow.getVisibility()).thenReturn(View.VISIBLE);
- when(notifRow.getParent()).thenReturn(mNssl);
-
- NotificationEntry mockEntry = mock(NotificationEntry.class);
- when(notifRow.getEntry()).thenReturn(mockEntry);
-
- int[] bucketRef = new int[] { bucket };
- when(mockEntry.getBucket()).thenAnswer(invocation -> bucketRef[0]);
- doAnswer(invocation -> {
- bucketRef[0] = invocation.getArgument(0);
- return null;
- }).when(mockEntry).setBucket(anyInt());
-
- when(notifRow.getVisibility()).thenReturn(isGone ? View.GONE : View.VISIBLE);
- return notifRow;
- }
-
- private void verifyMockStack(ChildType... expected) {
- final List<ChildType> actual = new ArrayList<>();
- int childCount = mNssl.getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = mNssl.getChildAt(i);
- if (child == mSectionsManager.getIncomingHeaderView()) {
- actual.add(ChildType.INCOMING_HEADER);
- continue;
- }
- if (child == mSectionsManager.getMediaControlsView()) {
- actual.add(ChildType.MEDIA_CONTROLS);
- continue;
- }
- if (child == mSectionsManager.getPeopleHeaderView()) {
- actual.add(ChildType.PEOPLE_HEADER);
- continue;
- }
- if (child == mSectionsManager.getAlertingHeaderView()) {
- actual.add(ChildType.ALERTING_HEADER);
- continue;
- }
- if (child == mSectionsManager.getSilentHeaderView()) {
- actual.add(ChildType.GENTLE_HEADER);
- continue;
- }
- if (child instanceof ExpandableNotificationRow) {
- switch (((ExpandableNotificationRow) child).getEntry().getBucket()) {
- case BUCKET_HEADS_UP:
- actual.add(ChildType.HEADS_UP);
- break;
- case BUCKET_FOREGROUND_SERVICE:
- actual.add(ChildType.FSN);
- break;
- case BUCKET_PEOPLE:
- actual.add(ChildType.PERSON);
- break;
- case BUCKET_ALERTING:
- actual.add(ChildType.ALERTING);
- break;
- case BUCKET_SILENT:
- actual.add(ChildType.GENTLE);
- break;
- default:
- actual.add(ChildType.OTHER);
- break;
- }
- continue;
- }
- actual.add(ChildType.OTHER);
- }
- assertThat(actual).containsExactly((Object[]) expected).inOrder();
- }
-
- private void setupMockStack(StackEntry... entries) {
- final List<View> children = new ArrayList<>();
- when(mNssl.getChildCount()).thenAnswer(invocation -> children.size());
- when(mNssl.getChildAt(anyInt()))
- .thenAnswer(invocation -> {
- Integer index = invocation.getArgument(0);
- if (index == null || index < 0 || index >= children.size()) {
- return null;
- }
- return children.get(index);
- });
- when(mNssl.indexOfChild(any()))
- .thenAnswer(invocation -> children.indexOf(invocation.getArgument(0)));
- doAnswer(invocation -> {
- View child = invocation.getArgument(0);
- int index = invocation.getArgument(1);
- children.add(index, child);
- return null;
- }).when(mNssl).addView(any(), anyInt());
- doAnswer(invocation -> {
- View child = invocation.getArgument(0);
- children.remove(child);
- return null;
- }).when(mNssl).removeView(any());
- doAnswer(invocation -> {
- View child = invocation.getArgument(0);
- int newIndex = invocation.getArgument(1);
- children.remove(child);
- children.add(newIndex, child);
- return null;
- }).when(mNssl).changeViewPosition(any(), anyInt());
- for (StackEntry entry : entries) {
- View child;
- switch (entry.mChildType) {
- case INCOMING_HEADER:
- child = mSectionsManager.getIncomingHeaderView();
- break;
- case MEDIA_CONTROLS:
- child = mSectionsManager.getMediaControlsView();
- break;
- case PEOPLE_HEADER:
- child = mSectionsManager.getPeopleHeaderView();
- break;
- case ALERTING_HEADER:
- child = mSectionsManager.getAlertingHeaderView();
- break;
- case GENTLE_HEADER:
- child = mSectionsManager.getSilentHeaderView();
- break;
- case FSN:
- child = mockNotification(BUCKET_FOREGROUND_SERVICE, entry.mIsGone);
- break;
- case PERSON:
- child = mockNotification(BUCKET_PEOPLE, entry.mIsGone);
- break;
- case ALERTING:
- child = mockNotification(BUCKET_ALERTING, entry.mIsGone);
- break;
- case GENTLE:
- child = mockNotification(BUCKET_SILENT, entry.mIsGone);
- break;
- case OTHER:
- child = mock(View.class);
- when(child.getVisibility()).thenReturn(View.VISIBLE);
- when(child.getParent()).thenReturn(mNssl);
- break;
- default:
- throw new RuntimeException("Unknown ChildType: " + entry.mChildType);
- }
- children.add(child);
- }
- }
-
- private static final StackEntry INCOMING_HEADER = new StackEntry(ChildType.INCOMING_HEADER);
- private static final StackEntry MEDIA_CONTROLS = new StackEntry(ChildType.MEDIA_CONTROLS);
- private static final StackEntry PEOPLE_HEADER = new StackEntry(ChildType.PEOPLE_HEADER);
- private static final StackEntry ALERTING_HEADER = new StackEntry(ChildType.ALERTING_HEADER);
- private static final StackEntry GENTLE_HEADER = new StackEntry(ChildType.GENTLE_HEADER);
- private static final StackEntry FSN = new StackEntry(ChildType.FSN);
- private static final StackEntry PERSON = new StackEntry(ChildType.PERSON);
- private static final StackEntry ALERTING = new StackEntry(ChildType.ALERTING);
- private static final StackEntry GENTLE = new StackEntry(ChildType.GENTLE);
-
- private static class StackEntry {
- final ChildType mChildType;
- final boolean mIsGone;
-
- StackEntry(ChildType childType) {
- this(childType, false);
- }
-
- StackEntry(ChildType childType, boolean isGone) {
- mChildType = childType;
- mIsGone = isGone;
- }
-
- public StackEntry gone() {
- return new StackEntry(mChildType, true);
- }
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 9bcea101f83b..1460e048d234 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -67,6 +67,7 @@ import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -121,7 +122,8 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
@Mock private NotificationSwipeHelper mNotificationSwipeHelper;
@Mock private CentralSurfaces mCentralSurfaces;
@Mock private ScrimController mScrimController;
- @Mock private NotificationGroupManagerLegacy mLegacyGroupManager;
+ @Mock private NotificationGroupManagerLegacy mGroupManagerLegacy;
+ @Mock private GroupExpansionManager mGroupExpansionManager;
@Mock private SectionHeaderController mSilentHeaderController;
@Mock private NotifPipelineFlags mNotifPipelineFlags;
@Mock private NotifPipeline mNotifPipeline;
@@ -174,8 +176,8 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
mNotificationSwipeHelperBuilder,
mCentralSurfaces,
mScrimController,
- mLegacyGroupManager,
- mLegacyGroupManager,
+ mGroupManagerLegacy,
+ mGroupExpansionManager,
mSilentHeaderController,
mNotifPipeline,
mNotifCollection,
@@ -184,7 +186,6 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
mShadeTransitionController,
mUiEventLogger,
mRemoteInputManager,
- mVisualStabilityManager,
mShadeController,
mJankMonitor,
mStackLogger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 37a48937419a..3c22edc0fcf2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -66,7 +66,6 @@ import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -101,8 +100,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Rule public MockitoRule mockito = MockitoJUnit.rule();
@Mock private CentralSurfaces mCentralSurfaces;
@Mock private SysuiStatusBarStateController mBarState;
- @Mock private NotificationGroupManagerLegacy mGroupMembershipManger;
- @Mock private NotificationGroupManagerLegacy mGroupExpansionManager;
+ @Mock private GroupMembershipManager mGroupMembershipManger;
+ @Mock private GroupExpansionManager mGroupExpansionManager;
@Mock private DumpManager mDumpManager;
@Mock private ExpandHelper mExpandHelper;
@Mock private EmptyShadeView mEmptyShadeView;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 7b7f45a44615..a0f7087ddb9e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -418,7 +418,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
wakefulnessLifecycle,
mStatusBarStateController,
Optional.of(mBubbles),
- mVisualStabilityManager,
mDeviceProvisionedController,
mNavigationBarController,
mAccessibilityFloatingMenuController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index b8c8b5f26f0f..ac3d0c28c474 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -26,7 +26,6 @@ import android.content.Context;
import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.view.View;
import androidx.test.filters.SmallTest;
@@ -36,8 +35,8 @@ import com.android.systemui.statusbar.AlertingNotificationManagerTest;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
@@ -61,19 +60,18 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest {
private HeadsUpManagerPhone mHeadsUpManager;
@Mock private HeadsUpManagerLogger mHeadsUpManagerLogger;
- @Mock private NotificationGroupManagerLegacy mGroupManager;
- @Mock private View mNotificationShadeWindowView;
+ @Mock private GroupMembershipManager mGroupManager;
@Mock private VisualStabilityProvider mVSProvider;
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private KeyguardBypassController mBypassController;
@Mock private ConfigurationControllerImpl mConfigurationController;
private boolean mLivesPastNormalTime;
- private final class TestableHeadsUpManagerPhone extends HeadsUpManagerPhone {
+ private static final class TestableHeadsUpManagerPhone extends HeadsUpManagerPhone {
TestableHeadsUpManagerPhone(
Context context,
HeadsUpManagerLogger headsUpManagerLogger,
- NotificationGroupManagerLegacy groupManager,
+ GroupMembershipManager groupManager,
VisualStabilityProvider visualStabilityProvider,
StatusBarStateController statusBarStateController,
KeyguardBypassController keyguardBypassController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt
index 44325dddd111..a2828d33375b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt
@@ -16,19 +16,28 @@
package com.android.systemui.statusbar.phone
+import android.app.WallpaperManager
+import android.app.WallpaperManager.OnColorsChangedListener
import android.graphics.Color
+import android.os.Handler
+import android.os.Looper
import android.testing.AndroidTestingRunner
import android.view.IWindowManager
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Expect
import com.google.common.truth.Truth.assertThat
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@@ -38,17 +47,41 @@ class LetterboxBackgroundProviderTest : SysuiTestCase() {
private val fakeSystemClock = FakeSystemClock()
private val fakeExecutor = FakeExecutor(fakeSystemClock)
+ private val mainHandler = Handler(Looper.getMainLooper())
+
+ @get:Rule var expect: Expect = Expect.create()
@Mock private lateinit var windowManager: IWindowManager
@Mock private lateinit var dumpManager: DumpManager
+ @Mock private lateinit var wallpaperManager: WallpaperManager
private lateinit var provider: LetterboxBackgroundProvider
+ private var wallpaperColorsListener: OnColorsChangedListener? = null
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- provider = LetterboxBackgroundProvider(windowManager, fakeExecutor, dumpManager)
+ setUpWallpaperManager()
+ provider =
+ LetterboxBackgroundProvider(
+ windowManager, fakeExecutor, dumpManager, wallpaperManager, mainHandler)
+ }
+
+ private fun setUpWallpaperManager() {
+ doAnswer { invocation ->
+ wallpaperColorsListener = invocation.arguments[0] as OnColorsChangedListener
+ return@doAnswer Unit
+ }
+ .`when`(wallpaperManager)
+ .addOnColorsChangedListener(any(), eq(mainHandler))
+ doAnswer {
+ wallpaperColorsListener = null
+ return@doAnswer Unit
+ }
+ .`when`(wallpaperManager)
+ .removeOnColorsChangedListener(any(OnColorsChangedListener::class.java))
}
@Test
@@ -76,6 +109,31 @@ class LetterboxBackgroundProviderTest : SysuiTestCase() {
}
@Test
+ fun letterboxBackgroundColor_returnsValueFromWindowManagerOnlyOnce() {
+ whenever(windowManager.letterboxBackgroundColorInArgb).thenReturn(Color.RED)
+ provider.start()
+ fakeExecutor.runAllReady()
+ expect.that(provider.letterboxBackgroundColor).isEqualTo(Color.RED)
+
+ whenever(windowManager.letterboxBackgroundColorInArgb).thenReturn(Color.GREEN)
+ fakeExecutor.runAllReady()
+ expect.that(provider.letterboxBackgroundColor).isEqualTo(Color.RED)
+ }
+
+ @Test
+ fun letterboxBackgroundColor_afterWallpaperChanges_returnsUpdatedColor() {
+ whenever(windowManager.letterboxBackgroundColorInArgb).thenReturn(Color.RED)
+ provider.start()
+ fakeExecutor.runAllReady()
+
+ whenever(windowManager.letterboxBackgroundColorInArgb).thenReturn(Color.GREEN)
+ wallpaperColorsListener!!.onColorsChanged(null, 0)
+ fakeExecutor.runAllReady()
+
+ assertThat(provider.letterboxBackgroundColor).isEqualTo(Color.GREEN)
+ }
+
+ @Test
fun isLetterboxBackgroundMultiColored_defaultValue_returnsFalse() {
assertThat(provider.isLetterboxBackgroundMultiColored).isEqualTo(false)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
deleted file mode 100644
index 56dfb0cee520..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
+++ /dev/null
@@ -1,490 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON;
-import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.Notification;
-import android.os.Handler;
-import android.service.notification.StatusBarNotification;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
-import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
-import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback;
-import com.android.systemui.statusbar.notification.row.RowContentBindParams;
-import com.android.systemui.statusbar.notification.row.RowContentBindStage;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
-import com.android.wm.shell.bubbles.Bubbles;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-import java.util.HashMap;
-import java.util.Optional;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase {
- @Rule public MockitoRule rule = MockitoJUnit.rule();
-
- private NotificationGroupAlertTransferHelper mGroupAlertTransferHelper;
- private NotificationGroupManagerLegacy mGroupManager;
- private HeadsUpManager mHeadsUpManager;
- @Mock private NotificationEntryManager mNotificationEntryManager;
- @Mock private RowContentBindStage mBindStage;
- @Mock PeopleNotificationIdentifier mPeopleNotificationIdentifier;
- @Mock StatusBarStateController mStatusBarStateController;
- @Captor private ArgumentCaptor<NotificationEntryListener> mListenerCaptor;
- private NotificationEntryListener mNotificationEntryListener;
- private final HashMap<String, NotificationEntry> mPendingEntries = new HashMap<>();
- private final NotificationGroupTestHelper mGroupTestHelper =
- new NotificationGroupTestHelper(mContext);
-
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mHeadsUpManager = new HeadsUpManager(mContext, mock(HeadsUpManagerLogger.class),
- mock(Handler.class)) {};
-
- when(mNotificationEntryManager.getPendingNotificationsIterator())
- .thenReturn(mPendingEntries.values());
-
- mGroupManager = new NotificationGroupManagerLegacy(
- mStatusBarStateController,
- () -> mPeopleNotificationIdentifier,
- Optional.of(mock(Bubbles.class)),
- mock(DumpManager.class));
- mDependency.injectTestDependency(NotificationGroupManagerLegacy.class, mGroupManager);
- mGroupManager.setHeadsUpManager(mHeadsUpManager);
-
- when(mBindStage.getStageParams(any())).thenReturn(new RowContentBindParams());
-
- mGroupAlertTransferHelper = new NotificationGroupAlertTransferHelper(
- mBindStage, mStatusBarStateController, mGroupManager);
- mGroupAlertTransferHelper.setHeadsUpManager(mHeadsUpManager);
-
- mGroupAlertTransferHelper.bind(mNotificationEntryManager, mGroupManager);
- verify(mNotificationEntryManager).addNotificationEntryListener(mListenerCaptor.capture());
- mNotificationEntryListener = mListenerCaptor.getValue();
- mHeadsUpManager.addListener(mGroupAlertTransferHelper);
- }
-
- private void mockHasHeadsUpContentView(NotificationEntry entry,
- boolean hasHeadsUpContentView) {
- RowContentBindParams params = new RowContentBindParams();
- if (hasHeadsUpContentView) {
- params.requireContentViews(FLAG_CONTENT_VIEW_HEADS_UP);
- }
- when(mBindStage.getStageParams(eq(entry))).thenReturn(params);
- }
-
- private void mockHasHeadsUpContentView(NotificationEntry entry) {
- mockHasHeadsUpContentView(entry, true);
- }
-
- private void mockIsPriority(NotificationEntry priorityEntry) {
- when(mPeopleNotificationIdentifier.getPeopleNotificationType(eq(priorityEntry)))
- .thenReturn(PeopleNotificationIdentifier.TYPE_IMPORTANT_PERSON);
- }
-
- @Test
- public void testSuppressedSummaryHeadsUpTransfersToChild() {
- NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
- mHeadsUpManager.showNotification(summaryEntry);
- NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
-
- mockHasHeadsUpContentView(childEntry);
-
- // Summary will be suppressed because there is only one child.
- mGroupManager.onEntryAdded(summaryEntry);
- mGroupManager.onEntryAdded(childEntry);
-
- // A suppressed summary should transfer its alert state to the child.
- assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
- assertTrue(mHeadsUpManager.isAlerting(childEntry.getKey()));
- }
-
- @Test
- public void testSuppressedSummaryHeadsUpTransfersToChildButBackAgain() {
- NotificationEntry summaryEntry =
- mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
- NotificationEntry childEntry =
- mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- NotificationEntry childEntry2 =
- mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- mHeadsUpManager.showNotification(summaryEntry);
- // Trigger a transfer of alert state from summary to child.
- mGroupManager.onEntryAdded(summaryEntry);
- mGroupManager.onEntryAdded(childEntry);
-
- // Add second child notification so that summary is no longer suppressed.
- mPendingEntries.put(childEntry2.getKey(), childEntry2);
- mNotificationEntryListener.onPendingEntryAdded(childEntry2);
- mGroupManager.onEntryAdded(childEntry2);
-
- // The alert state should transfer back to the summary as there is now more than one
- // child and the summary should no longer be suppressed.
- assertTrue(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
- assertFalse(mHeadsUpManager.isAlerting(childEntry.getKey()));
- }
-
- @Test
- public void testSuppressedSummaryHeadsUpDoesntTransferBackOnDozingChanged() {
- NotificationEntry summaryEntry =
- mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
- NotificationEntry childEntry =
- mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- NotificationEntry childEntry2 =
- mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- mHeadsUpManager.showNotification(summaryEntry);
- // Trigger a transfer of alert state from summary to child.
- mGroupManager.onEntryAdded(summaryEntry);
- mGroupManager.onEntryAdded(childEntry);
-
- // Set dozing to true.
- mGroupAlertTransferHelper.onDozingChanged(true);
-
- // Add second child notification so that summary is no longer suppressed.
- mPendingEntries.put(childEntry2.getKey(), childEntry2);
- mNotificationEntryListener.onPendingEntryAdded(childEntry2);
- mGroupManager.onEntryAdded(childEntry2);
-
- // Dozing changed so no reason to re-alert summary.
- assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
- }
-
- @Test
- public void testSuppressedSummaryHeadsUpTransferDoesNotAlertChildIfUninflated() {
- NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
- mHeadsUpManager.showNotification(summaryEntry);
- NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
- mockHasHeadsUpContentView(childEntry, false);
-
- mGroupManager.onEntryAdded(summaryEntry);
- mGroupManager.onEntryAdded(childEntry);
-
- // Alert is immediately removed from summary, but we do not show child yet either as its
- // content is not inflated.
- assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
- assertFalse(mHeadsUpManager.isAlerting(childEntry.getKey()));
- assertTrue(mGroupAlertTransferHelper.isAlertTransferPending(childEntry));
- }
-
- @Test
- public void testSuppressedSummaryHeadsUpTransferAlertsChildOnInflation() {
- NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
- mHeadsUpManager.showNotification(summaryEntry);
- NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
- mockHasHeadsUpContentView(childEntry, false);
-
- mGroupManager.onEntryAdded(summaryEntry);
- mGroupManager.onEntryAdded(childEntry);
-
- // Child entry finishes its inflation.
- ArgumentCaptor<BindCallback> callbackCaptor = ArgumentCaptor.forClass(BindCallback.class);
- verify(mBindStage).requestRebind(eq(childEntry), callbackCaptor.capture());
- callbackCaptor.getValue().onBindFinished(childEntry);
-
- // Alert is immediately removed from summary, and we show child as its content is inflated.
- assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
- assertTrue(mHeadsUpManager.isAlerting(childEntry.getKey()));
- }
-
- @Test
- public void testSuppressedSummaryHeadsUpTransferBackAbortsChildInflation() {
- NotificationEntry summaryEntry =
- mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
- NotificationEntry childEntry =
- mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- RowContentBindParams params = new RowContentBindParams();
- when(mBindStage.getStageParams(eq(childEntry))).thenReturn(params);
-
- NotificationEntry childEntry2 =
- mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- mHeadsUpManager.showNotification(summaryEntry);
- // Trigger a transfer of alert state from summary to child.
- mGroupManager.onEntryAdded(summaryEntry);
- mGroupManager.onEntryAdded(childEntry);
-
- // Add second child notification so that summary is no longer suppressed.
- mPendingEntries.put(childEntry2.getKey(), childEntry2);
- mNotificationEntryListener.onPendingEntryAdded(childEntry2);
- mGroupManager.onEntryAdded(childEntry2);
-
- // Child entry finishes its inflation.
- ArgumentCaptor<BindCallback> callbackCaptor = ArgumentCaptor.forClass(BindCallback.class);
- verify(mBindStage).requestRebind(eq(childEntry), callbackCaptor.capture());
- callbackCaptor.getValue().onBindFinished(childEntry);
-
- assertTrue((params.getContentViews() & FLAG_CONTENT_VIEW_HEADS_UP) == 0);
- assertFalse(mHeadsUpManager.isAlerting(childEntry.getKey()));
- }
-
- @Test
- public void testCleanUpPendingAlertInfo() {
- NotificationEntry summaryEntry =
- mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
- NotificationEntry childEntry =
- mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- mockHasHeadsUpContentView(childEntry, false);
-
- mHeadsUpManager.showNotification(summaryEntry);
- // Trigger a transfer of alert state from summary to child.
- mGroupManager.onEntryAdded(summaryEntry);
- mGroupManager.onEntryAdded(childEntry);
-
- mNotificationEntryListener.onEntryRemoved(
- childEntry, null, false, UNDEFINED_DISMISS_REASON);
-
- assertFalse(mGroupAlertTransferHelper.isAlertTransferPending(childEntry));
- }
-
- @Test
- public void testUpdateGroupChangeDoesNotTransfer() {
- NotificationEntry summaryEntry =
- mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
- NotificationEntry childEntry =
- mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- mockHasHeadsUpContentView(childEntry, false);
-
- mHeadsUpManager.showNotification(summaryEntry);
- // Trigger a transfer of alert state from summary to child.
- mGroupManager.onEntryAdded(summaryEntry);
- mGroupManager.onEntryAdded(childEntry);
-
- // Notify that entry changed groups.
- StatusBarNotification oldNotification = childEntry.getSbn();
- StatusBarNotification newSbn = spy(childEntry.getSbn().clone());
- doReturn("other_group").when(newSbn).getGroupKey();
- childEntry.setSbn(newSbn);
- mGroupManager.onEntryUpdated(childEntry, oldNotification);
-
- assertFalse(mGroupAlertTransferHelper.isAlertTransferPending(childEntry));
- }
-
- @Test
- public void testUpdateChildToSummaryDoesNotTransfer() {
- final String tag = "fooTag";
- NotificationEntry summaryEntry =
- mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
- NotificationEntry childEntry =
- mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY, 47, tag);
- mockHasHeadsUpContentView(childEntry, false);
-
- mHeadsUpManager.showNotification(summaryEntry);
- // Trigger a transfer of alert state from summary to child.
- mGroupManager.onEntryAdded(summaryEntry);
- mGroupManager.onEntryAdded(childEntry);
-
- // Update that child to a summary.
- StatusBarNotification oldNotification = childEntry.getSbn();
- childEntry.setSbn(
- mGroupTestHelper.createSummaryNotification(
- Notification.GROUP_ALERT_SUMMARY, 47, tag).getSbn());
- mGroupManager.onEntryUpdated(childEntry, oldNotification);
-
- assertFalse(mGroupAlertTransferHelper.isAlertTransferPending(childEntry));
- }
-
- @Test
- public void testOverriddenSummaryHeadsUpTransfersToPriority() {
- // Creation order is oldest to newest, meaning the priority will be deemed newest
- int groupAlert = Notification.GROUP_ALERT_SUMMARY;
- NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(groupAlert);
- NotificationEntry childEntry = mGroupTestHelper.createChildNotification(groupAlert);
- NotificationEntry priorityEntry = mGroupTestHelper.createChildNotification(groupAlert);
- mockIsPriority(priorityEntry);
-
- // summary gets heads up
- mHeadsUpManager.showNotification(summaryEntry);
-
- mockHasHeadsUpContentView(summaryEntry);
- mockHasHeadsUpContentView(priorityEntry);
- mockHasHeadsUpContentView(childEntry);
-
- // Summary will have an alertOverride.
- mGroupManager.onEntryAdded(summaryEntry);
- mGroupManager.onEntryAdded(priorityEntry);
- mGroupManager.onEntryAdded(childEntry);
-
- // An overridden summary should transfer its alert state to the priority.
- assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
- assertFalse(mHeadsUpManager.isAlerting(childEntry.getKey()));
- assertTrue(mHeadsUpManager.isAlerting(priorityEntry.getKey()));
- }
-
- @Test
- public void testOverriddenSummaryHeadsUpTransferDoesNotAlertPriorityIfUninflated() {
- // Creation order is oldest to newest, meaning the priority will be deemed newest
- int groupAlert = Notification.GROUP_ALERT_SUMMARY;
- NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(groupAlert);
- NotificationEntry childEntry = mGroupTestHelper.createChildNotification(groupAlert);
- NotificationEntry priorityEntry = mGroupTestHelper.createChildNotification(groupAlert);
- mockIsPriority(priorityEntry);
-
- // summary gets heads up
- mHeadsUpManager.showNotification(summaryEntry);
-
- mockHasHeadsUpContentView(summaryEntry);
- mockHasHeadsUpContentView(priorityEntry, false);
- mockHasHeadsUpContentView(childEntry);
-
- // Summary will have an alertOverride.
- mGroupManager.onEntryAdded(summaryEntry);
- mGroupManager.onEntryAdded(priorityEntry);
- mGroupManager.onEntryAdded(childEntry);
-
- // Alert is immediately removed from summary, but we do not show priority yet either as its
- // content is not inflated.
- assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
- assertFalse(mHeadsUpManager.isAlerting(childEntry.getKey()));
- assertFalse(mHeadsUpManager.isAlerting(priorityEntry.getKey()));
- assertTrue(mGroupAlertTransferHelper.isAlertTransferPending(priorityEntry));
- }
-
- @Test
- public void testOverriddenSummaryHeadsUpTransfersToPriorityButBackAgain() {
- // Creation order is oldest to newest, meaning the child2 will ultimately be deemed newest
- int groupAlert = Notification.GROUP_ALERT_SUMMARY;
- NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(groupAlert);
- NotificationEntry childEntry = mGroupTestHelper.createChildNotification(groupAlert);
- NotificationEntry priorityEntry = mGroupTestHelper.createChildNotification(groupAlert);
- NotificationEntry childEntry2 = mGroupTestHelper.createChildNotification(groupAlert);
- mockIsPriority(priorityEntry);
-
- // summary gets heads up
- mHeadsUpManager.showNotification(summaryEntry);
-
- mockHasHeadsUpContentView(summaryEntry);
- mockHasHeadsUpContentView(priorityEntry);
- mockHasHeadsUpContentView(childEntry);
- mockHasHeadsUpContentView(childEntry2);
-
- // Summary will have an alertOverride.
- mGroupManager.onEntryAdded(summaryEntry);
- mGroupManager.onEntryAdded(priorityEntry);
- mGroupManager.onEntryAdded(childEntry);
-
- // An overridden summary should transfer its alert state to the priority.
- assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
- assertFalse(mHeadsUpManager.isAlerting(childEntry.getKey()));
- assertTrue(mHeadsUpManager.isAlerting(priorityEntry.getKey()));
-
- mGroupManager.onEntryAdded(childEntry2);
-
- // An overridden summary should transfer its alert state to the priority.
- assertTrue(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
- assertFalse(mHeadsUpManager.isAlerting(childEntry.getKey()));
- assertFalse(mHeadsUpManager.isAlerting(childEntry2.getKey()));
- assertFalse(mHeadsUpManager.isAlerting(priorityEntry.getKey()));
- }
-
- @Test
- public void testOverriddenSuppressedSummaryHeadsUpTransfersToChildThenToPriority() {
- // Creation order is oldest to newest, meaning the priority will ultimately be deemed newest
- int groupAlert = Notification.GROUP_ALERT_SUMMARY;
- NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(groupAlert);
- NotificationEntry childEntry = mGroupTestHelper.createChildNotification(groupAlert);
- NotificationEntry priorityEntry = mGroupTestHelper.createChildNotification(groupAlert);
- mockIsPriority(priorityEntry);
-
- // summary gets heads up
- mHeadsUpManager.showNotification(summaryEntry);
-
- mockHasHeadsUpContentView(summaryEntry);
- mockHasHeadsUpContentView(priorityEntry);
- mockHasHeadsUpContentView(childEntry);
-
- // Summary will be suppressed, and the child will receive the alert
- mGroupManager.onEntryAdded(summaryEntry);
- mGroupManager.onEntryAdded(childEntry);
-
- assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
- assertTrue(mHeadsUpManager.isAlerting(childEntry.getKey()));
-
- // Alert should be transferred "back" from the child to the priority
- mGroupManager.onEntryAdded(priorityEntry);
-
- assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
- assertFalse(mHeadsUpManager.isAlerting(childEntry.getKey()));
- assertTrue(mHeadsUpManager.isAlerting(priorityEntry.getKey()));
- }
-
- @Test
- public void testOverriddenSuppressedSummaryHeadsUpTransfersToPriorityThenToChild() {
- // Creation order is oldest to newest, meaning the child will ultimately be deemed newest
- int groupAlert = Notification.GROUP_ALERT_SUMMARY;
- NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(groupAlert);
- NotificationEntry priorityEntry = mGroupTestHelper.createChildNotification(groupAlert);
- NotificationEntry childEntry = mGroupTestHelper.createChildNotification(groupAlert);
- mockIsPriority(priorityEntry);
-
- // summary gets heads up
- mHeadsUpManager.showNotification(summaryEntry);
-
- mockHasHeadsUpContentView(summaryEntry);
- mockHasHeadsUpContentView(priorityEntry);
- mockHasHeadsUpContentView(childEntry);
-
- // Summary will have alert override of the priority
- mGroupManager.onEntryAdded(summaryEntry);
- mGroupManager.onEntryAdded(priorityEntry);
-
- assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
- assertTrue(mHeadsUpManager.isAlerting(priorityEntry.getKey()));
-
- // Alert should be transferred "back" from the priority to the child (which is newer)
- mGroupManager.onEntryAdded(childEntry);
-
- assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
- assertTrue(mHeadsUpManager.isAlerting(childEntry.getKey()));
- assertFalse(mHeadsUpManager.isAlerting(priorityEntry.getKey()));
- }
-
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
deleted file mode 100644
index d002cebe5cb1..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
+++ /dev/null
@@ -1,406 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import static org.junit.Assert.assertNull;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import android.app.Notification;
-import android.service.notification.StatusBarNotification;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.util.Log;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.NotificationGroup;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.OnGroupChangeListener;
-import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.wm.shell.bubbles.Bubbles;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-import java.util.Optional;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class NotificationGroupManagerLegacyTest extends SysuiTestCase {
- @Rule
- public MockitoRule rule = MockitoJUnit.rule();
-
- private NotificationGroupManagerLegacy mGroupManager;
- private final NotificationGroupTestHelper mGroupTestHelper =
- new NotificationGroupTestHelper(mContext);
-
- @Mock
- PeopleNotificationIdentifier mPeopleNotificationIdentifier;
- @Mock
- HeadsUpManager mHeadsUpManager;
-
- @Before
- public void setup() {
- mDependency.injectMockDependency(Bubbles.class);
- initializeGroupManager();
- }
-
- private void initializeGroupManager() {
- mGroupManager = new NotificationGroupManagerLegacy(
- mock(StatusBarStateController.class),
- () -> mPeopleNotificationIdentifier,
- Optional.of(mock(Bubbles.class)),
- mock(DumpManager.class));
- mGroupManager.setHeadsUpManager(mHeadsUpManager);
- }
-
- @Test
- public void testIsOnlyChildInGroup() {
- NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
- NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
-
- mGroupManager.onEntryAdded(summaryEntry);
- mGroupManager.onEntryAdded(childEntry);
-
- assertTrue(mGroupManager.isOnlyChildInGroup(childEntry));
- }
-
- @Test
- public void testIsChildInGroupWithSummary() {
- NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
- NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
-
- mGroupManager.onEntryAdded(summaryEntry);
- mGroupManager.onEntryAdded(childEntry);
- mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
-
- assertTrue(mGroupManager.isChildInGroup(childEntry));
- }
-
- @Test
- public void testIsSummaryOfGroupWithChildren() {
- NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
- NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
-
- mGroupManager.onEntryAdded(summaryEntry);
- mGroupManager.onEntryAdded(childEntry);
- mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
-
- assertTrue(mGroupManager.isGroupSummary(summaryEntry));
- assertEquals(summaryEntry, mGroupManager.getGroupSummary(childEntry));
- }
-
- @Test
- public void testRemoveChildFromGroupWithSummary() {
- NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
- NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
- mGroupManager.onEntryAdded(summaryEntry);
- mGroupManager.onEntryAdded(childEntry);
- mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
-
- mGroupManager.onEntryRemoved(childEntry);
-
- assertFalse(mGroupManager.isChildInGroup(childEntry));
- }
-
- @Test
- public void testRemoveSummaryFromGroupWithSummary() {
- NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
- NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
- mGroupManager.onEntryAdded(summaryEntry);
- mGroupManager.onEntryAdded(childEntry);
- mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
-
- mGroupManager.onEntryRemoved(summaryEntry);
-
- assertNull(mGroupManager.getGroupSummary(childEntry));
- assertFalse(mGroupManager.isGroupSummary(summaryEntry));
- }
-
- @Test
- public void testHeadsUpEntryIsIsolated() {
- NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
- NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
- mGroupManager.onEntryAdded(summaryEntry);
- mGroupManager.onEntryAdded(childEntry);
- mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
- when(mHeadsUpManager.isAlerting(childEntry.getKey())).thenReturn(true);
-
- mGroupManager.onHeadsUpStateChanged(childEntry, true);
-
- // Child entries that are heads upped should be considered separate groups visually even if
- // they are the same group logically
- assertEquals(childEntry, mGroupManager.getGroupSummary(childEntry));
- assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(childEntry));
- }
-
- @Test
- public void testAlertOverrideWithSiblings_0() {
- helpTestAlertOverrideWithSiblings(0);
- }
-
- @Test
- public void testAlertOverrideWithSiblings_1() {
- helpTestAlertOverrideWithSiblings(1);
- }
-
- @Test
- public void testAlertOverrideWithSiblings_2() {
- helpTestAlertOverrideWithSiblings(2);
- }
-
- @Test
- public void testAlertOverrideWithSiblings_3() {
- helpTestAlertOverrideWithSiblings(3);
- }
-
- @Test
- public void testAlertOverrideWithSiblings_9() {
- helpTestAlertOverrideWithSiblings(9);
- }
-
- /**
- * Helper for testing various sibling counts
- */
- private void helpTestAlertOverrideWithSiblings(int numSiblings) {
- helpTestAlertOverride(
- /* numSiblings */ numSiblings,
- /* summaryGroupAlert */ Notification.GROUP_ALERT_SUMMARY,
- /* priorityGroupAlert */ Notification.GROUP_ALERT_SUMMARY,
- /* siblingGroupAlert */ Notification.GROUP_ALERT_SUMMARY,
- /* expectAlertOverride */ true);
- }
-
- @Test
- public void testAlertOverrideWithParentAlertAll() {
- // tests that summary can have GROUP_ALERT_ALL and this still works
- helpTestAlertOverride(
- /* numSiblings */ 1,
- /* summaryGroupAlert */ Notification.GROUP_ALERT_ALL,
- /* priorityGroupAlert */ Notification.GROUP_ALERT_SUMMARY,
- /* siblingGroupAlert */ Notification.GROUP_ALERT_SUMMARY,
- /* expectAlertOverride */ true);
- }
-
- @Test
- public void testAlertOverrideWithParentAlertChild() {
- // Tests that if the summary alerts CHILDREN, there's no alertOverride
- helpTestAlertOverride(
- /* numSiblings */ 1,
- /* summaryGroupAlert */ Notification.GROUP_ALERT_CHILDREN,
- /* priorityGroupAlert */ Notification.GROUP_ALERT_SUMMARY,
- /* siblingGroupAlert */ Notification.GROUP_ALERT_SUMMARY,
- /* expectAlertOverride */ false);
- }
-
- @Test
- public void testAlertOverrideWithChildrenAlertAll() {
- // Tests that if the children alert ALL, there's no alertOverride
- helpTestAlertOverride(
- /* numSiblings */ 1,
- /* summaryGroupAlert */ Notification.GROUP_ALERT_SUMMARY,
- /* priorityGroupAlert */ Notification.GROUP_ALERT_ALL,
- /* siblingGroupAlert */ Notification.GROUP_ALERT_ALL,
- /* expectAlertOverride */ false);
- }
-
- /**
- * This tests, for a group with a priority entry and the given number of siblings, that:
- * 1) the priority entry is identified as the alertOverride for the group
- * 2) the onAlertOverrideChanged method is called at that time
- * 3) when the priority entry is removed, these are reversed
- */
- private void helpTestAlertOverride(int numSiblings,
- @Notification.GroupAlertBehavior int summaryGroupAlert,
- @Notification.GroupAlertBehavior int priorityGroupAlert,
- @Notification.GroupAlertBehavior int siblingGroupAlert,
- boolean expectAlertOverride) {
- long when = 10000;
- // Create entries in an order so that the priority entry can be deemed the newest child.
- NotificationEntry[] siblings = new NotificationEntry[numSiblings];
- for (int i = 0; i < numSiblings; i++) {
- siblings[i] = mGroupTestHelper
- .createChildNotification(siblingGroupAlert, i, "sibling", ++when);
- }
- NotificationEntry priorityEntry =
- mGroupTestHelper.createChildNotification(priorityGroupAlert, 0, "priority", ++when);
- NotificationEntry summaryEntry =
- mGroupTestHelper.createSummaryNotification(summaryGroupAlert, 0, "summary", ++when);
-
- // The priority entry is an important conversation.
- when(mPeopleNotificationIdentifier.getPeopleNotificationType(eq(priorityEntry)))
- .thenReturn(PeopleNotificationIdentifier.TYPE_IMPORTANT_PERSON);
-
- // Register a listener so we can verify that the event is sent.
- OnGroupChangeListener groupChangeListener = mock(OnGroupChangeListener.class);
- mGroupManager.registerGroupChangeListener(groupChangeListener);
-
- // Add all the entries. The order here shouldn't matter.
- mGroupManager.onEntryAdded(summaryEntry);
- for (int i = 0; i < numSiblings; i++) {
- mGroupManager.onEntryAdded(siblings[i]);
- }
- mGroupManager.onEntryAdded(priorityEntry);
-
- if (!expectAlertOverride) {
- // Test expectation is that there will NOT be an alert, so verify that!
- NotificationGroup summaryGroup =
- mGroupManager.getGroupForSummary(summaryEntry.getSbn());
- assertNull(summaryGroup.alertOverride);
- return;
- }
- int max2Siblings = Math.min(2, numSiblings);
-
- // Verify that the summary group has the priority child as its alertOverride
- NotificationGroup summaryGroup = mGroupManager.getGroupForSummary(summaryEntry.getSbn());
- assertEquals(priorityEntry, summaryGroup.alertOverride);
- verify(groupChangeListener).onGroupAlertOverrideChanged(summaryGroup, null, priorityEntry);
- verify(groupChangeListener).onGroupSuppressionChanged(summaryGroup, true);
- if (numSiblings > 1) {
- verify(groupChangeListener).onGroupSuppressionChanged(summaryGroup, false);
- }
- verify(groupChangeListener).onGroupCreated(any(), eq(priorityEntry.getKey()));
- verify(groupChangeListener).onGroupCreated(any(), eq(summaryEntry.getSbn().getGroupKey()));
- verify(groupChangeListener, times(max2Siblings + 1)).onGroupsChanged();
- verifyNoMoreInteractions(groupChangeListener);
-
- // Verify that only the priority notification is isolated from the group
- assertEquals(priorityEntry, mGroupManager.getGroupSummary(priorityEntry));
- assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(priorityEntry));
- // Verify that the siblings are NOT isolated from the group
- for (int i = 0; i < numSiblings; i++) {
- assertEquals(summaryEntry, mGroupManager.getGroupSummary(siblings[i]));
- assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(siblings[i]));
- }
-
- // Remove the priority notification to validate that it is removed as the alertOverride
- mGroupManager.onEntryRemoved(priorityEntry);
-
- // verify that the alertOverride is removed when the priority notification is
- assertNull(summaryGroup.alertOverride);
- verify(groupChangeListener).onGroupAlertOverrideChanged(summaryGroup, priorityEntry, null);
- verify(groupChangeListener).onGroupRemoved(any(), eq(priorityEntry.getKey()));
- verify(groupChangeListener, times(max2Siblings + 2)).onGroupsChanged();
- if (numSiblings == 0) {
- verify(groupChangeListener).onGroupSuppressionChanged(summaryGroup, false);
- }
- verifyNoMoreInteractions(groupChangeListener);
- }
-
- @Test
- public void testAlertOverrideWhenUpdatingSummaryAtEnd() {
- long when = 10000;
- int numSiblings = 2;
- int groupAlert = Notification.GROUP_ALERT_SUMMARY;
- // Create entries in an order so that the priority entry can be deemed the newest child.
- NotificationEntry[] siblings = new NotificationEntry[numSiblings];
- for (int i = 0; i < numSiblings; i++) {
- siblings[i] =
- mGroupTestHelper.createChildNotification(groupAlert, i, "sibling", ++when);
- }
- NotificationEntry priorityEntry =
- mGroupTestHelper.createChildNotification(groupAlert, 0, "priority", ++when);
- NotificationEntry summaryEntry =
- mGroupTestHelper.createSummaryNotification(groupAlert, 0, "summary", ++when);
-
- // The priority entry is an important conversation.
- when(mPeopleNotificationIdentifier.getPeopleNotificationType(eq(priorityEntry)))
- .thenReturn(PeopleNotificationIdentifier.TYPE_IMPORTANT_PERSON);
-
- // Register a listener so we can verify that the event is sent.
- OnGroupChangeListener groupChangeListener = mock(OnGroupChangeListener.class);
- mGroupManager.registerGroupChangeListener(groupChangeListener);
-
- // Add all the entries. The order here shouldn't matter.
- mGroupManager.onEntryAdded(summaryEntry);
- for (int i = 0; i < numSiblings; i++) {
- mGroupManager.onEntryAdded(siblings[i]);
- }
- mGroupManager.onEntryAdded(priorityEntry);
-
- int max2Siblings = Math.min(2, numSiblings);
-
- // Verify that the summary group has the priority child as its alertOverride
- NotificationGroup summaryGroup = mGroupManager.getGroupForSummary(summaryEntry.getSbn());
- assertEquals(priorityEntry, summaryGroup.alertOverride);
- verify(groupChangeListener).onGroupAlertOverrideChanged(summaryGroup, null, priorityEntry);
- verify(groupChangeListener).onGroupSuppressionChanged(summaryGroup, true);
- if (numSiblings > 1) {
- verify(groupChangeListener).onGroupSuppressionChanged(summaryGroup, false);
- }
- verify(groupChangeListener).onGroupCreated(any(), eq(priorityEntry.getKey()));
- verify(groupChangeListener).onGroupCreated(any(), eq(summaryEntry.getSbn().getGroupKey()));
- verify(groupChangeListener, times(max2Siblings + 1)).onGroupsChanged();
- verifyNoMoreInteractions(groupChangeListener);
-
- // Verify that only the priority notification is isolated from the group
- assertEquals(priorityEntry, mGroupManager.getGroupSummary(priorityEntry));
- assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(priorityEntry));
- // Verify that the siblings are NOT isolated from the group
- for (int i = 0; i < numSiblings; i++) {
- assertEquals(summaryEntry, mGroupManager.getGroupSummary(siblings[i]));
- assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(siblings[i]));
- }
-
- Log.d("NotificationGroupManagerLegacyTest",
- "testAlertOverrideWhenUpdatingSummaryAtEnd: About to update summary");
-
- StatusBarNotification oldSummarySbn = mGroupTestHelper.incrementPost(summaryEntry, 10000);
- mGroupManager.onEntryUpdated(summaryEntry, oldSummarySbn);
-
- verify(groupChangeListener, times(max2Siblings + 2)).onGroupsChanged();
- verify(groupChangeListener).onGroupAlertOverrideChanged(summaryGroup, priorityEntry, null);
- verifyNoMoreInteractions(groupChangeListener);
-
- Log.d("NotificationGroupManagerLegacyTest",
- "testAlertOverrideWhenUpdatingSummaryAtEnd: About to update priority child");
-
- StatusBarNotification oldPrioritySbn = mGroupTestHelper.incrementPost(priorityEntry, 10000);
- mGroupManager.onEntryUpdated(priorityEntry, oldPrioritySbn);
-
- verify(groupChangeListener).onGroupRemoved(any(), eq(priorityEntry.getKey()));
- verify(groupChangeListener, times(2)).onGroupCreated(any(), eq(priorityEntry.getKey()));
- verify(groupChangeListener, times(2))
- .onGroupAlertOverrideChanged(summaryGroup, null, priorityEntry);
- verify(groupChangeListener, times(max2Siblings + 3)).onGroupsChanged();
- verifyNoMoreInteractions(groupChangeListener);
-
- Log.d("NotificationGroupManagerLegacyTest",
- "testAlertOverrideWhenUpdatingSummaryAtEnd: Done");
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index c896c0ad93e0..de43a1fabab6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -178,10 +178,10 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
bubbleSbn.getNotification().contentIntent = mContentIntent;
bubbleSbn.getNotification().flags |= Notification.FLAG_AUTO_CANCEL;
- ArrayList<NotificationEntry> activeNotifications = new ArrayList<>();
- activeNotifications.add(mNotificationRow.getEntry());
- activeNotifications.add(mBubbleNotificationRow.getEntry());
- when(mEntryManager.getVisibleNotifications()).thenReturn(activeNotifications);
+// ArrayList<NotificationEntry> activeNotifications = new ArrayList<>();
+// activeNotifications.add(mNotificationRow.getEntry());
+// activeNotifications.add(mBubbleNotificationRow.getEntry());
+// when(mEntryManager.getVisibleNotifications()).thenReturn(activeNotifications);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
when(mOnUserInteractionCallback.registerFutureDismissal(eq(mNotificationRow.getEntry()),
anyInt())).thenReturn(mFutureDismissalRunnable);
@@ -347,9 +347,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
// The content intent should NOT be sent on click.
verifyZeroInteractions(mContentIntent);
-
- // Notification should not be cancelled.
- verify(mEntryManager, never()).performRemoveNotification(eq(sbn), any(), anyInt());
}
@Test
@@ -380,9 +377,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
verify(mContentIntent).getIntent();
verify(mContentIntent).isActivity();
verifyNoMoreInteractions(mContentIntent);
-
- // Notification should not be cancelled.
- verify(mEntryManager, never()).performRemoveNotification(eq(sbn), any(), anyInt());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
index 23b14044b3e9..1ec4de9b573e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
@@ -38,7 +38,7 @@ import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
+import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -79,7 +79,7 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
mNotificationLockscreenUserManager);
mRemoteInputCallback = spy(new StatusBarRemoteInputCallback(mContext,
- mock(NotificationGroupManagerLegacy.class), mNotificationLockscreenUserManager,
+ mock(GroupExpansionManager.class), mNotificationLockscreenUserManager,
mKeyguardStateController, mStatusBarStateController, mStatusBarKeyguardViewManager,
mActivityStarter, mShadeController, new CommandQueue(mContext),
mock(ActionClickLogger.class), mFakeExecutor));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/FakeSharedPreferencesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/FakeSharedPreferencesTest.kt
new file mode 100644
index 000000000000..d886ffdf0e58
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/FakeSharedPreferencesTest.kt
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util
+
+import android.content.SharedPreferences
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.eq
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.anyString
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class FakeSharedPreferencesTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var listener: SharedPreferences.OnSharedPreferenceChangeListener
+
+ private lateinit var sharedPreferences: SharedPreferences
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ sharedPreferences = FakeSharedPreferences()
+ }
+
+ @Test
+ fun testGetString_default() {
+ val default = "default"
+ val result = sharedPreferences.getString("key", default)
+ assertThat(result).isEqualTo(default)
+ }
+
+ @Test
+ fun testGetStringSet_default() {
+ val default = setOf("one", "two")
+ val result = sharedPreferences.getStringSet("key", default)
+ assertThat(result).isEqualTo(default)
+ }
+
+ @Test
+ fun testGetInt_default() {
+ val default = 10
+ val result = sharedPreferences.getInt("key", default)
+ assertThat(result).isEqualTo(default)
+ }
+
+ @Test
+ fun testGetLong_default() {
+ val default = 11L
+ val result = sharedPreferences.getLong("key", default)
+ assertThat(result).isEqualTo(default)
+ }
+
+ @Test
+ fun testGetFloat_default() {
+ val default = 1.3f
+ val result = sharedPreferences.getFloat("key", default)
+ assertThat(result).isEqualTo(default)
+ }
+
+ @Test
+ fun testGetBoolean_default() {
+ val default = true
+ val result = sharedPreferences.getBoolean("key", default)
+ assertThat(result).isEqualTo(default)
+ }
+
+ @Test
+ fun testPutValuesAndRetrieve() {
+ val editor = sharedPreferences.edit()
+ val data = listOf<Data<*>>(
+ Data(
+ "keyString",
+ "value",
+ SharedPreferences.Editor::putString,
+ { getString(it, "") }
+ ),
+ Data(
+ "keyStringSet",
+ setOf("one", "two"),
+ SharedPreferences.Editor::putStringSet,
+ { getStringSet(it, emptySet()) }
+ ),
+ Data("keyInt", 10, SharedPreferences.Editor::putInt, { getInt(it, 0) }),
+ Data("keyLong", 11L, SharedPreferences.Editor::putLong, { getLong(it, 0L) }),
+ Data(
+ "keyFloat",
+ 1.3f,
+ SharedPreferences.Editor::putFloat,
+ { getFloat(it, 0f) }
+ ),
+ Data(
+ "keyBoolean",
+ true,
+ SharedPreferences.Editor::putBoolean,
+ { getBoolean(it, false) }
+ )
+ )
+
+ data.fold(editor) { ed, d ->
+ d.set(ed)
+ }
+ editor.commit()
+
+ data.forEach {
+ assertThat(it.get(sharedPreferences)).isEqualTo(it.value)
+ }
+ }
+
+ @Test
+ fun testContains() {
+ sharedPreferences.edit().putInt("key", 10).commit()
+
+ assertThat(sharedPreferences.contains("key")).isTrue()
+ assertThat(sharedPreferences.contains("other")).isFalse()
+ }
+
+ @Test
+ fun testOverwrite() {
+ sharedPreferences.edit().putInt("key", 10).commit()
+ sharedPreferences.edit().putInt("key", 11).commit()
+
+ assertThat(sharedPreferences.getInt("key", 0)).isEqualTo(11)
+ }
+
+ @Test
+ fun testDeleteString() {
+ sharedPreferences.edit().putString("key", "value").commit()
+ sharedPreferences.edit().putString("key", null).commit()
+
+ assertThat(sharedPreferences.contains("key")).isFalse()
+ }
+
+ @Test
+ fun testDeleteAndReplaceString() {
+ sharedPreferences.edit().putString("key", "value").commit()
+ sharedPreferences.edit().putString("key", "other").putString("key", null).commit()
+
+ assertThat(sharedPreferences.getString("key", "")).isEqualTo("other")
+ }
+
+ @Test
+ fun testDeleteStringSet() {
+ sharedPreferences.edit().putStringSet("key", setOf("one")).commit()
+ sharedPreferences.edit().putStringSet("key", setOf("two")).commit()
+
+ assertThat(sharedPreferences.getStringSet("key", emptySet())).isEqualTo(setOf("two"))
+ }
+
+ @Test
+ fun testClear() {
+ sharedPreferences.edit().putInt("keyInt", 1).putString("keyString", "a").commit()
+ sharedPreferences.edit().clear().commit()
+
+ assertThat(sharedPreferences.contains("keyInt")).isFalse()
+ assertThat(sharedPreferences.contains("keyString")).isFalse()
+ }
+
+ @Test
+ fun testClearAndWrite() {
+ sharedPreferences.edit().putInt("key", 10).commit()
+ sharedPreferences.edit().putInt("key", 11).clear().commit()
+
+ assertThat(sharedPreferences.getInt("key", 0)).isEqualTo(11)
+ }
+
+ @Test
+ fun testListenerNotifiedOnChanges() {
+ sharedPreferences.registerOnSharedPreferenceChangeListener(listener)
+
+ sharedPreferences.edit().putInt("keyInt", 10).putString("keyString", "value").commit()
+
+ verify(listener).onSharedPreferenceChanged(sharedPreferences, "keyInt")
+ verify(listener).onSharedPreferenceChanged(sharedPreferences, "keyString")
+ verifyNoMoreInteractions(listener)
+ }
+
+ @Test
+ fun testListenerNotifiedOnClear() {
+ sharedPreferences.edit().putInt("keyInt", 10).commit()
+ sharedPreferences.registerOnSharedPreferenceChangeListener(listener)
+
+ sharedPreferences.edit().clear().commit()
+
+ verify(listener).onSharedPreferenceChanged(sharedPreferences, null)
+ verifyNoMoreInteractions(listener)
+ }
+
+ @Test
+ fun testListenerNotifiedOnRemoval() {
+ sharedPreferences.edit()
+ .putString("keyString", "a")
+ .putStringSet("keySet", setOf("a"))
+ .commit()
+
+ sharedPreferences.registerOnSharedPreferenceChangeListener(listener)
+ sharedPreferences.edit().putString("keyString", null).putStringSet("keySet", null).commit()
+
+ verify(listener).onSharedPreferenceChanged(sharedPreferences, "keyString")
+ verify(listener).onSharedPreferenceChanged(sharedPreferences, "keySet")
+ verifyNoMoreInteractions(listener)
+ }
+
+ @Test
+ fun testListenerUnregistered() {
+ sharedPreferences.registerOnSharedPreferenceChangeListener(listener)
+ sharedPreferences.unregisterOnSharedPreferenceChangeListener(listener)
+ sharedPreferences.edit().putInt("key", 10).commit()
+
+ verify(listener, never()).onSharedPreferenceChanged(eq(sharedPreferences), anyString())
+ }
+
+ @Test
+ fun testSharedPreferencesOnlyModifiedOnCommit() {
+ sharedPreferences.edit().putInt("key", 10)
+
+ assertThat(sharedPreferences.contains("key")).isFalse()
+ }
+
+ private data class Data<T>(
+ val key: String,
+ val value: T,
+ private val setter: SharedPreferences.Editor.(String, T) -> SharedPreferences.Editor,
+ private val getter: SharedPreferences.(String) -> T
+ ) {
+ fun set(editor: SharedPreferences.Editor): SharedPreferences.Editor {
+ return editor.setter(key, value)
+ }
+
+ fun get(sharedPreferences: SharedPreferences): T {
+ return sharedPreferences.getter(key)
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeSharedPreferences.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeSharedPreferences.kt
new file mode 100644
index 000000000000..4a881a7ce898
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeSharedPreferences.kt
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util
+
+import android.content.SharedPreferences
+
+/**
+ * Fake [SharedPreferences] to use within tests
+ *
+ * This will act in the same way as a real one for a particular file, but will store all the
+ * data in memory in the instance.
+ *
+ * [SharedPreferences.Editor.apply] and [SharedPreferences.Editor.commit] both act in the same way,
+ * synchronously modifying the stored data. Listeners are dispatched in the same thread, also
+ * synchronously.
+ */
+class FakeSharedPreferences : SharedPreferences {
+ private val data = mutableMapOf<String, Any>()
+ private val listeners = mutableSetOf<SharedPreferences.OnSharedPreferenceChangeListener>()
+
+ override fun getAll(): Map<String, *> {
+ return data
+ }
+
+ override fun getString(key: String, defValue: String?): String? {
+ return data.getOrDefault(key, defValue) as? String?
+ }
+
+ override fun getStringSet(key: String, defValues: MutableSet<String>?): MutableSet<String>? {
+ return data.getOrDefault(key, defValues) as? MutableSet<String>?
+ }
+
+ override fun getInt(key: String, defValue: Int): Int {
+ return data.getOrDefault(key, defValue) as Int
+ }
+
+ override fun getLong(key: String, defValue: Long): Long {
+ return data.getOrDefault(key, defValue) as Long
+ }
+
+ override fun getFloat(key: String, defValue: Float): Float {
+ return data.getOrDefault(key, defValue) as Float
+ }
+
+ override fun getBoolean(key: String, defValue: Boolean): Boolean {
+ return data.getOrDefault(key, defValue) as Boolean
+ }
+
+ override fun contains(key: String): Boolean {
+ return key in data
+ }
+
+ override fun edit(): SharedPreferences.Editor {
+ return Editor()
+ }
+
+ override fun registerOnSharedPreferenceChangeListener(
+ listener: SharedPreferences.OnSharedPreferenceChangeListener
+ ) {
+ listeners.add(listener)
+ }
+
+ override fun unregisterOnSharedPreferenceChangeListener(
+ listener: SharedPreferences.OnSharedPreferenceChangeListener
+ ) {
+ listeners.remove(listener)
+ }
+
+ private inner class Editor : SharedPreferences.Editor {
+
+ private var clear = false
+ private val changes = mutableMapOf<String, Any>()
+ private val removals = mutableSetOf<String>()
+
+ override fun putString(key: String, value: String?): SharedPreferences.Editor {
+ if (value != null) {
+ changes[key] = value
+ } else {
+ removals.add(key)
+ }
+ return this
+ }
+
+ override fun putStringSet(
+ key: String,
+ values: MutableSet<String>?
+ ): SharedPreferences.Editor {
+ if (values != null) {
+ changes[key] = values
+ } else {
+ removals.add(key)
+ }
+ return this
+ }
+
+ override fun putInt(key: String, value: Int): SharedPreferences.Editor {
+ changes[key] = value
+ return this
+ }
+
+ override fun putLong(key: String, value: Long): SharedPreferences.Editor {
+ changes[key] = value
+ return this
+ }
+
+ override fun putFloat(key: String, value: Float): SharedPreferences.Editor {
+ changes[key] = value
+ return this
+ }
+
+ override fun putBoolean(key: String, value: Boolean): SharedPreferences.Editor {
+ changes[key] = value
+ return this
+ }
+
+ override fun remove(key: String): SharedPreferences.Editor {
+ removals.add(key)
+ return this
+ }
+
+ override fun clear(): SharedPreferences.Editor {
+ clear = true
+ return this
+ }
+
+ override fun commit(): Boolean {
+ if (clear) {
+ data.clear()
+ }
+ removals.forEach { data.remove(it) }
+ data.putAll(changes)
+ val keys = removals + data.keys
+ if (clear || removals.isNotEmpty() || data.isNotEmpty()) {
+ listeners.forEach { listener ->
+ if (clear) {
+ listener.onSharedPreferenceChanged(this@FakeSharedPreferences, null)
+ }
+ keys.forEach {
+ listener.onSharedPreferenceChanged(this@FakeSharedPreferences, it)
+ }
+ }
+ }
+ return true
+ }
+
+ override fun apply() {
+ commit()
+ }
+ }
+}
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/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index b34482f0964f..3324c526ecc2 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -354,16 +354,24 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
if (supportsFlagForNotImportantViews(info)) {
if ((info.flags & AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0) {
- mFetchFlags |= AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
+ mFetchFlags |=
+ AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS;
} else {
- mFetchFlags &= ~AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
+ mFetchFlags &=
+ ~AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS;
}
}
if ((info.flags & AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS) != 0) {
- mFetchFlags |= AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS;
+ mFetchFlags |= AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_REPORT_VIEW_IDS;
} else {
- mFetchFlags &= ~AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS;
+ mFetchFlags &= ~AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_REPORT_VIEW_IDS;
+ }
+
+ if (mAccessibilityServiceInfo.isAccessibilityTool()) {
+ mFetchFlags |= AccessibilityNodeInfo.FLAG_SERVICE_IS_ACCESSIBILITY_TOOL;
+ } else {
+ mFetchFlags &= ~AccessibilityNodeInfo.FLAG_SERVICE_IS_ACCESSIBILITY_TOOL;
}
mRequestTouchExplorationMode = (info.flags
@@ -1522,9 +1530,16 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
return false;
}
+ final boolean includeNotImportantViews = (mFetchFlags
+ & AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS) != 0;
if ((event.getWindowId() != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID)
&& !event.isImportantForAccessibility()
- && (mFetchFlags & AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) == 0) {
+ && !includeNotImportantViews) {
+ return false;
+ }
+
+ if (event.isAccessibilityDataPrivate()
+ && (mFetchFlags & AccessibilityNodeInfo.FLAG_SERVICE_IS_ACCESSIBILITY_TOOL) == 0) {
return false;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 6eabc981e9fe..6a6d2bb44d48 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -3693,6 +3693,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
info.setCapabilities(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT);
info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
info.flags |= AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
+ info.setAccessibilityTool(true);
final AccessibilityUserState userState;
synchronized (mLock) {
userState = getCurrentUserStateLocked();
diff --git a/services/backup/backuplib/java/com/android/server/backup/TransportManager.java b/services/backup/backuplib/java/com/android/server/backup/TransportManager.java
index 930f49e4d117..a4ea698c5c4f 100644
--- a/services/backup/backuplib/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/backuplib/java/com/android/server/backup/TransportManager.java
@@ -16,7 +16,9 @@
package com.android.server.backup;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
import android.annotation.Nullable;
@@ -166,6 +168,17 @@ public class TransportManager {
onPackageEnabled(packageName);
return;
}
+ case COMPONENT_ENABLED_STATE_DEFAULT: {
+ // Package is set to its default enabled state (as specified in its manifest).
+ // Unless explicitly specified in manifest, the default enabled state
+ // is 'enabled'. Here, we assume that default state always means enabled.
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Package " + packageName
+ + " was put in default enabled state.");
+ }
+ onPackageEnabled(packageName);
+ return;
+ }
case COMPONENT_ENABLED_STATE_DISABLED: {
if (MORE_DEBUG) {
Slog.d(TAG, "Package " + packageName + " was disabled.");
@@ -173,6 +186,13 @@ public class TransportManager {
onPackageDisabled(packageName);
return;
}
+ case COMPONENT_ENABLED_STATE_DISABLED_USER: {
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Package " + packageName + " was disabled by user.");
+ }
+ onPackageDisabled(packageName);
+ return;
+ }
default: {
Slog.w(TAG, "Package " + packageName + " enabled setting: " + enabled);
return;
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 38275f7cd348..1c571a7036ad 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -25,7 +25,6 @@ import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.admin.DevicePolicyManager;
import android.app.backup.BackupManager;
-import android.app.backup.BackupManager.OperationType;
import android.app.backup.IBackupManager;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IBackupObserver;
@@ -284,6 +283,7 @@ public class BackupManagerService extends IBackupManager.Stub {
*/
@Override
public boolean isUserReadyForBackup(int userId) {
+ enforceCallingPermissionOnUserId(userId, "isUserReadyForBackup()");
return mUserServices.get(UserHandle.USER_SYSTEM) != null
&& mUserServices.get(userId) != null;
}
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/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index e7ea903bf485..6aa472fcd586 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -166,6 +166,7 @@ import com.android.internal.app.procstats.ServiceState;
import com.android.internal.messages.nano.SystemMessageProto;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.SomeArgs;
+import com.android.internal.os.TimeoutRecord;
import com.android.internal.os.TransferPipe;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastPrintWriter;
@@ -5761,7 +5762,7 @@ public final class ActiveServices {
}
void serviceTimeout(ProcessRecord proc) {
- String anrMessage = null;
+ TimeoutRecord timeoutRecord = null;
synchronized(mAm) {
if (proc.isDebugging()) {
// The app's being debugged, ignore timeout.
@@ -5796,7 +5797,8 @@ public final class ActiveServices {
mLastAnrDump = sw.toString();
mAm.mHandler.removeCallbacks(mLastAnrDumpClearer);
mAm.mHandler.postDelayed(mLastAnrDumpClearer, LAST_ANR_LIFETIME_DURATION_MSECS);
- anrMessage = "executing service " + timeout.shortInstanceName;
+ String anrMessage = "executing service " + timeout.shortInstanceName;
+ timeoutRecord = TimeoutRecord.forServiceExec(anrMessage);
} else {
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_TIMEOUT_MSG);
@@ -5806,13 +5808,15 @@ public final class ActiveServices {
}
}
- if (anrMessage != null) {
- mAm.mAnrHelper.appNotResponding(proc, anrMessage);
+ if (timeoutRecord != null) {
+ mAm.mAnrHelper.appNotResponding(proc, timeoutRecord);
}
}
void serviceForegroundTimeout(ServiceRecord r) {
ProcessRecord app;
+ // Grab a timestamp before lock is taken.
+ long timeoutEndMs = SystemClock.uptimeMillis();
synchronized (mAm) {
if (!r.fgRequired || !r.fgWaiting || r.destroying) {
return;
@@ -5836,17 +5840,19 @@ public final class ActiveServices {
+ "Service.startForeground(): " + r;
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_ANR_MSG);
+ TimeoutRecord timeoutRecord = TimeoutRecord.forServiceStartWithEndTime(annotation,
+ timeoutEndMs);
SomeArgs args = SomeArgs.obtain();
args.arg1 = app;
- args.arg2 = annotation;
+ args.arg2 = timeoutRecord;
msg.obj = args;
mAm.mHandler.sendMessageDelayed(msg,
mAm.mConstants.mServiceStartForegroundAnrDelayMs);
}
}
- void serviceForegroundTimeoutANR(ProcessRecord app, String annotation) {
- mAm.mAnrHelper.appNotResponding(app, annotation);
+ void serviceForegroundTimeoutANR(ProcessRecord app, TimeoutRecord timeoutRecord) {
+ mAm.mAnrHelper.appNotResponding(app, timeoutRecord);
}
public void updateServiceApplicationInfoLocked(ApplicationInfo applicationInfo) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7f1cf0339460..e46639b83996 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -357,6 +357,7 @@ import com.android.internal.os.ByteTransferPipe;
import com.android.internal.os.IResultReceiver;
import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.os.SomeArgs;
+import com.android.internal.os.TimeoutRecord;
import com.android.internal.os.TransferPipe;
import com.android.internal.os.Zygote;
import com.android.internal.policy.AttributeCache;
@@ -1700,7 +1701,7 @@ public class ActivityManagerService extends IActivityManager.Stub
case SERVICE_FOREGROUND_TIMEOUT_ANR_MSG: {
SomeArgs args = (SomeArgs) msg.obj;
mServices.serviceForegroundTimeoutANR((ProcessRecord) args.arg1,
- (String) args.arg2);
+ (TimeoutRecord) args.arg2);
args.recycle();
} break;
case SERVICE_FOREGROUND_CRASH_MSG: {
@@ -5014,7 +5015,8 @@ public class ActivityManagerService extends IActivityManager.Stub
hostingRecord.getType(),
hostingRecord.getName(),
shortAction,
- HostingRecord.getHostingTypeIdStatsd(hostingRecord.getType()));
+ HostingRecord.getHostingTypeIdStatsd(hostingRecord.getType()),
+ HostingRecord.getTriggerTypeForStatsd(hostingRecord.getTriggerType()));
return true;
}
@@ -6416,6 +6418,7 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public void appNotResponding(final String reason) {
+ TimeoutRecord timeoutRecord = TimeoutRecord.forApp("App requested: " + reason);
final int callingPid = Binder.getCallingPid();
synchronized (mPidsSelfLocked) {
@@ -6425,7 +6428,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
mAnrHelper.appNotResponding(app, null, app.info, null, null, false,
- "App requested: " + reason);
+ timeoutRecord);
}
}
@@ -14339,10 +14342,12 @@ public class ActivityManagerService extends IActivityManager.Stub
if (oldRecord.resultTo != null) {
final BroadcastQueue oldQueue = broadcastQueueForIntent(oldRecord.intent);
try {
+ oldRecord.mIsReceiverAppRunning = true;
oldQueue.performReceiveLocked(oldRecord.callerApp, oldRecord.resultTo,
oldRecord.intent,
Activity.RESULT_CANCELED, null, null,
- false, false, oldRecord.userId, oldRecord.callingUid, callingUid);
+ false, false, oldRecord.userId, oldRecord.callingUid, callingUid,
+ SystemClock.uptimeMillis() - oldRecord.enqueueTime, 0);
} catch (RemoteException e) {
Slog.w(TAG, "Failure ["
+ queue.mQueueName + "] sending broadcast result of "
@@ -17169,18 +17174,19 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
- public long inputDispatchingTimedOut(int pid, boolean aboveSystem, String reason) {
- return ActivityManagerService.this.inputDispatchingTimedOut(pid, aboveSystem, reason);
+ public long inputDispatchingTimedOut(int pid, boolean aboveSystem,
+ TimeoutRecord timeoutRecord) {
+ return ActivityManagerService.this.inputDispatchingTimedOut(pid, aboveSystem,
+ timeoutRecord);
}
@Override
public boolean inputDispatchingTimedOut(Object proc, String activityShortComponentName,
ApplicationInfo aInfo, String parentShortComponentName, Object parentProc,
- boolean aboveSystem, String reason) {
+ boolean aboveSystem, TimeoutRecord timeoutRecord) {
return ActivityManagerService.this.inputDispatchingTimedOut((ProcessRecord) proc,
activityShortComponentName, aInfo, parentShortComponentName,
- (WindowProcessController) parentProc, aboveSystem, reason);
-
+ (WindowProcessController) parentProc, aboveSystem, timeoutRecord);
}
@Override
@@ -17742,7 +17748,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
- long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
+ long inputDispatchingTimedOut(int pid, final boolean aboveSystem, TimeoutRecord timeoutRecord) {
if (checkCallingPermission(FILTER_EVENTS) != PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires permission " + FILTER_EVENTS);
}
@@ -17753,7 +17759,7 @@ public class ActivityManagerService extends IActivityManager.Stub
final long timeoutMillis = proc != null ? proc.getInputDispatchingTimeoutMillis() :
DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
- if (inputDispatchingTimedOut(proc, null, null, null, null, aboveSystem, reason)) {
+ if (inputDispatchingTimedOut(proc, null, null, null, null, aboveSystem, timeoutRecord)) {
return 0;
}
@@ -17766,18 +17772,12 @@ public class ActivityManagerService extends IActivityManager.Stub
*/
boolean inputDispatchingTimedOut(ProcessRecord proc, String activityShortComponentName,
ApplicationInfo aInfo, String parentShortComponentName,
- WindowProcessController parentProcess, boolean aboveSystem, String reason) {
+ WindowProcessController parentProcess, boolean aboveSystem,
+ TimeoutRecord timeoutRecord) {
if (checkCallingPermission(FILTER_EVENTS) != PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires permission " + FILTER_EVENTS);
}
- final String annotation;
- if (reason == null) {
- annotation = "Input dispatching timed out";
- } else {
- annotation = "Input dispatching timed out (" + reason + ")";
- }
-
if (proc != null) {
synchronized (this) {
if (proc.isDebugging()) {
@@ -17787,13 +17787,13 @@ public class ActivityManagerService extends IActivityManager.Stub
if (proc.getActiveInstrumentation() != null) {
Bundle info = new Bundle();
info.putString("shortMsg", "keyDispatchingTimedOut");
- info.putString("longMsg", annotation);
+ info.putString("longMsg", timeoutRecord.mReason);
finishInstrumentationLocked(proc, Activity.RESULT_CANCELED, info);
return true;
}
}
mAnrHelper.appNotResponding(proc, activityShortComponentName, aInfo,
- parentShortComponentName, parentProcess, aboveSystem, annotation);
+ parentShortComponentName, parentProcess, aboveSystem, timeoutRecord);
}
return true;
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index dbabe99c79d5..4bbfa3eec67d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -51,6 +51,7 @@ import android.app.IActivityManager;
import android.app.IActivityTaskManager;
import android.app.IStopUserCallback;
import android.app.IUidObserver;
+import android.app.IUserSwitchObserver;
import android.app.KeyguardManager;
import android.app.ProfilerInfo;
import android.app.RemoteServiceException.CrashedByAdbException;
@@ -1951,31 +1952,36 @@ final class ActivityManagerShellCommand extends ShellCommand {
// Register switch observer.
final CountDownLatch switchLatch = new CountDownLatch(1);
- mInterface.registerUserSwitchObserver(
- new UserSwitchObserver() {
- @Override
- public void onUserSwitchComplete(int newUserId) {
- if (userId == newUserId) {
- switchLatch.countDown();
- }
- }
- }, ActivityManagerShellCommand.class.getName());
+ final IUserSwitchObserver userSwitchObserver = new UserSwitchObserver() {
+ @Override
+ public void onUserSwitchComplete(int newUserId) {
+ if (userId == newUserId) {
+ switchLatch.countDown();
+ }
+ }
+ };
+ try {
+ mInterface.registerUserSwitchObserver(userSwitchObserver,
+ ActivityManagerShellCommand.class.getName());
- // Switch.
- boolean switched = mInterface.switchUser(userId);
- if (!switched) {
- // Switching failed, don't wait for the user switch observer.
- return false;
- }
+ // Switch.
+ boolean switched = mInterface.switchUser(userId);
+ if (!switched) {
+ // Switching failed, don't wait for the user switch observer.
+ return false;
+ }
- // Wait.
- try {
- switched = switchLatch.await(USER_OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
- } catch (InterruptedException e) {
- getErrPrintWriter().println("Error: Thread interrupted unexpectedly.");
- }
+ // Wait.
+ try {
+ switched = switchLatch.await(USER_OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ getErrPrintWriter().println("Error: Thread interrupted unexpectedly.");
+ }
- return switched;
+ return switched;
+ } finally {
+ mInterface.unregisterUserSwitchObserver(userSwitchObserver);
+ }
}
int runSwitchUser(PrintWriter pw) throws RemoteException {
diff --git a/services/core/java/com/android/server/am/AnrHelper.java b/services/core/java/com/android/server/am/AnrHelper.java
index e3544953e63d..f1d8353392db 100644
--- a/services/core/java/com/android/server/am/AnrHelper.java
+++ b/services/core/java/com/android/server/am/AnrHelper.java
@@ -24,6 +24,7 @@ import android.os.SystemClock;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.TimeoutRecord;
import com.android.server.wm.WindowProcessController;
import java.util.ArrayList;
@@ -68,15 +69,16 @@ class AnrHelper {
mService = service;
}
- void appNotResponding(ProcessRecord anrProcess, String annotation) {
+ void appNotResponding(ProcessRecord anrProcess, TimeoutRecord timeoutRecord) {
appNotResponding(anrProcess, null /* activityShortComponentName */, null /* aInfo */,
null /* parentShortComponentName */, null /* parentProcess */,
- false /* aboveSystem */, annotation);
+ false /* aboveSystem */, timeoutRecord);
}
void appNotResponding(ProcessRecord anrProcess, String activityShortComponentName,
ApplicationInfo aInfo, String parentShortComponentName,
- WindowProcessController parentProcess, boolean aboveSystem, String annotation) {
+ WindowProcessController parentProcess, boolean aboveSystem,
+ TimeoutRecord timeoutRecord) {
final int incomingPid = anrProcess.mPid;
synchronized (mAnrRecords) {
if (incomingPid == 0) {
@@ -85,17 +87,19 @@ class AnrHelper {
return;
}
if (mProcessingPid == incomingPid) {
- Slog.i(TAG, "Skip duplicated ANR, pid=" + incomingPid + " " + annotation);
+ Slog.i(TAG,
+ "Skip duplicated ANR, pid=" + incomingPid + " " + timeoutRecord.mReason);
return;
}
for (int i = mAnrRecords.size() - 1; i >= 0; i--) {
if (mAnrRecords.get(i).mPid == incomingPid) {
- Slog.i(TAG, "Skip queued ANR, pid=" + incomingPid + " " + annotation);
+ Slog.i(TAG,
+ "Skip queued ANR, pid=" + incomingPid + " " + timeoutRecord.mReason);
return;
}
}
mAnrRecords.add(new AnrRecord(anrProcess, activityShortComponentName, aInfo,
- parentShortComponentName, parentProcess, aboveSystem, annotation));
+ parentShortComponentName, parentProcess, aboveSystem, timeoutRecord));
}
startAnrConsumerIfNeeded();
}
@@ -175,7 +179,7 @@ class AnrHelper {
final int mPid;
final String mActivityShortComponentName;
final String mParentShortComponentName;
- final String mAnnotation;
+ final TimeoutRecord mTimeoutRecord;
final ApplicationInfo mAppInfo;
final WindowProcessController mParentProcess;
final boolean mAboveSystem;
@@ -183,12 +187,13 @@ class AnrHelper {
AnrRecord(ProcessRecord anrProcess, String activityShortComponentName,
ApplicationInfo aInfo, String parentShortComponentName,
- WindowProcessController parentProcess, boolean aboveSystem, String annotation) {
+ WindowProcessController parentProcess, boolean aboveSystem,
+ TimeoutRecord timeoutRecord) {
mApp = anrProcess;
mPid = anrProcess.mPid;
mActivityShortComponentName = activityShortComponentName;
mParentShortComponentName = parentShortComponentName;
- mAnnotation = annotation;
+ mTimeoutRecord = timeoutRecord;
mAppInfo = aInfo;
mParentProcess = parentProcess;
mAboveSystem = aboveSystem;
@@ -196,7 +201,8 @@ class AnrHelper {
void appNotResponding(boolean onlyDumpSelf) {
mApp.mErrorState.appNotResponding(mActivityShortComponentName, mAppInfo,
- mParentShortComponentName, mParentProcess, mAboveSystem, mAnnotation,
+ mParentShortComponentName, mParentProcess, mAboveSystem,
+ mTimeoutRecord,
onlyDumpSelf);
}
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 768fdfd4ed5c..cc2b693d36f7 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -758,6 +758,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
.setMaxStatsAgeMs(0)
.includeProcessStateData()
.includeVirtualUids()
+ .includePowerModels()
.build();
bus = getBatteryUsageStats(List.of(querySinceReset)).get(0);
break;
@@ -768,6 +769,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
.includeProcessStateData()
.includeVirtualUids()
.powerProfileModeledOnly()
+ .includePowerModels()
.build();
bus = getBatteryUsageStats(List.of(queryPowerProfile)).get(0);
break;
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 6d520c3c6be8..aaaacef33696 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -82,6 +82,7 @@ import android.util.SparseIntArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.os.TimeoutRecord;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
@@ -321,7 +322,7 @@ public final class BroadcastQueue {
}
private final void processCurBroadcastLocked(BroadcastRecord r,
- ProcessRecord app, int receiverType, int processTemperature) throws RemoteException {
+ ProcessRecord app) throws RemoteException {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Process cur broadcast " + r + " for app " + app);
final IApplicationThread thread = app.getThread();
@@ -367,10 +368,6 @@ public final class BroadcastQueue {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Process cur broadcast " + r + " DELIVERED for app " + app);
started = true;
- FrameworkStatsLog.write(BROADCAST_DELIVERY_EVENT_REPORTED, app.uid,
- r.callingUid == -1 ? Process.SYSTEM_UID : r.callingUid,
- ActivityManagerService.getShortAction(r.intent.getAction()),
- receiverType, processTemperature);
} finally {
if (!started) {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
@@ -407,9 +404,8 @@ public final class BroadcastQueue {
}
try {
mPendingBroadcast = null;
- processCurBroadcastLocked(br, app,
- BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST,
- BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD);
+ br.mIsReceiverAppRunning = false;
+ processCurBroadcastLocked(br, app);
didSomething = true;
} catch (Exception e) {
Slog.w(TAG, "Exception in new application when starting receiver "
@@ -517,6 +513,22 @@ public final class BroadcastQueue {
final long finishTime = SystemClock.uptimeMillis();
final long elapsed = finishTime - r.receiverTime;
r.state = BroadcastRecord.IDLE;
+ final int curIndex = r.nextReceiver - 1;
+ if (curIndex >= 0 && curIndex < r.receivers.size() && r.curApp != null) {
+ final Object curReceiver = r.receivers.get(curIndex);
+ FrameworkStatsLog.write(BROADCAST_DELIVERY_EVENT_REPORTED, r.curApp.uid,
+ r.callingUid == -1 ? Process.SYSTEM_UID : r.callingUid,
+ ActivityManagerService.getShortAction(r.intent.getAction()),
+ curReceiver instanceof BroadcastFilter
+ ? BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME
+ : BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST,
+ r.mIsReceiverAppRunning
+ ? BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM
+ : BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD,
+ r.dispatchTime - r.enqueueTime,
+ r.receiverTime - r.dispatchTime,
+ finishTime - r.receiverTime);
+ }
if (state == BroadcastRecord.IDLE) {
Slog.w(TAG_BROADCAST, "finishReceiver [" + mQueueName + "] called but state is IDLE");
}
@@ -640,7 +652,8 @@ public final class BroadcastQueue {
void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
Intent intent, int resultCode, String data, Bundle extras,
boolean ordered, boolean sticky, int sendingUser,
- int receiverUid, int callingUid) throws RemoteException {
+ int receiverUid, int callingUid, long dispatchDelay,
+ long receiveDelay) throws RemoteException {
// Send the intent to the receiver asynchronously using one-way binder calls.
if (app != null) {
final IApplicationThread thread = app.getThread();
@@ -674,12 +687,15 @@ public final class BroadcastQueue {
receiver.performReceive(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
}
- FrameworkStatsLog.write(BROADCAST_DELIVERY_EVENT_REPORTED,
- receiverUid == -1 ? Process.SYSTEM_UID : receiverUid,
- callingUid == -1 ? Process.SYSTEM_UID : callingUid,
- ActivityManagerService.getShortAction(intent.getAction()),
- BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME,
- BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM);
+ if (!ordered) {
+ FrameworkStatsLog.write(BROADCAST_DELIVERY_EVENT_REPORTED,
+ receiverUid == -1 ? Process.SYSTEM_UID : receiverUid,
+ callingUid == -1 ? Process.SYSTEM_UID : callingUid,
+ ActivityManagerService.getShortAction(intent.getAction()),
+ BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME,
+ BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM,
+ dispatchDelay, receiveDelay, 0 /* finish_delay */);
+ }
}
private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
@@ -983,7 +999,9 @@ public final class BroadcastQueue {
performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
new Intent(r.intent), r.resultCode, r.resultData,
r.resultExtras, r.ordered, r.initialSticky, r.userId,
- filter.receiverList.uid, r.callingUid);
+ filter.receiverList.uid, r.callingUid,
+ r.dispatchTime - r.enqueueTime,
+ r.receiverTime - r.dispatchTime);
// parallel broadcasts are fire-and-forget, not bookended by a call to
// finishReceiverLocked(), so we manage their activity-start token here
if (filter.receiverList.app != null
@@ -1166,6 +1184,7 @@ public final class BroadcastQueue {
r.dispatchTime = SystemClock.uptimeMillis();
r.dispatchRealTime = SystemClock.elapsedRealtime();
r.dispatchClockTime = System.currentTimeMillis();
+ r.mIsReceiverAppRunning = true;
if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
@@ -1333,10 +1352,18 @@ public final class BroadcastQueue {
Slog.i(TAG_BROADCAST, "Finishing broadcast [" + mQueueName + "] "
+ r.intent.getAction() + " app=" + r.callerApp);
}
+ if (r.dispatchTime == 0) {
+ // The dispatch time here could be 0, in case it's a parallel
+ // broadcast but it has a result receiver. Set it to now.
+ r.dispatchTime = now;
+ }
+ r.mIsReceiverAppRunning = true;
performReceiveLocked(r.callerApp, r.resultTo,
new Intent(r.intent), r.resultCode,
r.resultData, r.resultExtras, false, false, r.userId,
- r.callingUid, r.callingUid);
+ r.callingUid, r.callingUid,
+ r.dispatchTime - r.enqueueTime,
+ now - r.dispatchTime);
logBootCompletedBroadcastCompletionLatencyIfPossible(r);
// Set this to null so that the reference
// (local and remote) isn't kept in the mBroadcastHistory.
@@ -1493,6 +1520,7 @@ public final class BroadcastQueue {
"Delivering ordered ["
+ mQueueName + "] to registered "
+ filter + ": " + r);
+ r.mIsReceiverAppRunning = true;
deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
if (r.receiver == null || !r.ordered) {
// The receiver has already finished, so schedule to
@@ -1856,9 +1884,8 @@ public final class BroadcastQueue {
app.addPackage(info.activityInfo.packageName,
info.activityInfo.applicationInfo.longVersionCode, mService.mProcessStats);
maybeAddAllowBackgroundActivityStartsToken(app, r);
- processCurBroadcastLocked(r, app,
- BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST,
- BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM);
+ r.mIsReceiverAppRunning = true;
+ processCurBroadcastLocked(r, app);
return;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when sending broadcast to "
@@ -1892,7 +1919,8 @@ public final class BroadcastQueue {
info.activityInfo.applicationInfo, true,
r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
new HostingRecord(HostingRecord.HOSTING_TYPE_BROADCAST, r.curComponent,
- r.intent.getAction()),
+ r.intent.getAction(), (r.alarm ? HostingRecord.TRIGGER_TYPE_ALARM
+ : HostingRecord.TRIGGER_TYPE_UNKNOWN)),
isActivityCapable ? ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE : ZYGOTE_POLICY_FLAG_EMPTY,
(r.intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false);
if (r.curApp == null) {
@@ -2134,7 +2162,7 @@ public final class BroadcastQueue {
}
ProcessRecord app = null;
- String anrMessage = null;
+ TimeoutRecord timeoutRecord = null;
Object curReceiver;
if (r.nextReceiver > 0) {
@@ -2159,9 +2187,10 @@ public final class BroadcastQueue {
}
if (app != null) {
- anrMessage =
+ String anrMessage =
"Broadcast of " + r.intent.toString() + ", waited " + timeoutDurationMs
+ "ms";
+ timeoutRecord = TimeoutRecord.forBroadcastReceiver(anrMessage);
}
if (mPendingBroadcast == r) {
@@ -2173,8 +2202,8 @@ public final class BroadcastQueue {
r.resultExtras, r.resultAbort, false);
scheduleBroadcastsLocked();
- if (!debugging && anrMessage != null) {
- mService.mAnrHelper.appNotResponding(app, anrMessage);
+ if (!debugging && timeoutRecord != null) {
+ mService.mAnrHelper.appNotResponding(app, timeoutRecord);
}
}
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index ae91d75ef0ce..ce4528bca887 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -135,6 +135,8 @@ final class BroadcastRecord extends Binder {
ComponentName curComponent; // the receiver class that is currently running.
ActivityInfo curReceiver; // info about the receiver that is currently running.
+ boolean mIsReceiverAppRunning; // Was the receiver's app already running.
+
// Private refcount-management bookkeeping; start > 0
static AtomicInteger sNextToken = new AtomicInteger(1);
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 45265ac0c8f3..363c9d0a963a 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -1392,25 +1392,17 @@ public final class CachedAppOptimizer {
void cancelAllCompactions(CancelCompactReason reason) {
synchronized (mProcLock) {
- int size = mPendingCompactionProcesses.size();
- ProcessRecord record;
- for (int i=0; i < size; ++i) {
- record = mPendingCompactionProcesses.get(i);
- cancelCompactionForProcess(record, reason);
- // The process record is kept alive after compactions are cleared,
- // so make sure to reset the compaction state to avoid skipping any future
- // compactions due to a stale value here.
- record.mOptRecord.setHasPendingCompact(false);
+ while(!mPendingCompactionProcesses.isEmpty()) {
+ cancelCompactionForProcess(mPendingCompactionProcesses.get(0), reason);
}
mPendingCompactionProcesses.clear();
}
- cancelCompaction();
}
@GuardedBy("mProcLock")
void cancelCompactionForProcess(ProcessRecord app, CancelCompactReason cancelReason) {
boolean cancelled = false;
- if (!mPendingCompactionProcesses.isEmpty() && mPendingCompactionProcesses.contains(app)) {
+ if (mPendingCompactionProcesses.contains(app)) {
app.mOptRecord.setHasPendingCompact(false);
mPendingCompactionProcesses.remove(app);
cancelled = true;
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 4ff1a129f691..9abd01ac3ee9 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -75,6 +75,7 @@ import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.os.TimeoutRecord;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
@@ -935,7 +936,9 @@ public class ContentProviderHelper {
return;
}
- mService.mAnrHelper.appNotResponding(host, "ContentProvider not responding");
+ TimeoutRecord timeoutRecord = TimeoutRecord.forContentProvider(
+ "ContentProvider not responding");
+ mService.mAnrHelper.appNotResponding(host, timeoutRecord);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
diff --git a/services/core/java/com/android/server/am/HostingRecord.java b/services/core/java/com/android/server/am/HostingRecord.java
index f88a8ce83d02..efc2a2719dad 100644
--- a/services/core/java/com/android/server/am/HostingRecord.java
+++ b/services/core/java/com/android/server/am/HostingRecord.java
@@ -16,10 +16,27 @@
package com.android.server.am;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_ACTIVITY;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_ADDED_APPLICATION;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_BACKUP;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_BROADCAST;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_CONTENT_PROVIDER;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_EMPTY;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_LINK_FAIL;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_NEXT_ACTIVITY;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_NEXT_TOP_ACTIVITY;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_ON_HOLD;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_RESTART;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_SERVICE;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_SYSTEM;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_TOP_ACTIVITY;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__TRIGGER_TYPE__TRIGGER_TYPE_ALARM;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__TRIGGER_TYPE__TRIGGER_TYPE_UNKNOWN;
+import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__TYPE__UNKNOWN;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
-import android.os.ProcessStartTime;
/**
* This class describes various information required to start a process.
@@ -32,6 +49,9 @@ import android.os.ProcessStartTime;
*
* The {@code mHostingZygote} field describes from which Zygote the new process should be spawned.
*
+ * The {@code mTriggerType} field describes the trigger that started this processs. This could be
+ * an alarm or a push-message for a broadcast, for example. This is purely for logging and stats.
+ *
* {@code mDefiningPackageName} contains the packageName of the package that defines the
* component we want to start; this can be different from the packageName and uid in the
* ApplicationInfo that we're creating the process with, in case the service is a
@@ -71,7 +91,10 @@ public final class HostingRecord {
public static final String HOSTING_TYPE_TOP_ACTIVITY = "top-activity";
public static final String HOSTING_TYPE_EMPTY = "";
- private @NonNull final String mHostingType;
+ public static final String TRIGGER_TYPE_UNKNOWN = "unknown";
+ public static final String TRIGGER_TYPE_ALARM = "alarm";
+
+ @NonNull private final String mHostingType;
private final String mHostingName;
private final int mHostingZygote;
private final String mDefiningPackageName;
@@ -79,11 +102,12 @@ public final class HostingRecord {
private final boolean mIsTopApp;
private final String mDefiningProcessName;
@Nullable private final String mAction;
+ @NonNull private final String mTriggerType;
public HostingRecord(@NonNull String hostingType) {
this(hostingType, null /* hostingName */, REGULAR_ZYGOTE, null /* definingPackageName */,
-1 /* mDefiningUid */, false /* isTopApp */, null /* definingProcessName */,
- null /* action */);
+ null /* action */, TRIGGER_TYPE_UNKNOWN);
}
public HostingRecord(@NonNull String hostingType, ComponentName hostingName) {
@@ -91,22 +115,23 @@ public final class HostingRecord {
}
public HostingRecord(@NonNull String hostingType, ComponentName hostingName,
- @Nullable String action) {
+ @Nullable String action, @Nullable String triggerType) {
this(hostingType, hostingName.toShortString(), REGULAR_ZYGOTE,
null /* definingPackageName */, -1 /* mDefiningUid */, false /* isTopApp */,
- null /* definingProcessName */, action);
+ null /* definingProcessName */, action, triggerType);
}
public HostingRecord(@NonNull String hostingType, ComponentName hostingName,
String definingPackageName, int definingUid, String definingProcessName) {
this(hostingType, hostingName.toShortString(), REGULAR_ZYGOTE, definingPackageName,
- definingUid, false /* isTopApp */, definingProcessName, null /* action */);
+ definingUid, false /* isTopApp */, definingProcessName, null /* action */,
+ TRIGGER_TYPE_UNKNOWN);
}
public HostingRecord(@NonNull String hostingType, ComponentName hostingName, boolean isTopApp) {
this(hostingType, hostingName.toShortString(), REGULAR_ZYGOTE,
null /* definingPackageName */, -1 /* mDefiningUid */, isTopApp /* isTopApp */,
- null /* definingProcessName */, null /* action */);
+ null /* definingProcessName */, null /* action */, TRIGGER_TYPE_UNKNOWN);
}
public HostingRecord(@NonNull String hostingType, String hostingName) {
@@ -121,12 +146,12 @@ public final class HostingRecord {
private HostingRecord(@NonNull String hostingType, String hostingName, int hostingZygote) {
this(hostingType, hostingName, hostingZygote, null /* definingPackageName */,
-1 /* mDefiningUid */, false /* isTopApp */, null /* definingProcessName */,
- null /* action */);
+ null /* action */, TRIGGER_TYPE_UNKNOWN);
}
private HostingRecord(@NonNull String hostingType, String hostingName, int hostingZygote,
String definingPackageName, int definingUid, boolean isTopApp,
- String definingProcessName, @Nullable String action) {
+ String definingProcessName, @Nullable String action, String triggerType) {
mHostingType = hostingType;
mHostingName = hostingName;
mHostingZygote = hostingZygote;
@@ -135,6 +160,7 @@ public final class HostingRecord {
mIsTopApp = isTopApp;
mDefiningProcessName = definingProcessName;
mAction = action;
+ mTriggerType = triggerType;
}
public @NonNull String getType() {
@@ -188,6 +214,11 @@ public final class HostingRecord {
return mAction;
}
+ /** Returns the type of trigger that led to this process start. */
+ public @NonNull String getTriggerType() {
+ return mTriggerType;
+ }
+
/**
* Creates a HostingRecord for a process that must spawn from the webview zygote
* @param hostingName name of the component to be hosted in this process
@@ -197,7 +228,7 @@ public final class HostingRecord {
String definingPackageName, int definingUid, String definingProcessName) {
return new HostingRecord(HostingRecord.HOSTING_TYPE_EMPTY, hostingName.toShortString(),
WEBVIEW_ZYGOTE, definingPackageName, definingUid, false /* isTopApp */,
- definingProcessName, null /* action */);
+ definingProcessName, null /* action */, TRIGGER_TYPE_UNKNOWN);
}
/**
@@ -211,7 +242,7 @@ public final class HostingRecord {
int definingUid, String definingProcessName) {
return new HostingRecord(HostingRecord.HOSTING_TYPE_EMPTY, hostingName.toShortString(),
APP_ZYGOTE, definingPackageName, definingUid, false /* isTopApp */,
- definingProcessName, null /* action */);
+ definingProcessName, null /* action */, TRIGGER_TYPE_UNKNOWN);
}
/**
@@ -236,35 +267,49 @@ public final class HostingRecord {
public static int getHostingTypeIdStatsd(@NonNull String hostingType) {
switch(hostingType) {
case HOSTING_TYPE_ACTIVITY:
- return ProcessStartTime.HOSTING_TYPE_ACTIVITY;
+ return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_ACTIVITY;
case HOSTING_TYPE_ADDED_APPLICATION:
- return ProcessStartTime.HOSTING_TYPE_ADDED_APPLICATION;
+ return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_ADDED_APPLICATION;
case HOSTING_TYPE_BACKUP:
- return ProcessStartTime.HOSTING_TYPE_BACKUP;
+ return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_BACKUP;
case HOSTING_TYPE_BROADCAST:
- return ProcessStartTime.HOSTING_TYPE_BROADCAST;
+ return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_BROADCAST;
case HOSTING_TYPE_CONTENT_PROVIDER:
- return ProcessStartTime.HOSTING_TYPE_CONTENT_PROVIDER;
+ return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_CONTENT_PROVIDER;
case HOSTING_TYPE_LINK_FAIL:
- return ProcessStartTime.HOSTING_TYPE_LINK_FAIL;
+ return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_LINK_FAIL;
case HOSTING_TYPE_ON_HOLD:
- return ProcessStartTime.HOSTING_TYPE_ON_HOLD;
+ return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_ON_HOLD;
case HOSTING_TYPE_NEXT_ACTIVITY:
- return ProcessStartTime.HOSTING_TYPE_NEXT_ACTIVITY;
+ return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_NEXT_ACTIVITY;
case HOSTING_TYPE_NEXT_TOP_ACTIVITY:
- return ProcessStartTime.HOSTING_TYPE_NEXT_TOP_ACTIVITY;
+ return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_NEXT_TOP_ACTIVITY;
case HOSTING_TYPE_RESTART:
- return ProcessStartTime.HOSTING_TYPE_RESTART;
+ return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_RESTART;
case HOSTING_TYPE_SERVICE:
- return ProcessStartTime.HOSTING_TYPE_SERVICE;
+ return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_SERVICE;
case HOSTING_TYPE_SYSTEM:
- return ProcessStartTime.HOSTING_TYPE_SYSTEM;
+ return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_SYSTEM;
case HOSTING_TYPE_TOP_ACTIVITY:
- return ProcessStartTime.HOSTING_TYPE_TOP_ACTIVITY;
+ return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_TOP_ACTIVITY;
case HOSTING_TYPE_EMPTY:
- return ProcessStartTime.HOSTING_TYPE_EMPTY;
+ return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_EMPTY;
+ default:
+ return PROCESS_START_TIME__TYPE__UNKNOWN;
+ }
+ }
+
+ /**
+ * Map the string triggerType to enum TriggerType defined in ProcessStartTime proto.
+ * @param triggerType
+ * @return enum TriggerType defined in ProcessStartTime proto
+ */
+ public static int getTriggerTypeForStatsd(@NonNull String triggerType) {
+ switch(triggerType) {
+ case TRIGGER_TYPE_ALARM:
+ return PROCESS_START_TIME__TRIGGER_TYPE__TRIGGER_TYPE_ALARM;
default:
- return ProcessStartTime.HOSTING_TYPE_UNKNOWN;
+ return PROCESS_START_TIME__TRIGGER_TYPE__TRIGGER_TYPE_UNKNOWN;
}
}
}
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index b27665a7e491..3a8a077c4e8e 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -50,6 +50,7 @@ import com.android.internal.annotations.CompositeRWLock;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.ProcessCpuTracker;
+import com.android.internal.os.TimeoutRecord;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.ResourcePressureUtil;
import com.android.server.criticalevents.CriticalEventLog;
@@ -254,7 +255,9 @@ class ProcessErrorStateRecord {
void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo,
String parentShortComponentName, WindowProcessController parentProcess,
- boolean aboveSystem, String annotation, boolean onlyDumpSelf) {
+ boolean aboveSystem, TimeoutRecord timeoutRecord,
+ boolean onlyDumpSelf) {
+ String annotation = timeoutRecord.mReason;
ArrayList<Integer> firstPids = new ArrayList<>(5);
SparseArray<Boolean> lastPids = new SparseArray<>(20);
diff --git a/services/core/java/com/android/server/app/GameManagerShellCommand.java b/services/core/java/com/android/server/app/GameManagerShellCommand.java
index 487d19aa6aba..6e289b10258e 100644
--- a/services/core/java/com/android/server/app/GameManagerShellCommand.java
+++ b/services/core/java/com/android/server/app/GameManagerShellCommand.java
@@ -220,16 +220,40 @@ public class GameManagerShellCommand extends ShellCommand {
final GameManagerService gameManagerService = (GameManagerService)
ServiceManager.getService(Context.GAME_SERVICE);
+ boolean batteryModeSupported = false;
+ boolean perfModeSupported = false;
+ int [] modes = gameManagerService.getAvailableGameModes(packageName);
+
+ for (int mode : modes) {
+ if (mode == GameManager.GAME_MODE_PERFORMANCE) {
+ perfModeSupported = true;
+ } else if (mode == GameManager.GAME_MODE_BATTERY) {
+ batteryModeSupported = true;
+ }
+ }
+
switch (gameMode.toLowerCase(Locale.getDefault())) {
case "2":
case "performance":
- gameManagerService.setGameModeConfigOverride(packageName, userId,
- GameManager.GAME_MODE_PERFORMANCE, fpsStr, downscaleRatio);
+ if (perfModeSupported) {
+ gameManagerService.setGameModeConfigOverride(packageName, userId,
+ GameManager.GAME_MODE_PERFORMANCE, fpsStr, downscaleRatio);
+ } else {
+ pw.println("Game mode: " + gameMode + " not supported by "
+ + packageName);
+ return -1;
+ }
break;
case "3":
case "battery":
- gameManagerService.setGameModeConfigOverride(packageName, userId,
- GameManager.GAME_MODE_BATTERY, fpsStr, downscaleRatio);
+ if (batteryModeSupported) {
+ gameManagerService.setGameModeConfigOverride(packageName, userId,
+ GameManager.GAME_MODE_BATTERY, fpsStr, downscaleRatio);
+ } else {
+ pw.println("Game mode: " + gameMode + " not supported by "
+ + packageName);
+ return -1;
+ }
break;
default:
pw.println("Invalid game mode: " + gameMode);
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/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
index 26a63120d793..5620dc36c971 100644
--- a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
@@ -337,7 +337,7 @@ public final class RecordingActivityMonitor implements AudioSystem.AudioRecordin
boolean hasPublicClients = false;
while (clientIterator.hasNext()) {
RecMonitorClient rmc = clientIterator.next();
- if (rcdb.equals(rmc.mDispatcherCb)) {
+ if (rcdb.asBinder().equals(rmc.mDispatcherCb.asBinder())) {
rmc.release();
clientIterator.remove();
} else {
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index e53aef733293..99e709ea3fd8 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -127,12 +127,13 @@ abstract class DisplayDevice {
/**
* Returns the default size of the surface associated with the display, or null if the surface
- * is not provided for layer mirroring by SurfaceFlinger.
- * Only used for mirroring started from MediaProjection.
+ * is not provided for layer mirroring by SurfaceFlinger. For non virtual displays, this will
+ * be the actual display device's size.
*/
@Nullable
public Point getDisplaySurfaceDefaultSizeLocked() {
- return null;
+ DisplayDeviceInfo displayDeviceInfo = getDisplayDeviceInfoLocked();
+ return new Point(displayDeviceInfo.width, displayDeviceInfo.height);
}
/**
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 74ee6800eb63..4f3fd6409cd8 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -66,6 +66,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
+import java.util.Locale;
import javax.xml.datatype.DatatypeConfigurationException;
@@ -708,8 +709,8 @@ public class DisplayDeviceConfig {
private static DisplayDeviceConfig getConfigFromSuffix(Context context, File baseDirectory,
String suffixFormat, long idNumber) {
- final String suffix = String.format(suffixFormat, idNumber);
- final String filename = String.format(CONFIG_FILE_FORMAT, suffix);
+ final String suffix = String.format(Locale.ROOT, suffixFormat, idNumber);
+ final String filename = String.format(Locale.ROOT, CONFIG_FILE_FORMAT, suffix);
final File filePath = Environment.buildPath(
baseDirectory, ETC_DIR, DISPLAY_CONFIG_DIR, filename);
final DisplayDeviceConfig config = new DisplayDeviceConfig(context);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 2dd3864a9505..e53ac379536b 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -70,8 +70,6 @@ import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayGroupListener;
import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener;
-import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
-import android.hardware.display.DisplayManagerInternal.RefreshRateRange;
import android.hardware.display.DisplayViewport;
import android.hardware.display.DisplayedContentSample;
import android.hardware.display.DisplayedContentSamplingAttributes;
@@ -2214,24 +2212,10 @@ public final class DisplayManagerService extends SystemService {
private void configureDisplayLocked(SurfaceControl.Transaction t, DisplayDevice device) {
final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
- final boolean ownContent = (info.flags & DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY) != 0;
// Find the logical display that the display device is showing.
// Certain displays only ever show their own content.
LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
- // Proceed with display-managed mirroring only if window manager will not be handling it.
- if (!ownContent && !device.isWindowManagerMirroringLocked()) {
- // Only mirror the display if content recording is not taking place in WM.
- if (display != null && !display.hasContentLocked()) {
- // If the display does not have any content of its own, then
- // automatically mirror the requested logical display contents if possible.
- display = mLogicalDisplayMapper.getDisplayLocked(
- device.getDisplayIdToMirrorLocked());
- }
- if (display == null) {
- display = mLogicalDisplayMapper.getDisplayLocked(Display.DEFAULT_DISPLAY);
- }
- }
// Apply the logical display configuration to the display device.
if (display == null) {
@@ -2546,18 +2530,6 @@ public final class DisplayManagerService extends SystemService {
}
@VisibleForTesting
- int getDisplayIdToMirrorInternal(int displayId) {
- synchronized (mSyncRoot) {
- final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
- if (display != null) {
- final DisplayDevice displayDevice = display.getPrimaryDisplayDeviceLocked();
- return displayDevice.getDisplayIdToMirrorLocked();
- }
- return Display.INVALID_DISPLAY;
- }
- }
-
- @VisibleForTesting
Surface getVirtualDisplaySurfaceInternal(IBinder appToken) {
synchronized (mSyncRoot) {
if (mVirtualDisplayAdapter == null) {
@@ -3853,6 +3825,37 @@ public final class DisplayManagerService extends SystemService {
return null;
}
}
+
+ @Override
+ public int getDisplayIdToMirror(int displayId) {
+ synchronized (mSyncRoot) {
+ final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
+ if (display == null) {
+ return Display.INVALID_DISPLAY;
+ }
+
+ final DisplayDevice displayDevice = display.getPrimaryDisplayDeviceLocked();
+ final boolean ownContent = (displayDevice.getDisplayDeviceInfoLocked().flags
+ & DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY) != 0;
+ // If the display has enabled mirroring, but specified that it will be managed by
+ // WindowManager, return an invalid display id. This is to ensure we don't
+ // accidentally select the display id to mirror based on DM logic and instead allow
+ // the caller to specify what area to mirror.
+ if (ownContent || displayDevice.isWindowManagerMirroringLocked()) {
+ return Display.INVALID_DISPLAY;
+ }
+
+ int displayIdToMirror = displayDevice.getDisplayIdToMirrorLocked();
+ LogicalDisplay displayToMirror = mLogicalDisplayMapper.getDisplayLocked(
+ displayIdToMirror);
+ // If the displayId for the requested mirror doesn't exist, fallback to mirroring
+ // default display.
+ if (displayToMirror == null) {
+ displayIdToMirror = Display.DEFAULT_DISPLAY;
+ }
+ return displayIdToMirror;
+ }
+ }
}
class DesiredDisplayModeSpecsObserver
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 90e4596af420..6f3a0c516a5b 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1328,9 +1328,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
final boolean autoBrightnessAdjustmentChanged = updateAutoBrightnessAdjustment();
- if (autoBrightnessAdjustmentChanged) {
- mTemporaryAutoBrightnessAdjustment = Float.NaN;
- }
// Use the autobrightness adjustment override if set.
final float autoBrightnessAdjustment;
@@ -2309,14 +2306,15 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
private void handleSettingsChange(boolean userSwitch) {
mPendingScreenBrightnessSetting = getScreenBrightnessSetting();
+ mPendingAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
if (userSwitch) {
// Don't treat user switches as user initiated change.
setCurrentScreenBrightness(mPendingScreenBrightnessSetting);
+ updateAutoBrightnessAdjustment();
if (mAutomaticBrightnessController != null) {
mAutomaticBrightnessController.resetShortTermModel();
}
}
- mPendingAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
// We don't bother with a pending variable for VR screen brightness since we just
// immediately adapt to it.
mScreenBrightnessForVr = getScreenBrightnessForVrSetting();
@@ -2385,6 +2383,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
mAutoBrightnessAdjustment = mPendingAutoBrightnessAdjustment;
mPendingAutoBrightnessAdjustment = Float.NaN;
+ mTemporaryAutoBrightnessAdjustment = Float.NaN;
return true;
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
index 3cb3431e906b..ffdb66d9230c 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
@@ -53,6 +53,7 @@ import android.util.Log;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.location.ClientBrokerProto;
import java.util.Collections;
@@ -162,7 +163,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
* The remote callback interface for this client. This will be set to null whenever the
* client connection is closed (either explicitly or via binder death).
*/
- private IContextHubClientCallback mCallbackInterface;
+ private IContextHubClientCallback mContextHubClientCallback;
/*
* True if the client is still registered with the Context Hub Service, false otherwise.
@@ -184,12 +185,13 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
* unregistered.
*/
@GuardedBy("mWakeLock")
- private boolean mIsWakeLockActive = true;
+ private boolean mIsWakelockUsable = true;
/*
* Internal interface used to invoke client callbacks.
*/
- private interface CallbackConsumer {
+ @VisibleForTesting
+ interface CallbackConsumer {
void accept(IContextHubClientCallback callback) throws RemoteException;
}
@@ -325,17 +327,24 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
}
}
- private ContextHubClientBroker(Context context, IContextHubWrapper contextHubProxy,
- ContextHubClientManager clientManager, ContextHubInfo contextHubInfo,
- short hostEndPointId, IContextHubClientCallback callback, String attributionTag,
- ContextHubTransactionManager transactionManager, PendingIntent pendingIntent,
- long nanoAppId, String packageName) {
+ private ContextHubClientBroker(
+ Context context,
+ IContextHubWrapper contextHubProxy,
+ ContextHubClientManager clientManager,
+ ContextHubInfo contextHubInfo,
+ short hostEndPointId,
+ IContextHubClientCallback callback,
+ String attributionTag,
+ ContextHubTransactionManager transactionManager,
+ PendingIntent pendingIntent,
+ long nanoAppId,
+ String packageName) {
mContext = context;
mContextHubProxy = contextHubProxy;
mClientManager = clientManager;
mAttachedContextHubInfo = contextHubInfo;
mHostEndPointId = hostEndPointId;
- mCallbackInterface = callback;
+ mContextHubClientCallback = callback;
if (pendingIntent == null) {
mPendingIntentRequest = new PendingIntentRequest();
} else {
@@ -369,7 +378,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
sendHostEndpointConnectedEvent();
}
- /* package */ ContextHubClientBroker(
+ ContextHubClientBroker(
Context context,
IContextHubWrapper contextHubProxy,
ContextHubClientManager clientManager,
@@ -393,7 +402,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
packageName);
}
- /* package */ ContextHubClientBroker(
+ ContextHubClientBroker(
Context context,
IContextHubWrapper contextHubProxy,
ContextHubClientManager clientManager,
@@ -504,36 +513,50 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
}
}
- /* package */ String getPackageName() {
+ String getPackageName() {
return mPackage;
}
+ @VisibleForTesting
+ boolean isWakelockUsable() {
+ synchronized (mWakeLock) {
+ return mIsWakelockUsable;
+ }
+ }
+
+ @VisibleForTesting
+ WakeLock getWakeLock() {
+ synchronized (mWakeLock) {
+ return mWakeLock;
+ }
+ }
+
/**
* Used to override the attribution tag with a newer value if a PendingIntent broker is
* retrieved.
*/
- /* package */ void setAttributionTag(String attributionTag) {
+ void setAttributionTag(String attributionTag) {
mAttributionTag = attributionTag;
}
/**
* @return the attribution tag associated with this broker.
*/
- /* package */ String getAttributionTag() {
+ String getAttributionTag() {
return mAttributionTag;
}
/**
* @return the ID of the context hub this client is attached to
*/
- /* package */ int getAttachedContextHubId() {
+ int getAttachedContextHubId() {
return mAttachedContextHubInfo.getId();
}
/**
* @return the host endpoint ID of this client
*/
- /* package */ short getHostEndPointId() {
+ short getHostEndPointId() {
return mHostEndPointId;
}
@@ -542,17 +565,19 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
*
* @param message the message that came from a nanoapp
* @param nanoappPermissions permissions required to communicate with the nanoapp sending this
- * message
+ * message
* @param messagePermissions permissions required to consume the message being delivered. These
- * permissions are what will be attributed to the client through noteOp.
+ * permissions are what will be attributed to the client through noteOp.
*/
- /* package */ void sendMessageToClient(
- NanoAppMessage message, List<String> nanoappPermissions,
+ void sendMessageToClient(
+ NanoAppMessage message,
+ List<String> nanoappPermissions,
List<String> messagePermissions) {
long nanoAppId = message.getNanoAppId();
- int authState = updateNanoAppAuthState(nanoAppId, nanoappPermissions,
- false /* gracePeriodExpired */);
+ int authState =
+ updateNanoAppAuthState(
+ nanoAppId, nanoappPermissions, false /* gracePeriodExpired */);
// If in the grace period, the host may not receive any messages containing permissions
// covered data.
@@ -584,7 +609,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
*
* @param nanoAppId the ID of the nanoapp that was loaded.
*/
- /* package */ void onNanoAppLoaded(long nanoAppId) {
+ void onNanoAppLoaded(long nanoAppId) {
// Check the latest state to see if the loaded nanoapp's permissions changed such that the
// host app can communicate with it again.
checkNanoappPermsAsync();
@@ -599,16 +624,14 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
*
* @param nanoAppId the ID of the nanoapp that was unloaded.
*/
- /* package */ void onNanoAppUnloaded(long nanoAppId) {
+ void onNanoAppUnloaded(long nanoAppId) {
invokeCallback(callback -> callback.onNanoAppUnloaded(nanoAppId));
sendPendingIntent(
() -> createIntent(ContextHubManager.EVENT_NANOAPP_UNLOADED, nanoAppId), nanoAppId);
}
- /**
- * Notifies the client of a hub reset event if the connection is open.
- */
- /* package */ void onHubReset() {
+ /** Notifies the client of a hub reset event if the connection is open. */
+ void onHubReset() {
invokeCallback(IContextHubClientCallback::onHubReset);
sendPendingIntent(() -> createIntent(ContextHubManager.EVENT_HUB_RESET));
@@ -622,7 +645,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
* @param nanoAppId the ID of the nanoapp that aborted
* @param abortCode the nanoapp specific abort code
*/
- /* package */ void onNanoAppAborted(long nanoAppId, int abortCode) {
+ void onNanoAppAborted(long nanoAppId, int abortCode) {
invokeCallback(callback -> callback.onNanoAppAborted(nanoAppId, abortCode));
Supplier<Intent> supplier =
@@ -632,18 +655,19 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
}
/**
- * @param intent the PendingIntent to compare to
+ * @param intent the PendingIntent to compare to
* @param nanoAppId the ID of the nanoapp of the PendingIntent to compare to
* @return true if the given PendingIntent is currently registered, false otherwise
*/
- /* package */ boolean hasPendingIntent(PendingIntent intent, long nanoAppId) {
+ boolean hasPendingIntent(PendingIntent intent, long nanoAppId) {
PendingIntent pendingIntent;
long intentNanoAppId;
synchronized (this) {
pendingIntent = mPendingIntentRequest.getPendingIntent();
intentNanoAppId = mPendingIntentRequest.getNanoAppId();
}
- return (pendingIntent != null) && pendingIntent.equals(intent)
+ return (pendingIntent != null)
+ && pendingIntent.equals(intent)
&& intentNanoAppId == nanoAppId;
}
@@ -652,9 +676,9 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
*
* @throws RemoteException if the client process already died
*/
- /* package */ void attachDeathRecipient() throws RemoteException {
- if (mCallbackInterface != null) {
- mCallbackInterface.asBinder().linkToDeath(this, 0 /* flags */);
+ void attachDeathRecipient() throws RemoteException {
+ if (mContextHubClientCallback != null) {
+ mContextHubClientCallback.asBinder().linkToDeath(this, 0 /* flags */);
}
}
@@ -664,7 +688,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
* @param permissions list of permissions to check
* @return true if the client has all of the permissions granted
*/
- /* package */ boolean hasPermissions(List<String> permissions) {
+ boolean hasPermissions(List<String> permissions) {
for (String permission : permissions) {
if (mContext.checkPermission(permission, mPid, mUid) != PERMISSION_GRANTED) {
return false;
@@ -678,10 +702,10 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
*
* @param permissions list of permissions covering data the client is about to receive
* @param noteMessage message that should be noted alongside permissions attribution to
- * facilitate debugging
+ * facilitate debugging
* @return true if client has ability to use all of the provided permissions
*/
- /* package */ boolean notePermissions(List<String> permissions, String noteMessage) {
+ boolean notePermissions(List<String> permissions, String noteMessage) {
for (String permission : permissions) {
int opCode = AppOpsManager.permissionToOpCode(permission);
if (opCode != AppOpsManager.OP_NONE) {
@@ -691,8 +715,14 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
return false;
}
} catch (SecurityException e) {
- Log.e(TAG, "SecurityException: noteOp for pkg " + mPackage + " opcode "
- + opCode + ": " + e.getMessage());
+ Log.e(
+ TAG,
+ "SecurityException: noteOp for pkg "
+ + mPackage
+ + " opcode "
+ + opCode
+ + ": "
+ + e.getMessage());
return false;
}
}
@@ -704,7 +734,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
/**
* @return true if the client is a PendingIntent client that has been cancelled.
*/
- /* package */ boolean isPendingIntentCancelled() {
+ boolean isPendingIntentCancelled() {
return mIsPendingIntentCancelled.get();
}
@@ -712,7 +742,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
* Handles timer expiry for a client whose auth state with a nanoapp was previously in the grace
* period.
*/
- /* package */ void handleAuthStateTimerExpiry(long nanoAppId) {
+ void handleAuthStateTimerExpiry(long nanoAppId) {
AuthStateDenialTimer timer;
synchronized (mMessageChannelNanoappIdMap) {
timer = mNappToAuthTimerMap.remove(nanoAppId);
@@ -720,7 +750,8 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
if (timer != null) {
updateNanoAppAuthState(
- nanoAppId, Collections.emptyList() /* nanoappPermissions */,
+ nanoAppId,
+ Collections.emptyList() /* nanoappPermissions */,
true /* gracePeriodExpired */);
}
}
@@ -755,8 +786,10 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
* it should transition to denied
* @return the latest auth state as of the completion of this method.
*/
- /* package */ int updateNanoAppAuthState(
- long nanoAppId, List<String> nanoappPermissions, boolean gracePeriodExpired,
+ int updateNanoAppAuthState(
+ long nanoAppId,
+ List<String> nanoappPermissions,
+ boolean gracePeriodExpired,
boolean forceDenied) {
int curAuthState;
int newAuthState;
@@ -834,13 +867,17 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
* @param consumer the consumer specifying the callback to invoke
*/
private synchronized void invokeCallback(CallbackConsumer consumer) {
- if (mCallbackInterface != null) {
+ if (mContextHubClientCallback != null) {
try {
acquireWakeLock();
- consumer.accept(mCallbackInterface);
+ consumer.accept(mContextHubClientCallback);
} catch (RemoteException e) {
- Log.e(TAG, "RemoteException while invoking client callback (host endpoint ID = "
- + mHostEndPointId + ")", e);
+ Log.e(
+ TAG,
+ "RemoteException while invoking client callback (host endpoint ID = "
+ + mHostEndPointId
+ + ")",
+ e);
}
}
}
@@ -879,20 +916,20 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
*/
private synchronized void sendPendingIntent(Supplier<Intent> supplier) {
if (mPendingIntentRequest.hasPendingIntent()) {
- doSendPendingIntent(mPendingIntentRequest.getPendingIntent(), supplier.get());
+ doSendPendingIntent(mPendingIntentRequest.getPendingIntent(), supplier.get(), this);
}
}
/**
* Sends an intent to any existing PendingIntent
*
- * @param supplier method to create the extra Intent
+ * @param supplier method to create the extra Intent
* @param nanoAppId the ID of the nanoapp which this event is for
*/
private synchronized void sendPendingIntent(Supplier<Intent> supplier, long nanoAppId) {
if (mPendingIntentRequest.hasPendingIntent()
&& mPendingIntentRequest.getNanoAppId() == nanoAppId) {
- doSendPendingIntent(mPendingIntentRequest.getPendingIntent(), supplier.get());
+ doSendPendingIntent(mPendingIntentRequest.getPendingIntent(), supplier.get(), this);
}
}
@@ -902,7 +939,11 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
* @param pendingIntent the PendingIntent
* @param intent the extra Intent data
*/
- private void doSendPendingIntent(PendingIntent pendingIntent, Intent intent) {
+ @VisibleForTesting
+ void doSendPendingIntent(
+ PendingIntent pendingIntent,
+ Intent intent,
+ PendingIntent.OnFinished onFinishedCallback) {
try {
String requiredPermission = Manifest.permission.ACCESS_CONTEXT_HUB;
acquireWakeLock();
@@ -910,7 +951,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
mContext,
/* code= */ 0,
intent,
- /* onFinished= */ this,
+ /* onFinished= */ onFinishedCallback,
/* handler= */ null,
requiredPermission,
/* options= */ null);
@@ -934,13 +975,11 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
return mRegistered;
}
- /**
- * Invoked when a client exits either explicitly or by binder death.
- */
+ /** Invoked when a client exits either explicitly or by binder death. */
private synchronized void onClientExit() {
- if (mCallbackInterface != null) {
- mCallbackInterface.asBinder().unlinkToDeath(this, 0 /* flags */);
- mCallbackInterface = null;
+ if (mContextHubClientCallback != null) {
+ mContextHubClientCallback.asBinder().unlinkToDeath(this, 0 /* flags */);
+ mContextHubClientCallback = null;
}
// The client is only unregistered and cleared when there is NOT any PendingIntent
if (!mPendingIntentRequest.hasPendingIntent() && mRegistered) {
@@ -1056,7 +1095,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
Binder.withCleanCallingIdentity(
() -> {
synchronized (mWakeLock) {
- if (mIsWakeLockActive) {
+ if (mIsWakelockUsable) {
mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS);
}
}
@@ -1092,7 +1131,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
Binder.withCleanCallingIdentity(
() -> {
synchronized (mWakeLock) {
- mIsWakeLockActive = false;
+ mIsWakelockUsable = false;
while (mWakeLock.isHeld()) {
try {
mWakeLock.release();
diff --git a/services/core/java/com/android/server/location/contexthub/TEST_MAPPING b/services/core/java/com/android/server/location/contexthub/TEST_MAPPING
new file mode 100644
index 000000000000..2f6aa5308086
--- /dev/null
+++ b/services/core/java/com/android/server/location/contexthub/TEST_MAPPING
@@ -0,0 +1,26 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.location.contexthub."
+ },
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ],
+ "imports": [
+ {
+ "path": "frameworks/base/services/tests/servicestests/src/com/android/server/location/contexthub"
+ }
+ ]
+}
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/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 7d12ede754ef..ed8d852ad2da 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -335,8 +335,8 @@ public final class MediaProjectionManagerService extends SystemService
@Override // Binder call
public void stopActiveProjection() {
- if (mContext.checkCallingPermission(Manifest.permission.MANAGE_MEDIA_PROJECTION)
- != PackageManager.PERMISSION_GRANTED) {
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.MANAGE_MEDIA_PROJECTION)
+ != PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to add "
+ "projection callbacks");
}
@@ -393,9 +393,14 @@ public final class MediaProjectionManagerService extends SystemService
if (!isValidMediaProjection(projection)) {
throw new SecurityException("Invalid media projection");
}
- LocalServices.getService(
+ if (!LocalServices.getService(
WindowManagerInternal.class).setContentRecordingSession(
- incomingSession);
+ incomingSession)) {
+ // Unable to start mirroring, so tear down this projection.
+ if (mProjectionGrant != null) {
+ mProjectionGrant.stop();
+ }
+ }
}
} finally {
Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e798adf1c22a..72f3850891ad 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6423,6 +6423,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService
@Override
public void setVisibilityLogging(String packageName, boolean enable) {
+ PackageManagerServiceUtils.enforceSystemOrRootOrShell(
+ "Only the system or shell can set visibility logging.");
final PackageStateInternal packageState =
snapshot().getPackageStateInternal(packageName);
if (packageState == null) {
diff --git a/services/core/java/com/android/server/pm/PackageVerificationState.java b/services/core/java/com/android/server/pm/PackageVerificationState.java
index a574fc9225a8..8acdb0e47522 100644
--- a/services/core/java/com/android/server/pm/PackageVerificationState.java
+++ b/services/core/java/com/android/server/pm/PackageVerificationState.java
@@ -31,6 +31,7 @@ class PackageVerificationState {
private final SparseBooleanArray mSufficientVerifierUids;
private final SparseBooleanArray mRequiredVerifierUids;
+ private final SparseBooleanArray mUnrespondedRequiredVerifierUids;
private boolean mSufficientVerificationComplete;
@@ -52,6 +53,7 @@ class PackageVerificationState {
mVerifyingSession = verifyingSession;
mSufficientVerifierUids = new SparseBooleanArray();
mRequiredVerifierUids = new SparseBooleanArray();
+ mUnrespondedRequiredVerifierUids = new SparseBooleanArray();
mRequiredVerificationComplete = false;
mRequiredVerificationPassed = true;
mExtendedTimeout = false;
@@ -64,6 +66,7 @@ class PackageVerificationState {
/** Add the user ID of the required package verifier. */
void addRequiredVerifierUid(int uid) {
mRequiredVerifierUids.put(uid, true);
+ mUnrespondedRequiredVerifierUids.put(uid, true);
}
/** Returns true if the uid a required verifier. */
@@ -103,8 +106,8 @@ class PackageVerificationState {
mRequiredVerificationPassed = false;
}
- mRequiredVerifierUids.delete(uid);
- if (mRequiredVerifierUids.size() == 0) {
+ mUnrespondedRequiredVerifierUids.delete(uid);
+ if (mUnrespondedRequiredVerifierUids.size() == 0) {
mRequiredVerificationComplete = true;
}
return true;
@@ -129,7 +132,7 @@ class PackageVerificationState {
* Mark the session as passed required verification.
*/
void passRequiredVerification() {
- if (mRequiredVerifierUids.size() > 0) {
+ if (mUnrespondedRequiredVerifierUids.size() > 0) {
throw new RuntimeException("Required verifiers still present.");
}
mRequiredVerificationPassed = true;
diff --git a/services/core/java/com/android/server/policy/SideFpsEventHandler.java b/services/core/java/com/android/server/policy/SideFpsEventHandler.java
index af2b902c97e8..8582f5424321 100644
--- a/services/core/java/com/android/server/policy/SideFpsEventHandler.java
+++ b/services/core/java/com/android/server/policy/SideFpsEventHandler.java
@@ -23,11 +23,8 @@ import static android.hardware.biometrics.BiometricStateListener.STATE_KEYGUARD_
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.AlertDialog;
-import android.app.Dialog;
import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
@@ -35,12 +32,11 @@ import android.hardware.biometrics.BiometricStateListener;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
-import android.os.Build;
import android.os.Handler;
import android.os.PowerManager;
-import android.os.UserHandle;
-import android.provider.Settings;
import android.util.Log;
+import android.view.View;
+import android.view.Window;
import android.view.WindowManager;
import com.android.internal.R;
@@ -48,49 +44,48 @@ import com.android.internal.annotations.VisibleForTesting;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.Supplier;
/**
* Defines behavior for handling interactions between power button events and fingerprint-related
* operations, for devices where the fingerprint sensor (side fps) lives on the power button.
*/
-public class SideFpsEventHandler {
+public class SideFpsEventHandler implements View.OnClickListener {
private static final int DEBOUNCE_DELAY_MILLIS = 500;
- private int getTapWaitForPowerDuration(Context context) {
- int tap = context.getResources().getInteger(
- R.integer.config_sidefpsEnrollPowerPressWindow);
- if (Build.isDebuggable()) {
- tap = Settings.Secure.getIntForUser(context.getContentResolver(),
- Settings.Secure.FINGERPRINT_SIDE_FPS_ENROLL_TAP_WINDOW, tap,
- UserHandle.USER_CURRENT);
- }
- return tap;
- }
-
private static final String TAG = "SideFpsEventHandler";
- @NonNull private final Context mContext;
- @NonNull private final Handler mHandler;
- @NonNull private final PowerManager mPowerManager;
- @NonNull private final Supplier<AlertDialog.Builder> mDialogSupplier;
- @NonNull private final AtomicBoolean mSideFpsEventHandlerReady;
-
- @Nullable private Dialog mDialog;
+ @NonNull
+ private final Context mContext;
+ @NonNull
+ private final Handler mHandler;
+ @NonNull
+ private final PowerManager mPowerManager;
+ @NonNull
+ private final AtomicBoolean mSideFpsEventHandlerReady;
+ private final int mDismissDialogTimeout;
+ @Nullable
+ private SideFpsToast mDialog;
private final Runnable mTurnOffDialog =
() -> {
dismissDialog("mTurnOffDialog");
};
-
- @NonNull private final DialogInterface.OnDismissListener mDialogDismissListener;
-
private @BiometricStateListener.State int mBiometricState;
- private final int mTapWaitForPowerDuration;
private FingerprintManager mFingerprintManager;
+ private DialogProvider mDialogProvider;
+ private long mLastPowerPressTime;
- SideFpsEventHandler(Context context, Handler handler, PowerManager powerManager) {
- this(context, handler, powerManager, () -> new AlertDialog.Builder(context));
+ SideFpsEventHandler(
+ Context context,
+ Handler handler,
+ PowerManager powerManager) {
+ this(context, handler, powerManager, (ctx) -> {
+ SideFpsToast dialog = new SideFpsToast(ctx);
+ dialog.getWindow()
+ .setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
+ dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+ return dialog;
+ });
}
@VisibleForTesting
@@ -98,23 +93,13 @@ public class SideFpsEventHandler {
Context context,
Handler handler,
PowerManager powerManager,
- Supplier<AlertDialog.Builder> dialogSupplier) {
+ DialogProvider provider) {
mContext = context;
mHandler = handler;
mPowerManager = powerManager;
- mDialogSupplier = dialogSupplier;
mBiometricState = STATE_IDLE;
mSideFpsEventHandlerReady = new AtomicBoolean(false);
- mDialogDismissListener =
- (dialog) -> {
- if (mDialog == dialog) {
- if (mHandler != null) {
- mHandler.removeCallbacks(mTurnOffDialog);
- }
- mDialog = null;
- }
- };
-
+ mDialogProvider = provider;
// ensure dialog is dismissed if screen goes off for unrelated reasons
context.registerReceiver(
new BroadcastReceiver() {
@@ -127,7 +112,13 @@ public class SideFpsEventHandler {
}
},
new IntentFilter(Intent.ACTION_SCREEN_OFF));
- mTapWaitForPowerDuration = getTapWaitForPowerDuration(context);
+ mDismissDialogTimeout = context.getResources().getInteger(
+ R.integer.config_sideFpsToastTimeout);
+ }
+
+ @Override
+ public void onClick(View v) {
+ goToSleep(mLastPowerPressTime);
}
/**
@@ -165,8 +156,9 @@ public class SideFpsEventHandler {
Log.v(TAG, "Detected a tap to turn off dialog, ignoring");
mHandler.removeCallbacks(mTurnOffDialog);
}
+ showDialog(eventTime, "Enroll Power Press");
+ mHandler.postDelayed(mTurnOffDialog, mDismissDialogTimeout);
});
- showDialog(eventTime, "Enroll Power Press");
return true;
case STATE_BP_AUTH:
return true;
@@ -176,54 +168,11 @@ public class SideFpsEventHandler {
}
}
- @NonNull
- private static Dialog showConfirmDialog(
- @NonNull AlertDialog.Builder dialogBuilder,
- @NonNull PowerManager powerManager,
- long eventTime,
- @BiometricStateListener.State int biometricState,
- @NonNull DialogInterface.OnDismissListener dismissListener) {
- final boolean enrolling = biometricState == STATE_ENROLLING;
- final int title =
- enrolling
- ? R.string.fp_power_button_enrollment_title
- : R.string.fp_power_button_bp_title;
- final int message =
- enrolling
- ? R.string.fp_power_button_enrollment_message
- : R.string.fp_power_button_bp_message;
- final int positiveText =
- enrolling
- ? R.string.fp_power_button_enrollment_positive_button
- : R.string.fp_power_button_bp_positive_button;
- final int negativeText =
- enrolling
- ? R.string.fp_power_button_enrollment_negative_button
- : R.string.fp_power_button_bp_negative_button;
-
- final Dialog confirmScreenOffDialog =
- dialogBuilder
- .setTitle(title)
- .setMessage(message)
- .setPositiveButton(
- positiveText,
- (dialog, which) -> {
- dialog.dismiss();
- powerManager.goToSleep(
- eventTime,
- PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
- 0 /* flags */);
- })
- .setNegativeButton(negativeText, (dialog, which) -> dialog.dismiss())
- .setOnDismissListener(dismissListener)
- .setCancelable(false)
- .create();
- confirmScreenOffDialog
- .getWindow()
- .setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
- confirmScreenOffDialog.show();
-
- return confirmScreenOffDialog;
+ private void goToSleep(long eventTime) {
+ mPowerManager.goToSleep(
+ eventTime,
+ PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
+ 0 /* flags */);
}
/**
@@ -247,7 +196,8 @@ public class SideFpsEventHandler {
if (fingerprintManager.isPowerbuttonFps()) {
fingerprintManager.registerBiometricStateListener(
new BiometricStateListener() {
- @Nullable private Runnable mStateRunnable = null;
+ @Nullable
+ private Runnable mStateRunnable = null;
@Override
public void onStateChanged(
@@ -281,13 +231,6 @@ public class SideFpsEventHandler {
public void onBiometricAction(
@BiometricStateListener.Action int action) {
Log.d(TAG, "onBiometricAction " + action);
- switch (action) {
- case BiometricStateListener.ACTION_SENSOR_TOUCH:
- mHandler.postDelayed(
- mTurnOffDialog,
- mTapWaitForPowerDuration);
- break;
- }
}
});
mSideFpsEventHandlerReady.set(true);
@@ -309,12 +252,13 @@ public class SideFpsEventHandler {
Log.d(TAG, "Ignoring show dialog");
return;
}
- mDialog =
- showConfirmDialog(
- mDialogSupplier.get(),
- mPowerManager,
- time,
- mBiometricState,
- mDialogDismissListener);
+ mDialog = mDialogProvider.provideDialog(mContext);
+ mLastPowerPressTime = time;
+ mDialog.show();
+ mDialog.setOnClickListener(this);
+ }
+
+ interface DialogProvider {
+ SideFpsToast provideDialog(Context context);
}
-}
+} \ No newline at end of file
diff --git a/services/core/java/com/android/server/policy/SideFpsToast.java b/services/core/java/com/android/server/policy/SideFpsToast.java
new file mode 100644
index 000000000000..db074670de9b
--- /dev/null
+++ b/services/core/java/com/android/server/policy/SideFpsToast.java
@@ -0,0 +1,69 @@
+/*
+ * 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.policy;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.Button;
+
+import com.android.internal.R;
+
+/**
+ * Toast for side fps. This is typically shown during enrollment
+ * when a user presses the power button.
+ *
+ * This dialog is used by {@link SideFpsEventHandler}
+ */
+public class SideFpsToast extends Dialog {
+
+ SideFpsToast(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.side_fps_toast);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ final Window window = this.getWindow();
+ WindowManager.LayoutParams windowParams = window.getAttributes();
+ windowParams.dimAmount = 0;
+ windowParams.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
+ windowParams.gravity = Gravity.BOTTOM;
+ window.setAttributes(windowParams);
+ }
+
+ /**
+ * Sets the onClickListener for the toast dialog.
+ * @param listener
+ */
+ public void setOnClickListener(View.OnClickListener listener) {
+ final Button turnOffScreen = findViewById(R.id.turn_off_screen);
+ if (turnOffScreen != null) {
+ turnOffScreen.setOnClickListener(listener);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index f1dbad61a76d..c04b19558770 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -30,6 +30,7 @@ import static com.android.server.accessibility.AccessibilityTraceFileProto.ENTRY
import static com.android.server.accessibility.AccessibilityTraceFileProto.MAGIC_NUMBER;
import static com.android.server.accessibility.AccessibilityTraceFileProto.MAGIC_NUMBER_H;
import static com.android.server.accessibility.AccessibilityTraceFileProto.MAGIC_NUMBER_L;
+import static com.android.server.accessibility.AccessibilityTraceFileProto.REAL_TO_ELAPSED_TIME_OFFSET_NANOS;
import static com.android.server.accessibility.AccessibilityTraceProto.ACCESSIBILITY_SERVICE;
import static com.android.server.accessibility.AccessibilityTraceProto.CALENDAR_TIME;
import static com.android.server.accessibility.AccessibilityTraceProto.CALLING_PARAMS;
@@ -116,6 +117,7 @@ import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.TimeUnit;
/**
* This class contains the accessibility related logic of the window manager.
@@ -2215,6 +2217,11 @@ final class AccessibilityController {
try {
ProtoOutputStream proto = new ProtoOutputStream();
proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
+ long timeOffsetNs =
+ TimeUnit.NANOSECONDS.convert(System.currentTimeMillis(),
+ TimeUnit.NANOSECONDS)
+ - SystemClock.elapsedRealtimeNanos();
+ proto.write(REAL_TO_ELAPSED_TIME_OFFSET_NANOS, timeOffsetNs);
mBuffer.writeTraceToFile(mTraceFile, proto);
} catch (IOException e) {
Slog.e(TAG, "Unable to write buffer to file", e);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 135cf5d59dc4..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;
@@ -336,6 +341,7 @@ import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity;
import com.android.internal.content.ReferrerIntent;
+import com.android.internal.os.TimeoutRecord;
import com.android.internal.os.TransferPipe;
import com.android.internal.policy.AttributeCache;
import com.android.internal.protolog.common.ProtoLog;
@@ -6734,7 +6740,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* @param windowPid The pid of the window input dispatching timed out on.
* @return True if input dispatching should be aborted.
*/
- public boolean inputDispatchingTimedOut(String reason, int windowPid) {
+ public boolean inputDispatchingTimedOut(TimeoutRecord timeoutRecord, int windowPid) {
ActivityRecord anrActivity;
WindowProcessController anrApp;
boolean blameActivityProcess;
@@ -6748,12 +6754,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (blameActivityProcess) {
return mAtmService.mAmInternal.inputDispatchingTimedOut(anrApp.mOwner,
anrActivity.shortComponentName, anrActivity.info.applicationInfo,
- shortComponentName, app, false, reason);
+ shortComponentName, app, false, timeoutRecord);
} else {
// In this case another process added windows using this activity token. So, we call the
// generic service input dispatch timed out method so that the right process is blamed.
long timeoutMillis = mAtmService.mAmInternal.inputDispatchingTimedOut(
- windowPid, false /* aboveSystem */, reason);
+ windowPid, false /* aboveSystem */, timeoutRecord);
return timeoutMillis <= 0;
}
}
@@ -8750,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/AnrController.java b/services/core/java/com/android/server/wm/AnrController.java
index 1ed5a851549c..e0ac37ae0fe4 100644
--- a/services/core/java/com/android/server/wm/AnrController.java
+++ b/services/core/java/com/android/server/wm/AnrController.java
@@ -31,6 +31,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.view.InputApplicationHandle;
+import com.android.internal.os.TimeoutRecord;
import com.android.server.am.ActivityManagerService;
import com.android.server.criticalevents.CriticalEventLog;
@@ -60,7 +61,8 @@ class AnrController {
mService = service;
}
- void notifyAppUnresponsive(InputApplicationHandle applicationHandle, String reason) {
+ void notifyAppUnresponsive(InputApplicationHandle applicationHandle,
+ TimeoutRecord timeoutRecord) {
preDumpIfLockTooSlow();
final ActivityRecord activity;
synchronized (mService.mGlobalLock) {
@@ -70,31 +72,31 @@ class AnrController {
+ ". Dropping notifyNoFocusedWindowAnr request");
return;
}
- Slog.i(TAG_WM, "ANR in " + activity.getName() + ". Reason: " + reason);
- dumpAnrStateLocked(activity, null /* windowState */, reason);
+ Slog.i(TAG_WM, "ANR in " + activity.getName() + ". Reason: " + timeoutRecord.mReason);
+ dumpAnrStateLocked(activity, null /* windowState */, timeoutRecord.mReason);
mUnresponsiveAppByDisplay.put(activity.getDisplayId(), activity);
}
- activity.inputDispatchingTimedOut(reason, INVALID_PID);
+ activity.inputDispatchingTimedOut(timeoutRecord, INVALID_PID);
}
/**
* Notify a window was unresponsive.
*
- * @param token - the input token of the window
- * @param pid - the pid of the window, if known
- * @param reason - the reason for the window being unresponsive
+ * @param token - the input token of the window
+ * @param pid - the pid of the window, if known
+ * @param timeoutRecord - details for the timeout
*/
void notifyWindowUnresponsive(@NonNull IBinder token, @NonNull OptionalInt pid,
- @NonNull String reason) {
- if (notifyWindowUnresponsive(token, reason)) {
+ @NonNull TimeoutRecord timeoutRecord) {
+ if (notifyWindowUnresponsive(token, timeoutRecord)) {
return;
}
if (!pid.isPresent()) {
Slog.w(TAG_WM, "Failed to notify that window token=" + token + " was unresponsive.");
return;
}
- notifyWindowUnresponsive(pid.getAsInt(), reason);
+ notifyWindowUnresponsive(pid.getAsInt(), timeoutRecord);
}
/**
@@ -103,7 +105,8 @@ class AnrController {
* @return true if the window was identified by the given input token and the request was
* handled, false otherwise.
*/
- private boolean notifyWindowUnresponsive(@NonNull IBinder inputToken, String reason) {
+ private boolean notifyWindowUnresponsive(@NonNull IBinder inputToken,
+ TimeoutRecord timeoutRecord) {
preDumpIfLockTooSlow();
final int pid;
final boolean aboveSystem;
@@ -119,14 +122,14 @@ class AnrController {
// embedded, then we will blame the pid instead.
activity = (windowState.mInputChannelToken == inputToken)
? windowState.mActivityRecord : null;
- Slog.i(TAG_WM, "ANR in " + target + ". Reason:" + reason);
+ Slog.i(TAG_WM, "ANR in " + target + ". Reason:" + timeoutRecord.mReason);
aboveSystem = isWindowAboveSystem(windowState);
- dumpAnrStateLocked(activity, windowState, reason);
+ dumpAnrStateLocked(activity, windowState, timeoutRecord.mReason);
}
if (activity != null) {
- activity.inputDispatchingTimedOut(reason, pid);
+ activity.inputDispatchingTimedOut(timeoutRecord, pid);
} else {
- mService.mAmInternal.inputDispatchingTimedOut(pid, aboveSystem, reason);
+ mService.mAmInternal.inputDispatchingTimedOut(pid, aboveSystem, timeoutRecord);
}
return true;
}
@@ -134,15 +137,16 @@ class AnrController {
/**
* Notify a window owned by the provided pid was unresponsive.
*/
- private void notifyWindowUnresponsive(int pid, String reason) {
+ private void notifyWindowUnresponsive(int pid, TimeoutRecord timeoutRecord) {
+ Slog.i(TAG_WM,
+ "ANR in input window owned by pid=" + pid + ". Reason: " + timeoutRecord.mReason);
synchronized (mService.mGlobalLock) {
- Slog.i(TAG_WM, "ANR in input window owned by pid=" + pid + ". Reason: " + reason);
- dumpAnrStateLocked(null /* activity */, null /* windowState */, reason);
+ dumpAnrStateLocked(null /* activity */, null /* windowState */, timeoutRecord.mReason);
}
// We cannot determine the z-order of the window, so place the anr dialog as high
// as possible.
- mService.mAmInternal.inputDispatchingTimedOut(pid, true /*aboveSystem*/, reason);
+ mService.mAmInternal.inputDispatchingTimedOut(pid, true /*aboveSystem*/, timeoutRecord);
}
/**
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index 5d2d5826f227..acbf1a4cd8f1 100644
--- a/services/core/java/com/android/server/wm/ContentRecorder.java
+++ b/services/core/java/com/android/server/wm/ContentRecorder.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY;
import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK;
@@ -26,6 +27,7 @@ import android.annotation.Nullable;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
+import android.media.projection.MediaProjectionManager;
import android.os.IBinder;
import android.provider.DeviceConfig;
import android.view.ContentRecordingSession;
@@ -38,7 +40,7 @@ import com.android.internal.protolog.common.ProtoLog;
/**
* Manages content recording for a particular {@link DisplayContent}.
*/
-final class ContentRecorder {
+final class ContentRecorder implements WindowContainerListener {
/**
* The key for accessing the device config that controls if task recording is supported.
@@ -51,6 +53,8 @@ final class ContentRecorder {
@NonNull
private final DisplayContent mDisplayContent;
+ @Nullable private final MediaProjectionManagerWrapper mMediaProjectionManager;
+
/**
* The session for content recording, or null if this DisplayContent is not being used for
* recording.
@@ -73,8 +77,26 @@ final class ContentRecorder {
*/
@Nullable private Rect mLastRecordedBounds = null;
+ /**
+ * The last configuration orientation.
+ */
+ private int mLastOrientation = ORIENTATION_UNDEFINED;
+
ContentRecorder(@NonNull DisplayContent displayContent) {
+ this(displayContent, () -> {
+ MediaProjectionManager mpm = displayContent.mWmService.mContext.getSystemService(
+ MediaProjectionManager.class);
+ if (mpm != null) {
+ mpm.stopActiveProjection();
+ }
+ });
+ }
+
+ @VisibleForTesting
+ ContentRecorder(@NonNull DisplayContent displayContent,
+ @NonNull MediaProjectionManagerWrapper mediaProjectionManager) {
mDisplayContent = displayContent;
+ mMediaProjectionManager = mediaProjectionManager;
}
/**
@@ -87,6 +109,10 @@ final class ContentRecorder {
mContentRecordingSession = session;
}
+ boolean isContentRecordingSessionSet() {
+ return mContentRecordingSession != null;
+ }
+
/**
* Returns {@code true} if this DisplayContent is currently recording.
*/
@@ -95,7 +121,7 @@ final class ContentRecorder {
}
/**
- * Start recording if this DisplayContent no longer has content. Stop recording if it now
+ * Start recording if this DisplayContent no longer has content. Pause recording if it now
* has content or the display is not on.
*/
@VisibleForTesting void updateRecording() {
@@ -187,7 +213,7 @@ final class ContentRecorder {
/**
* Stops recording on this DisplayContent, and updates the session details.
*/
- void remove() {
+ void stopRecording() {
if (mRecordedSurface != null) {
// Do not wait for the mirrored surface to be garbage collected, but clean up
// immediately.
@@ -195,7 +221,20 @@ final class ContentRecorder {
mRecordedSurface = null;
clearContentRecordingSession();
// Do not need to force remove the VirtualDisplay; this is handled by the media
- // projection service.
+ // projection service when the display is removed.
+ }
+ }
+
+
+ /**
+ * Ensure recording does not fall back to the display stack; ensure the recording is stopped
+ * and the client notified by tearing down the virtual display.
+ */
+ void stopMediaProjection() {
+ ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
+ "Stop MediaProjection on virtual display %d", mDisplayContent.getDisplayId());
+ if (mMediaProjectionManager != null) {
+ mMediaProjectionManager.stopActiveProjection();
}
}
@@ -299,8 +338,7 @@ final class ContentRecorder {
mDisplayContent.mWmService.mWindowContextListenerController.getContainer(
tokenToRecord);
if (wc == null) {
- // Un-set the window token to record for this VirtualDisplay. Fall back to
- // Display stack capture for the entire display.
+ // Fall back to screenrecording using the data sent to DisplayManager
mDisplayContent.mWmService.mDisplayManagerInternal.setWindowManagerMirroring(
mDisplayContent.getDisplayId(), false);
handleStartRecordingFailed();
@@ -326,6 +364,8 @@ final class ContentRecorder {
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
"Unable to retrieve task to start recording for "
+ "display %d", mDisplayContent.getDisplayId());
+ } else {
+ taskToRecord.registerWindowContainerListener(this);
}
return taskToRecord;
default:
@@ -342,9 +382,9 @@ final class ContentRecorder {
/**
* Exit this recording session.
* <p>
- * If this is a task session, tear down the recording entirely. Do not fall back
- * to recording the entire display on the display stack; this would surprise the user
- * given they selected task capture.
+ * If this is a task session, stop the recording entirely, including the MediaProjection.
+ * Do not fall back to recording the entire display on the display stack; this would surprise
+ * the user given they selected task capture.
* </p><p>
* If this is a display session, just stop recording by layer mirroring. Fall back to recording
* from the display stack.
@@ -353,26 +393,15 @@ final class ContentRecorder {
private void handleStartRecordingFailed() {
final boolean shouldExitTaskRecording = mContentRecordingSession != null
&& mContentRecordingSession.getContentToRecord() == RECORD_CONTENT_TASK;
+ clearContentRecordingSession();
if (shouldExitTaskRecording) {
- // Clean up the cached session first, since tearing down the display will generate
- // display
- // events which will trickle back to here.
- clearContentRecordingSession();
- tearDownVirtualDisplay();
- } else {
- clearContentRecordingSession();
+ // Clean up the cached session first to ensure recording doesn't re-start, since
+ // tearing down the display will generate display events which will trickle back here.
+ stopMediaProjection();
}
}
/**
- * Ensure recording does not fall back to the display stack; ensure the recording is stopped
- * and the client notified by tearing down the virtual display.
- */
- private void tearDownVirtualDisplay() {
- // TODO(b/219761722) Clean up the VirtualDisplay if task mirroring fails
- }
-
- /**
* Apply transformations to the mirrored surface to ensure the captured contents are scaled to
* fit and centred in the output surface.
*
@@ -442,4 +471,37 @@ final class ContentRecorder {
}
return surfaceSize;
}
+
+ // WindowContainerListener
+ @Override
+ public void onRemoved() {
+ ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
+ "Recorded task is removed, so stop recording on display %d",
+ mDisplayContent.getDisplayId());
+ Task recordedTask = mRecordedWindowContainer.asTask();
+ if (recordedTask == null
+ || mContentRecordingSession.getContentToRecord() != RECORD_CONTENT_TASK) {
+ return;
+ }
+ recordedTask.unregisterWindowContainerListener(this);
+ // Stop mirroring and teardown.
+ clearContentRecordingSession();
+ // Clean up the cached session first to ensure recording doesn't re-start, since
+ // tearing down the display will generate display events which will trickle back here.
+ stopMediaProjection();
+ }
+
+ // WindowContainerListener
+ @Override
+ public void onMergedOverrideConfigurationChanged(
+ Configuration mergedOverrideConfiguration) {
+ WindowContainerListener.super.onMergedOverrideConfigurationChanged(
+ mergedOverrideConfiguration);
+ onConfigurationChanged(mLastOrientation);
+ mLastOrientation = mergedOverrideConfiguration.orientation;
+ }
+
+ @VisibleForTesting interface MediaProjectionManagerWrapper {
+ void stopActiveProjection();
+ }
}
diff --git a/services/core/java/com/android/server/wm/ContentRecordingController.java b/services/core/java/com/android/server/wm/ContentRecordingController.java
index fff7637acc7e..1efc202a49ae 100644
--- a/services/core/java/com/android/server/wm/ContentRecordingController.java
+++ b/services/core/java/com/android/server/wm/ContentRecordingController.java
@@ -56,14 +56,13 @@ final class ContentRecordingController {
* Updates the current recording session. If a new display is taking over recording, then
* stops the prior display from recording.
*
- * @param incomingSession the new recording session. Should either be {@code null}, to stop
- * the current session, or a session on a new/different display than the
- * current session.
+ * @param incomingSession the new recording session. Should either have a {@code null} token, to
+ * stop the current session, or a session on a new/different display
+ * than the current session.
* @param wmService the window manager service
*/
void setContentRecordingSessionLocked(@Nullable ContentRecordingSession incomingSession,
@NonNull WindowManagerService wmService) {
- // TODO(b/219761722) handle a null session arriving due to task setup failing
if (incomingSession != null && (!ContentRecordingSession.isValid(incomingSession)
|| ContentRecordingSession.isSameDisplay(mSession, incomingSession))) {
// Ignore an invalid session, or a session for the same display as currently recording.
@@ -82,8 +81,7 @@ final class ContentRecordingController {
}
if (mSession != null) {
// Update the pre-existing display about the new session.
- ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
- "Pause the recording session on display %s",
+ ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, "Pause the recording session on display %s",
mDisplayContent.getDisplayId());
mDisplayContent.pauseRecording();
mDisplayContent.setContentRecordingSession(null);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index b54cd415d8df..fccf54d83198 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -4416,13 +4416,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;
}
}
@@ -6075,7 +6082,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mRemoved = true;
if (mContentRecorder != null) {
- mContentRecorder.remove();
+ mContentRecorder.stopRecording();
}
// Only update focus/visibility for the last one because there may be many root tasks are
@@ -6356,6 +6363,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
/**
+ * The MediaProjection instance is torn down.
+ */
+ @VisibleForTesting void stopMediaProjection() {
+ if (mContentRecorder != null) {
+ mContentRecorder.stopMediaProjection();
+ }
+ }
+
+ /**
* Sets the incoming recording session. Should only be used when starting to record on
* this display; stopping recording is handled separately when the display is destroyed.
*
@@ -6366,11 +6382,73 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
/**
+ * This is to enable mirroring on virtual displays that specify the
+ * {@link android.hardware.display.DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR} but don't
+ * mirror using MediaProjection. When done through MediaProjection API, the
+ * ContentRecordingSession will be created automatically.
+ *
+ * This should only be called when there's no ContentRecordingSession already set for this
+ * display. The code will ask DMS if this display should enable display mirroring and which
+ * displayId to mirror from.
+ *
+ * @return true if the {@link ContentRecordingSession} was set for display mirroring using data
+ * from DMS, false if there was no ContentRecordingSession created.
+ */
+ boolean setDisplayMirroring() {
+ int mirrorDisplayId = mWmService.mDisplayManagerInternal.getDisplayIdToMirror(mDisplayId);
+ if (mirrorDisplayId == INVALID_DISPLAY) {
+ return false;
+ }
+
+ if (mirrorDisplayId == mDisplayId) {
+ if (mDisplayId != DEFAULT_DISPLAY) {
+ ProtoLog.w(WM_DEBUG_CONTENT_RECORDING,
+ "Attempting to mirror self on %d", mirrorDisplayId);
+ }
+ return false;
+ }
+
+ // This is very unlikely, and probably impossible, but if the current display is
+ // DEFAULT_DISPLAY and the displayId to mirror results in an invalid display, we don't want
+ // to mirror the DEFAULT_DISPLAY so instead we just return
+ DisplayContent mirrorDc = mRootWindowContainer.getDisplayContentOrCreate(mirrorDisplayId);
+ if (mirrorDc == null && mDisplayId == DEFAULT_DISPLAY) {
+ ProtoLog.w(WM_DEBUG_CONTENT_RECORDING, "Found no matching mirror display for id=%d for"
+ + " DEFAULT_DISPLAY. Nothing to mirror.", mirrorDisplayId);
+ return false;
+ }
+
+ if (mirrorDc == null) {
+ mirrorDc = mRootWindowContainer.getDefaultDisplay();
+ ProtoLog.w(WM_DEBUG_CONTENT_RECORDING,
+ "Attempting to mirror %d from %d but no DisplayContent associated. Changing "
+ + "to mirror default display.",
+ mirrorDisplayId, mDisplayId);
+ }
+
+ ContentRecordingSession session = ContentRecordingSession
+ .createDisplaySession(mirrorDc.getDisplayUiContext().getWindowContextToken())
+ .setDisplayId(mDisplayId);
+ setContentRecordingSession(session);
+ ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
+ "Successfully created a ContentRecordingSession for displayId=%d to mirror "
+ + "content from displayId=%d",
+ mDisplayId, mirrorDisplayId);
+ return true;
+ }
+
+ /**
* Start recording if this DisplayContent no longer has content. Stop recording if it now
* has content or the display is not on.
*/
@VisibleForTesting void updateRecording() {
- getContentRecorder().updateRecording();
+ if (mContentRecorder == null || !mContentRecorder.isContentRecordingSessionSet()) {
+ if (!setDisplayMirroring()) {
+ return;
+ }
+ }
+
+ mContentRecorder.updateRecording();
}
/**
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 33cdd2e98113..1e9d451b1a69 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -36,6 +36,7 @@ import android.view.SurfaceControl;
import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants;
+import com.android.internal.os.TimeoutRecord;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.input.InputManagerService;
@@ -95,14 +96,17 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal
*/
@Override
public void notifyNoFocusedWindowAnr(@NonNull InputApplicationHandle applicationHandle) {
- mService.mAnrController.notifyAppUnresponsive(
- applicationHandle, "Application does not have a focused window");
+ TimeoutRecord timeoutRecord = TimeoutRecord.forInputDispatchNoFocusedWindow(
+ timeoutMessage("Application does not have a focused window"));
+ mService.mAnrController.notifyAppUnresponsive(applicationHandle, timeoutRecord);
}
@Override
public void notifyWindowUnresponsive(@NonNull IBinder token, @NonNull OptionalInt pid,
- @NonNull String reason) {
- mService.mAnrController.notifyWindowUnresponsive(token, pid, reason);
+ String reason) {
+ TimeoutRecord timeoutRecord = TimeoutRecord.forInputDispatchWindowUnresponsive(
+ timeoutMessage(reason));
+ mService.mAnrController.notifyWindowUnresponsive(token, pid, timeoutRecord);
}
@Override
@@ -341,6 +345,13 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal
mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen);
}
+ private String timeoutMessage(String reason) {
+ if (reason == null) {
+ return "Input dispatching timed out";
+ }
+ return "Input dispatching timed out (" + reason + ")";
+ }
+
void dump(PrintWriter pw, String prefix) {
if (mInputFreezeReason != null) {
pw.println(prefix + "mInputFreezeReason=" + mInputFreezeReason);
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/Task.java b/services/core/java/com/android/server/wm/Task.java
index 8de556ac4667..0332935348e9 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -489,6 +489,7 @@ class Task extends TaskFragment {
static final int FLAG_FORCE_HIDDEN_FOR_PINNED_TASK = 1;
static final int FLAG_FORCE_HIDDEN_FOR_TASK_ORG = 1 << 1;
private int mForceHiddenFlags = 0;
+ private boolean mForceTranslucent = false;
// TODO(b/160201781): Revisit double invocation issue in Task#removeChild.
/**
@@ -4342,6 +4343,10 @@ class Task extends TaskFragment {
return true;
}
+ void setForceTranslucent(boolean set) {
+ mForceTranslucent = set;
+ }
+
@Override
public boolean isAlwaysOnTop() {
return !isForceHidden() && super.isAlwaysOnTop();
@@ -4360,6 +4365,11 @@ class Task extends TaskFragment {
}
@Override
+ protected boolean isForceTranslucent() {
+ return mForceTranslucent;
+ }
+
+ @Override
long getProtoFieldId() {
return TASK;
}
@@ -5333,7 +5343,7 @@ class Task extends TaskFragment {
abort = true;
}
if (abort) {
- Slog.e(TAG, "Cannot navigateUpTo, intent =" + destIntent);
+ android.util.EventLog.writeEvent(0x534e4554, "238605611", callingUid, "");
foundParentInTask = false;
} else {
parent.deliverNewIntentLocked(callingUid, destIntent, destGrants,
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index e6afdd6c5876..c1f06e442294 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -740,6 +740,10 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return false;
}
+ protected boolean isForceTranslucent() {
+ return false;
+ }
+
boolean isLeafTaskFragment() {
for (int i = mChildren.size() - 1; i >= 0; --i) {
if (mChildren.get(i).asTaskFragment() != null) {
@@ -865,7 +869,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
*/
@VisibleForTesting
boolean isTranslucent(ActivityRecord starting) {
- if (!isAttached() || isForceHidden()) {
+ if (!isAttached() || isForceHidden() || isForceTranslucent()) {
return true;
}
final PooledPredicate p = PooledLambda.obtainPredicate(TaskFragment::isOpaqueActivity,
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 6121ad4da05f..da5f5d0e29fd 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -882,6 +882,8 @@ public abstract class WindowManagerInternal {
* Must be invoked for a valid MediaProjection session.
*
* @param incomingSession the nullable incoming content recording session
+ * @return {@code true} if successfully set the session, or {@code false} if the session
+ * could not be prepared and the session needs to be torn down.
*/
- public abstract void setContentRecordingSession(ContentRecordingSession incomingSession);
+ public abstract boolean setContentRecordingSession(ContentRecordingSession incomingSession);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 79037ab6a787..fd54f78b22fe 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -899,6 +899,11 @@ public class WindowManagerService extends IWindowManager.Stub
mMaximumObscuringOpacityForTouch = Settings.Global.getFloat(resolver,
Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH,
InputManager.DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH);
+ if (mMaximumObscuringOpacityForTouch < 0.0f
+ || mMaximumObscuringOpacityForTouch > 1.0f) {
+ mMaximumObscuringOpacityForTouch =
+ InputManager.DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH;
+ }
}
void updateSystemUiSettings(boolean handleChange) {
@@ -8286,14 +8291,15 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public void setContentRecordingSession(@Nullable ContentRecordingSession incomingSession) {
+ public boolean setContentRecordingSession(
+ @Nullable ContentRecordingSession incomingSession) {
synchronized (mGlobalLock) {
- // Allow the controller to handle teardown or a non-task session.
+ // Allow the controller to handle teardown of a non-task session.
if (incomingSession == null
|| incomingSession.getContentToRecord() != RECORD_CONTENT_TASK) {
mContentRecordingController.setContentRecordingSessionLocked(incomingSession,
WindowManagerService.this);
- return;
+ return true;
}
// For a task session, find the activity identified by the launch cookie.
final WindowContainerToken wct = getTaskWindowContainerTokenForLaunchCookie(
@@ -8301,15 +8307,14 @@ public class WindowManagerService extends IWindowManager.Stub
if (wct == null) {
Slog.w(TAG, "Handling a new recording session; unable to find the "
+ "WindowContainerToken");
- mContentRecordingController.setContentRecordingSessionLocked(null,
- WindowManagerService.this);
- return;
+ return false;
}
// Replace the launch cookie in the session details with the task's
// WindowContainerToken.
incomingSession.setTokenToRecord(wct.asBinder());
mContentRecordingController.setContentRecordingSessionLocked(incomingSession,
WindowManagerService.this);
+ return true;
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 95db746698d5..3b9cd368a93b 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -646,6 +646,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
}
+ if ((c.getChangeMask()
+ & WindowContainerTransaction.Change.CHANGE_FORCE_TRANSLUCENT) != 0) {
+ tr.setForceTranslucent(c.getForceTranslucent());
+ effects = TRANSACT_EFFECTS_LIFECYCLE;
+ }
+
final int childWindowingMode = c.getActivityWindowingMode();
if (childWindowingMode > -1) {
tr.setActivityWindowingMode(childWindowingMode);
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
index ca834c56e0f0..efcca5d28416 100644
--- a/services/core/java/com/android/server/wm/WindowTracing.java
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -22,6 +22,7 @@ import static com.android.server.wm.WindowManagerTraceFileProto.ENTRY;
import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER;
import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_H;
import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_L;
+import static com.android.server.wm.WindowManagerTraceFileProto.REAL_TO_ELAPSED_TIME_OFFSET_NANOS;
import static com.android.server.wm.WindowManagerTraceProto.ELAPSED_REALTIME_NANOS;
import static com.android.server.wm.WindowManagerTraceProto.WHERE;
import static com.android.server.wm.WindowManagerTraceProto.WINDOW_MANAGER_SERVICE;
@@ -40,6 +41,7 @@ import com.android.internal.util.TraceBuffer;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
+import java.util.concurrent.TimeUnit;
/**
* A class that allows window manager to dump its state continuously to a trace file, such that a
@@ -352,6 +354,10 @@ class WindowTracing {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeTraceToFileLocked");
ProtoOutputStream proto = new ProtoOutputStream();
proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
+ long timeOffsetNs =
+ TimeUnit.NANOSECONDS.convert(System.currentTimeMillis(), TimeUnit.NANOSECONDS)
+ - SystemClock.elapsedRealtimeNanos();
+ proto.write(REAL_TO_ELAPSED_TIME_OFFSET_NANOS, timeOffsetNs);
mBuffer.writeTraceToFile(mTraceFile, proto);
} catch (IOException e) {
Log.e(TAG, "Unable to write buffer to file", e);
diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
index 2219d477630e..e2f56ba56f3d 100644
--- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
@@ -301,6 +301,35 @@ public class BackupManagerServiceRoboTest {
verify(mUserOneService, never()).initializeTransports(transports, /* observer */ null);
}
+ /**
+ * Test that the backup services throws a {@link SecurityException} if the caller does not have
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testIsUserReadyForBackup_withoutPermission_throwsSecurityException() {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ expectThrows(
+ SecurityException.class,
+ () -> backupManagerService.isUserReadyForBackup(mUserOneId));
+ }
+
+ /**
+ * Test that the backup service does not throw a {@link SecurityException} if the caller has
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testIsUserReadyForBackup_withPermission_callsMethodForUser() {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, UserHandle.USER_SYSTEM, mUserSystemService);
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ true);
+
+ assertThat(backupManagerService.isUserReadyForBackup(mUserOneId)).isTrue();
+ }
+
/** Test that the backup service routes methods correctly to the user that requests it. */
@Test
public void testClearBackupData_onRegisteredUser_callsMethodForUser() throws Exception {
diff --git a/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java b/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java
index 8a9845b1c2d2..b25f265c16dc 100644
--- a/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/TransportManagerTest.java
@@ -18,6 +18,7 @@ package com.android.server.backup;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
import static com.android.server.backup.testing.TransportData.genericTransport;
@@ -326,7 +327,25 @@ public class TransportManagerTest {
.setApplicationEnabledSetting(
PACKAGE_A,
Integer.valueOf(COMPONENT_ENABLED_STATE_DISABLED),
- 0 /*flags*/);
+ /* flags */ 0);
+ transportManager.onPackageChanged(PACKAGE_A, PACKAGE_A);
+
+ assertRegisteredTransports(transportManager, singletonList(mTransportB1));
+ verify(mListener, never()).onTransportRegistered(any(), any());
+ }
+
+ @Test
+ public void testOnPackageChanged_whenPackageChanged_packageDisabledByUserUnregistersTransport()
+ throws Exception {
+ TransportManager transportManager =
+ createTransportManagerWithRegisteredTransports(mTransportA1, mTransportB1);
+ reset(mListener);
+
+ mContext.getPackageManager()
+ .setApplicationEnabledSetting(
+ PACKAGE_A,
+ Integer.valueOf(COMPONENT_ENABLED_STATE_DISABLED_USER),
+ /* flags */ 0);
transportManager.onPackageChanged(PACKAGE_A, PACKAGE_A);
assertRegisteredTransports(transportManager, singletonList(mTransportB1));
@@ -344,7 +363,7 @@ public class TransportManagerTest {
.setApplicationEnabledSetting(
PACKAGE_A,
Integer.valueOf(COMPONENT_ENABLED_STATE_DISABLED),
- 0 /*flags*/);
+ /* flags */ 0);
transportManager.onPackageChanged(PACKAGE_A, PACKAGE_A);
assertRegisteredTransports(transportManager, singletonList(mTransportB1));
@@ -354,7 +373,7 @@ public class TransportManagerTest {
.setApplicationEnabledSetting(
PACKAGE_A,
Integer.valueOf(COMPONENT_ENABLED_STATE_ENABLED),
- 0 /*flags*/);
+ /* flags */ 0);
transportManager.onPackageChanged(PACKAGE_A, PACKAGE_A);
assertRegisteredTransports(transportManager, asList(mTransportA1, mTransportB1));
@@ -363,7 +382,7 @@ public class TransportManagerTest {
}
@Test
- public void testOnPackageChanged_whenPackageChanged_unknownComponentStateIsIgnored()
+ public void testOnPackageChanged_whenPackageChanged_packageDefaultEnabledRegistersTransport()
throws Exception {
TransportManager transportManager =
createTransportManagerWithRegisteredTransports(mTransportA1, mTransportB1);
@@ -372,12 +391,23 @@ public class TransportManagerTest {
mContext.getPackageManager()
.setApplicationEnabledSetting(
PACKAGE_A,
+ Integer.valueOf(COMPONENT_ENABLED_STATE_DISABLED_USER),
+ /* flags */ 0);
+ transportManager.onPackageChanged(PACKAGE_A, PACKAGE_A);
+
+ assertRegisteredTransports(transportManager, singletonList(mTransportB1));
+ verify(mListener, never()).onTransportRegistered(any(), any());
+
+ mContext.getPackageManager()
+ .setApplicationEnabledSetting(
+ PACKAGE_A,
Integer.valueOf(COMPONENT_ENABLED_STATE_DEFAULT),
- 0 /*flags*/);
+ /* flags */ 0);
transportManager.onPackageChanged(PACKAGE_A, PACKAGE_A);
assertRegisteredTransports(transportManager, asList(mTransportA1, mTransportB1));
- verify(mListener, never()).onTransportRegistered(any(), any());
+ verify(mListener)
+ .onTransportRegistered(mTransportA1.transportName, mTransportA1.transportDirName);
}
@Test
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..35e6db947ac3 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,11 +16,15 @@
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.DEFAULT_FLEXIBILITY_DEADLINE;
import static com.android.server.job.controllers.FlexibilityController.FcConstants.KEY_FLEXIBILITY_ENABLED;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_BATTERY_NOT_LOW;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CHARGING;
@@ -67,7 +71,7 @@ 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;
@@ -80,6 +84,8 @@ public class FlexibilityControllerTest {
private Context mContext;
@Mock
private JobSchedulerService mJobSchedulerService;
+ @Mock
+ private PrefetchController mPrefetchController;
@Before
public void setup() {
@@ -117,11 +123,12 @@ 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);
+ mFlexibilityController = new FlexibilityController(mJobSchedulerService,
+ mPrefetchController);
mFcConstants = mFlexibilityController.getFcConstants();
setDeviceConfigBoolean(KEY_FLEXIBILITY_ENABLED, true);
@@ -149,76 +156,202 @@ 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;
}
@Test
- public void testGetNextConstraintDropTimeElapsed() {
+ 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_FLEXIBILITY_DEADLINE,
+ mFlexibilityController.getLifeCycleEndElapsedLocked(js, 100L));
+ }
+
+ @Test
public void testWontStopJobFromRunning() {
JobStatus js = createJobStatus("testWontStopJobFromRunning", createJob(101));
// Stop satisfied constraints from causing a false positive.
@@ -233,8 +366,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 +416,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_FLEXIBILITY_DEADLINE / 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 +460,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 +530,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 +542,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 +559,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 +576,73 @@ public class FlexibilityControllerTest {
}
}
+ @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);
+
+ mFlexibilityController.maybeStartTrackingJobLocked(js, null);
+ mFlexibilityController.mFlexibilityTracker.adjustJobsRequiredConstraints(js, -1);
+
+ when(mPrefetchController.getNextEstimatedLaunchTimeLocked(js)).thenReturn(
+ 1150L + mFlexibilityController.mConstants.PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS);
+
+ 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(0, js.getNumDroppedFlexibleConstraints());
+ assertEquals(650L, mFlexibilityController
+ .getNextConstraintDropTimeElapsedLocked(js));
+ 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/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/accessibility/AccessibilityInteractionControllerNodeRequestsTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInteractionControllerNodeRequestsTest.java
index 842b23c91e41..4a16874c7acf 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInteractionControllerNodeRequestsTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInteractionControllerNodeRequestsTest.java
@@ -17,13 +17,13 @@
package com.android.server.accessibility;
-import static android.view.accessibility.AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
import static android.view.accessibility.AccessibilityNodeInfo.FLAG_PREFETCH_ANCESTORS;
import static android.view.accessibility.AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST;
import static android.view.accessibility.AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST;
import static android.view.accessibility.AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS_HYBRID;
import static android.view.accessibility.AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS;
import static android.view.accessibility.AccessibilityNodeInfo.FLAG_PREFETCH_UNINTERRUPTIBLE;
+import static android.view.accessibility.AccessibilityNodeInfo.FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS;
import static android.view.accessibility.AccessibilityNodeInfo.ROOT_NODE_ID;
import static org.junit.Assert.assertEquals;
@@ -528,7 +528,8 @@ public class AccessibilityInteractionControllerNodeRequestsTest {
// different client that holds different fetch flags for TextView1.
sendNodeRequestToController(nodeId, mMockClientCallback2,
mMockClient2InteractionId,
- FLAG_PREFETCH_SIBLINGS | FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
+ FLAG_PREFETCH_SIBLINGS
+ | FLAG_SERVICE_REQUESTS_INCLUDE_NOT_IMPORTANT_VIEWS
| FLAG_PREFETCH_ANCESTORS);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
index c940ef5c290d..0b84a60ae5a6 100644
--- a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
@@ -37,6 +37,7 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
+import com.android.internal.os.TimeoutRecord;
import com.android.server.appop.AppOpsService;
import com.android.server.wm.WindowProcessController;
@@ -111,12 +112,15 @@ public class AnrHelperTest {
final WindowProcessController parentProcess = mock(WindowProcessController.class);
final boolean aboveSystem = false;
final String annotation = "test";
+ final TimeoutRecord timeoutRecord = TimeoutRecord.forInputDispatchWindowUnresponsive(
+ annotation);
mAnrHelper.appNotResponding(mAnrApp, activityShortComponentName, appInfo,
- parentShortComponentName, parentProcess, aboveSystem, annotation);
+ parentShortComponentName, parentProcess, aboveSystem, timeoutRecord);
verify(mAnrApp.mErrorState, timeout(TIMEOUT_MS)).appNotResponding(
eq(activityShortComponentName), eq(appInfo), eq(parentShortComponentName),
- eq(parentProcess), eq(aboveSystem), eq(annotation), eq(false) /* onlyDumpSelf */);
+ eq(parentProcess), eq(aboveSystem), eq(timeoutRecord),
+ eq(false) /* onlyDumpSelf */);
}
@Test
@@ -129,11 +133,13 @@ public class AnrHelperTest {
processingLatch.await();
return null;
}).when(mAnrApp.mErrorState).appNotResponding(anyString(), any(), any(), any(),
- anyBoolean(), anyString(), anyBoolean());
+ anyBoolean(), any(), anyBoolean());
final ApplicationInfo appInfo = new ApplicationInfo();
+ final TimeoutRecord timeoutRecord = TimeoutRecord.forInputDispatchWindowUnresponsive(
+ "annotation");
final Runnable reportAnr = () -> mAnrHelper.appNotResponding(mAnrApp,
"activityShortComponentName", appInfo, "parentShortComponentName",
- null /* parentProcess */, false /* aboveSystem */, "annotation");
+ null /* parentProcess */, false /* aboveSystem */, timeoutRecord);
reportAnr.run();
// This should be skipped because the pid is pending in queue.
reportAnr.run();
@@ -149,6 +155,6 @@ public class AnrHelperTest {
processingLatch.countDown();
// There is only one ANR reported.
verify(mAnrApp.mErrorState, timeout(TIMEOUT_MS).only()).appNotResponding(
- anyString(), any(), any(), any(), anyBoolean(), anyString(), anyBoolean());
+ anyString(), any(), any(), any(), anyBoolean(), any(), anyBoolean());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java
index 6538a17cf46a..70519e401f45 100644
--- a/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ProcessRecordTests.java
@@ -34,6 +34,7 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.filters.FlakyTest;
+import com.android.internal.os.TimeoutRecord;
import com.android.server.LocalServices;
import com.android.server.wm.ActivityTaskManagerService;
@@ -195,8 +196,9 @@ public class ProcessRecordTests {
private static void appNotResponding(ProcessErrorStateRecord processErrorState,
String annotation) {
+ TimeoutRecord timeoutRecord = TimeoutRecord.forInputDispatchNoFocusedWindow(annotation);
processErrorState.appNotResponding(null /* activityShortComponentName */, null /* aInfo */,
null /* parentShortComponentName */, null /* parentProcess */,
- false /* aboveSystem */, annotation, false /* onlyDumpSelf */);
+ false /* aboveSystem */, timeoutRecord, false /* onlyDumpSelf */);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index c11e2b02cd97..3eb1dea80c7f 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -17,6 +17,7 @@
package com.android.server.display;
import static android.Manifest.permission.ADD_TRUSTED_DISPLAY;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
import static com.android.server.display.VirtualDisplayAdapter.UNIQUE_ID_PREFIX;
@@ -577,6 +578,7 @@ public class DisplayManagerServiceTest {
// This is effectively the DisplayManager service published to ServiceManager.
DisplayManagerService.BinderService binderService = displayManager.new BinderService();
+ DisplayManagerService.LocalService localDisplayManager = displayManager.new LocalService();
final String uniqueId = "uniqueId --- displayIdToMirrorTest";
final int width = 600;
@@ -606,12 +608,58 @@ public class DisplayManagerServiceTest {
displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */);
// The displayId to mirror should be a default display if there is none initially.
- assertEquals(displayManager.getDisplayIdToMirrorInternal(firstDisplayId),
+ assertEquals(localDisplayManager.getDisplayIdToMirror(firstDisplayId),
Display.DEFAULT_DISPLAY);
- assertEquals(displayManager.getDisplayIdToMirrorInternal(secondDisplayId),
+ assertEquals(localDisplayManager.getDisplayIdToMirror(secondDisplayId),
firstDisplayId);
}
+ @Test
+ public void testGetDisplayIdToMirror() throws Exception {
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ registerDefaultDisplays(displayManager);
+
+ // This is effectively the DisplayManager service published to ServiceManager.
+ DisplayManagerService.BinderService binderService = displayManager.new BinderService();
+ DisplayManagerService.LocalService localDisplayManager = displayManager.new LocalService();
+
+ final String uniqueId = "uniqueId --- displayIdToMirrorTest";
+ final int width = 600;
+ final int height = 800;
+ final int dpi = 320;
+
+ when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
+ final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(
+ VIRTUAL_DISPLAY_NAME, width, height, dpi)
+ .setUniqueId(uniqueId)
+ .setFlags(VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
+ final int firstDisplayId = binderService.createVirtualDisplay(builder.build(),
+ mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME);
+
+ // The second virtual display requests to mirror the first virtual display.
+ final String uniqueId2 = "uniqueId --- displayIdToMirrorTest #2";
+ when(mMockAppToken2.asBinder()).thenReturn(mMockAppToken2);
+ final VirtualDisplayConfig.Builder builder2 = new VirtualDisplayConfig.Builder(
+ VIRTUAL_DISPLAY_NAME, width, height, dpi)
+ .setUniqueId(uniqueId2)
+ .setWindowManagerMirroring(true);
+ final int secondDisplayId = binderService.createVirtualDisplay(builder2.build(),
+ mMockAppToken2 /* callback */, null /* projection */,
+ PACKAGE_NAME);
+ displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
+
+ // flush the handler
+ displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */);
+
+ // The displayId to mirror should be a invalid since the display had flag OWN_CONTENT_ONLY
+ assertEquals(localDisplayManager.getDisplayIdToMirror(firstDisplayId),
+ Display.INVALID_DISPLAY);
+ // The second display has mirroring managed by WindowManager so the mirror displayId should
+ // be invalid.
+ assertEquals(localDisplayManager.getDisplayIdToMirror(secondDisplayId),
+ Display.INVALID_DISPLAY);
+ }
+
/**
* Tests that the virtual display is created with
* {@link VirtualDisplayConfig.Builder#setSurface(Surface)}
diff --git a/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubClientBrokerTest.java b/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubClientBrokerTest.java
new file mode 100644
index 000000000000..5e929839c9a9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubClientBrokerTest.java
@@ -0,0 +1,243 @@
+/*
+ * 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.location.contexthub;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.location.ContextHubInfo;
+import android.hardware.location.IContextHubClientCallback;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+@Presubmit
+public class ContextHubClientBrokerTest {
+ private static final short HOST_ENDPOINT_ID = 123;
+ private static final String ATTRIBUTE_TAG = "attribute_tag";
+ private static final long NANOAPP_ID = 3210L;
+ private ContextHubClientManager mClientManager;
+ private Context mContext;
+ @Mock private IContextHubWrapper mMockContextHubWrapper;
+ @Mock private ContextHubInfo mMockContextHubInfo;
+ @Mock private IContextHubClientCallback mMockCallback;
+ @Rule public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Before
+ public void setUp() throws RemoteException {
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ mClientManager = new ContextHubClientManager(mContext, mMockContextHubWrapper);
+ when(mMockCallback.asBinder()).thenReturn(new Binder());
+ }
+
+ private ContextHubClientBroker createFromCallback() {
+ ContextHubClientBroker broker =
+ new ContextHubClientBroker(
+ mContext,
+ mMockContextHubWrapper,
+ mClientManager,
+ mMockContextHubInfo,
+ HOST_ENDPOINT_ID,
+ mMockCallback,
+ ATTRIBUTE_TAG,
+ new ContextHubTransactionManager(
+ mMockContextHubWrapper, mClientManager, new NanoAppStateManager()),
+ mContext.getPackageName());
+ assertThat(broker.isWakelockUsable()).isTrue();
+ assertThat(broker.getWakeLock().isHeld()).isFalse();
+ return broker;
+ }
+
+ private ContextHubClientBroker createFromPendingIntent(PendingIntent pendingIntent) {
+ ContextHubClientBroker broker =
+ new ContextHubClientBroker(
+ mContext,
+ mMockContextHubWrapper,
+ mClientManager,
+ mMockContextHubInfo,
+ HOST_ENDPOINT_ID,
+ pendingIntent,
+ NANOAPP_ID,
+ ATTRIBUTE_TAG,
+ new ContextHubTransactionManager(
+ mMockContextHubWrapper, mClientManager, new NanoAppStateManager()));
+ assertThat(broker.isWakelockUsable()).isTrue();
+ assertThat(broker.getWakeLock().isHeld()).isFalse();
+ return broker;
+ }
+
+ @Test
+ // TODO(b/241016627): We should have similar tests for other public callbacks too.
+ public void testWakeLock_callback_onNanoAppLoaded() {
+ ContextHubClientBroker broker = createFromCallback();
+
+ broker.onNanoAppLoaded(NANOAPP_ID);
+ assertThat(broker.isWakelockUsable()).isTrue();
+ assertThat(broker.getWakeLock().isHeld()).isTrue();
+
+ broker.callbackFinished();
+ assertThat(broker.isWakelockUsable()).isTrue();
+ assertThat(broker.getWakeLock().isHeld()).isFalse();
+ }
+
+ @Test
+ public void testWakeLock_callback_multiple() {
+ ContextHubClientBroker broker = createFromCallback();
+
+ broker.onNanoAppLoaded(NANOAPP_ID);
+ broker.onNanoAppUnloaded(NANOAPP_ID);
+ broker.onHubReset();
+
+ broker.callbackFinished();
+ assertThat(broker.isWakelockUsable()).isTrue();
+ assertThat(broker.getWakeLock().isHeld()).isTrue();
+
+ broker.callbackFinished();
+ assertThat(broker.isWakelockUsable()).isTrue();
+ assertThat(broker.getWakeLock().isHeld()).isTrue();
+
+ broker.callbackFinished();
+ assertThat(broker.isWakelockUsable()).isTrue();
+ assertThat(broker.getWakeLock().isHeld()).isFalse();
+ }
+
+ @Test
+ public void testWakeLock_callback_binderDied() {
+ ContextHubClientBroker broker = createFromCallback();
+
+ broker.binderDied();
+
+ assertThat(broker.isWakelockUsable()).isFalse();
+ assertThat(broker.getWakeLock().isHeld()).isFalse();
+ }
+
+ @Test
+ public void testWakeLock_pendingIntent() throws InterruptedException {
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(), 0);
+ ContextHubClientBroker broker = createFromPendingIntent(pendingIntent);
+ CountDownLatch latch = new CountDownLatch(1);
+ PendingIntent.OnFinished onFinishedCallback =
+ (PendingIntent unusedPendingIntent,
+ Intent unusedIntent,
+ int resultCode,
+ String resultData,
+ Bundle resultExtras) -> {
+ // verify that the wakelock is held before calling the OnFinished callback.
+ assertThat(broker.isWakelockUsable()).isTrue();
+ assertThat(broker.getWakeLock().isHeld()).isTrue();
+ broker.onSendFinished(
+ unusedPendingIntent,
+ unusedIntent,
+ resultCode,
+ resultData,
+ resultExtras);
+ latch.countDown();
+ };
+
+ broker.doSendPendingIntent(pendingIntent, new Intent(), onFinishedCallback);
+
+ assertThat(latch.await(5, TimeUnit.SECONDS)).isTrue();
+ assertThat(broker.isWakelockUsable()).isTrue();
+ assertThat(broker.getWakeLock().isHeld()).isFalse();
+ }
+
+ @Test
+ public void testWakeLock_pendingIntent_multipleTimes() throws InterruptedException {
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(), 0);
+ ContextHubClientBroker broker = createFromPendingIntent(pendingIntent);
+ CountDownLatch latch = new CountDownLatch(3);
+ PendingIntent.OnFinished onFinishedCallback =
+ (PendingIntent unusedPendingIntent,
+ Intent unusedIntent,
+ int resultCode,
+ String resultData,
+ Bundle resultExtras) -> {
+ // verify that the wakelock is held before calling the OnFinished callback.
+ assertThat(broker.isWakelockUsable()).isTrue();
+ assertThat(broker.getWakeLock().isHeld()).isTrue();
+ broker.onSendFinished(
+ unusedPendingIntent,
+ unusedIntent,
+ resultCode,
+ resultData,
+ resultExtras);
+ latch.countDown();
+ };
+
+ broker.doSendPendingIntent(pendingIntent, new Intent(), onFinishedCallback);
+ broker.doSendPendingIntent(pendingIntent, new Intent(), onFinishedCallback);
+ broker.doSendPendingIntent(pendingIntent, new Intent(), onFinishedCallback);
+
+ assertThat(latch.await(5, TimeUnit.SECONDS)).isTrue();
+ assertThat(broker.isWakelockUsable()).isTrue();
+ assertThat(broker.getWakeLock().isHeld()).isFalse();
+ }
+
+ @Test
+ public void testWakeLock_pendingIntent_binderDied() {
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(), 0);
+ ContextHubClientBroker broker = createFromPendingIntent(pendingIntent);
+
+ broker.binderDied();
+
+ // The wakelock should still be usable because a pending intent exists.
+ assertThat(broker.isWakelockUsable()).isTrue();
+ assertThat(broker.getWakeLock().isHeld()).isFalse();
+ }
+
+ @Test
+ public void testWakeLock_close_pendingIntent() {
+ ContextHubClientBroker broker = createFromCallback();
+
+ broker.close();
+
+ // The wakelock should be unusable because broker.close() clears out the pending intent.
+ assertThat(broker.isWakelockUsable()).isFalse();
+ assertThat(broker.getWakeLock().isHeld()).isFalse();
+ }
+
+ @Test
+ public void testWakeLock_onNanoAppUnloaded_closeBeforeCallback() {
+ ContextHubClientBroker broker = createFromCallback();
+
+ broker.onNanoAppUnloaded(NANOAPP_ID);
+ broker.close();
+
+ assertThat(broker.isWakelockUsable()).isFalse();
+ assertThat(broker.getWakeLock().isHeld()).isFalse();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/location/contexthub/OWNERS b/services/tests/servicestests/src/com/android/server/location/contexthub/OWNERS
new file mode 100644
index 000000000000..5bdc0a57b23a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/location/contexthub/OWNERS
@@ -0,0 +1,2 @@
+# Bug component ID: 156070
+include /services/core/java/com/android/server/location/contexthub/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/location/contexthub/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/location/contexthub/TEST_MAPPING
new file mode 100644
index 000000000000..6035250500ec
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/location/contexthub/TEST_MAPPING
@@ -0,0 +1,40 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.location.contexthub."
+ },
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.location.contexthub."
+ },
+ {
+ "include-annotation": "android.platform.test.annotations.Postsubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageVerificationStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageVerificationStateTest.java
index a62569fc81c3..8715afda5cee 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageVerificationStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageVerificationStateTest.java
@@ -329,6 +329,27 @@ public class PackageVerificationStateTest extends AndroidTestCase {
state.isInstallAllowed());
}
+ public void testAreAllVerificationsComplete_timeoutSuccessWithSufficient() {
+ PackageVerificationState state = new PackageVerificationState(null);
+
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
+ state.addSufficientVerifier(SUFFICIENT_UID_1);
+
+ assertFalse(state.areAllVerificationsComplete());
+ assertFalse(state.isVerificationComplete());
+ assertFalse(state.isInstallAllowed());
+
+ // Required verifier responded, but still waiting for sufficient.
+ state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_ALLOW);
+ assertFalse(state.isVerificationComplete());
+
+ // Timeout, verification complete and installation allowed.
+ state.setVerifierResponse(REQUIRED_UID_1,
+ PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT);
+ assertTrue(state.isVerificationComplete());
+ assertTrue(state.isInstallAllowed());
+ }
+
public void testAreAllVerificationsComplete_onlyVerificationPasses() {
PackageVerificationState state = new PackageVerificationState(null);
state.addRequiredVerifierUid(REQUIRED_UID_1);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 06b711251c0f..5d48501b9bc5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -266,6 +266,8 @@ public final class UserManagerTest {
}
assertThat(createUser("User beyond", userType, 0)).isNull();
+
+ assertThat(mUserManager.canAddMoreUsers(mUserManager.USER_TYPE_FULL_GUEST)).isTrue();
}
@MediumTest
@@ -467,8 +469,10 @@ public final class UserManagerTest {
@MediumTest
@Test
public void testThereCanBeOnlyOneGuest() throws Exception {
+ assertThat(mUserManager.canAddMoreUsers(mUserManager.USER_TYPE_FULL_GUEST)).isTrue();
UserInfo userInfo1 = createUser("Guest 1", UserInfo.FLAG_GUEST);
assertThat(userInfo1).isNotNull();
+ assertThat(mUserManager.canAddMoreUsers(mUserManager.USER_TYPE_FULL_GUEST)).isFalse();
UserInfo userInfo2 = createUser("Guest 2", UserInfo.FLAG_GUEST);
assertThat(userInfo2).isNull();
}
diff --git a/services/tests/servicestests/src/com/android/server/policy/SideFpsEventHandlerTest.java b/services/tests/servicestests/src/com/android/server/policy/SideFpsEventHandlerTest.java
index 7746bd6859d6..0635cc4239f4 100644
--- a/services/tests/servicestests/src/com/android/server/policy/SideFpsEventHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/SideFpsEventHandlerTest.java
@@ -24,7 +24,6 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.app.AlertDialog;
import android.content.pm.PackageManager;
import android.hardware.biometrics.BiometricStateListener;
import android.hardware.fingerprint.FingerprintManager;
@@ -36,10 +35,13 @@ import android.os.test.TestLooper;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
+import android.testing.TestableResources;
import android.view.Window;
import androidx.test.InstrumentationRegistry;
+import com.android.internal.R;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -47,7 +49,6 @@ import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
import java.util.List;
@@ -68,17 +69,22 @@ public class SideFpsEventHandlerTest {
BiometricStateListener.STATE_BP_AUTH,
BiometricStateListener.STATE_AUTH_OTHER);
+ private static final Integer AUTO_DISMISS_DIALOG = 500;
+
@Rule
public TestableContext mContext =
new TestableContext(InstrumentationRegistry.getContext(), null);
- @Mock private PackageManager mPackageManager;
- @Mock private FingerprintManager mFingerprintManager;
- @Spy private AlertDialog.Builder mDialogBuilder = new AlertDialog.Builder(mContext);
- @Mock private AlertDialog mAlertDialog;
- @Mock private Window mWindow;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private FingerprintManager mFingerprintManager;
+ @Mock
+ private SideFpsToast mDialog;
+ @Mock
+ private Window mWindow;
- private TestLooper mLooper = new TestLooper();
+ private final TestLooper mLooper = new TestLooper();
private SideFpsEventHandler mEventHandler;
private BiometricStateListener mBiometricStateListener;
@@ -88,16 +94,17 @@ public class SideFpsEventHandlerTest {
mContext.addMockSystemService(PackageManager.class, mPackageManager);
mContext.addMockSystemService(FingerprintManager.class, mFingerprintManager);
+ TestableResources resources = mContext.getOrCreateTestableResources();
+ resources.addOverride(R.integer.config_sideFpsToastTimeout, AUTO_DISMISS_DIALOG);
- when(mDialogBuilder.create()).thenReturn(mAlertDialog);
- when(mAlertDialog.getWindow()).thenReturn(mWindow);
+ when(mDialog.getWindow()).thenReturn(mWindow);
mEventHandler =
new SideFpsEventHandler(
mContext,
new Handler(mLooper.getLooper()),
mContext.getSystemService(PowerManager.class),
- () -> mDialogBuilder);
+ (ctx) -> mDialog);
}
@Test
@@ -108,7 +115,7 @@ public class SideFpsEventHandlerTest {
assertThat(mEventHandler.shouldConsumeSinglePress(60L)).isFalse();
mLooper.dispatchAll();
- verify(mAlertDialog, never()).show();
+ verify(mDialog, never()).show();
}
@Test
@@ -120,7 +127,7 @@ public class SideFpsEventHandlerTest {
assertThat(mEventHandler.shouldConsumeSinglePress(200L)).isFalse();
mLooper.dispatchAll();
- verify(mAlertDialog, never()).show();
+ verify(mDialog, never()).show();
}
}
@@ -133,7 +140,7 @@ public class SideFpsEventHandlerTest {
assertThat(mEventHandler.shouldConsumeSinglePress(400L)).isFalse();
mLooper.dispatchAll();
- verify(mAlertDialog, never()).show();
+ verify(mDialog, never()).show();
}
}
@@ -148,7 +155,7 @@ public class SideFpsEventHandlerTest {
assertThat(mEventHandler.shouldConsumeSinglePress(90000L)).isFalse();
mLooper.dispatchAll();
- verify(mAlertDialog, never()).show();
+ verify(mDialog, never()).show();
}
@Test
@@ -159,18 +166,18 @@ public class SideFpsEventHandlerTest {
assertThat(mEventHandler.shouldConsumeSinglePress(80000L)).isFalse();
mLooper.dispatchAll();
- verify(mAlertDialog, never()).show();
+ verify(mDialog, never()).show();
}
@Test
- public void promptsWhenBPisActive() throws Exception {
+ public void doesNotpromptWhenBPisActive() throws Exception {
setupWithSensor(true /* hasSideFps */, true /* initialized */);
setBiometricState(BiometricStateListener.STATE_BP_AUTH);
assertThat(mEventHandler.shouldConsumeSinglePress(80000L)).isTrue();
mLooper.dispatchAll();
- verify(mAlertDialog, never()).show();
+ verify(mDialog, never()).show();
}
@Test
@@ -181,57 +188,40 @@ public class SideFpsEventHandlerTest {
assertThat(mEventHandler.shouldConsumeSinglePress(80000L)).isTrue();
mLooper.dispatchAll();
- verify(mAlertDialog).show();
- verify(mAlertDialog, never()).dismiss();
+ verify(mDialog).show();
}
@Test
- public void dismissesDialogOnTouchWhenEnrolling() throws Exception {
+ public void dialogDismissesAfterTime() throws Exception {
setupWithSensor(true /* hasSfps */, true /* initialized */);
setBiometricState(BiometricStateListener.STATE_ENROLLING);
- when(mAlertDialog.isShowing()).thenReturn(true);
+ when(mDialog.isShowing()).thenReturn(true);
assertThat(mEventHandler.shouldConsumeSinglePress(80000L)).isTrue();
mLooper.dispatchAll();
- verify(mAlertDialog).show();
-
- mBiometricStateListener.onBiometricAction(BiometricStateListener.ACTION_SENSOR_TOUCH);
- mLooper.moveTimeForward(10000);
+ verify(mDialog).show();
+ mLooper.moveTimeForward(AUTO_DISMISS_DIALOG);
mLooper.dispatchAll();
-
- verify(mAlertDialog).dismiss();
+ verify(mDialog).dismiss();
}
@Test
- public void dismissesDialogFailsWhenPowerPressedAndDialogShowing() throws Exception {
+ public void dialogDoesNotDismissOnSensorTouch() throws Exception {
setupWithSensor(true /* hasSfps */, true /* initialized */);
setBiometricState(BiometricStateListener.STATE_ENROLLING);
- when(mAlertDialog.isShowing()).thenReturn(true);
+ when(mDialog.isShowing()).thenReturn(true);
assertThat(mEventHandler.shouldConsumeSinglePress(80000L)).isTrue();
mLooper.dispatchAll();
- verify(mAlertDialog).show();
+ verify(mDialog).show();
mBiometricStateListener.onBiometricAction(BiometricStateListener.ACTION_SENSOR_TOUCH);
- assertThat(mEventHandler.shouldConsumeSinglePress(60L)).isTrue();
-
+ mLooper.moveTimeForward(AUTO_DISMISS_DIALOG - 1);
mLooper.dispatchAll();
- verify(mAlertDialog, never()).dismiss();
- }
- @Test
- public void showDialogAfterTap() throws Exception {
- setupWithSensor(true /* hasSfps */, true /* initialized */);
-
- setBiometricState(BiometricStateListener.STATE_ENROLLING);
- when(mAlertDialog.isShowing()).thenReturn(true);
- mBiometricStateListener.onBiometricAction(BiometricStateListener.ACTION_SENSOR_TOUCH);
- assertThat(mEventHandler.shouldConsumeSinglePress(60L)).isTrue();
-
- mLooper.dispatchAll();
- verify(mAlertDialog).show();
+ verify(mDialog, never()).dismiss();
}
private void setBiometricState(@BiometricStateListener.State int newState) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
index c5117bb83976..e2c94c5b4b3d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
@@ -19,6 +19,7 @@ package com.android.server.wm;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
+import static android.view.Display.INVALID_DISPLAY;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -31,9 +32,11 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.never;
+import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.display.VirtualDisplay;
@@ -53,6 +56,8 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.util.concurrent.CountDownLatch;
@@ -68,31 +73,40 @@ import java.util.concurrent.CountDownLatch;
public class ContentRecorderTests extends WindowTestsBase {
private static final IBinder TEST_TOKEN = new RecordingTestToken();
private static IBinder sTaskWindowContainerToken;
+ private Task mTask;
private final ContentRecordingSession mDisplaySession =
ContentRecordingSession.createDisplaySession(TEST_TOKEN);
private ContentRecordingSession mTaskSession;
private static Point sSurfaceSize;
private ContentRecorder mContentRecorder;
+ @Mock private ContentRecorder.MediaProjectionManagerWrapper mMediaProjectionManagerWrapper;
private SurfaceControl mRecordedSurface;
// Handle feature flag.
private ConfigListener mConfigListener;
private CountDownLatch mLatch;
+ private VirtualDisplay mVirtualDisplay;
+
@Before public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
// GIVEN SurfaceControl can successfully mirror the provided surface.
sSurfaceSize = new Point(
mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().width(),
mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().height());
mRecordedSurface = surfaceControlMirrors(sSurfaceSize);
+ doReturn(INVALID_DISPLAY).when(mWm.mDisplayManagerInternal).getDisplayIdToMirror(anyInt());
+
// GIVEN the VirtualDisplay associated with the session (so the display has state ON).
- VirtualDisplay virtualDisplay = mWm.mDisplayManager.createVirtualDisplay("VirtualDisplay",
+ mVirtualDisplay = mWm.mDisplayManager.createVirtualDisplay("VirtualDisplay",
sSurfaceSize.x, sSurfaceSize.y,
DisplayMetrics.DENSITY_140, new Surface(), VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR);
- final int displayId = virtualDisplay.getDisplay().getDisplayId();
+ final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
mWm.mRoot.onDisplayAdded(displayId);
final DisplayContent virtualDisplayContent = mWm.mRoot.getDisplayContent(displayId);
- mContentRecorder = new ContentRecorder(virtualDisplayContent);
+ mContentRecorder = new ContentRecorder(virtualDisplayContent,
+ mMediaProjectionManagerWrapper);
spyOn(virtualDisplayContent);
// GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to
@@ -116,6 +130,8 @@ public class ContentRecorderTests extends WindowTestsBase {
@After
public void teardown() {
DeviceConfig.removeOnPropertiesChangedListener(mConfigListener);
+ mVirtualDisplay.release();
+ mWm.mRoot.onDisplayRemoved(mVirtualDisplay.getDisplay().getDisplayId());
}
@Test
@@ -179,7 +195,7 @@ public class ContentRecorderTests extends WindowTestsBase {
mContentRecorder.setContentRecordingSession(session);
mContentRecorder.updateRecording();
assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
- // TODO(b/219761722) validate VirtualDisplay is torn down when can't set up task recording.
+ verify(mMediaProjectionManagerWrapper).stopActiveProjection();
}
@Test
@@ -190,7 +206,7 @@ public class ContentRecorderTests extends WindowTestsBase {
mContentRecorder.setContentRecordingSession(invalidTaskSession);
mContentRecorder.updateRecording();
assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
- // TODO(b/219761722) validate VirtualDisplay is torn down when can't set up task recording.
+ verify(mMediaProjectionManagerWrapper).stopActiveProjection();
}
@Test
@@ -218,9 +234,24 @@ public class ContentRecorderTests extends WindowTestsBase {
mContentRecorder.updateRecording();
mContentRecorder.onConfigurationChanged(ORIENTATION_PORTRAIT);
- verify(mTransaction, atLeastOnce()).setPosition(eq(mRecordedSurface), anyFloat(),
+ verify(mTransaction, atLeast(2)).setPosition(eq(mRecordedSurface), anyFloat(),
anyFloat());
- verify(mTransaction, atLeastOnce()).setMatrix(eq(mRecordedSurface), anyFloat(), anyFloat(),
+ verify(mTransaction, atLeast(2)).setMatrix(eq(mRecordedSurface), anyFloat(), anyFloat(),
+ anyFloat(), anyFloat());
+ }
+
+ @Test
+ public void testOnTaskConfigurationChanged_resizesSurface() {
+ mContentRecorder.setContentRecordingSession(mTaskSession);
+ mContentRecorder.updateRecording();
+
+ Configuration config = mTask.getConfiguration();
+ config.orientation = ORIENTATION_PORTRAIT;
+ mTask.onConfigurationChanged(config);
+
+ verify(mTransaction, atLeast(2)).setPosition(eq(mRecordedSurface), anyFloat(),
+ anyFloat());
+ verify(mTransaction, atLeast(2)).setMatrix(eq(mRecordedSurface), anyFloat(), anyFloat(),
anyFloat(), anyFloat());
}
@@ -240,21 +271,31 @@ public class ContentRecorderTests extends WindowTestsBase {
}
@Test
- public void testRemove_stopsRecording() {
+ public void testStopRecording_stopsRecording() {
mContentRecorder.setContentRecordingSession(mDisplaySession);
mContentRecorder.updateRecording();
- mContentRecorder.remove();
+ mContentRecorder.stopRecording();
assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
}
@Test
- public void testRemove_neverRecording() {
- mContentRecorder.remove();
+ public void testStopRecording_neverRecording() {
+ mContentRecorder.stopRecording();
assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
}
@Test
+ public void testRemoveTask_stopsRecording() {
+ mContentRecorder.setContentRecordingSession(mTaskSession);
+ mContentRecorder.updateRecording();
+
+ mTask.removeImmediately();
+
+ verify(mMediaProjectionManagerWrapper).stopActiveProjection();
+ }
+
+ @Test
public void testUpdateMirroredSurface_capturedAreaResized() {
mContentRecorder.setContentRecordingSession(mDisplaySession);
mContentRecorder.updateRecording();
@@ -297,10 +338,10 @@ public class ContentRecorderTests extends WindowTestsBase {
*/
private IBinder setUpTaskWindowContainerToken(DisplayContent displayContent) {
final Task rootTask = createTask(displayContent);
- final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
+ mTask = createTaskInRootTask(rootTask, 0 /* userId */);
// Ensure the task is not empty.
- createActivityRecord(displayContent, task);
- return task.getTaskInfo().token.asBinder();
+ createActivityRecord(displayContent, mTask);
+ return mTask.getTaskInfo().token.asBinder();
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecordingControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecordingControllerTests.java
index 7698033d8df2..342d68b16823 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ContentRecordingControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecordingControllerTests.java
@@ -87,7 +87,7 @@ public class ContentRecordingControllerTests extends WindowTestsBase {
@Test
public void testSetContentRecordingSessionLocked_invalidToken_notAccepted() {
ContentRecordingController controller = new ContentRecordingController();
- // GIVEN an invalid display session (null token).
+ // GIVEN a session with a null token.
ContentRecordingSession session = ContentRecordingSession.createDisplaySession(null);
session.setDisplayId(DEFAULT_DISPLAY);
// WHEN updating the session.
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 641a3adf337a..28d2aa157e0a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -31,6 +31,7 @@ import static android.os.Build.VERSION_CODES.P;
import static android.os.Build.VERSION_CODES.Q;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_PRIVATE;
+import static android.view.Display.INVALID_DISPLAY;
import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
import static android.view.DisplayCutout.fromBoundingRect;
import static android.view.InsetsState.ITYPE_IME;
@@ -1251,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());
}
@@ -2588,6 +2597,43 @@ public class DisplayContentTests extends WindowTestsBase {
display.release();
}
+ @Test
+ public void testVirtualDisplayContent_displayMirroring() {
+ // GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to
+ // mirror.
+ final IBinder tokenToMirror = setUpDefaultTaskDisplayAreaWindowToken();
+
+ // GIVEN SurfaceControl can successfully mirror the provided surface.
+ Point surfaceSize = new Point(
+ mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().width(),
+ mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().height());
+ surfaceControlMirrors(surfaceSize);
+ // Initially disable getDisplayIdToMirror since the DMS may create the DC outside the direct
+ // call in the test. We need to spy on the DC before updateRecording is called or we can't
+ // verify setDisplayMirroring is called
+ doReturn(INVALID_DISPLAY).when(mWm.mDisplayManagerInternal).getDisplayIdToMirror(anyInt());
+
+ // GIVEN a new VirtualDisplay with an associated surface.
+ final VirtualDisplay display = createVirtualDisplay(surfaceSize, new Surface());
+ final int displayId = display.getDisplay().getDisplayId();
+
+ // GIVEN a session for this display.
+ mWm.mRoot.onDisplayAdded(displayId);
+
+ // WHEN getting the DisplayContent for the new virtual display.
+ DisplayContent actualDC = mWm.mRoot.getDisplayContent(displayId);
+ spyOn(actualDC);
+ // Return the default display as the value to mirror to ensure the VD with flag mirroring
+ // creates a ContentRecordingSession automatically.
+ doReturn(DEFAULT_DISPLAY).when(mWm.mDisplayManagerInternal).getDisplayIdToMirror(anyInt());
+ actualDC.updateRecording();
+
+ // THEN mirroring is initiated for the default display's DisplayArea.
+ verify(actualDC).setDisplayMirroring();
+ assertThat(actualDC.isCurrentlyRecording()).isTrue();
+ display.release();
+ }
+
private static class MirroringTestToken extends Binder {
}
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/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 969353216d20..a62625cea46c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -578,6 +578,22 @@ public class WindowOrganizerTests extends WindowTestsBase {
}
@Test
+ public void testContainerTranslucentChanges() {
+ removeGlobalMinSizeRestriction();
+ final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN).build();
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(rootTask).build();
+ WindowContainerTransaction t = new WindowContainerTransaction();
+ assertFalse(rootTask.isTranslucent(activity));
+ t.setForceTranslucent(rootTask.mRemoteToken.toWindowContainerToken(), true);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
+ assertTrue(rootTask.isTranslucent(activity));
+ t.setForceTranslucent(rootTask.mRemoteToken.toWindowContainerToken(), false);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
+ assertFalse(rootTask.isTranslucent(activity));
+ }
+
+ @Test
public void testSetIgnoreOrientationRequest_taskDisplayArea() {
removeGlobalMinSizeRestriction();
final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 85b9f759beac..9562c1a5bcf9 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -508,6 +508,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
// current USB state
private boolean mHostConnected;
private boolean mUsbAccessoryConnected;
+ private boolean mInHostModeWithNoAccessoryConnected;
private boolean mSourcePower;
private boolean mSinkPower;
private boolean mConfigured;
@@ -959,6 +960,17 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
mSupportsAllCombinations = false;
}
+ if (mHostConnected) {
+ if (!mUsbAccessoryConnected) {
+ mInHostModeWithNoAccessoryConnected = true;
+ } else {
+ mInHostModeWithNoAccessoryConnected = false;
+ }
+ } else {
+ // if not in host mode, reset value to false
+ mInHostModeWithNoAccessoryConnected = false;
+ }
+
mAudioAccessorySupported = port.isModeSupported(MODE_AUDIO_ACCESSORY);
args.recycle();
@@ -983,6 +995,12 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
Slog.i(TAG, "HOST_STATE connected:" + mUsbAccessoryConnected);
}
+ if (!devices.hasNext()) {
+ mInHostModeWithNoAccessoryConnected = true;
+ } else {
+ mInHostModeWithNoAccessoryConnected = false;
+ }
+
mHideUsbNotification = false;
while (devices.hasNext()) {
Map.Entry pair = (Map.Entry) devices.next();
@@ -1192,7 +1210,8 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
// Dont show the notification when connected to a USB peripheral
// and the link does not support PR_SWAP and DR_SWAP
- if (mHideUsbNotification && !mSupportsAllCombinations) {
+ if ((mHideUsbNotification || mInHostModeWithNoAccessoryConnected)
+ && !mSupportsAllCombinations) {
if (mUsbNotificationId != 0) {
mNotificationManager.cancelAsUser(null, mUsbNotificationId,
UserHandle.ALL);
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/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 1f97d862fb6b..f28408e1abc9 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -194,13 +194,24 @@ public class SubscriptionManager {
}
@Override
- public T recompute(Void aVoid) {
+ public T recompute(Void query) {
+ // This always throws on any error. The exceptions must be handled outside
+ // the cache.
+ try {
+ return mInterfaceMethod.applyOrThrow(TelephonyManager.getSubscriptionService());
+ } catch (Exception re) {
+ throw new RuntimeException(re);
+ }
+ }
+
+ @Override
+ public T query(Void query) {
T result = mDefaultValue;
try {
ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
- result = mInterfaceMethod.applyOrThrow(iSub);
+ result = super.query(query);
}
} catch (Exception ex) {
Rlog.w(LOG_TAG, "Failed to recompute cache key for " + mCacheKeyProperty);
@@ -229,12 +240,24 @@ public class SubscriptionManager {
@Override
public T recompute(Integer query) {
+ // This always throws on any error. The exceptions must be handled outside
+ // the cache.
+ try {
+ return mInterfaceMethod.applyOrThrow(
+ TelephonyManager.getSubscriptionService(), query);
+ } catch (Exception re) {
+ throw new RuntimeException(re);
+ }
+ }
+
+ @Override
+ public T query(Integer query) {
T result = mDefaultValue;
try {
ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
- result = mInterfaceMethod.applyOrThrow(iSub, query);
+ result = super.query(query);
}
} catch (Exception ex) {
Rlog.w(LOG_TAG, "Failed to recompute cache key for " + mCacheKeyProperty);
@@ -4118,4 +4141,3 @@ public class SubscriptionManager {
usageSetting, subscriptionId, mContext.getOpPackageName()));
}
}
-
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/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/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index cd07b1ebbf92..46a846bce35f 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -641,7 +641,23 @@ class PackageFlattener {
local_key_index = (uint32_t)key_pool_.MakeRef(entry.name).index();
} else {
// resource isn't exempt from collapse, add it as obfuscated value
- local_key_index = (uint32_t)key_pool_.MakeRef(obfuscated_resource_name).index();
+ if (entry.overlayable_item) {
+ // if the resource name of the specific entry is obfuscated and this
+ // entry is in the overlayable list, the overlay can't work on this
+ // overlayable at runtime because the name has been obfuscated in
+ // resources.arsc during flatten operation.
+ const OverlayableItem& item = entry.overlayable_item.value();
+ context_->GetDiagnostics()->Warn(android::DiagMessage(item.overlayable->source)
+ << "The resource name of overlayable entry "
+ << resource_name.to_string() << "'"
+ << " shouldn't be obfuscated in resources.arsc");
+
+ local_key_index = (uint32_t)key_pool_.MakeRef(entry.name).index();
+ } else {
+ // TODO(b/228192695): output the entry.name and Resource id to make
+ // de-obfuscated possible.
+ local_key_index = (uint32_t)key_pool_.MakeRef(obfuscated_resource_name).index();
+ }
}
// Group values by configuration.
for (auto& config_value : entry.values) {
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index 1dd2468b5868..e48fca61fef8 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -878,4 +878,52 @@ TEST_F(TableFlattenerTest, FlattenCustomResourceTypes) {
Res_value::TYPE_STRING, (uint32_t)*idx, 0u));
}
+TEST_F(TableFlattenerTest, FlattenTypeEntryWithNameCollapseNotInExemption) {
+ OverlayableItem overlayable_item(std::make_shared<Overlayable>("TestName", "overlay://theme"));
+ overlayable_item.policies |= PolicyFlags::PUBLIC;
+
+ std::string name = "com.app.test:color/overlayable_color";
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .AddValue("com.app.test:color/overlayable_color", ResourceId(0x7f010000),
+ util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_COLOR_ARGB8),
+ 0xffaabbcc))
+ .SetOverlayable(name, overlayable_item)
+ .Build();
+
+ TableFlattenerOptions options;
+ options.collapse_key_stringpool = true;
+
+ ResTable res_table;
+ EXPECT_THAT(Flatten(context_.get(), options, table.get(), &res_table), testing::IsTrue());
+ EXPECT_THAT(Exists(&res_table, "com.app.test:color/overlayable_color", ResourceId(0x7f010000), {},
+ Res_value::TYPE_INT_COLOR_ARGB8, 0xffaabbcc, 0u),
+ testing::IsTrue());
+}
+
+TEST_F(TableFlattenerTest, FlattenTypeEntryWithNameCollapseInExemption) {
+ OverlayableItem overlayable_item(std::make_shared<Overlayable>("TestName", "overlay://theme"));
+ overlayable_item.policies |= PolicyFlags::PUBLIC;
+
+ std::string name = "com.app.test:color/overlayable_color";
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .AddValue("com.app.test:color/overlayable_color", ResourceId(0x7f010000),
+ util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_COLOR_ARGB8),
+ 0xffaabbcc))
+ .SetOverlayable(name, overlayable_item)
+ .Build();
+
+ TableFlattenerOptions options;
+ options.collapse_key_stringpool = true;
+ options.name_collapse_exemptions.insert(
+ ResourceName({}, ResourceType::kColor, "overlayable_color"));
+
+ ResTable res_table;
+ EXPECT_THAT(Flatten(context_.get(), options, table.get(), &res_table), testing::IsTrue());
+ EXPECT_THAT(Exists(&res_table, "com.app.test:color/overlayable_color", ResourceId(0x7f010000), {},
+ Res_value::TYPE_INT_COLOR_ARGB8, 0xffaabbcc, 0u),
+ testing::IsTrue());
+}
+
} // namespace aapt