summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--INPUT_OWNERS3
-rw-r--r--PREUPLOAD.cfg7
-rw-r--r--apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java8
-rw-r--r--apct-tests/perftests/surfaceflinger/AndroidTest.xml8
-rw-r--r--apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java1
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java14
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java8
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java87
-rw-r--r--boot/hiddenapi/hiddenapi-max-target-r-loprio.txt1
-rw-r--r--cmds/uinput/src/com/android/commands/uinput/Event.java10
-rw-r--r--core/api/current.txt26
-rw-r--r--core/api/test-current.txt2
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java429
-rw-r--r--core/java/android/app/ActivityManager.java2
-rw-r--r--core/java/android/app/ActivityOptions.java5
-rw-r--r--core/java/android/app/ActivityTaskManager.java22
-rw-r--r--core/java/android/app/ActivityThread.java3
-rw-r--r--core/java/android/app/ActivityTransitionState.java10
-rw-r--r--core/java/android/app/GameManagerInternal.java30
-rw-r--r--core/java/android/app/HomeVisibilityListener.java45
-rw-r--r--core/java/android/app/IActivityTaskManager.aidl4
-rw-r--r--core/java/android/app/IGameManagerService.aidl2
-rw-r--r--core/java/android/app/PropertyInvalidatedCache.java43
-rw-r--r--core/java/android/appwidget/AppWidgetHostView.java16
-rw-r--r--core/java/android/content/ContentProvider.java66
-rw-r--r--core/java/android/content/Intent.java14
-rw-r--r--core/java/android/content/pm/PackageInfo.java7
-rw-r--r--core/java/android/content/pm/PackageManager.java2
-rw-r--r--core/java/android/content/res/Configuration.java4
-rw-r--r--core/java/android/database/TEST_MAPPING7
-rw-r--r--core/java/android/hardware/location/ContextHubClient.java12
-rw-r--r--core/java/android/inputmethodservice/IInputMethodWrapper.java13
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java25
-rw-r--r--core/java/android/os/BaseBundle.java63
-rw-r--r--core/java/android/os/IUserManager.aidl1
-rw-r--r--core/java/android/os/Parcel.java13
-rw-r--r--core/java/android/os/UserManager.java44
-rw-r--r--core/java/android/provider/DeviceConfig.java7
-rw-r--r--core/java/android/service/voice/VoiceInteractionManagerInternal.java16
-rw-r--r--core/java/android/service/voice/VoiceInteractionService.java2
-rw-r--r--core/java/android/service/voice/VoiceInteractionSession.java3
-rw-r--r--core/java/android/text/Selection.java95
-rw-r--r--core/java/android/text/method/ArrowKeyMovementMethod.java21
-rw-r--r--core/java/android/text/method/BaseMovementMethod.java29
-rw-r--r--core/java/android/view/IWindowFocusObserver.aidl2
-rw-r--r--core/java/android/view/InsetsController.java2
-rw-r--r--core/java/android/view/RemoteAnimationTarget.java10
-rw-r--r--core/java/android/view/SurfaceControl.java10
-rw-r--r--core/java/android/view/SurfaceControlViewHost.java2
-rw-r--r--core/java/android/view/ViewRootImpl.java88
-rw-r--r--core/java/android/view/ViewTreeObserver.java19
-rw-r--r--core/java/android/view/WindowManager.java4
-rw-r--r--core/java/android/view/inputmethod/EditorInfo.java37
-rw-r--r--core/java/android/view/inputmethod/IInputMethodManagerInvoker.java5
-rw-r--r--core/java/android/view/inputmethod/InputMethod.java9
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java5
-rw-r--r--core/java/android/widget/RemoteViews.java382
-rw-r--r--core/java/android/window/ITaskFragmentOrganizer.aidl49
-rw-r--r--core/java/android/window/TaskFragmentOrganizer.java154
-rw-r--r--core/java/android/window/TaskFragmentTransaction.aidl20
-rw-r--r--core/java/android/window/TaskFragmentTransaction.java335
-rw-r--r--core/java/android/window/WindowOnBackInvokedDispatcher.java3
-rw-r--r--core/java/com/android/internal/app/AssistUtils.java48
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java2
-rw-r--r--core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl13
-rw-r--r--core/java/com/android/internal/app/ResolverListAdapter.java2
-rw-r--r--core/java/com/android/internal/inputmethod/IInputMethod.aidl2
-rw-r--r--core/java/com/android/internal/inputmethod/ImeTracingServerImpl.java16
-rw-r--r--core/java/com/android/internal/jank/FrameTracker.java15
-rw-r--r--core/java/com/android/internal/jank/InteractionJankMonitor.java38
-rw-r--r--core/java/com/android/internal/os/BinderCallsStats.java8
-rw-r--r--core/java/com/android/internal/util/ScreenshotHelper.java5
-rw-r--r--core/java/com/android/internal/view/IInputMethodManager.aidl2
-rw-r--r--core/java/com/android/internal/widget/PointerLocationView.java2
-rw-r--r--core/java/com/android/server/SystemConfig.java20
-rw-r--r--core/jni/android_view_SurfaceControl.cpp8
-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/values-sw600dp/config.xml2
-rw-r--r--core/res/res/values/attrs_manifest.xml15
-rw-r--r--core/res/res/values/config.xml7
-rw-r--r--core/res/res/values/symbols.xml3
-rw-r--r--core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java2
-rw-r--r--core/tests/coretests/src/android/content/res/ConfigurationTest.java16
-rw-r--r--core/tests/coretests/src/android/graphics/PathOffsetTest.java91
-rw-r--r--core/tests/coretests/src/android/graphics/PathTest.java39
-rw-r--r--core/tests/coretests/src/android/os/FileUtilsTest.java12
-rw-r--r--core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java11
-rw-r--r--core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java158
-rw-r--r--core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java206
-rw-r--r--core/tests/mockingcoretests/src/android/os/BundleRecyclingTest.java146
-rw-r--r--core/tests/overlaytests/device/AndroidTest.xml2
-rw-r--r--core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java184
-rw-r--r--data/etc/services.core.protolog.json6
-rw-r--r--graphics/java/android/graphics/BaseCanvas.java6
-rw-r--r--graphics/java/android/graphics/BaseRecordingCanvas.java6
-rw-r--r--graphics/java/android/graphics/HardwareRenderer.java43
-rw-r--r--graphics/java/android/graphics/Path.java97
-rw-r--r--graphics/java/android/graphics/Typeface.java34
-rw-r--r--graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java4
-rw-r--r--graphics/java/android/view/PixelCopy.java251
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java17
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java34
-rw-r--r--libs/WindowManager/Jetpack/window-extensions-release.aarbin19210 -> 21364 bytes
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java37
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java135
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java45
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java54
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java71
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/docs/sysui.md30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java28
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java20
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java62
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java29
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java31
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java147
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenShellCommandHandler.java96
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java83
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellCommandHandler.java174
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java47
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java26
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java14
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt41
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt204
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt36
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt192
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt195
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java23
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java23
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java16
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutControllerTest.java24
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java14
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java39
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java78
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java33
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt10
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java23
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java72
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java59
-rw-r--r--libs/hwui/CopyRequest.h42
-rw-r--r--libs/hwui/Readback.cpp79
-rw-r--r--libs/hwui/Readback.h18
-rw-r--r--libs/hwui/jni/Typeface.cpp50
-rw-r--r--libs/hwui/jni/YuvToJpegEncoder.cpp2
-rw-r--r--libs/hwui/jni/android_graphics_HardwareRenderer.cpp53
-rw-r--r--libs/hwui/jni/fonts/Font.cpp8
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp11
-rw-r--r--libs/hwui/renderthread/RenderProxy.h6
-rw-r--r--libs/hwui/tests/common/scenes/MagnifierAnimation.cpp51
-rw-r--r--media/java/android/media/AudioPlaybackConfiguration.java17
-rw-r--r--media/java/android/media/AudioSystem.java11
-rw-r--r--media/java/android/media/ImageReader.java61
-rw-r--r--media/java/android/media/MediaCodec.java131
-rw-r--r--media/java/android/media/MediaCodecInfo.java2
-rw-r--r--media/java/android/media/session/ISession.aidl2
-rw-r--r--media/java/android/media/session/MediaSession.java2
-rw-r--r--media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java441
-rwxr-xr-xmedia/java/android/media/tv/interactive/TvInteractiveAppService.java188
-rw-r--r--media/jni/android_media_ImageReader.cpp13
-rw-r--r--mime/java-res/android.mime.types1
-rw-r--r--packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml12
-rw-r--r--packages/SettingsLib/ActionButtonsPreference/res/layout-v33/settingslib_action_buttons.xml85
-rw-r--r--packages/SettingsLib/AppPreference/res/layout-v33/preference_app.xml100
-rw-r--r--packages/SettingsLib/AppPreference/res/layout-v33/preference_app_header.xml38
-rw-r--r--packages/SettingsLib/AppPreference/res/layout/preference_app.xml2
-rw-r--r--packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java4
-rw-r--r--packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java7
-rw-r--r--packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java7
-rw-r--r--packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml1
-rw-r--r--packages/SettingsLib/FooterPreference/res/layout-v33/preference_footer.xml71
-rw-r--r--packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml70
-rw-r--r--packages/SettingsLib/RadioButtonPreference/res/layout-v33/preference_radio.xml127
-rw-r--r--packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml2
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/layout-v33/preference_selector_with_widget.xml127
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_selector_with_widget.xml2
-rw-r--r--packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml2
-rw-r--r--packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml80
-rw-r--r--packages/SettingsLib/Spa/.gitignore15
-rw-r--r--packages/SettingsLib/Spa/OWNERS6
-rw-r--r--packages/SettingsLib/Spa/TEST_MAPPING7
-rw-r--r--packages/SettingsLib/Spa/build.gradle28
-rw-r--r--packages/SettingsLib/Spa/codelab/Android.bp33
-rw-r--r--packages/SettingsLib/Spa/codelab/AndroidManifest.xml34
-rw-r--r--packages/SettingsLib/Spa/codelab/build.gradle66
-rw-r--r--packages/SettingsLib/Spa/codelab/res/values/strings.xml19
-rw-r--r--packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/MainActivity.kt22
-rw-r--r--packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/ArgumentPage.kt87
-rw-r--r--packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/HomePage.kt64
-rw-r--r--packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PageRepository.kt30
-rw-r--r--packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PreferencePage.kt109
-rw-r--r--packages/SettingsLib/Spa/gradle.properties39
-rw-r--r--packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jarbin0 -> 59203 bytes
-rw-r--r--packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties22
-rwxr-xr-xpackages/SettingsLib/Spa/gradlew185
-rw-r--r--packages/SettingsLib/Spa/gradlew.bat89
-rw-r--r--packages/SettingsLib/Spa/settings.gradle34
-rw-r--r--packages/SettingsLib/Spa/spa/Android.bp36
-rw-r--r--packages/SettingsLib/Spa/spa/AndroidManifest.xml17
-rw-r--r--packages/SettingsLib/Spa/spa/build.gradle69
-rw-r--r--packages/SettingsLib/Spa/spa/res/values-night/themes.xml20
-rw-r--r--packages/SettingsLib/Spa/spa/res/values/themes.xml27
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/api/SettingsPageProvider.kt38
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/api/SettingsPageRepository.kt22
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/NavControllerWrapper.kt57
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/RuntimeUtils.kt34
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/SpaActivity.kt62
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/MaterialColors.kt35
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsDimension.kt33
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsOpacity.kt22
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsTheme.kt34
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt117
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BasePreference.kt89
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt73
-rw-r--r--packages/SettingsLib/Spa/tests/Android.bp36
-rw-r--r--packages/SettingsLib/Spa/tests/AndroidManifest.xml29
-rw-r--r--packages/SettingsLib/Spa/tests/build.gradle67
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/PreferenceTest.kt80
-rw-r--r--packages/SettingsLib/TopIntroPreference/res/layout-v33/top_intro_preference.xml39
-rw-r--r--packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml1
-rw-r--r--packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target.xml2
-rw-r--r--packages/SettingsLib/TwoTargetPreference/res/layout-v33/preference_two_target.xml74
-rw-r--r--packages/SettingsLib/res/layout-v33/preference_access_point.xml110
-rw-r--r--packages/SettingsLib/res/layout-v33/preference_checkable_two_target.xml97
-rw-r--r--packages/SettingsLib/res/layout-v33/restricted_switch_preference.xml97
-rw-r--r--packages/SettingsLib/res/layout/preference_access_point.xml2
-rw-r--r--packages/SettingsLib/res/layout/preference_checkable_two_target.xml2
-rw-r--r--packages/SettingsLib/res/layout/restricted_switch_preference.xml3
-rw-r--r--packages/SettingsLib/res/values-af/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-af/strings.xml7
-rw-r--r--packages/SettingsLib/res/values-am/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-am/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-ar/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-ar/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-as/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-as/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-az/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-az/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-b+sr+Latn/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-b+sr+Latn/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-be/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-be/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-bg/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-bg/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-bn/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-bn/strings.xml7
-rw-r--r--packages/SettingsLib/res/values-bs/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-bs/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-ca/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-ca/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-cs/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-cs/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-da/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-da/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-de/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-de/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-el/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-el/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-en-rAU/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-en-rAU/strings.xml7
-rw-r--r--packages/SettingsLib/res/values-en-rCA/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-en-rCA/strings.xml7
-rw-r--r--packages/SettingsLib/res/values-en-rGB/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-en-rGB/strings.xml7
-rw-r--r--packages/SettingsLib/res/values-en-rIN/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-en-rIN/strings.xml7
-rw-r--r--packages/SettingsLib/res/values-en-rXC/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-en-rXC/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-es-rUS/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-es-rUS/strings.xml7
-rw-r--r--packages/SettingsLib/res/values-es/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-es/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-et/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-et/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-eu/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-eu/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-fa/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-fa/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-fi/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-fi/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-fr-rCA/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-fr-rCA/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-fr/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-fr/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-gl/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-gl/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-gu/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-gu/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-hi/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-hi/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-hr/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-hr/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-hu/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-hu/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-hy/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-hy/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-in/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-in/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-is/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-is/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-it/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-it/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-iw/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-iw/strings.xml7
-rw-r--r--packages/SettingsLib/res/values-ja/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-ja/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-ka/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-ka/strings.xml7
-rw-r--r--packages/SettingsLib/res/values-kk/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-kk/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-km/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-km/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-kn/arrays.xml22
-rw-r--r--packages/SettingsLib/res/values-kn/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-ko/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-ko/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-ky/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-ky/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-lo/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-lo/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-lt/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-lt/strings.xml7
-rw-r--r--packages/SettingsLib/res/values-lv/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-lv/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-mk/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-mk/strings.xml7
-rw-r--r--packages/SettingsLib/res/values-ml/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-ml/strings.xml7
-rw-r--r--packages/SettingsLib/res/values-mn/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-mn/strings.xml7
-rw-r--r--packages/SettingsLib/res/values-mr/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-mr/strings.xml7
-rw-r--r--packages/SettingsLib/res/values-ms/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-ms/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-my/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-my/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-nb/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-nb/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-ne/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-ne/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-nl/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-nl/strings.xml7
-rw-r--r--packages/SettingsLib/res/values-or/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-or/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-pa/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-pa/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-pl/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-pl/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-pt-rBR/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-pt-rBR/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-pt-rPT/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-pt-rPT/strings.xml7
-rw-r--r--packages/SettingsLib/res/values-pt/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-pt/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-ro/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-ro/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-ru/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-ru/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-si/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-si/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-sk/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-sk/strings.xml7
-rw-r--r--packages/SettingsLib/res/values-sl/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-sl/strings.xml7
-rw-r--r--packages/SettingsLib/res/values-sq/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-sq/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-sr/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-sr/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-sv/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-sv/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-sw/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-sw/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-ta/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-ta/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-te/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-te/strings.xml7
-rw-r--r--packages/SettingsLib/res/values-th/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-th/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-tl/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-tl/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-tr/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-tr/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-uk/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-uk/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-ur/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-ur/strings.xml7
-rw-r--r--packages/SettingsLib/res/values-uz/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-uz/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-vi/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-vi/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-zh-rCN/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-zh-rCN/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-zh-rHK/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-zh-rHK/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-zh-rTW/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-zh-rTW/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-zu/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values-zu/strings.xml8
-rw-r--r--packages/SettingsLib/res/values/arrays.xml6
-rw-r--r--packages/SettingsLib/res/values/strings.xml3
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java17
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java8
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java7
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java7
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ButtonPreferenceTest.java1
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt34
-rw-r--r--packages/SystemUI/docs/device-entry/keyguard.md4
-rw-r--r--packages/SystemUI/docs/device-entry/quickaffordance.md25
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml4
-rw-r--r--packages/SystemUI/res/drawable-hdpi/textfield_default_filled.9.pngbin0 -> 5959 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/textfield_default_filled.9.pngbin0 -> 5927 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/textfield_default_filled.9.pngbin0 -> 5781 bytes
-rw-r--r--packages/SystemUI/res/drawable-xxhdpi/textfield_default_filled.9.pngbin0 -> 5843 bytes
-rw-r--r--packages/SystemUI/res/drawable/edit_text_filled.xml36
-rw-r--r--packages/SystemUI/res/layout-land/auth_credential_password_view.xml92
-rw-r--r--packages/SystemUI/res/layout/auth_credential_password_view.xml101
-rw-r--r--packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml10
-rw-r--r--packages/SystemUI/res/values-land/styles.xml9
-rw-r--r--packages/SystemUI/res/values/dimens.xml4
-rw-r--r--packages/SystemUI/res/values/strings.xml3
-rw-r--r--packages/SystemUI/res/values/styles.xml9
-rw-r--r--packages/SystemUI/res/xml/qqs_header.xml10
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt51
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl10
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java7
-rw-r--r--packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt5
-rw-r--r--packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java15
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java5
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java9
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java14
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java31
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java7
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java40
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherAnchor.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java506
-rw-r--r--packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java165
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistManager.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java82
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java59
-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.java48
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java87
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.java128
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt80
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt85
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/LogMessage.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java79
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java66
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java48
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java109
-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.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java66
-rw-r--r--packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt188
-rw-r--r--packages/SystemUI/src/com/android/systemui/ripple/RippleShaderUtilLibrary.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt82
-rw-r--r--packages/SystemUI/src/com/android/systemui/ripple/SdfShaderLibrary.kt112
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ImageCapture.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt78
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt68
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/PanelView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java104
-rw-r--r--packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenAndDreamTargetFilter.kt (renamed from packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenTargetFilter.kt)6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java294
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt66
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileStatusTrackerFactory.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java105
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt49
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollector.kt49
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollectorImpl.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java50
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java43
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java537
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUserSwitcherAnchorTest.kt53
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt46
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/TestValues.kt23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt5
-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/dreams/complication/ComplicationUtilsTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlags.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt39
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt175
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt)43
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt105
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java142
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java16
-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/qs/tileimpl/QSTileImplTest.java62
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ripple/RippleViewTest.kt51
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt84
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt106
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java89
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenTargetFilterTest.kt)14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java39
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt100
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java101
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessorTest.kt87
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/FakeConnectivityInfoCollector.kt33
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt57
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/FakeSharedPreferencesTest.kt259
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java101
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogLaunchAnimator.kt46
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeLaunchAnimator.kt44
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeSharedPreferences.kt164
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt)0
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt20
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt5
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt10
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java14
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java48
-rw-r--r--services/core/java/com/android/server/VpnManagerService.java9
-rw-r--r--services/core/java/com/android/server/Watchdog.java1
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java15
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java29
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java47
-rw-r--r--services/core/java/com/android/server/am/AssistDataRequester.java66
-rw-r--r--services/core/java/com/android/server/am/CachedAppOptimizer.java834
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java142
-rw-r--r--services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java63
-rw-r--r--services/core/java/com/android/server/am/UserController.java6
-rw-r--r--services/core/java/com/android/server/app/GameManagerService.java468
-rw-r--r--services/core/java/com/android/server/app/GameManagerShellCommand.java79
-rw-r--r--services/core/java/com/android/server/app/GameTaskInfoProvider.java5
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java246
-rw-r--r--services/core/java/com/android/server/appop/AppOpsServiceInterface.java98
-rw-r--r--services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java198
-rw-r--r--services/core/java/com/android/server/appop/PersistenceScheduler.java35
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java36
-rw-r--r--services/core/java/com/android/server/audio/BtHelper.java4
-rw-r--r--services/core/java/com/android/server/audio/PlaybackActivityMonitor.java6
-rw-r--r--services/core/java/com/android/server/audio/SpatializerHelper.java49
-rw-r--r--services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java31
-rw-r--r--services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java32
-rw-r--r--services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java41
-rw-r--r--services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java46
-rw-r--r--services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java30
-rw-r--r--services/core/java/com/android/server/display/AutomaticBrightnessController.java4
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceConfig.java162
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java4
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java274
-rw-r--r--services/core/java/com/android/server/display/RampAnimator.java159
-rw-r--r--services/core/java/com/android/server/display/VirtualDisplayAdapter.java31
-rw-r--r--services/core/java/com/android/server/display/brightness/BrightnessEvent.java8
-rw-r--r--services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java7
-rw-r--r--services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java11
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java22
-rw-r--r--services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java5
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java70
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsStorage.java7
-rw-r--r--services/core/java/com/android/server/locksettings/PasswordSlotManager.java6
-rw-r--r--services/core/java/com/android/server/locksettings/RebootEscrowKeyStoreManager.java8
-rw-r--r--services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java3
-rw-r--r--services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java70
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java2
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/storage/CleanupManager.java4
-rw-r--r--services/core/java/com/android/server/media/MediaButtonReceiverHolder.java2
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java15
-rw-r--r--services/core/java/com/android/server/pm/CrossProfileIntentFilter.java63
-rw-r--r--services/core/java/com/android/server/pm/DeletePackageHelper.java7
-rw-r--r--services/core/java/com/android/server/pm/DumpHelper.java64
-rw-r--r--services/core/java/com/android/server/pm/InitAppsHelper.java6
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java33
-rw-r--r--services/core/java/com/android/server/pm/KnownPackages.java8
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java146
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageVerificationState.java70
-rw-r--r--services/core/java/com/android/server/pm/SuspendPackageHelper.java7
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java187
-rw-r--r--services/core/java/com/android/server/pm/UserTypeDetails.java37
-rw-r--r--services/core/java/com/android/server/pm/UserTypeFactory.java2
-rw-r--r--services/core/java/com/android/server/pm/VerifyingSession.java203
-rw-r--r--services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java2
-rw-r--r--services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java131
-rw-r--r--services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java4
-rw-r--r--services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java18
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java7
-rw-r--r--services/core/java/com/android/server/wm/ActivityClientController.java8
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java44
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java22
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java8
-rw-r--r--services/core/java/com/android/server/wm/CompatModePackages.java15
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java33
-rw-r--r--services/core/java/com/android/server/wm/InsetsPolicy.java18
-rw-r--r--services/core/java/com/android/server/wm/LockTaskController.java22
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java17
-rw-r--r--services/core/java/com/android/server/wm/RunningTasks.java6
-rw-r--r--services/core/java/com/android/server/wm/Task.java21
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java5
-rw-r--r--services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java448
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java7
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java15
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java14
-rw-r--r--services/core/java/com/android/server/wm/WindowToken.java7
-rw-r--r--services/core/java/com/android/server/wm/WindowTracing.java6
-rw-r--r--services/core/jni/com_android_server_am_CachedAppOptimizer.cpp24
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp8
-rw-r--r--services/core/xsd/display-device-config/display-device-config.xsd18
-rw-r--r--services/core/xsd/display-device-config/schema/current.txt10
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java5
-rw-r--r--services/proguard.flags6
-rw-r--r--services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt1
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java193
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java155
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java8
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java236
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java21
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/InitAppsHelperTest.kt135
-rw-r--r--services/tests/servicestests/Android.bp3
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java29
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/FakeGateKeeperService.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStrongAuthTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/PasswordSlotManagerTests.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/CleanupManagerTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageVerificationStateTest.java168
-rw-r--r--services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java53
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java38
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/CompatModePackagesTests.java95
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java18
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java63
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java38
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java109
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskTests.java1
-rw-r--r--services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java9
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java90
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java10
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceShellCommand.java4
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java13
-rw-r--r--telephony/java/android/telephony/AccessNetworkConstants.java16
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java7
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java2
-rw-r--r--test-mock/api/test-current.txt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt6
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt26
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/CameraAppHelper.kt54
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt189
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt40
-rw-r--r--tools/aapt2/link/ManifestFixer.cpp1
-rw-r--r--tools/stringslint/stringslint.py234
-rwxr-xr-xtools/stringslint/stringslint_sha.sh6
-rw-r--r--tools/xmlpersistence/src/main/kotlin/Generator.kt1
762 files changed, 19495 insertions, 8829 deletions
diff --git a/INPUT_OWNERS b/INPUT_OWNERS
index 051216fdf3c6..e02ba770cdf8 100644
--- a/INPUT_OWNERS
+++ b/INPUT_OWNERS
@@ -1,4 +1,7 @@
# Bug component: 136048
+hcutts@google.com
+joseprio@google.com
michaelwr@google.com
prabirmsp@google.com
svv@google.com
+vdevmurari@google.com
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 65b2511bb30f..f8aa7e9bab30 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -17,17 +17,12 @@ clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
tests/
tools/
bpfmt = -d
+
[Hook Scripts]
checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
-strings_lint_hook = ${REPO_ROOT}/frameworks/base/tools/stringslint/stringslint_sha.sh ${PREUPLOAD_COMMIT}
-
hidden_api_txt_checksorted_hook = ${REPO_ROOT}/tools/platform-compat/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
hidden_api_txt_exclude_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/exclude.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py -f ${PREUPLOAD_FILES}
-
-owners_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "OWNERS$"
-
-shell_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "^packages/Shell/"
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index 40a3c9292574..448ee6160ce0 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -316,8 +316,6 @@ public class UserLifecycleTests {
mIam.startUserInBackground(userId);
}, Intent.ACTION_USER_STARTED, Intent.ACTION_MEDIA_MOUNTED);
- waitForBroadcastIdle();
-
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
@@ -680,8 +678,6 @@ public class UserLifecycleTests {
}
private void stopUser(int userId, boolean force) throws RemoteException {
- waitForBroadcastIdle();
-
final CountDownLatch latch = new CountDownLatch(1);
mIam.stopUser(userId, force /* force */, new IStopUserCallback.Stub() {
@Override
@@ -886,8 +882,4 @@ public class UserLifecycleTests {
assertEquals("", ShellHelper.runShellCommand("setprop " + name + " " + value));
return oldValue;
}
-
- private void waitForBroadcastIdle() {
- ShellHelper.runShellCommand("am wait-for-broadcast-idle");
- }
}
diff --git a/apct-tests/perftests/surfaceflinger/AndroidTest.xml b/apct-tests/perftests/surfaceflinger/AndroidTest.xml
index b8bd8b42917b..0f3a0681f85e 100644
--- a/apct-tests/perftests/surfaceflinger/AndroidTest.xml
+++ b/apct-tests/perftests/surfaceflinger/AndroidTest.xml
@@ -52,12 +52,20 @@
<!-- PerfettoListener related arguments -->
<option name="instrumentation-arg" key="perfetto_config_text_proto" value="true" />
<option name="instrumentation-arg" key="perfetto_config_file" value="trace_config.textproto" />
+
+ <!-- SimpleperfListener related arguments -->
+ <option name="instrumentation-arg" key="report" value="true" />
+ <option name="instrumentation-arg" key="arguments" value="&quot;&quot;" />
+ <option name="instrumentation-arg" key="events_to_record" value="instructions,cpu-cycles,raw-l3d-cache-refill,sched:sched_waking" />
+ <option name="instrumentation-arg" key="processes_to_record" value="surfaceflinger" />
+ <option name="instrumentation-arg" key="symbols_to_report" value="&quot;android::SurfaceFlinger::commit(long, long, long)&quot;" />
</test>
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
<option name="directory-keys" value="/data/local/tmp/SurfaceFlingerPerfTests" />
<!-- Needed for pulling the collected trace config on to the host -->
<option name="pull-pattern-keys" value="perfetto_file_path" />
+ <option name="pull-pattern-keys" value="simpleperf_file_path" />
</metrics_collector>
</configuration>
diff --git a/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java b/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java
index 347cb7867a36..8efe48daed22 100644
--- a/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java
+++ b/apct-tests/perftests/surfaceflinger/src/android/surfaceflinger/SurfaceFlingerPerfTest.java
@@ -48,6 +48,5 @@ public class SurfaceFlingerPerfTest {
while (state.keepRunning()) {
// Do Something
}
-
}
}
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 cd93551469ac..d8e25b6bf5a2 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -5218,13 +5218,15 @@ public class AlarmManagerService extends SystemService {
+ TareBill.getName(bill) + " changed to " + canAfford);
}
- ArrayMap<EconomyManagerInternal.ActionBill, Boolean> actionAffordability =
- mAffordabilityCache.get(userId, packageName);
- if (actionAffordability == null) {
- actionAffordability = new ArrayMap<>();
- mAffordabilityCache.add(userId, packageName, actionAffordability);
+ synchronized (mLock) {
+ ArrayMap<EconomyManagerInternal.ActionBill, Boolean> actionAffordability =
+ mAffordabilityCache.get(userId, packageName);
+ if (actionAffordability == null) {
+ actionAffordability = new ArrayMap<>();
+ mAffordabilityCache.add(userId, packageName, actionAffordability);
+ }
+ actionAffordability.put(bill, canAfford);
}
- actionAffordability.put(bill, canAfford);
mHandler.obtainMessage(AlarmHandler.TARE_AFFORDABILITY_CHANGED, userId,
canAfford ? 1 : 0, packageName)
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 29cd9963f8e4..52882fe5d565 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
@@ -546,6 +546,9 @@ public final class JobStatus {
if (latestRunTimeElapsedMillis != NO_LATEST_RUNTIME) {
requiredConstraints |= CONSTRAINT_DEADLINE;
}
+ if (job.isPrefetch()) {
+ requiredConstraints |= CONSTRAINT_PREFETCH;
+ }
boolean exemptedMediaUrisOnly = false;
if (job.getTriggerContentUris() != null) {
requiredConstraints |= CONSTRAINT_CONTENT_TRIGGER;
@@ -1135,11 +1138,6 @@ public final class JobStatus {
return hasConstraint(CONSTRAINT_IDLE);
}
- /** Returns true if the job has a prefetch constraint */
- public boolean hasPrefetchConstraint() {
- return hasConstraint(CONSTRAINT_PREFETCH);
- }
-
public boolean hasContentTriggerConstraint() {
// No need to check mDynamicConstraints since content trigger will only be in that list if
// it's already in the requiredConstraints list.
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
index 2da00c7a8e2c..6d5c16021ea9 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -274,24 +274,19 @@ public class InternalResourceService extends SystemService {
public void onBootPhase(int phase) {
mBootPhase = phase;
- if (PHASE_SYSTEM_SERVICES_READY == phase) {
- mConfigObserver.start();
- mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
- ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
- setupEverything();
- } else if (PHASE_BOOT_COMPLETED == phase) {
- if (!mExemptListLoaded) {
- synchronized (mLock) {
- try {
- mExemptedApps =
- new ArraySet<>(mDeviceIdleController.getFullPowerWhitelist());
- } catch (RemoteException e) {
- // Shouldn't happen.
- Slog.wtf(TAG, e);
- }
- mExemptListLoaded = true;
- }
- }
+ switch (phase) {
+ case PHASE_SYSTEM_SERVICES_READY:
+ mConfigObserver.start();
+ mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
+ ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
+ onBootPhaseSystemServicesReady();
+ break;
+ case PHASE_THIRD_PARTY_APPS_CAN_START:
+ onBootPhaseThirdPartyAppsCanStart();
+ break;
+ case PHASE_BOOT_COMPLETED:
+ onBootPhaseBootCompleted();
+ break;
}
}
@@ -403,10 +398,9 @@ public class InternalResourceService extends SystemService {
final ArraySet<String> added = new ArraySet<>();
try {
mExemptedApps = new ArraySet<>(mDeviceIdleController.getFullPowerWhitelist());
+ mExemptListLoaded = true;
} catch (RemoteException e) {
// Shouldn't happen.
- Slog.wtf(TAG, e);
- return;
}
for (int i = mExemptedApps.size() - 1; i >= 0; --i) {
@@ -695,17 +689,11 @@ public class InternalResourceService extends SystemService {
/** Perform long-running and/or heavy setup work. This should be called off the main thread. */
private void setupHeavyWork() {
+ if (mBootPhase < PHASE_THIRD_PARTY_APPS_CAN_START || !mIsEnabled) {
+ return;
+ }
synchronized (mLock) {
loadInstalledPackageListLocked();
- if (mBootPhase >= PHASE_BOOT_COMPLETED && !mExemptListLoaded) {
- try {
- mExemptedApps = new ArraySet<>(mDeviceIdleController.getFullPowerWhitelist());
- } catch (RemoteException e) {
- // Shouldn't happen.
- Slog.wtf(TAG, e);
- }
- mExemptListLoaded = true;
- }
final boolean isFirstSetup = !mScribe.recordExists();
if (isFirstSetup) {
mAgent.grantBirthrightsLocked();
@@ -726,18 +714,57 @@ public class InternalResourceService extends SystemService {
}
}
- private void setupEverything() {
+ private void onBootPhaseSystemServicesReady() {
if (mBootPhase < PHASE_SYSTEM_SERVICES_READY || !mIsEnabled) {
return;
}
synchronized (mLock) {
registerListeners();
mCurrentBatteryLevel = getCurrentBatteryLevel();
+ }
+ }
+
+ private void onBootPhaseThirdPartyAppsCanStart() {
+ if (mBootPhase < PHASE_THIRD_PARTY_APPS_CAN_START || !mIsEnabled) {
+ return;
+ }
+ synchronized (mLock) {
mHandler.post(this::setupHeavyWork);
mCompleteEconomicPolicy.setup(mConfigObserver.getAllDeviceConfigProperties());
}
}
+ private void onBootPhaseBootCompleted() {
+ if (mBootPhase < PHASE_BOOT_COMPLETED || !mIsEnabled) {
+ return;
+ }
+ synchronized (mLock) {
+ if (!mExemptListLoaded) {
+ try {
+ mExemptedApps = new ArraySet<>(mDeviceIdleController.getFullPowerWhitelist());
+ mExemptListLoaded = true;
+ } catch (RemoteException e) {
+ // Shouldn't happen.
+ }
+ }
+ }
+ }
+
+ private void setupEverything() {
+ if (!mIsEnabled) {
+ return;
+ }
+ if (mBootPhase >= PHASE_SYSTEM_SERVICES_READY) {
+ onBootPhaseSystemServicesReady();
+ }
+ if (mBootPhase >= PHASE_THIRD_PARTY_APPS_CAN_START) {
+ onBootPhaseThirdPartyAppsCanStart();
+ }
+ if (mBootPhase >= PHASE_BOOT_COMPLETED) {
+ onBootPhaseBootCompleted();
+ }
+ }
+
private void tearDownEverything() {
if (mIsEnabled) {
return;
diff --git a/boot/hiddenapi/hiddenapi-max-target-r-loprio.txt b/boot/hiddenapi/hiddenapi-max-target-r-loprio.txt
index dbc3b5102efe..f5184e7963d7 100644
--- a/boot/hiddenapi/hiddenapi-max-target-r-loprio.txt
+++ b/boot/hiddenapi/hiddenapi-max-target-r-loprio.txt
@@ -39,7 +39,6 @@ Lcom/android/ims/internal/uce/options/IOptionsService$Stub;-><init>()V
Lcom/android/ims/internal/uce/presence/IPresenceService$Stub;-><init>()V
Lcom/android/ims/internal/uce/uceservice/IUceListener$Stub;-><init>()V
Lcom/android/ims/internal/uce/uceservice/IUceService$Stub;-><init>()V
-Lcom/android/internal/app/IVoiceInteractionManagerService$Stub$Proxy;->showSessionFromSession(Landroid/os/IBinder;Landroid/os/Bundle;I)Z
Lcom/android/internal/appwidget/IAppWidgetService$Stub;->TRANSACTION_bindAppWidgetId:I
Lcom/android/internal/telephony/ITelephony$Stub;->DESCRIPTOR:Ljava/lang/String;
Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_dial:I
diff --git a/cmds/uinput/src/com/android/commands/uinput/Event.java b/cmds/uinput/src/com/android/commands/uinput/Event.java
index 9add310effaa..4b090f5a713c 100644
--- a/cmds/uinput/src/com/android/commands/uinput/Event.java
+++ b/cmds/uinput/src/com/android/commands/uinput/Event.java
@@ -25,6 +25,7 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.stream.IntStream;
import src.com.android.commands.uinput.InputAbsInfo;
@@ -338,7 +339,7 @@ public class Event {
mReader.beginArray();
while (mReader.hasNext()) {
int type = 0;
- int[] data = null;
+ IntStream data = null;
mReader.beginObject();
while (mReader.hasNext()) {
String name = mReader.nextName();
@@ -347,8 +348,7 @@ public class Event {
type = readInt();
break;
case "data":
- data = readIntList().stream()
- .mapToInt(Integer::intValue).toArray();
+ data = readIntList().stream().mapToInt(Integer::intValue);
break;
default:
consumeRemainingElements();
@@ -359,7 +359,9 @@ public class Event {
}
mReader.endObject();
if (data != null) {
- configuration.put(type, data);
+ final int[] existing = configuration.get(type);
+ configuration.put(type, existing == null ? data.toArray()
+ : IntStream.concat(IntStream.of(existing), data).toArray());
}
}
mReader.endArray();
diff --git a/core/api/current.txt b/core/api/current.txt
index af187922d275..7ed1a4382054 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -11523,6 +11523,7 @@ package android.content.pm {
field public static final int INSTALL_LOCATION_INTERNAL_ONLY = 1; // 0x1
field public static final int INSTALL_LOCATION_PREFER_EXTERNAL = 2; // 0x2
field public static final int REQUESTED_PERMISSION_GRANTED = 2; // 0x2
+ field public static final int REQUESTED_PERMISSION_IMPLICIT = 4; // 0x4
field public static final int REQUESTED_PERMISSION_NEVER_FOR_LOCATION = 65536; // 0x10000
field public android.content.pm.ActivityInfo[] activities;
field public android.content.pm.ApplicationInfo applicationInfo;
@@ -18904,6 +18905,7 @@ package android.inputmethodservice {
method public void onUnbindInput();
method @Deprecated public void onUpdateCursor(android.graphics.Rect);
method public void onUpdateCursorAnchorInfo(android.view.inputmethod.CursorAnchorInfo);
+ method public void onUpdateEditorToolType(int);
method public void onUpdateExtractedText(int, android.view.inputmethod.ExtractedText);
method public void onUpdateExtractingViews(android.view.inputmethod.EditorInfo);
method public void onUpdateExtractingVisibility(android.view.inputmethod.EditorInfo);
@@ -32261,6 +32263,7 @@ package android.os {
method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public boolean isUserRunningOrStopping(android.os.UserHandle);
method public boolean isUserUnlocked();
method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public boolean isUserUnlocked(android.os.UserHandle);
+ method public boolean isUserVisible();
method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.MODIFY_QUIET_MODE"}, conditional=true) public boolean requestQuietModeEnabled(boolean, @NonNull android.os.UserHandle);
method public boolean requestQuietModeEnabled(boolean, @NonNull android.os.UserHandle, int);
method @Deprecated public boolean setRestrictionsChallenge(String);
@@ -45246,6 +45249,8 @@ package android.text {
method public static boolean extendRight(android.text.Spannable, android.text.Layout);
method public static final void extendSelection(android.text.Spannable, int);
method public static boolean extendToLeftEdge(android.text.Spannable, android.text.Layout);
+ method public static boolean extendToParagraphEnd(@NonNull android.text.Spannable);
+ method public static boolean extendToParagraphStart(@NonNull android.text.Spannable);
method public static boolean extendToRightEdge(android.text.Spannable, android.text.Layout);
method public static boolean extendUp(android.text.Spannable, android.text.Layout);
method public static final int getSelectionEnd(CharSequence);
@@ -45254,6 +45259,8 @@ package android.text {
method public static boolean moveLeft(android.text.Spannable, android.text.Layout);
method public static boolean moveRight(android.text.Spannable, android.text.Layout);
method public static boolean moveToLeftEdge(android.text.Spannable, android.text.Layout);
+ method public static boolean moveToParagraphEnd(@NonNull android.text.Spannable, @NonNull android.text.Layout);
+ method public static boolean moveToParagraphStart(@NonNull android.text.Spannable, @NonNull android.text.Layout);
method public static boolean moveToRightEdge(android.text.Spannable, android.text.Layout);
method public static boolean moveUp(android.text.Spannable, android.text.Layout);
method public static final void removeSelection(android.text.Spannable);
@@ -45702,6 +45709,7 @@ package android.text.method {
method protected boolean left(android.widget.TextView, android.text.Spannable);
method protected boolean lineEnd(android.widget.TextView, android.text.Spannable);
method protected boolean lineStart(android.widget.TextView, android.text.Spannable);
+ method public boolean nextParagraph(@NonNull android.widget.TextView, @NonNull android.text.Spannable);
method public boolean onGenericMotionEvent(android.widget.TextView, android.text.Spannable, android.view.MotionEvent);
method public boolean onKeyDown(android.widget.TextView, android.text.Spannable, int, android.view.KeyEvent);
method public boolean onKeyOther(android.widget.TextView, android.text.Spannable, android.view.KeyEvent);
@@ -45711,6 +45719,7 @@ package android.text.method {
method public boolean onTrackballEvent(android.widget.TextView, android.text.Spannable, android.view.MotionEvent);
method protected boolean pageDown(android.widget.TextView, android.text.Spannable);
method protected boolean pageUp(android.widget.TextView, android.text.Spannable);
+ method public boolean previousParagraph(@NonNull android.widget.TextView, @NonNull android.text.Spannable);
method protected boolean right(android.widget.TextView, android.text.Spannable);
method protected boolean top(android.widget.TextView, android.text.Spannable);
method protected boolean up(android.widget.TextView, android.text.Spannable);
@@ -49221,6 +49230,10 @@ package android.view {
}
public final class PixelCopy {
+ method @NonNull public static android.view.PixelCopy.Request ofSurface(@NonNull android.view.Surface, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.PixelCopy.CopyResult>);
+ method @NonNull public static android.view.PixelCopy.Request ofSurface(@NonNull android.view.SurfaceView, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.PixelCopy.CopyResult>);
+ method @NonNull public static android.view.PixelCopy.Request ofWindow(@NonNull android.view.Window, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.PixelCopy.CopyResult>);
+ method @NonNull public static android.view.PixelCopy.Request ofWindow(@NonNull android.view.View, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.PixelCopy.CopyResult>);
method public static void request(@NonNull android.view.SurfaceView, @NonNull android.graphics.Bitmap, @NonNull android.view.PixelCopy.OnPixelCopyFinishedListener, @NonNull android.os.Handler);
method public static void request(@NonNull android.view.SurfaceView, @Nullable android.graphics.Rect, @NonNull android.graphics.Bitmap, @NonNull android.view.PixelCopy.OnPixelCopyFinishedListener, @NonNull android.os.Handler);
method public static void request(@NonNull android.view.Surface, @NonNull android.graphics.Bitmap, @NonNull android.view.PixelCopy.OnPixelCopyFinishedListener, @NonNull android.os.Handler);
@@ -49235,10 +49248,21 @@ package android.view {
field public static final int SUCCESS = 0; // 0x0
}
+ public static final class PixelCopy.CopyResult {
+ method @NonNull public android.graphics.Bitmap getBitmap();
+ method public int getStatus();
+ }
+
public static interface PixelCopy.OnPixelCopyFinishedListener {
method public void onPixelCopyFinished(int);
}
+ public static final class PixelCopy.Request {
+ method public void request();
+ method @NonNull public android.view.PixelCopy.Request setDestinationBitmap(@Nullable android.graphics.Bitmap);
+ method @NonNull public android.view.PixelCopy.Request setSourceRect(@Nullable android.graphics.Rect);
+ }
+
public final class PointerIcon implements android.os.Parcelable {
method @NonNull public static android.view.PointerIcon create(@NonNull android.graphics.Bitmap, float, float);
method public int describeContents();
@@ -52968,9 +52992,11 @@ package android.view.inputmethod {
method @Nullable public android.view.inputmethod.SurroundingText getInitialSurroundingText(@IntRange(from=0) int, @IntRange(from=0) int, int);
method @Nullable public CharSequence getInitialTextAfterCursor(@IntRange(from=0) int, int);
method @Nullable public CharSequence getInitialTextBeforeCursor(@IntRange(from=0) int, int);
+ method public int getInitialToolType();
method public final void makeCompatible(int);
method public void setInitialSurroundingSubText(@NonNull CharSequence, int);
method public void setInitialSurroundingText(@NonNull CharSequence);
+ method public void setInitialToolType(int);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.EditorInfo> CREATOR;
field public static final int IME_ACTION_DONE = 6; // 0x6
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 09d4ba6e0bff..ddbccfa8f404 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -824,6 +824,8 @@ package android.content.pm {
}
public abstract class PackageManager {
+ method public abstract void addCrossProfileIntentFilter(@NonNull android.content.IntentFilter, int, int, int);
+ method public abstract void clearCrossProfileIntentFilters(int);
method @Deprecated @Nullable public final String getContentCaptureServicePackageName();
method @Nullable public String getDefaultTextClassifierPackageName();
method @RequiresPermission(android.Manifest.permission.INJECT_EVENTS) public android.os.IBinder getHoldLockToken();
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 90b78288d0c6..e86d2f35ef16 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -43,9 +43,9 @@ import android.hardware.display.DisplayManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.Looper;
-import android.os.Message;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -72,10 +72,7 @@ import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback;
import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
import com.android.internal.inputmethod.RemoteAccessibilityInputConnection;
-import com.android.internal.os.HandlerCaller;
-import com.android.internal.os.SomeArgs;
import com.android.internal.util.Preconditions;
-import com.android.internal.util.function.pooled.PooledLambda;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -2634,7 +2631,7 @@ public abstract class AccessibilityService extends Service {
*/
@Override
public final IBinder onBind(Intent intent) {
- return new IAccessibilityServiceClientWrapper(this, getMainLooper(), new Callbacks() {
+ return new IAccessibilityServiceClientWrapper(this, getMainExecutor(), new Callbacks() {
@Override
public void onServiceConnected() {
AccessibilityService.this.dispatchServiceConnected();
@@ -2751,30 +2748,12 @@ public abstract class AccessibilityService extends Service {
*
* @hide
*/
- public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub
- implements HandlerCaller.Callback {
- private static final int DO_INIT = 1;
- private static final int DO_ON_INTERRUPT = 2;
- private static final int DO_ON_ACCESSIBILITY_EVENT = 3;
- private static final int DO_ON_GESTURE = 4;
- private static final int DO_CLEAR_ACCESSIBILITY_CACHE = 5;
- private static final int DO_ON_KEY_EVENT = 6;
- private static final int DO_ON_MAGNIFICATION_CHANGED = 7;
- private static final int DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED = 8;
- private static final int DO_GESTURE_COMPLETE = 9;
- private static final int DO_ON_FINGERPRINT_ACTIVE_CHANGED = 10;
- private static final int DO_ON_FINGERPRINT_GESTURE = 11;
- private static final int DO_ACCESSIBILITY_BUTTON_CLICKED = 12;
- private static final int DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED = 13;
- private static final int DO_ON_SYSTEM_ACTIONS_CHANGED = 14;
- private static final int DO_CREATE_IME_SESSION = 15;
- private static final int DO_SET_IME_SESSION_ENABLED = 16;
- private static final int DO_START_INPUT = 19;
-
- private final HandlerCaller mCaller;
+ public static class IAccessibilityServiceClientWrapper extends
+ IAccessibilityServiceClient.Stub {
private final Callbacks mCallback;
private final Context mContext;
+ private final Executor mExecutor;
private int mConnectionId = AccessibilityInteractionClient.NO_ID;
@@ -2793,103 +2772,199 @@ public abstract class AccessibilityService extends Service {
@Nullable
CancellationGroup mCancellationGroup = null;
+ public IAccessibilityServiceClientWrapper(Context context, Executor executor,
+ Callbacks callback) {
+ mCallback = callback;
+ mContext = context;
+ mExecutor = executor;
+ }
+
public IAccessibilityServiceClientWrapper(Context context, Looper looper,
Callbacks callback) {
mCallback = callback;
mContext = context;
- mCaller = new HandlerCaller(context, looper, this, true /*asyncHandler*/);
+ mExecutor = new HandlerExecutor(new Handler(looper));
}
public void init(IAccessibilityServiceConnection connection, int connectionId,
IBinder windowToken) {
- Message message = mCaller.obtainMessageIOO(DO_INIT, connectionId,
- connection, windowToken);
- mCaller.sendMessage(message);
+ mExecutor.execute(() -> {
+ mConnectionId = connectionId;
+ if (connection != null) {
+ AccessibilityInteractionClient.getInstance(mContext).addConnection(
+ mConnectionId, connection, /*initializeCache=*/true);
+ if (mContext != null) {
+ try {
+ connection.setAttributionTag(mContext.getAttributionTag());
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Error while setting attributionTag", re);
+ re.rethrowFromSystemServer();
+ }
+ }
+ mCallback.init(mConnectionId, windowToken);
+ mCallback.onServiceConnected();
+ } else {
+ AccessibilityInteractionClient.getInstance(mContext)
+ .clearCache(mConnectionId);
+ AccessibilityInteractionClient.getInstance(mContext).removeConnection(
+ mConnectionId);
+ mConnectionId = AccessibilityInteractionClient.NO_ID;
+ mCallback.init(AccessibilityInteractionClient.NO_ID, null);
+ }
+ return;
+ });
}
public void onInterrupt() {
- Message message = mCaller.obtainMessage(DO_ON_INTERRUPT);
- mCaller.sendMessage(message);
+ mExecutor.execute(() -> {
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ mCallback.onInterrupt();
+ }
+ });
}
public void onAccessibilityEvent(AccessibilityEvent event, boolean serviceWantsEvent) {
- Message message = mCaller.obtainMessageBO(
- DO_ON_ACCESSIBILITY_EVENT, serviceWantsEvent, event);
- mCaller.sendMessage(message);
+ mExecutor.execute(() -> {
+ if (event != null) {
+ // Send the event to AccessibilityCache via AccessibilityInteractionClient
+ AccessibilityInteractionClient.getInstance(mContext).onAccessibilityEvent(
+ event, mConnectionId);
+ if (serviceWantsEvent
+ && (mConnectionId != AccessibilityInteractionClient.NO_ID)) {
+ // Send the event to AccessibilityService
+ mCallback.onAccessibilityEvent(event);
+ }
+ }
+ return;
+ });
}
@Override
public void onGesture(AccessibilityGestureEvent gestureInfo) {
- Message message = mCaller.obtainMessageO(DO_ON_GESTURE, gestureInfo);
- mCaller.sendMessage(message);
+ mExecutor.execute(() -> {
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ mCallback.onGesture(gestureInfo);
+ }
+ return;
+ });
}
public void clearAccessibilityCache() {
- Message message = mCaller.obtainMessage(DO_CLEAR_ACCESSIBILITY_CACHE);
- mCaller.sendMessage(message);
+ mExecutor.execute(() -> {
+ AccessibilityInteractionClient.getInstance(mContext).clearCache(mConnectionId);
+ return;
+ });
}
@Override
public void onKeyEvent(KeyEvent event, int sequence) {
- Message message = mCaller.obtainMessageIO(DO_ON_KEY_EVENT, sequence, event);
- mCaller.sendMessage(message);
+ mExecutor.execute(() -> {
+ try {
+ IAccessibilityServiceConnection connection = AccessibilityInteractionClient
+ .getInstance(mContext).getConnection(mConnectionId);
+ if (connection != null) {
+ final boolean result = mCallback.onKeyEvent(event);
+ try {
+ connection.setOnKeyEventResult(result, sequence);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ }
+ } finally {
+ // Make sure the event is recycled.
+ try {
+ event.recycle();
+ } catch (IllegalStateException ise) {
+ /* ignore - best effort */
+ }
+ }
+ return;
+ });
}
/** Magnification changed callbacks for different displays */
public void onMagnificationChanged(int displayId, @NonNull Region region,
MagnificationConfig config) {
- final SomeArgs args = SomeArgs.obtain();
- args.arg1 = region;
- args.arg2 = config;
- args.argi1 = displayId;
-
- final Message message = mCaller.obtainMessageO(DO_ON_MAGNIFICATION_CHANGED, args);
- mCaller.sendMessage(message);
+ mExecutor.execute(() -> {
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ mCallback.onMagnificationChanged(displayId, region, config);
+ }
+ return;
+ });
}
public void onSoftKeyboardShowModeChanged(int showMode) {
- final Message message =
- mCaller.obtainMessageI(DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED, showMode);
- mCaller.sendMessage(message);
+ mExecutor.execute(() -> {
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ mCallback.onSoftKeyboardShowModeChanged(showMode);
+ }
+ return;
+ });
}
public void onPerformGestureResult(int sequence, boolean successfully) {
- Message message = mCaller.obtainMessageII(DO_GESTURE_COMPLETE, sequence,
- successfully ? 1 : 0);
- mCaller.sendMessage(message);
+ mExecutor.execute(() -> {
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ mCallback.onPerformGestureResult(sequence, successfully);
+ }
+ return;
+ });
}
public void onFingerprintCapturingGesturesChanged(boolean active) {
- mCaller.sendMessage(mCaller.obtainMessageI(
- DO_ON_FINGERPRINT_ACTIVE_CHANGED, active ? 1 : 0));
+ mExecutor.execute(() -> {
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ mCallback.onFingerprintCapturingGesturesChanged(active);
+ }
+ return;
+ });
}
public void onFingerprintGesture(int gesture) {
- mCaller.sendMessage(mCaller.obtainMessageI(DO_ON_FINGERPRINT_GESTURE, gesture));
+ mExecutor.execute(() -> {
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ mCallback.onFingerprintGesture(gesture);
+ }
+ return;
+ });
}
/** Accessibility button clicked callbacks for different displays */
public void onAccessibilityButtonClicked(int displayId) {
- final Message message = mCaller.obtainMessageI(DO_ACCESSIBILITY_BUTTON_CLICKED,
- displayId);
- mCaller.sendMessage(message);
+ mExecutor.execute(() -> {
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ mCallback.onAccessibilityButtonClicked(displayId);
+ }
+ return;
+ });
}
public void onAccessibilityButtonAvailabilityChanged(boolean available) {
- final Message message = mCaller.obtainMessageI(
- DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED, (available ? 1 : 0));
- mCaller.sendMessage(message);
+ mExecutor.execute(() -> {
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ mCallback.onAccessibilityButtonAvailabilityChanged(available);
+ }
+ return;
+ });
}
/** This is called when the system action list is changed. */
public void onSystemActionsChanged() {
- mCaller.sendMessage(mCaller.obtainMessage(DO_ON_SYSTEM_ACTIONS_CHANGED));
+ mExecutor.execute(() -> {
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ mCallback.onSystemActionsChanged();
+ }
+ return;
+ });
}
/** This is called when an app requests ime sessions or when the service is enabled. */
public void createImeSession(IAccessibilityInputMethodSessionCallback callback) {
- final Message message = mCaller.obtainMessageO(DO_CREATE_IME_SESSION, callback);
- mCaller.sendMessage(message);
+ mExecutor.execute(() -> {
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ mCallback.createImeSession(callback);
+ }
+ });
}
/**
@@ -2905,8 +2980,12 @@ public abstract class AccessibilityService extends Service {
Log.w(LOG_TAG, "Session is already finished: " + session);
return;
}
- mCaller.sendMessage(mCaller.obtainMessageIO(
- DO_SET_IME_SESSION_ENABLED, enabled ? 1 : 0, ls));
+ mExecutor.execute(() -> {
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ ls.setEnabled(enabled);
+ }
+ return;
+ });
} catch (ClassCastException e) {
Log.w(LOG_TAG, "Incoming session not of correct type: " + session, e);
}
@@ -2938,213 +3017,29 @@ public abstract class AccessibilityService extends Service {
Log.e(LOG_TAG, "startInput must be called after bindInput.");
mCancellationGroup = new CancellationGroup();
}
- final Message message = mCaller.obtainMessageOOOOII(DO_START_INPUT, null /* unused */,
- connection, editorInfo, mCancellationGroup, restarting ? 1 : 0,
- 0 /* unused */);
- mCaller.sendMessage(message);
+ mExecutor.execute(() -> {
+ if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+ final RemoteAccessibilityInputConnection ic = connection == null ? null
+ : new RemoteAccessibilityInputConnection(
+ connection, mCancellationGroup);
+ editorInfo.makeCompatible(mContext.getApplicationInfo().targetSdkVersion);
+ mCallback.startInput(ic, editorInfo, restarting);
+ }
+ });
}
@Override
public void onMotionEvent(MotionEvent event) {
- final Message message = PooledLambda.obtainMessage(
- Callbacks::onMotionEvent, mCallback, event);
- mCaller.sendMessage(message);
+ mExecutor.execute(() -> {
+ mCallback.onMotionEvent(event);
+ });
}
@Override
public void onTouchStateChanged(int displayId, int state) {
- final Message message = PooledLambda.obtainMessage(Callbacks::onTouchStateChanged,
- mCallback,
- displayId, state);
- mCaller.sendMessage(message);
- }
-
- @Override
- public void executeMessage(Message message) {
- switch (message.what) {
- case DO_ON_ACCESSIBILITY_EVENT: {
- AccessibilityEvent event = (AccessibilityEvent) message.obj;
- boolean serviceWantsEvent = message.arg1 != 0;
- if (event != null) {
- // Send the event to AccessibilityCache via AccessibilityInteractionClient
- AccessibilityInteractionClient.getInstance(mContext).onAccessibilityEvent(
- event, mConnectionId);
- if (serviceWantsEvent
- && (mConnectionId != AccessibilityInteractionClient.NO_ID)) {
- // Send the event to AccessibilityService
- mCallback.onAccessibilityEvent(event);
- }
- // Make sure the event is recycled.
- try {
- event.recycle();
- } catch (IllegalStateException ise) {
- /* ignore - best effort */
- }
- }
- return;
- }
- case DO_ON_INTERRUPT: {
- if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- mCallback.onInterrupt();
- }
- return;
- }
- case DO_INIT: {
- mConnectionId = message.arg1;
- SomeArgs args = (SomeArgs) message.obj;
- IAccessibilityServiceConnection connection =
- (IAccessibilityServiceConnection) args.arg1;
- IBinder windowToken = (IBinder) args.arg2;
- args.recycle();
- if (connection != null) {
- AccessibilityInteractionClient.getInstance(mContext).addConnection(
- mConnectionId, connection, /*initializeCache=*/true);
- if (mContext != null) {
- try {
- connection.setAttributionTag(mContext.getAttributionTag());
- } catch (RemoteException re) {
- Log.w(LOG_TAG, "Error while setting attributionTag", re);
- re.rethrowFromSystemServer();
- }
- }
- mCallback.init(mConnectionId, windowToken);
- mCallback.onServiceConnected();
- } else {
- AccessibilityInteractionClient.getInstance(mContext)
- .clearCache(mConnectionId);
- AccessibilityInteractionClient.getInstance(mContext).removeConnection(
- mConnectionId);
- mConnectionId = AccessibilityInteractionClient.NO_ID;
- mCallback.init(AccessibilityInteractionClient.NO_ID, null);
- }
- return;
- }
- case DO_ON_GESTURE: {
- if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- mCallback.onGesture((AccessibilityGestureEvent) message.obj);
- }
- return;
- }
- case DO_CLEAR_ACCESSIBILITY_CACHE: {
- AccessibilityInteractionClient.getInstance(mContext).clearCache(mConnectionId);
- return;
- }
- case DO_ON_KEY_EVENT: {
- KeyEvent event = (KeyEvent) message.obj;
- try {
- IAccessibilityServiceConnection connection = AccessibilityInteractionClient
- .getInstance(mContext).getConnection(mConnectionId);
- if (connection != null) {
- final boolean result = mCallback.onKeyEvent(event);
- final int sequence = message.arg1;
- try {
- connection.setOnKeyEventResult(result, sequence);
- } catch (RemoteException re) {
- /* ignore */
- }
- }
- } finally {
- // Make sure the event is recycled.
- try {
- event.recycle();
- } catch (IllegalStateException ise) {
- /* ignore - best effort */
- }
- }
- return;
- }
- case DO_ON_MAGNIFICATION_CHANGED: {
- if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- final SomeArgs args = (SomeArgs) message.obj;
- final Region region = (Region) args.arg1;
- final MagnificationConfig config = (MagnificationConfig) args.arg2;
- final int displayId = args.argi1;
- args.recycle();
- mCallback.onMagnificationChanged(displayId, region, config);
- }
- return;
- }
- case DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED: {
- if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- final int showMode = (int) message.arg1;
- mCallback.onSoftKeyboardShowModeChanged(showMode);
- }
- return;
- }
- case DO_GESTURE_COMPLETE: {
- if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- final boolean successfully = message.arg2 == 1;
- mCallback.onPerformGestureResult(message.arg1, successfully);
- }
- return;
- }
- case DO_ON_FINGERPRINT_ACTIVE_CHANGED: {
- if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- mCallback.onFingerprintCapturingGesturesChanged(message.arg1 == 1);
- }
- return;
- }
- case DO_ON_FINGERPRINT_GESTURE: {
- if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- mCallback.onFingerprintGesture(message.arg1);
- }
- return;
- }
- case DO_ACCESSIBILITY_BUTTON_CLICKED: {
- if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- mCallback.onAccessibilityButtonClicked(message.arg1);
- }
- return;
- }
- case DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED: {
- if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- final boolean available = (message.arg1 != 0);
- mCallback.onAccessibilityButtonAvailabilityChanged(available);
- }
- return;
- }
- case DO_ON_SYSTEM_ACTIONS_CHANGED: {
- if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- mCallback.onSystemActionsChanged();
- }
- return;
- }
- case DO_CREATE_IME_SESSION: {
- if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- IAccessibilityInputMethodSessionCallback callback =
- (IAccessibilityInputMethodSessionCallback) message.obj;
- mCallback.createImeSession(callback);
- }
- return;
- }
- case DO_SET_IME_SESSION_ENABLED: {
- if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- AccessibilityInputMethodSession session =
- (AccessibilityInputMethodSession) message.obj;
- session.setEnabled(message.arg1 != 0);
- }
- return;
- }
- case DO_START_INPUT: {
- if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- final SomeArgs args = (SomeArgs) message.obj;
- final IRemoteAccessibilityInputConnection connection =
- (IRemoteAccessibilityInputConnection) args.arg2;
- final EditorInfo info = (EditorInfo) args.arg3;
- final CancellationGroup cancellationGroup = (CancellationGroup) args.arg4;
- final boolean restarting = args.argi5 == 1;
- final RemoteAccessibilityInputConnection ic = connection == null ? null
- : new RemoteAccessibilityInputConnection(
- connection, cancellationGroup);
- info.makeCompatible(mContext.getApplicationInfo().targetSdkVersion);
- mCallback.startInput(ic, info, restarting);
- args.recycle();
- }
- return;
- }
- default:
- Log.w(LOG_TAG, "Unknown message type " + message.what);
- }
+ mExecutor.execute(() -> {
+ mCallback.onTouchStateChanged(displayId, state);
+ });
}
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index e25e374122cf..449729e18376 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -5124,7 +5124,7 @@ public class ActivityManager {
Preconditions.checkNotNull(listener);
Preconditions.checkNotNull(executor);
try {
- listener.init(mContext, executor, this);
+ listener.init(mContext, executor);
getService().registerProcessObserver(listener.mObserver);
// Notify upon first registration.
executor.execute(() ->
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 1a06c6fd1642..f36220460505 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -1453,6 +1453,11 @@ public class ActivityOptions extends ComponentOptions {
}
/** @hide */
+ public void setRemoteTransition(@Nullable RemoteTransition remoteTransition) {
+ mRemoteTransition = remoteTransition;
+ }
+
+ /** @hide */
public static ActivityOptions fromBundle(Bundle bOptions) {
return bOptions != null ? new ActivityOptions(bOptions) : null;
}
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 6fc0c260d89e..f17d5b7c0190 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -16,6 +16,8 @@
package android.app;
+import static android.view.Display.INVALID_DISPLAY;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -369,7 +371,8 @@ public class ActivityTaskManager {
* @hide
*/
public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) {
- return getTasks(maxNum, false /* filterForVisibleRecents */);
+ return getTasks(maxNum, false /* filterForVisibleRecents */, false /* keepIntentExtra */,
+ INVALID_DISPLAY);
}
/**
@@ -378,7 +381,8 @@ public class ActivityTaskManager {
*/
public List<ActivityManager.RunningTaskInfo> getTasks(
int maxNum, boolean filterOnlyVisibleRecents) {
- return getTasks(maxNum, filterOnlyVisibleRecents, false /* keepIntentExtra */);
+ return getTasks(maxNum, filterOnlyVisibleRecents, false /* keepIntentExtra */,
+ INVALID_DISPLAY);
}
/**
@@ -388,8 +392,20 @@ public class ActivityTaskManager {
*/
public List<ActivityManager.RunningTaskInfo> getTasks(
int maxNum, boolean filterOnlyVisibleRecents, boolean keepIntentExtra) {
+ return getTasks(maxNum, filterOnlyVisibleRecents, keepIntentExtra, INVALID_DISPLAY);
+ }
+
+ /**
+ * @return List of running tasks that can be filtered by visibility and displayId in recents
+ * and keep intent extra.
+ * @param displayId the target display id, or {@link INVALID_DISPLAY} not to filter by displayId
+ * @hide
+ */
+ public List<ActivityManager.RunningTaskInfo> getTasks(
+ int maxNum, boolean filterOnlyVisibleRecents, boolean keepIntentExtra, int displayId) {
try {
- return getService().getTasks(maxNum, filterOnlyVisibleRecents, keepIntentExtra);
+ return getService().getTasks(maxNum, filterOnlyVisibleRecents, keepIntentExtra,
+ displayId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index b383d7daafd0..6b3dc820635e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -107,6 +107,7 @@ import android.net.ConnectivityManager;
import android.net.Proxy;
import android.net.TrafficStats;
import android.net.Uri;
+import android.net.wifi.WifiFrameworkInitializer;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.BluetoothServiceManager;
@@ -7900,6 +7901,8 @@ public final class ActivityThread extends ClientTransactionHandler
BluetoothFrameworkInitializer.setBluetoothServiceManager(new BluetoothServiceManager());
BluetoothFrameworkInitializer.setBinderCallsStatsInitializer(context -> {
BinderCallsStats.startForBluetooth(context); });
+ WifiFrameworkInitializer.setBinderCallsStatsInitializer(context -> {
+ BinderCallsStats.startForWifi(context); });
}
private void purgePendingResources() {
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index 57dacd024ba1..877e7d3b3bf7 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -263,11 +263,6 @@ class ActivityTransitionState {
// After orientation change, the onResume can come in before the top Activity has
// left, so if the Activity is not top, wait a second for the top Activity to exit.
if (mEnterTransitionCoordinator == null || activity.isTopOfTask()) {
- if (mEnterTransitionCoordinator != null) {
- mEnterTransitionCoordinator.runAfterTransitionsComplete(() -> {
- mEnterTransitionCoordinator = null;
- });
- }
restoreExitedViews();
restoreReenteringViews();
} else {
@@ -276,11 +271,6 @@ class ActivityTransitionState {
public void run() {
if (mEnterTransitionCoordinator == null ||
mEnterTransitionCoordinator.isWaitingForRemoteExit()) {
- if (mEnterTransitionCoordinator != null) {
- mEnterTransitionCoordinator.runAfterTransitionsComplete(() -> {
- mEnterTransitionCoordinator = null;
- });
- }
restoreExitedViews();
restoreReenteringViews();
} else if (mEnterTransitionCoordinator.isReturning()) {
diff --git a/core/java/android/app/GameManagerInternal.java b/core/java/android/app/GameManagerInternal.java
new file mode 100644
index 000000000000..c8ff2a84a536
--- /dev/null
+++ b/core/java/android/app/GameManagerInternal.java
@@ -0,0 +1,30 @@
+/*
+ * 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.app;
+
+/**
+ * Game manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+
+public abstract class GameManagerInternal {
+ /**
+ * Used by the CompatModePackages to read game's compat scaling override.
+ */
+ public abstract float getResolutionScalingFactor(String packageName, int userId);
+}
diff --git a/core/java/android/app/HomeVisibilityListener.java b/core/java/android/app/HomeVisibilityListener.java
index 57d868b2b969..ca20648a871a 100644
--- a/core/java/android/app/HomeVisibilityListener.java
+++ b/core/java/android/app/HomeVisibilityListener.java
@@ -16,16 +16,17 @@
package android.app;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+import static android.view.Display.DEFAULT_DISPLAY;
+
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.content.ComponentName;
import android.content.Context;
import android.os.Binder;
-import java.util.ArrayList;
import java.util.List;
-import java.util.Objects;
import java.util.concurrent.Executor;
/**
@@ -39,20 +40,21 @@ import java.util.concurrent.Executor;
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@TestApi
public abstract class HomeVisibilityListener {
- private Context mContext;
- private ActivityManager mActivityManager;
+ private ActivityTaskManager mActivityTaskManager;
private Executor mExecutor;
+ private int mMaxScanTasksForHomeVisibility;
/** @hide */
android.app.IProcessObserver.Stub mObserver;
/** @hide */
boolean mIsHomeActivityVisible;
/** @hide */
- void init(Context context, Executor executor, ActivityManager activityManager) {
- mContext = context;
- mActivityManager = activityManager;
- mIsHomeActivityVisible = isHomeActivityVisible();
+ void init(Context context, Executor executor) {
+ mActivityTaskManager = ActivityTaskManager.getInstance();
mExecutor = executor;
+ mMaxScanTasksForHomeVisibility = context.getResources().getInteger(
+ com.android.internal.R.integer.config_maxScanTasksForHomeVisibility);
+ mIsHomeActivityVisible = isHomeActivityVisible();
}
/**
@@ -91,22 +93,21 @@ public abstract class HomeVisibilityListener {
}
private boolean isHomeActivityVisible() {
- List<ActivityManager.RunningTaskInfo> tasks = mActivityManager.getRunningTasks(1);
- if (tasks == null || tasks.isEmpty()) {
+ List<ActivityManager.RunningTaskInfo> tasksTopToBottom = mActivityTaskManager.getTasks(
+ mMaxScanTasksForHomeVisibility, /* filterOnlyVisibleRecents= */ true,
+ /* keepIntentExtra= */ false, DEFAULT_DISPLAY);
+ if (tasksTopToBottom == null || tasksTopToBottom.isEmpty()) {
return false;
}
- String top = tasks.get(0).topActivity.getPackageName();
- if (top == null) {
- return false;
+ for (int i = 0, taskSize = tasksTopToBottom.size(); i < taskSize; ++i) {
+ ActivityManager.RunningTaskInfo task = tasksTopToBottom.get(i);
+ if (!task.isVisible()
+ || (task.baseIntent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0) {
+ continue;
+ }
+ return task.getActivityType() == ACTIVITY_TYPE_HOME;
}
-
- // We can assume that the screen is idle if the home application is in the foreground.
- ComponentName defaultHomeComponent = mContext.getPackageManager()
- .getHomeActivities(new ArrayList<>());
- if (defaultHomeComponent == null) return false;
-
- String defaultHomePackage = defaultHomeComponent.getPackageName();
- return Objects.equals(top, defaultHomePackage);
+ return false;
}
}
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 0bf16a0c471b..6576a1a5d3c2 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -158,7 +158,7 @@ interface IActivityTaskManager {
boolean removeTask(int taskId);
void removeAllVisibleRecentTasks();
List<ActivityManager.RunningTaskInfo> getTasks(int maxNum, boolean filterOnlyVisibleRecents,
- boolean keepIntentExtra);
+ boolean keepIntentExtra, int displayId);
void moveTaskToFront(in IApplicationThread app, in String callingPackage, int task,
int flags, in Bundle options);
ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
@@ -231,7 +231,7 @@ interface IActivityTaskManager {
in IBinder activityToken, int flags);
boolean isAssistDataAllowedOnCurrentActivity();
boolean requestAssistDataForTask(in IAssistDataReceiver receiver, int taskId,
- in String callingPackageName);
+ in String callingPackageName, String callingAttributionTag);
/**
* Notify the system that the keyguard is going away.
diff --git a/core/java/android/app/IGameManagerService.aidl b/core/java/android/app/IGameManagerService.aidl
index e60a74acdc1c..481e7b0050ab 100644
--- a/core/java/android/app/IGameManagerService.aidl
+++ b/core/java/android/app/IGameManagerService.aidl
@@ -31,4 +31,6 @@ interface IGameManagerService {
void setGameState(String packageName, in GameState gameState, int userId);
GameModeInfo getGameModeInfo(String packageName, int userId);
void setGameServiceProvider(String packageName);
+ void updateResolutionScalingFactor(String packageName, int gameMode, float scalingFactor, int userId);
+ float getResolutionScalingFactor(String packageName, int gameMode, int userId);
}
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 58ddd49ec6d1..13934e592d40 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -310,6 +310,21 @@ public class PropertyInvalidatedCache<Query, Result> {
public static final String MODULE_TELEPHONY = "telephony";
/**
+ * Constants that affect retries when the process is unable to write the property.
+ * The first constant is the number of times the process will attempt to set the
+ * property. The second constant is the delay between attempts.
+ */
+
+ /**
+ * Wait 200ms between retry attempts and the retry limit is 5. That gives a total possible
+ * delay of 1s, which should be less than ANR timeouts. The goal is to have the system crash
+ * because the property could not be set (which is a condition that is easily recognized) and
+ * not crash because of an ANR (which can be confusing to debug).
+ */
+ private static final int PROPERTY_FAILURE_RETRY_DELAY_MILLIS = 200;
+ private static final int PROPERTY_FAILURE_RETRY_LIMIT = 5;
+
+ /**
* Construct a system property that matches the rules described above. The module is
* one of the permitted values above. The API is a string that is a legal Java simple
* identifier. The api is modified to conform to the system property style guide by
@@ -670,7 +685,33 @@ public class PropertyInvalidatedCache<Query, Result> {
}
}
}
- SystemProperties.set(name, Long.toString(val));
+ RuntimeException failure = null;
+ for (int attempt = 0; attempt < PROPERTY_FAILURE_RETRY_LIMIT; attempt++) {
+ try {
+ SystemProperties.set(name, Long.toString(val));
+ if (attempt > 0) {
+ // This log is not guarded. Based on known bug reports, it should
+ // occur once a week or less. The purpose of the log message is to
+ // identify the retries as a source of delay that might be otherwise
+ // be attributed to the cache itself.
+ Log.w(TAG, "Nonce set after " + attempt + " tries");
+ }
+ return;
+ } catch (RuntimeException e) {
+ if (failure == null) {
+ failure = e;
+ }
+ try {
+ Thread.sleep(PROPERTY_FAILURE_RETRY_DELAY_MILLIS);
+ } catch (InterruptedException x) {
+ // Ignore this exception. The desired delay is only approximate and
+ // there is no issue if the sleep sometimes terminates early.
+ }
+ }
+ }
+ // This point is reached only if SystemProperties.set() fails at least once.
+ // Rethrow the first exception that was received.
+ throw failure;
}
// Set the nonce in a static context. No handle is available.
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 8be2b4873c67..e3bca9c9aadb 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -103,7 +103,6 @@ public class AppWidgetHostView extends FrameLayout {
private boolean mOnLightBackground;
private SizeF mCurrentSize = null;
private RemoteViews.ColorResources mColorResources = null;
- private SparseIntArray mColorMapping = null;
// Stores the last remote views last inflated.
private RemoteViews mLastInflatedRemoteViews = null;
private long mLastInflatedRemoteViewsId = -1;
@@ -900,11 +899,19 @@ public class AppWidgetHostView extends FrameLayout {
* {@link android.R.color#system_neutral1_500}.
*/
public void setColorResources(@NonNull SparseIntArray colorMapping) {
- if (mColorMapping != null && isSameColorMapping(mColorMapping, colorMapping)) {
+ if (mColorResources != null
+ && isSameColorMapping(mColorResources.getColorMapping(), colorMapping)) {
return;
}
- mColorMapping = colorMapping.clone();
- mColorResources = RemoteViews.ColorResources.create(mContext, mColorMapping);
+ setColorResources(RemoteViews.ColorResources.create(mContext, colorMapping));
+ }
+
+ /** @hide **/
+ public void setColorResources(RemoteViews.ColorResources colorResources) {
+ if (colorResources == mColorResources) {
+ return;
+ }
+ mColorResources = colorResources;
mColorMappingChanged = true;
mViewMode = VIEW_MODE_NOINIT;
reapplyLastRemoteViews();
@@ -934,7 +941,6 @@ public class AppWidgetHostView extends FrameLayout {
public void resetColorResources() {
if (mColorResources != null) {
mColorResources = null;
- mColorMapping = null;
mColorMappingChanged = true;
mViewMode = VIEW_MODE_NOINIT;
reapplyLastRemoteViews();
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 0c171ade1361..da486eebf9d1 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -19,7 +19,7 @@ package android.content;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.os.Process.myUserHandle;
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.os.Trace.TRACE_TAG_DATABASE;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -278,7 +278,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
// Return an empty cursor for all columns.
return new MatrixCursor(cursor.getColumnNames(), 0);
}
- traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "query: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_DATABASE, "query: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -289,7 +289,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -298,13 +298,13 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
// getCallingPackage() isn't available in getType(), as the javadoc states.
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "getType: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_DATABASE, "getType: ", uri.getAuthority());
try {
return mInterface.getType(uri);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -336,7 +336,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
setCallingAttributionSource(original);
}
}
- traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "insert: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_DATABASE, "insert: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -345,7 +345,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -358,7 +358,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
!= PermissionChecker.PERMISSION_GRANTED) {
return 0;
}
- traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "bulkInsert: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_DATABASE, "bulkInsert: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -367,7 +367,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -404,7 +404,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
}
}
}
- traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "applyBatch: ", authority);
+ traceBegin(TRACE_TAG_DATABASE, "applyBatch: ", authority);
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -423,7 +423,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -436,7 +436,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
!= PermissionChecker.PERMISSION_GRANTED) {
return 0;
}
- traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "delete: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_DATABASE, "delete: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -445,7 +445,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -458,7 +458,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
!= PermissionChecker.PERMISSION_GRANTED) {
return 0;
}
- traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "update: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_DATABASE, "update: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -467,7 +467,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -478,7 +478,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
enforceFilePermission(attributionSource, uri, mode);
- traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "openFile: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_DATABASE, "openFile: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -488,7 +488,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -499,7 +499,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
enforceFilePermission(attributionSource, uri, mode);
- traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "openAssetFile: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_DATABASE, "openAssetFile: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -509,7 +509,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -518,7 +518,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
String method, @Nullable String arg, @Nullable Bundle extras) {
validateIncomingAuthority(authority);
Bundle.setDefusable(extras, true);
- traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "call: ", authority);
+ traceBegin(TRACE_TAG_DATABASE, "call: ", authority);
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -527,7 +527,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -536,13 +536,13 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
// getCallingPackage() isn't available in getType(), as the javadoc states.
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "getStreamTypes: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_DATABASE, "getStreamTypes: ", uri.getAuthority());
try {
return mInterface.getStreamTypes(uri, mimeTypeFilter);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -554,7 +554,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
enforceFilePermission(attributionSource, uri, "r");
- traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "openTypedAssetFile: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_DATABASE, "openTypedAssetFile: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -564,7 +564,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -582,7 +582,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
!= PermissionChecker.PERMISSION_GRANTED) {
return null;
}
- traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "canonicalize: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_DATABASE, "canonicalize: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -591,7 +591,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -618,7 +618,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
!= PermissionChecker.PERMISSION_GRANTED) {
return null;
}
- traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "uncanonicalize: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_DATABASE, "uncanonicalize: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -627,7 +627,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -654,7 +654,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
!= PermissionChecker.PERMISSION_GRANTED) {
return false;
}
- traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "refresh: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_DATABASE, "refresh: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -662,7 +662,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
CancellationSignal.fromTransport(cancellationSignal));
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
@@ -671,7 +671,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
int uid, int modeFlags) {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "checkUriPermission: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_DATABASE, "checkUriPermission: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -680,7 +680,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 26b2f071e3f5..0e0b2dc3df8a 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -11051,10 +11051,7 @@ public class Intent implements Parcelable, Cloneable {
if (!Objects.equals(this.mData, other.mData)) return false;
if (!Objects.equals(this.mType, other.mType)) return false;
if (!Objects.equals(this.mIdentifier, other.mIdentifier)) return false;
- if (!(this.hasPackageEquivalentComponent() && other.hasPackageEquivalentComponent())
- && !Objects.equals(this.mPackage, other.mPackage)) {
- return false;
- }
+ if (!Objects.equals(this.mPackage, other.mPackage)) return false;
if (!Objects.equals(this.mComponent, other.mComponent)) return false;
if (!Objects.equals(this.mCategories, other.mCategories)) return false;
@@ -11062,15 +11059,6 @@ public class Intent implements Parcelable, Cloneable {
}
/**
- * Return {@code true} if the component name is not null and is in the same package that this
- * intent limited to. otherwise return {@code false}.
- */
- private boolean hasPackageEquivalentComponent() {
- return mComponent != null
- && (mPackage == null || mPackage.equals(mComponent.getPackageName()));
- }
-
- /**
* Generate hash code that matches semantics of filterEquals().
*
* @return Returns the hash value of the action, data, type, class, and
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 970849373fb4..4259600e8e92 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -252,6 +252,13 @@ public class PackageInfo implements Parcelable {
public static final int REQUESTED_PERMISSION_NEVER_FOR_LOCATION = 0x00010000;
/**
+ * Flag for {@link #requestedPermissionsFlags}: the requested permission was
+ * not explicitly requested via uses-permission, but was instead implicitly
+ * requested (e.g., for version compatibility reasons).
+ */
+ public static final int REQUESTED_PERMISSION_IMPLICIT = 0x00000004;
+
+ /**
* Array of all signatures read from the package file. This is only filled
* in if the flag {@link PackageManager#GET_SIGNATURES} was set. A package
* must be signed with at least one certificate which is at position zero.
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index dd517c9b3a5a..5839b877a448 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -9473,6 +9473,7 @@ public abstract class PackageManager {
*/
@SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
+ @TestApi
public abstract void addCrossProfileIntentFilter(@NonNull IntentFilter filter,
@UserIdInt int sourceUserId, @UserIdInt int targetUserId, int flags);
@@ -9485,6 +9486,7 @@ public abstract class PackageManager {
*/
@SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
+ @TestApi
public abstract void clearCrossProfileIntentFilters(@UserIdInt int sourceUserId);
/**
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 3dedc4131251..f47c1e0723b5 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -1974,10 +1974,10 @@ public final class Configuration implements Parcelable, Comparable<Configuration
return true;
}
int diff = other.seq - seq;
- if (diff > 0x10000) {
+ if (Math.abs(diff) > 0x10000000) {
// If there has been a sufficiently large jump, assume the
// sequence has wrapped around.
- return false;
+ return diff < 0;
}
return diff > 0;
}
diff --git a/core/java/android/database/TEST_MAPPING b/core/java/android/database/TEST_MAPPING
new file mode 100644
index 000000000000..4a7fa6652d4f
--- /dev/null
+++ b/core/java/android/database/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsDatabaseTestCases"
+ }
+ ]
+}
diff --git a/core/java/android/hardware/location/ContextHubClient.java b/core/java/android/hardware/location/ContextHubClient.java
index 08486972bb76..6ed87fff9e10 100644
--- a/core/java/android/hardware/location/ContextHubClient.java
+++ b/core/java/android/hardware/location/ContextHubClient.java
@@ -81,7 +81,7 @@ public class ContextHubClient implements Closeable {
*
* @param clientProxy the proxy of the client at the service
*/
- /* package */ void setClientProxy(IContextHubClient clientProxy) {
+ /* package */ synchronized void setClientProxy(IContextHubClient clientProxy) {
Objects.requireNonNull(clientProxy, "IContextHubClient cannot be null");
if (mClientProxy != null) {
throw new IllegalStateException("Cannot change client proxy multiple times");
@@ -93,6 +93,7 @@ public class ContextHubClient implements Closeable {
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
+ this.notifyAll();
}
/**
@@ -221,8 +222,15 @@ public class ContextHubClient implements Closeable {
}
/** @hide */
- public void callbackFinished() {
+ public synchronized void callbackFinished() {
try {
+ while (mClientProxy == null) {
+ try {
+ this.wait();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
mClientProxy.callbackFinished();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index e960df112ae3..3d593872757d 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -80,6 +80,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
private static final int DO_START_STYLUS_HANDWRITING = 110;
private static final int DO_INIT_INK_WINDOW = 120;
private static final int DO_FINISH_STYLUS_HANDWRITING = 130;
+ private static final int DO_UPDATE_TOOL_TYPE = 140;
final WeakReference<InputMethodServiceInternal> mTarget;
final Context mContext;
@@ -234,6 +235,10 @@ class IInputMethodWrapper extends IInputMethod.Stub
inputMethod.canStartStylusHandwriting(msg.arg1);
return;
}
+ case DO_UPDATE_TOOL_TYPE: {
+ inputMethod.updateEditorToolType(msg.arg1);
+ return;
+ }
case DO_START_STYLUS_HANDWRITING: {
final SomeArgs args = (SomeArgs) msg.obj;
inputMethod.startStylusHandwriting(msg.arg1, (InputChannel) args.arg1,
@@ -402,6 +407,14 @@ class IInputMethodWrapper extends IInputMethod.Stub
@BinderThread
@Override
+ public void updateEditorToolType(int toolType)
+ throws RemoteException {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageI(DO_UPDATE_TOOL_TYPE, toolType));
+ }
+
+ @BinderThread
+ @Override
public void startStylusHandwriting(int requestId, @NonNull InputChannel channel,
@Nullable List<MotionEvent> stylusEvents)
throws RemoteException {
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index b108490e54e9..c2027b136f0e 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -953,6 +953,15 @@ public class InputMethodService extends AbstractInputMethodService {
* @hide
*/
@Override
+ public void updateEditorToolType(int toolType) {
+ onUpdateEditorToolType(toolType);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @Override
public void canStartStylusHandwriting(int requestId) {
if (DEBUG) Log.v(TAG, "canStartStylusHandwriting()");
if (mHandwritingRequestId.isPresent()) {
@@ -3012,11 +3021,14 @@ public class InputMethodService extends AbstractInputMethodService {
* not call to inform the IME of this interaction.
* @param focusChanged true if the user changed the focused view by this click.
* @see InputMethodManager#viewClicked(View)
+ * @see #onUpdateEditorToolType(int)
* @deprecated The method may not be called for composite {@link View} that works as a giant
* "Canvas", which can host its own UI hierarchy and sub focus state.
* {@link android.webkit.WebView} is a good example. Application / IME developers
* should not rely on this method. If your goal is just being notified when an
* on-going input is interrupted, simply monitor {@link #onFinishInput()}.
+ * If your goal is to know what {@link MotionEvent#getToolType(int)} clicked on
+ * editor, use {@link #onUpdateEditorToolType(int)} instead.
*/
@Deprecated
public void onViewClicked(boolean focusChanged) {
@@ -3024,6 +3036,19 @@ public class InputMethodService extends AbstractInputMethodService {
}
/**
+ * Called when the user tapped or clicked an {@link android.widget.Editor}.
+ * This can be useful when IME makes a decision of showing Virtual keyboard based on what
+ * {@link MotionEvent#getToolType(int)} was used to click the editor.
+ * e.g. when toolType is {@link MotionEvent#TOOL_TYPE_STYLUS}, IME may choose to show a
+ * companion widget instead of normal virtual keyboard.
+ * <p> Default implementation does nothing. </p>
+ * @param toolType what {@link MotionEvent#getToolType(int)} was used to click on editor.
+ */
+ public void onUpdateEditorToolType(int toolType) {
+ // Intentionally empty
+ }
+
+ /**
* Called when the application has reported a new location of its text
* cursor. This is only called if explicitly requested by the input method.
* The default implementation does nothing.
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index 0418a4bb9f80..87579eb20890 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -29,6 +29,7 @@ import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.Preconditions;
import java.io.Serializable;
import java.lang.ref.WeakReference;
@@ -113,17 +114,21 @@ public class BaseBundle {
*/
private boolean mParcelledByNative;
- /*
+ /**
* Flag indicating if mParcelledData is only referenced in this bundle.
- * mParcelledData could be referenced by other bundles if mMap contains lazy values,
+ * mParcelledData could be referenced elsewhere if mMap contains lazy values,
* and bundle data is copied to another bundle using putAll or the copy constructors.
*/
boolean mOwnsLazyValues = true;
- /*
+ /** Tracks how many lazy values are referenced in mMap */
+ private int mLazyValues = 0;
+
+ /**
* As mParcelledData is set to null when it is unparcelled, we keep a weak reference to
* it to aid in recycling it. Do not use this reference otherwise.
- */
+ * Is non-null iff mMap contains lazy values.
+ */
private WeakReference<Parcel> mWeakParcelledData = null;
/**
@@ -310,7 +315,8 @@ public class BaseBundle {
synchronized (this) {
final Parcel source = mParcelledData;
if (source != null) {
- initializeFromParcelLocked(source, /*recycleParcel=*/ true, mParcelledByNative);
+ Preconditions.checkState(mOwnsLazyValues);
+ initializeFromParcelLocked(source, /*ownsParcel*/ true, mParcelledByNative);
} else {
if (DEBUG) {
Log.d(TAG, "unparcel "
@@ -401,11 +407,23 @@ public class BaseBundle {
}
}
mMap.setValueAt(i, object);
+ mLazyValues--;
+ if (mOwnsLazyValues) {
+ Preconditions.checkState(mLazyValues >= 0, "Lazy values ref count below 0");
+ // No more lazy values in mMap, so we can recycle the parcel early rather than
+ // waiting for the next GC run
+ if (mLazyValues == 0) {
+ Preconditions.checkState(mWeakParcelledData.get() != null,
+ "Parcel recycled earlier than expected");
+ recycleParcel(mWeakParcelledData.get());
+ mWeakParcelledData = null;
+ }
+ }
}
return (clazz != null) ? clazz.cast(object) : (T) object;
}
- private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel,
+ private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean ownsParcel,
boolean parcelledByNative) {
if (isEmptyParcel(parcelledData)) {
if (DEBUG) {
@@ -437,9 +455,10 @@ public class BaseBundle {
map.erase();
map.ensureCapacity(count);
}
+ int numLazyValues = 0;
try {
- recycleParcel &= parcelledData.readArrayMap(map, count, !parcelledByNative,
- /* lazy */ true, mClassLoader);
+ numLazyValues = parcelledData.readArrayMap(map, count, !parcelledByNative,
+ /* lazy */ ownsParcel, mClassLoader);
} catch (BadParcelableException e) {
if (sShouldDefuse) {
Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e);
@@ -448,14 +467,19 @@ public class BaseBundle {
throw e;
}
} finally {
- mMap = map;
- if (recycleParcel) {
- recycleParcel(parcelledData);
- mWeakParcelledData = null;
- } else {
- mWeakParcelledData = new WeakReference<>(parcelledData);
+ mWeakParcelledData = null;
+ if (ownsParcel) {
+ if (numLazyValues == 0) {
+ recycleParcel(parcelledData);
+ } else {
+ mWeakParcelledData = new WeakReference<>(parcelledData);
+ }
}
+
+ mLazyValues = numLazyValues;
mParcelledByNative = false;
+ mMap = map;
+ // Set field last as it is volatile
mParcelledData = null;
}
if (DEBUG) {
@@ -592,13 +616,17 @@ public class BaseBundle {
/**
* Removes all elements from the mapping of this Bundle.
+ * Recycles the underlying parcel if it is still present.
*/
public void clear() {
unparcel();
if (mOwnsLazyValues && mWeakParcelledData != null) {
recycleParcel(mWeakParcelledData.get());
- mWeakParcelledData = null;
}
+
+ mWeakParcelledData = null;
+ mLazyValues = 0;
+ mOwnsLazyValues = true;
mMap.clear();
}
@@ -1844,8 +1872,8 @@ public class BaseBundle {
// had been constructed with parcel or to make sure they trigger deserialization of the
// bundle immediately; neither of which is obvious.
synchronized (this) {
- initializeFromParcelLocked(parcel, /*recycleParcel=*/ false, isNativeBundle);
- unparcel(/* itemwise */ true);
+ mOwnsLazyValues = false;
+ initializeFromParcelLocked(parcel, /*ownsParcel*/ false, isNativeBundle);
}
return;
}
@@ -1862,6 +1890,7 @@ public class BaseBundle {
+ ": " + length + " bundle bytes starting at " + offset);
p.setDataPosition(0);
+ mOwnsLazyValues = true;
mParcelledByNative = isNativeBundle;
mParcelledData = p;
}
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index e5de3e157c88..f69d6b065246 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -125,6 +125,7 @@ interface IUserManager {
boolean isUserUnlocked(int userId);
boolean isUserRunning(int userId);
boolean isUserForeground(int userId);
+ boolean isUserVisible(int userId);
boolean isUserNameSet(int userId);
boolean hasRestrictedProfiles(int userId);
boolean requestQuietModeEnabled(String callingPackage, boolean enableQuietMode, int userId, in IntentSender target, int flags);
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index d55313273e3b..80201d327f64 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -71,7 +71,6 @@ import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.IntFunction;
-import java.util.function.Supplier;
/**
* Container for a message (data and object references) that can
@@ -5242,20 +5241,20 @@ public final class Parcel {
* Reads a map into {@code map}.
*
* @param sorted Whether the keys are sorted by their hashes, if so we use an optimized path.
- * @param lazy Whether to populate the map with lazy {@link Supplier} objects for
+ * @param lazy Whether to populate the map with lazy {@link Function} objects for
* length-prefixed values. See {@link Parcel#readLazyValue(ClassLoader)} for more
* details.
- * @return whether the parcel can be recycled or not.
+ * @return a count of the lazy values in the map
* @hide
*/
- boolean readArrayMap(ArrayMap<? super String, Object> map, int size, boolean sorted,
+ int readArrayMap(ArrayMap<? super String, Object> map, int size, boolean sorted,
boolean lazy, @Nullable ClassLoader loader) {
- boolean recycle = true;
+ int lazyValues = 0;
while (size > 0) {
String key = readString();
Object value = (lazy) ? readLazyValue(loader) : readValue(loader);
if (value instanceof LazyValue) {
- recycle = false;
+ lazyValues++;
}
if (sorted) {
map.append(key, value);
@@ -5267,7 +5266,7 @@ public final class Parcel {
if (sorted) {
map.validate();
}
- return recycle;
+ return lazyValues;
}
/**
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 8d3509c451a4..c802e56c8dc7 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2838,6 +2838,50 @@ public class UserManager {
}
/**
+ * @hide
+ */
+ public static boolean isUsersOnSecondaryDisplaysEnabled() {
+ return SystemProperties.getBoolean("fw.users_on_secondary_displays",
+ Resources.getSystem()
+ .getBoolean(R.bool.config_multiuserUsersOnSecondaryDisplays));
+ }
+
+ /**
+ * Returns whether the device allows users to run (and launch activities) on secondary displays.
+ *
+ * @return {@code false} for most devices, except automotive vehicles with passenger displays.
+ *
+ * @hide
+ */
+ public boolean isUsersOnSecondaryDisplaysSupported() {
+ return isUsersOnSecondaryDisplaysEnabled();
+ }
+
+ /**
+ * Checks if the user is visible at the moment.
+ *
+ * <p>Roughly speaking, a "visible user" is a user that can present UI on at least one display.
+ * It includes:
+ *
+ * <ol>
+ * <li>The current foreground user in the main display.
+ * <li>Current background users in secondary displays (for example, passenger users on
+ * automotive, using the display associated with their seats).
+ * <li>Profile users (in the running / started state) of other visible users.
+ * </ol>
+ *
+ * @return whether the user is visible at the moment, as defined above.
+ */
+ @UserHandleAware
+ public boolean isUserVisible() {
+ try {
+ return mService.isUserVisible(mUserId);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Return whether the context user is running in an "unlocked" state.
* <p>
* On devices with direct boot, a user is unlocked only after they've
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 063762c79da5..9cd23250d673 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -761,6 +761,13 @@ public final class DeviceConfig {
*/
public static final String NAMESPACE_MEMORY_SAFETY_NATIVE = "memory_safety_native";
+ /**
+ * Namespace for wear OS platform features.
+ *
+ * @hide
+ */
+ public static final String NAMESPACE_WEAR = "wear";
+
private static final Object sLock = new Object();
@GuardedBy("sLock")
private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
diff --git a/core/java/android/service/voice/VoiceInteractionManagerInternal.java b/core/java/android/service/voice/VoiceInteractionManagerInternal.java
index b20dccc8adfa..c47fdd39e30c 100644
--- a/core/java/android/service/voice/VoiceInteractionManagerInternal.java
+++ b/core/java/android/service/voice/VoiceInteractionManagerInternal.java
@@ -16,7 +16,9 @@
package android.service.voice;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.os.Bundle;
import android.os.IBinder;
@@ -32,9 +34,12 @@ public abstract class VoiceInteractionManagerInternal {
* Start a new voice interaction session when requested from within an activity
* by Activity.startLocalVoiceInteraction()
* @param callingActivity The binder token representing the calling activity.
- * @param options
+ * @param attributionTag The attribution tag of the calling context or {@code null} for default
+ * attribution
+ * @param options A Bundle of private arguments to the current voice interaction service
*/
- public abstract void startLocalVoiceInteraction(IBinder callingActivity, Bundle options);
+ public abstract void startLocalVoiceInteraction(@NonNull IBinder callingActivity,
+ @Nullable String attributionTag, @NonNull Bundle options);
/**
* Returns whether the currently selected voice interaction service supports local voice
@@ -65,6 +70,13 @@ public abstract class VoiceInteractionManagerInternal {
public abstract HotwordDetectionServiceIdentity getHotwordDetectionServiceIdentity();
/**
+ * Called by {@code UMS.convertPreCreatedUserIfPossible()} when a new user is not created from
+ * scratched, but converted from the pool of existing pre-created users.
+ */
+ // TODO(b/226201975): remove method once RoleService supports pre-created users
+ public abstract void onPreCreatedUserConversion(@UserIdInt int userId);
+
+ /**
* Provides the uids of the currently active
* {@link android.service.voice.HotwordDetectionService} and its owning package. The
* HotwordDetectionService is an isolated service, so it has a separate uid.
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 1170237a346e..1b46107de02c 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -242,7 +242,7 @@ public class VoiceInteractionService extends Service {
throw new IllegalStateException("Not available until onReady() is called");
}
try {
- mSystemService.showSession(args, flags);
+ mSystemService.showSession(args, flags, getAttributionTag());
} catch (RemoteException e) {
}
}
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index b7ec486d4830..d08a67e68676 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -1384,7 +1384,8 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
throw new IllegalStateException("Can't call before onCreate()");
}
try {
- mSystemService.showSessionFromSession(mToken, args, flags);
+ mSystemService.showSessionFromSession(mToken, args, flags,
+ mContext.getAttributionTag());
} catch (RemoteException e) {
}
}
diff --git a/core/java/android/text/Selection.java b/core/java/android/text/Selection.java
index 57c1d237d4ed..6a54d2378f6c 100644
--- a/core/java/android/text/Selection.java
+++ b/core/java/android/text/Selection.java
@@ -16,6 +16,7 @@
package android.text;
+import android.annotation.NonNull;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -346,6 +347,100 @@ public class Selection {
return false;
}
+ private static final char PARAGRAPH_SEPARATOR = '\n';
+
+ /**
+ * Move the cusrot to the closest paragraph start offset.
+ *
+ * @param text the spannable text
+ * @param layout layout to be used for drawing.
+ * @return true if the cursor is moved, otherwise false.
+ */
+ public static boolean moveToParagraphStart(@NonNull Spannable text, @NonNull Layout layout) {
+ int start = getSelectionStart(text);
+ int end = getSelectionEnd(text);
+
+ if (start != end) {
+ setSelection(text, chooseHorizontal(layout, -1, start, end));
+ return true;
+ } else {
+ int to = TextUtils.lastIndexOf(text, PARAGRAPH_SEPARATOR, start - 1);
+ if (to == -1) {
+ to = 0; // If not found, use the document start offset as a paragraph start.
+ }
+ if (to != end) {
+ setSelection(text, to);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Move the cursor to the closest paragraph end offset.
+ *
+ * @param text the spannable text
+ * @param layout layout to be used for drawing.
+ * @return true if the cursor is moved, otherwise false.
+ */
+ public static boolean moveToParagraphEnd(@NonNull Spannable text, @NonNull Layout layout) {
+ int start = getSelectionStart(text);
+ int end = getSelectionEnd(text);
+
+ if (start != end) {
+ setSelection(text, chooseHorizontal(layout, 1, start, end));
+ return true;
+ } else {
+ int to = TextUtils.indexOf(text, PARAGRAPH_SEPARATOR, end + 1);
+ if (to == -1) {
+ to = text.length();
+ }
+ if (to != end) {
+ setSelection(text, to);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Extend the selection to the closest paragraph start offset.
+ *
+ * @param text the spannable text
+ * @return true if the selection is extended, otherwise false
+ */
+ public static boolean extendToParagraphStart(@NonNull Spannable text) {
+ int end = getSelectionEnd(text);
+ int to = TextUtils.lastIndexOf(text, PARAGRAPH_SEPARATOR, end - 1);
+ if (to == -1) {
+ to = 0; // If not found, use the document start offset as a paragraph start.
+ }
+ if (to != end) {
+ extendSelection(text, to);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Extend the selection to the closest paragraph end offset.
+ *
+ * @param text the spannable text
+ * @return true if the selection is extended, otherwise false
+ */
+ public static boolean extendToParagraphEnd(@NonNull Spannable text) {
+ int end = getSelectionEnd(text);
+ int to = TextUtils.indexOf(text, PARAGRAPH_SEPARATOR, end + 1);
+ if (to == -1) {
+ to = text.length();
+ }
+ if (to != end) {
+ extendSelection(text, to);
+ return true;
+ }
+ return false;
+ }
+
/**
* Move the selection end to the buffer offset physically above
* the current selection end.
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index 57fe1315ab5d..d3367d014e7d 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -16,6 +16,7 @@
package android.text.method;
+import android.annotation.NonNull;
import android.graphics.Rect;
import android.text.Layout;
import android.text.Selection;
@@ -222,6 +223,26 @@ public class ArrowKeyMovementMethod extends BaseMovementMethod implements Moveme
}
@Override
+ public boolean previousParagraph(@NonNull TextView widget, @NonNull Spannable buffer) {
+ final Layout layout = widget.getLayout();
+ if (isSelecting(buffer)) {
+ return Selection.extendToParagraphStart(buffer);
+ } else {
+ return Selection.moveToParagraphStart(buffer, layout);
+ }
+ }
+
+ @Override
+ public boolean nextParagraph(@NonNull TextView widget, @NonNull Spannable buffer) {
+ final Layout layout = widget.getLayout();
+ if (isSelecting(buffer)) {
+ return Selection.extendToParagraphEnd(buffer);
+ } else {
+ return Selection.moveToParagraphEnd(buffer, layout);
+ }
+ }
+
+ @Override
public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
int initialScrollX = -1;
int initialScrollY = -1;
diff --git a/core/java/android/text/method/BaseMovementMethod.java b/core/java/android/text/method/BaseMovementMethod.java
index 155a2c4fcfa0..7a4b3a095ae9 100644
--- a/core/java/android/text/method/BaseMovementMethod.java
+++ b/core/java/android/text/method/BaseMovementMethod.java
@@ -16,6 +16,7 @@
package android.text.method;
+import android.annotation.NonNull;
import android.text.Layout;
import android.text.Spannable;
import android.view.InputDevice;
@@ -190,6 +191,9 @@ public class BaseMovementMethod implements MovementMethod {
} else if (KeyEvent.metaStateHasModifiers(movementMetaState,
KeyEvent.META_ALT_ON)) {
return top(widget, buffer);
+ } else if (KeyEvent.metaStateHasModifiers(movementMetaState,
+ KeyEvent.META_CTRL_ON)) {
+ return previousParagraph(widget, buffer);
}
break;
@@ -199,6 +203,9 @@ public class BaseMovementMethod implements MovementMethod {
} else if (KeyEvent.metaStateHasModifiers(movementMetaState,
KeyEvent.META_ALT_ON)) {
return bottom(widget, buffer);
+ } else if (KeyEvent.metaStateHasModifiers(movementMetaState,
+ KeyEvent.META_CTRL_ON)) {
+ return nextParagraph(widget, buffer);
}
break;
@@ -399,6 +406,28 @@ public class BaseMovementMethod implements MovementMethod {
return false;
}
+ /**
+ * Performs a previous paragraph movement action.
+ *
+ * @param widget the text view
+ * @param buffer the text buffer
+ * @return true if the event was handled, otherwise false.
+ */
+ public boolean previousParagraph(@NonNull TextView widget, @NonNull Spannable buffer) {
+ return false;
+ }
+
+ /**
+ * Performs a next paragraph movement action.
+ *
+ * @param widget the text view
+ * @param buffer the text buffer
+ * @return true if the event was handled, otherwise false.
+ */
+ public boolean nextParagraph(@NonNull TextView widget, @NonNull Spannable buffer) {
+ return false;
+ }
+
private int getTopLine(TextView widget) {
return widget.getLayout().getLineForVertical(widget.getScrollY());
}
diff --git a/core/java/android/view/IWindowFocusObserver.aidl b/core/java/android/view/IWindowFocusObserver.aidl
index d14bb4817a67..3b23c777b188 100644
--- a/core/java/android/view/IWindowFocusObserver.aidl
+++ b/core/java/android/view/IWindowFocusObserver.aidl
@@ -16,7 +16,7 @@
package android.view;
/** {@hide} */
-interface IWindowFocusObserver
+oneway interface IWindowFocusObserver
{
void focusGained(IBinder inputToken);
void focusLost(IBinder inputToken);
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index cce3e8c84451..a2cb1d5c6bd2 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -733,7 +733,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
}
for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) {
// Only update the server side insets here.
- if (type == ITYPE_CAPTION_BAR) continue;
+ if (!CAPTION_ON_SHELL && type == ITYPE_CAPTION_BAR) continue;
InsetsSource source = mState.peekSource(type);
if (source == null) continue;
if (newState.peekSource(type) == null) {
diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java
index 44f419a8097d..0db28d4f83a7 100644
--- a/core/java/android/view/RemoteAnimationTarget.java
+++ b/core/java/android/view/RemoteAnimationTarget.java
@@ -363,11 +363,11 @@ public class RemoteAnimationTarget implements Parcelable {
pw.print(prefix); pw.print("leash="); pw.println(leash);
pw.print(prefix); pw.print("taskInfo="); pw.println(taskInfo);
pw.print(prefix); pw.print("allowEnterPip="); pw.println(allowEnterPip);
- pw.print(prefix); pw.print("windowType="); pw.print(windowType);
- pw.print(prefix); pw.print("hasAnimatingParent="); pw.print(hasAnimatingParent);
- pw.print(prefix); pw.print("backgroundColor="); pw.print(backgroundColor);
- pw.print(prefix); pw.print("showBackdrop="); pw.print(showBackdrop);
- pw.print(prefix); pw.print("willShowImeOnTarget="); pw.print(willShowImeOnTarget);
+ pw.print(prefix); pw.print("windowType="); pw.println(windowType);
+ pw.print(prefix); pw.print("hasAnimatingParent="); pw.println(hasAnimatingParent);
+ pw.print(prefix); pw.print("backgroundColor="); pw.println(backgroundColor);
+ pw.print(prefix); pw.print("showBackdrop="); pw.println(showBackdrop);
+ pw.print(prefix); pw.print("willShowImeOnTarget="); pw.println(willShowImeOnTarget);
}
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 5f6c24bff3f7..84f04c12cf51 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -172,7 +172,6 @@ public final class SurfaceControl implements Parcelable {
private static native boolean nativeGetAnimationFrameStats(WindowAnimationFrameStats outStats);
private static native long[] nativeGetPhysicalDisplayIds();
- private static native long nativeGetPrimaryPhysicalDisplayId();
private static native IBinder nativeGetPhysicalDisplayToken(long physicalDisplayId);
private static native IBinder nativeCreateDisplay(String name, boolean secure);
private static native void nativeDestroyDisplay(IBinder displayToken);
@@ -2395,15 +2394,6 @@ public final class SurfaceControl implements Parcelable {
}
/**
- * Exposed to identify the correct display to apply the primary display orientation. Avoid using
- * for any other purpose.
- * @hide
- */
- public static long getPrimaryPhysicalDisplayId() {
- return nativeGetPrimaryPhysicalDisplayId();
- }
-
- /**
* @hide
*/
public static IBinder getPhysicalDisplayToken(long physicalDisplayId) {
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index d75ff2fc7dc2..5721fa6dd11a 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -422,7 +422,7 @@ public class SurfaceControlViewHost {
public void relayout(WindowManager.LayoutParams attrs,
WindowlessWindowManager.ResizeCompleteCallback callback) {
mViewRoot.setLayoutParams(attrs, false);
- mViewRoot.setReportNextDraw(true /* syncBuffer */);
+ mViewRoot.setReportNextDraw(true /* syncBuffer */, "scvh_relayout");
mWm.setCompletionCallback(mViewRoot.mWindow.asBinder(), callback);
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1bedbfc826c2..b2854a3fec44 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -277,7 +277,7 @@ public final class ViewRootImpl implements ViewParent,
* @hide
*/
public static final boolean CAPTION_ON_SHELL =
- SystemProperties.getBoolean("persist.debug.caption_on_shell", false);
+ SystemProperties.getBoolean("persist.wm.debug.caption_on_shell", false);
/**
* Whether the client should compute the window frame on its own.
@@ -587,8 +587,21 @@ public final class ViewRootImpl implements ViewParent,
int mContentCaptureEnabled = CONTENT_CAPTURE_ENABLED_NOT_CHECKED;
boolean mPerformContentCapture;
-
boolean mReportNextDraw;
+ /** Set only while mReportNextDraw=true, indicating the last reason that was triggered */
+ String mLastReportNextDrawReason;
+ /** The reaason the last call to performDraw() returned false */
+ String mLastPerformDrawSkippedReason;
+ /** The reason the last call to performTraversals() returned without drawing */
+ String mLastPerformTraversalsSkipDrawReason;
+ /** The state of the local sync, if one is in progress. Can be one of the states below. */
+ int mLocalSyncState;
+
+ // The possible states of the local sync, see createSyncIfNeeded()
+ private final int LOCAL_SYNC_NONE = 0;
+ private final int LOCAL_SYNC_PENDING = 1;
+ private final int LOCAL_SYNC_RETURNED = 2;
+ private final int LOCAL_SYNC_MERGED = 3;
/**
* Set whether the draw should send the buffer to system server. When set to true, VRI will
@@ -734,6 +747,8 @@ public final class ViewRootImpl implements ViewParent,
final PointF mDragPoint = new PointF();
final PointF mLastTouchPoint = new PointF();
int mLastTouchSource;
+ /** Tracks last {@link MotionEvent#getToolType(int)} with {@link MotionEvent#ACTION_UP}. **/
+ private int mLastClickToolType;
private boolean mProfileRendering;
private Choreographer.FrameCallback mRenderProfiler;
@@ -1834,7 +1849,7 @@ public final class ViewRootImpl implements ViewParent,
mSyncSeqId = args.argi4 > mSyncSeqId ? args.argi4 : mSyncSeqId;
if (msg == MSG_RESIZED_REPORT) {
- reportNextDraw();
+ reportNextDraw("resized");
}
if (mView != null && (frameChanged || configChanged)) {
@@ -2739,6 +2754,8 @@ public final class ViewRootImpl implements ViewParent,
}
private void performTraversals() {
+ mLastPerformTraversalsSkipDrawReason = null;
+
// cache mView since it is used so much below...
final View host = mView;
if (DBG) {
@@ -2748,12 +2765,14 @@ public final class ViewRootImpl implements ViewParent,
}
if (host == null || !mAdded) {
+ mLastPerformTraversalsSkipDrawReason = host == null ? "no_host" : "not_added";
return;
}
mIsInTraversal = true;
mWillDrawSoon = true;
boolean cancelDraw = false;
+ String cancelReason = null;
boolean isSyncRequest = false;
boolean windowSizeMayChange = false;
@@ -3036,13 +3055,14 @@ public final class ViewRootImpl implements ViewParent,
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
cancelDraw = (relayoutResult & RELAYOUT_RES_CANCEL_AND_REDRAW)
== RELAYOUT_RES_CANCEL_AND_REDRAW;
+ cancelReason = "relayout";
final boolean dragResizing = mPendingDragResizing;
if (mSyncSeqId > mLastSyncSeqId) {
mLastSyncSeqId = mSyncSeqId;
if (DEBUG_BLAST) {
Log.d(mTag, "Relayout called with blastSync");
}
- reportNextDraw();
+ reportNextDraw("relayout");
mSyncBuffer = true;
isSyncRequest = true;
if (!cancelDraw) {
@@ -3145,6 +3165,7 @@ public final class ViewRootImpl implements ViewParent,
}
} catch (OutOfResourcesException e) {
handleOutOfResourcesException(e);
+ mLastPerformTraversalsSkipDrawReason = "oom_initialize_renderer";
return;
}
}
@@ -3182,6 +3203,7 @@ public final class ViewRootImpl implements ViewParent,
mAttachInfo.mThreadedRenderer.updateSurface(mSurface);
} catch (OutOfResourcesException e) {
handleOutOfResourcesException(e);
+ mLastPerformTraversalsSkipDrawReason = "oom_update_surface";
return;
}
}
@@ -3347,6 +3369,7 @@ public final class ViewRootImpl implements ViewParent,
if (mCheckIfCanDraw) {
try {
cancelDraw = mWindowSession.cancelDraw(mWindow);
+ cancelReason = "wm_sync";
if (DEBUG_BLAST) {
Log.d(mTag, "cancelDraw returned " + cancelDraw);
}
@@ -3569,19 +3592,21 @@ public final class ViewRootImpl implements ViewParent,
mImeFocusController.onTraversal(hasWindowFocus, mWindowAttributes);
if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
- reportNextDraw();
+ reportNextDraw("first_relayout");
}
mCheckIfCanDraw = isSyncRequest || cancelDraw;
- boolean cancelAndRedraw =
- mAttachInfo.mTreeObserver.dispatchOnPreDraw() || (cancelDraw && mDrewOnceForSync);
+ boolean cancelDueToPreDrawListener = mAttachInfo.mTreeObserver.dispatchOnPreDraw();
+ boolean cancelAndRedraw = cancelDueToPreDrawListener
+ || (cancelDraw && mDrewOnceForSync);
if (!cancelAndRedraw) {
createSyncIfNeeded();
mDrewOnceForSync = true;
}
if (!isViewVisible) {
+ mLastPerformTraversalsSkipDrawReason = "view_not_visible";
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).endChangingAnimations();
@@ -3593,6 +3618,9 @@ public final class ViewRootImpl implements ViewParent,
mSyncBufferCallback.onBufferReady(null);
}
} else if (cancelAndRedraw) {
+ mLastPerformTraversalsSkipDrawReason = cancelDueToPreDrawListener
+ ? "predraw_" + mAttachInfo.mTreeObserver.getLastDispatchOnPreDrawCanceledReason()
+ : "cancel_" + cancelReason;
// Try again
scheduleTraversals();
} else {
@@ -3616,11 +3644,13 @@ public final class ViewRootImpl implements ViewParent,
if (!cancelAndRedraw) {
mReportNextDraw = false;
+ mLastReportNextDrawReason = null;
mSyncBufferCallback = null;
mSyncBuffer = false;
if (isInLocalSync()) {
mSyncGroup.markSyncReady();
mSyncGroup = null;
+ mLocalSyncState = LOCAL_SYNC_NONE;
}
}
}
@@ -3632,12 +3662,15 @@ public final class ViewRootImpl implements ViewParent,
}
final int seqId = mSyncSeqId;
+ mLocalSyncState = LOCAL_SYNC_PENDING;
mSyncGroup = new SurfaceSyncGroup(transaction -> {
+ mLocalSyncState = LOCAL_SYNC_RETURNED;
// Callback will be invoked on executor thread so post to main thread.
mHandler.postAtFrontOfQueue(() -> {
if (transaction != null) {
mSurfaceChangedTransaction.merge(transaction);
}
+ mLocalSyncState = LOCAL_SYNC_MERGED;
reportDrawFinished(seqId);
});
});
@@ -4351,9 +4384,12 @@ public final class ViewRootImpl implements ViewParent,
}
private boolean performDraw() {
+ mLastPerformDrawSkippedReason = null;
if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
+ mLastPerformDrawSkippedReason = "screen_off";
return false;
} else if (mView == null) {
+ mLastPerformDrawSkippedReason = "no_root_view";
return false;
}
@@ -6417,11 +6453,16 @@ public final class ViewRootImpl implements ViewParent,
event.offsetLocation(0, mCurScrollY);
}
- // Remember the touch position for possible drag-initiation.
if (event.isTouchEvent()) {
+ // Remember the touch position for possible drag-initiation.
mLastTouchPoint.x = event.getRawX();
mLastTouchPoint.y = event.getRawY();
mLastTouchSource = event.getSource();
+
+ // Register last ACTION_UP. This will be propagated to IME.
+ if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+ mLastClickToolType = event.getToolType(event.getActionIndex());
+ }
}
return FORWARD;
}
@@ -8017,6 +8058,14 @@ public final class ViewRootImpl implements ViewParent,
return mLastTouchSource;
}
+ /**
+ * Used by InputMethodManager.
+ * @hide
+ */
+ public int getLastClickToolType() {
+ return mLastClickToolType;
+ }
+
public void setDragFocus(View newDragTarget, DragEvent event) {
if (mCurrentDragView != newDragTarget && !View.sCascadedDragDrop) {
// Send EXITED and ENTERED notifications to the old and new drag focus views.
@@ -8443,6 +8492,21 @@ public final class ViewRootImpl implements ViewParent,
if (mTraversalScheduled) {
writer.println(innerPrefix + " (barrier=" + mTraversalBarrier + ")");
}
+ writer.println(innerPrefix + "mReportNextDraw=" + mReportNextDraw);
+ if (mReportNextDraw) {
+ writer.println(innerPrefix + " (reason=" + mLastReportNextDrawReason + ")");
+ }
+ if (mLastPerformTraversalsSkipDrawReason != null) {
+ writer.println(innerPrefix + "mLastPerformTraversalsFailedReason="
+ + mLastPerformTraversalsSkipDrawReason);
+ }
+ if (mLastPerformDrawSkippedReason != null) {
+ writer.println(innerPrefix + "mLastPerformDrawFailedReason="
+ + mLastPerformDrawSkippedReason);
+ }
+ if (mLocalSyncState != LOCAL_SYNC_NONE) {
+ writer.println(innerPrefix + "mLocalSyncState=" + mLocalSyncState);
+ }
writer.println(innerPrefix + "mIsAmbientMode=" + mIsAmbientMode);
writer.println(innerPrefix + "mUnbufferedInputSource="
+ Integer.toHexString(mUnbufferedInputSource));
@@ -9943,11 +10007,12 @@ public final class ViewRootImpl implements ViewParent,
}
}
- private void reportNextDraw() {
+ private void reportNextDraw(String reason) {
if (DEBUG_BLAST) {
Log.d(mTag, "reportNextDraw " + Debug.getCallers(5));
}
mReportNextDraw = true;
+ mLastReportNextDrawReason = reason;
}
/**
@@ -9960,11 +10025,12 @@ public final class ViewRootImpl implements ViewParent,
* @param syncBuffer If true, the transaction that contains the buffer from the draw should be
* sent to system to be synced. If false, VRI will not try to sync the buffer,
* but only report back that a buffer was drawn.
+ * @param reason A debug string indicating the reason for reporting the next draw
* @hide
*/
- public void setReportNextDraw(boolean syncBuffer) {
+ public void setReportNextDraw(boolean syncBuffer, String reason) {
mSyncBuffer = syncBuffer;
- reportNextDraw();
+ reportNextDraw(reason);
invalidate();
}
diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java
index 2c077c3e9e63..c9526fde27df 100644
--- a/core/java/android/view/ViewTreeObserver.java
+++ b/core/java/android/view/ViewTreeObserver.java
@@ -74,6 +74,9 @@ public final class ViewTreeObserver {
* that the listener will be immediately called. */
private boolean mWindowShown;
+ // The reason that the last call to dispatchOnPreDraw() returned true to cancel and redraw
+ private String mLastDispatchOnPreDrawCanceledReason;
+
private boolean mAlive = true;
/**
@@ -1161,6 +1164,7 @@ public final class ViewTreeObserver {
*/
@SuppressWarnings("unchecked")
public final boolean dispatchOnPreDraw() {
+ mLastDispatchOnPreDrawCanceledReason = null;
boolean cancelDraw = false;
final CopyOnWriteArray<OnPreDrawListener> listeners = mOnPreDrawListeners;
if (listeners != null && listeners.size() > 0) {
@@ -1168,7 +1172,11 @@ public final class ViewTreeObserver {
try {
int count = access.size();
for (int i = 0; i < count; i++) {
- cancelDraw |= !(access.get(i).onPreDraw());
+ final OnPreDrawListener preDrawListener = access.get(i);
+ cancelDraw |= !(preDrawListener.onPreDraw());
+ if (cancelDraw) {
+ mLastDispatchOnPreDrawCanceledReason = preDrawListener.getClass().getName();
+ }
}
} finally {
listeners.end();
@@ -1178,6 +1186,15 @@ public final class ViewTreeObserver {
}
/**
+ * @return the reason that the last call to dispatchOnPreDraw() returned true to cancel the
+ * current draw, or null if the last call did not cancel.
+ * @hide
+ */
+ final String getLastDispatchOnPreDrawCanceledReason() {
+ return mLastDispatchOnPreDrawCanceledReason;
+ }
+
+ /**
* Notifies registered listeners that the window is now shown
* @hide
*/
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index e56c43e57aa0..6f6936127cde 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -834,8 +834,8 @@ public interface WindowManager extends ViewManager {
*
* <p>Default is {@code false}.
*
- * <p>The system enforcement will be added in Android 14, but some devices may start following
- * the requirement before that. The best practice for apps is to always explicitly set this
+ * <p>The system enforcement is added in Android 14, but some devices may start following the
+ * requirement before that. The best practice for apps is to always explicitly set this
* property in AndroidManifest instead of relying on the default value.
*
* <p>Example usage:
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index fdff7a30a6e9..55a2d22d51b7 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -30,6 +30,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.content.res.Configuration;
+import android.inputmethodservice.InputMethodService;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.LocaleList;
@@ -40,6 +41,7 @@ import android.text.InputType;
import android.text.TextUtils;
import android.util.Printer;
import android.util.proto.ProtoOutputStream;
+import android.view.MotionEvent;
import android.view.View;
import android.view.autofill.AutofillId;
@@ -564,6 +566,12 @@ public class EditorInfo implements InputType, Parcelable {
@Nullable
private SurroundingText mInitialSurroundingText = null;
+ /**
+ * Initial {@link MotionEvent#ACTION_UP} tool type {@link MotionEvent#getToolType(int)} that
+ * was used to focus this editor.
+ */
+ private int mInitialToolType = MotionEvent.TOOL_TYPE_UNKNOWN;
+
/**
* Editors may use this method to provide initial input text to IMEs. As the surrounding text
@@ -937,6 +945,31 @@ public class EditorInfo implements InputType, Parcelable {
}
/**
+ * Returns the initial {@link MotionEvent#ACTION_UP} tool type
+ * {@link MotionEvent#getToolType(int)} responsible for focus on the current editor.
+ *
+ * @see #setInitialToolType(int)
+ * @see MotionEvent#getToolType(int)
+ * @see InputMethodService#onUpdateEditorToolType(int)
+ * @return toolType {@link MotionEvent#getToolType(int)}.
+ */
+ public int getInitialToolType() {
+ return mInitialToolType;
+ }
+
+ /**
+ * Set the initial {@link MotionEvent#ACTION_UP} tool type {@link MotionEvent#getToolType(int)}.
+ * that brought focus to the view.
+ *
+ * @see #getInitialToolType()
+ * @see MotionEvent#getToolType(int)
+ * @see InputMethodService#onUpdateEditorToolType(int)
+ */
+ public void setInitialToolType(int toolType) {
+ mInitialToolType = toolType;
+ }
+
+ /**
* Export the state of {@link EditorInfo} into a protocol buffer output stream.
*
* @param proto Stream to write the state to
@@ -972,6 +1005,7 @@ public class EditorInfo implements InputType, Parcelable {
+ " actionId=" + actionId);
pw.println(prefix + "initialSelStart=" + initialSelStart
+ " initialSelEnd=" + initialSelEnd
+ + " initialToolType=" + mInitialToolType
+ " initialCapsMode=0x"
+ Integer.toHexString(initialCapsMode));
pw.println(prefix + "hintText=" + hintText
@@ -1006,6 +1040,7 @@ public class EditorInfo implements InputType, Parcelable {
newEditorInfo.initialSelStart = initialSelStart;
newEditorInfo.initialSelEnd = initialSelEnd;
newEditorInfo.initialCapsMode = initialCapsMode;
+ newEditorInfo.mInitialToolType = mInitialToolType;
newEditorInfo.hintText = TextUtils.stringOrSpannedString(hintText);
newEditorInfo.label = TextUtils.stringOrSpannedString(label);
newEditorInfo.packageName = packageName;
@@ -1036,6 +1071,7 @@ public class EditorInfo implements InputType, Parcelable {
dest.writeInt(initialSelStart);
dest.writeInt(initialSelEnd);
dest.writeInt(initialCapsMode);
+ dest.writeInt(mInitialToolType);
TextUtils.writeToParcel(hintText, dest, flags);
TextUtils.writeToParcel(label, dest, flags);
dest.writeString(packageName);
@@ -1072,6 +1108,7 @@ public class EditorInfo implements InputType, Parcelable {
res.initialSelStart = source.readInt();
res.initialSelEnd = source.readInt();
res.initialCapsMode = source.readInt();
+ res.mInitialToolType = source.readInt();
res.hintText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
res.label = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
res.packageName = source.readString();
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerInvoker.java
index 78a90d99f67d..429b0b80aec3 100644
--- a/core/java/android/view/inputmethod/IInputMethodManagerInvoker.java
+++ b/core/java/android/view/inputmethod/IInputMethodManagerInvoker.java
@@ -111,10 +111,11 @@ final class IInputMethodManagerInvoker {
@AnyThread
boolean showSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken,
- int flags, @Nullable ResultReceiver resultReceiver,
+ int flags, int lastClickToolType, @Nullable ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
try {
- return mTarget.showSoftInput(client, windowToken, flags, resultReceiver, reason);
+ return mTarget.showSoftInput(
+ client, windowToken, flags, lastClickToolType, resultReceiver, reason);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index 95add2978879..bfe6ae6447d0 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -377,6 +377,15 @@ public interface InputMethod {
}
/**
+ * This method is called when the user tapped or clicked an {@link android.widget.Editor}.
+ * @param toolType {@link android.view.MotionEvent#getToolType(int)} used for clicking editor.
+ * @hide
+ */
+ default void updateEditorToolType(int toolType) {
+ // intentionally empty
+ }
+
+ /**
* Start stylus handwriting session.
* @hide
*/
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 899e5fc4d42c..0b4a29899020 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1929,6 +1929,7 @@ public final class InputMethodManager {
mClient,
view.getWindowToken(),
flags,
+ mCurRootView.getLastClickToolType(),
resultReceiver,
reason);
}
@@ -1960,6 +1961,7 @@ public final class InputMethodManager {
mClient,
mCurRootView.getView().getWindowToken(),
flags,
+ mCurRootView.getLastClickToolType(),
resultReceiver,
SoftInputShowHideReason.SHOW_SOFT_INPUT);
}
@@ -2339,6 +2341,9 @@ public final class InputMethodManager {
editorInfo.packageName = view.getContext().getOpPackageName();
editorInfo.autofillId = view.getAutofillId();
editorInfo.fieldId = view.getId();
+ synchronized (mH) {
+ editorInfo.setInitialToolType(mCurRootView.getLastClickToolType());
+ }
InputConnection ic = view.onCreateInputConnection(editorInfo);
if (DEBUG) Log.v(TAG, "Starting input: editorInfo=" + editorInfo + " ic=" + ic);
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 2879cd888d2d..a33906267736 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -594,8 +594,8 @@ public class RemoteViews implements Parcelable, Filter {
* SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!!
*/
private abstract static class Action implements Parcelable {
- public abstract void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) throws ActionException;
+ public abstract void apply(View root, ViewGroup rootParent, ActionApplyParams params)
+ throws ActionException;
public static final int MERGE_REPLACE = 0;
public static final int MERGE_APPEND = 1;
@@ -626,7 +626,7 @@ public class RemoteViews implements Parcelable, Filter {
* Override this if some of the tasks can be performed async.
*/
public Action initActionAsync(ViewTree root, ViewGroup rootParent,
- InteractionHandler handler, ColorResources colorResources) {
+ ActionApplyParams params) {
return this;
}
@@ -661,9 +661,7 @@ public class RemoteViews implements Parcelable, Filter {
// Constant used during async execution. It is not parcelable.
private static final Action ACTION_NOOP = new RuntimeAction() {
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
- }
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { }
};
/**
@@ -798,8 +796,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View view = root.findViewById(viewId);
if (!(view instanceof AdapterView<?>)) return;
@@ -834,8 +831,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, final InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -846,7 +842,7 @@ public class RemoteViews implements Parcelable, Filter {
OnItemClickListener listener = (parent, view, position, id) -> {
RemoteResponse response = findRemoteResponseTag(view);
if (response != null) {
- response.handleViewInteraction(view, handler);
+ response.handleViewInteraction(view, params.handler);
}
};
av.setOnItemClickListener(listener);
@@ -910,8 +906,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -935,7 +930,7 @@ public class RemoteViews implements Parcelable, Filter {
((RemoteViewsListAdapter) a).setViewsList(list);
} else {
v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount,
- colorResources));
+ params.colorResources));
}
} else if (target instanceof AdapterViewAnimator) {
AdapterViewAnimator v = (AdapterViewAnimator) target;
@@ -944,7 +939,7 @@ public class RemoteViews implements Parcelable, Filter {
((RemoteViewsListAdapter) a).setViewsList(list);
} else {
v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount,
- colorResources));
+ params.colorResources));
}
}
}
@@ -1025,8 +1020,8 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) throws ActionException {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
+ throws ActionException {
View target = root.findViewById(viewId);
if (target == null) return;
@@ -1053,7 +1048,7 @@ public class RemoteViews implements Parcelable, Filter {
&& adapter.getViewTypeCount() >= mItems.getViewTypeCount()) {
try {
((RemoteCollectionItemsAdapter) adapter).setData(
- mItems, handler, colorResources);
+ mItems, params.handler, params.colorResources);
} catch (Throwable throwable) {
// setData should never failed with the validation in the items builder, but if
// it does, catch and rethrow.
@@ -1063,8 +1058,8 @@ public class RemoteViews implements Parcelable, Filter {
}
try {
- adapterView.setAdapter(
- new RemoteCollectionItemsAdapter(mItems, handler, colorResources));
+ adapterView.setAdapter(new RemoteCollectionItemsAdapter(mItems,
+ params.handler, params.colorResources));
} catch (Throwable throwable) {
// This could throw if the AdapterView somehow doesn't accept BaseAdapter due to
// a type error.
@@ -1095,8 +1090,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -1124,17 +1118,17 @@ public class RemoteViews implements Parcelable, Filter {
if (target instanceof AbsListView) {
AbsListView v = (AbsListView) target;
v.setRemoteViewsAdapter(intent, isAsync);
- v.setRemoteViewsInteractionHandler(handler);
+ v.setRemoteViewsInteractionHandler(params.handler);
} else if (target instanceof AdapterViewAnimator) {
AdapterViewAnimator v = (AdapterViewAnimator) target;
v.setRemoteViewsAdapter(intent, isAsync);
- v.setRemoteViewsOnClickHandler(handler);
+ v.setRemoteViewsOnClickHandler(params.handler);
}
}
@Override
public Action initActionAsync(ViewTree root, ViewGroup rootParent,
- InteractionHandler handler, ColorResources colorResources) {
+ ActionApplyParams params) {
SetRemoteViewsAdapterIntent copy = new SetRemoteViewsAdapterIntent(viewId, intent);
copy.isAsync = true;
return copy;
@@ -1173,8 +1167,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, final InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -1215,7 +1208,7 @@ public class RemoteViews implements Parcelable, Filter {
target.setTagInternal(com.android.internal.R.id.fillInIntent, null);
return;
}
- target.setOnClickListener(v -> mResponse.handleViewInteraction(v, handler));
+ target.setOnClickListener(v -> mResponse.handleViewInteraction(v, params.handler));
}
@Override
@@ -1253,8 +1246,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, final InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null) return;
if (!(target instanceof CompoundButton)) {
@@ -1287,7 +1279,7 @@ public class RemoteViews implements Parcelable, Filter {
}
OnCheckedChangeListener onCheckedChangeListener =
- (v, isChecked) -> mResponse.handleViewInteraction(v, handler);
+ (v, isChecked) -> mResponse.handleViewInteraction(v, params.handler);
button.setTagInternal(R.id.remote_checked_change_listener_tag, onCheckedChangeListener);
button.setOnCheckedChangeListener(onCheckedChangeListener);
}
@@ -1459,8 +1451,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -1517,8 +1508,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -1561,8 +1551,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View view = root.findViewById(viewId);
if (view == null) return;
@@ -1598,7 +1587,13 @@ public class RemoteViews implements Parcelable, Filter {
public BitmapCache(Parcel source) {
mBitmaps = source.createTypedArrayList(Bitmap.CREATOR);
- mBitmapHashes = source.readSparseIntArray();
+ mBitmapHashes = new SparseIntArray();
+ for (int i = 0; i < mBitmaps.size(); i++) {
+ Bitmap b = mBitmaps.get(i);
+ if (b != null) {
+ mBitmapHashes.put(b.hashCode(), i);
+ }
+ }
}
public int getBitmapId(Bitmap b) {
@@ -1614,7 +1609,7 @@ public class RemoteViews implements Parcelable, Filter {
b = b.asShared();
}
mBitmaps.add(b);
- mBitmapHashes.put(mBitmaps.size() - 1, hash);
+ mBitmapHashes.put(hash, mBitmaps.size() - 1);
mBitmapMemory = -1;
return (mBitmaps.size() - 1);
}
@@ -1631,7 +1626,6 @@ public class RemoteViews implements Parcelable, Filter {
public void writeBitmapsToParcel(Parcel dest, int flags) {
dest.writeTypedList(mBitmaps, flags);
- dest.writeSparseIntArray(mBitmapHashes);
}
public int getBitmapMemory() {
@@ -1675,12 +1669,12 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) throws ActionException {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
+ throws ActionException {
ReflectionAction ra = new ReflectionAction(viewId, methodName,
BaseReflectionAction.BITMAP,
bitmap);
- ra.apply(root, rootParent, handler, colorResources);
+ ra.apply(root, rootParent, params);
}
@Override
@@ -1756,8 +1750,7 @@ public class RemoteViews implements Parcelable, Filter {
protected abstract Object getParameterValue(@Nullable View view) throws ActionException;
@Override
- public final void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public final void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View view = root.findViewById(viewId);
if (view == null) return;
@@ -1775,7 +1768,7 @@ public class RemoteViews implements Parcelable, Filter {
@Override
public final Action initActionAsync(ViewTree root, ViewGroup rootParent,
- InteractionHandler handler, ColorResources colorResources) {
+ ActionApplyParams params) {
final View view = root.findViewById(viewId);
if (view == null) return ACTION_NOOP;
@@ -2307,8 +2300,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
mRunnable.run();
}
}
@@ -2421,8 +2413,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final Context context = root.getContext();
final ViewGroup target = root.findViewById(viewId);
@@ -2451,8 +2442,7 @@ public class RemoteViews implements Parcelable, Filter {
target.removeViews(nextChild, recycledViewIndex - nextChild);
}
setNextRecyclableChild(target, nextChild + 1, target.getChildCount());
- rvToApply.reapplyNestedViews(context, child, rootParent, handler,
- null /* size */, colorResources);
+ rvToApply.reapplyNestedViews(context, child, rootParent, params);
return;
}
// If we cannot recycle the views, we still remove all views in between to
@@ -2463,8 +2453,7 @@ public class RemoteViews implements Parcelable, Filter {
// If we cannot recycle, insert the new view before the next recyclable child.
// Inflate nested views and add as children
- View nestedView = rvToApply.applyNestedViews(context, target, rootParent, handler,
- null /* size */, colorResources);
+ View nestedView = rvToApply.apply(context, target, rootParent, null /* size */, params);
if (mStableId != NO_ID) {
setStableId(nestedView, mStableId);
}
@@ -2477,7 +2466,7 @@ public class RemoteViews implements Parcelable, Filter {
@Override
public Action initActionAsync(ViewTree root, ViewGroup rootParent,
- InteractionHandler handler, ColorResources colorResources) {
+ ActionApplyParams params) {
// In the async implementation, update the view tree so that subsequent calls to
// findViewById return the current view.
root.createTree();
@@ -2511,8 +2500,7 @@ public class RemoteViews implements Parcelable, Filter {
setNextRecyclableChild(targetVg, nextChild + 1, target.mChildren.size());
final AsyncApplyTask reapplyTask = rvToApply.getInternalAsyncApplyTask(
context,
- targetVg, null /* listener */, handler, null /* size */,
- colorResources,
+ targetVg, null /* listener */, params, null /* size */,
recycled.mRoot);
final ViewTree tree = reapplyTask.doInBackground();
if (tree == null) {
@@ -2521,8 +2509,7 @@ public class RemoteViews implements Parcelable, Filter {
return new RuntimeAction() {
@Override
public void apply(View root, ViewGroup rootParent,
- InteractionHandler handler, ColorResources colorResources)
- throws ActionException {
+ ActionApplyParams params) throws ActionException {
reapplyTask.onPostExecute(tree);
if (recycledViewIndex > nextChild) {
targetVg.removeViews(nextChild, recycledViewIndex - nextChild);
@@ -2533,23 +2520,22 @@ public class RemoteViews implements Parcelable, Filter {
// If the layout id is different, still remove the children as if we recycled
// the view, to insert at the same place.
target.removeChildren(nextChild, recycledViewIndex - nextChild + 1);
- return insertNewView(context, target, handler, colorResources,
+ return insertNewView(context, target, params,
() -> targetVg.removeViews(nextChild,
recycledViewIndex - nextChild + 1));
}
}
// If we cannot recycle, simply add the view at the same available slot.
- return insertNewView(context, target, handler, colorResources, () -> {});
+ return insertNewView(context, target, params, () -> {});
}
- private Action insertNewView(Context context, ViewTree target, InteractionHandler handler,
- ColorResources colorResources, Runnable finalizeAction) {
+ private Action insertNewView(Context context, ViewTree target,
+ ActionApplyParams params, Runnable finalizeAction) {
ViewGroup targetVg = (ViewGroup) target.mRoot;
int nextChild = getNextRecyclableChild(targetVg);
final AsyncApplyTask task = mNestedViews.getInternalAsyncApplyTask(context, targetVg,
- null /* listener */, handler, null /* size */, colorResources,
- null /* result */);
+ null /* listener */, params, null /* size */, null /* result */);
final ViewTree tree = task.doInBackground();
if (tree == null) {
@@ -2569,8 +2555,7 @@ public class RemoteViews implements Parcelable, Filter {
return new RuntimeAction() {
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) throws ActionException {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
task.onPostExecute(tree);
finalizeAction.run();
targetVg.addView(task.mResult, insertIndex);
@@ -2627,8 +2612,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final ViewGroup target = root.findViewById(viewId);
if (target == null) {
@@ -2652,7 +2636,7 @@ public class RemoteViews implements Parcelable, Filter {
@Override
public Action initActionAsync(ViewTree root, ViewGroup rootParent,
- InteractionHandler handler, ColorResources colorResources) {
+ ActionApplyParams params) {
// In the async implementation, update the view tree so that subsequent calls to
// findViewById return the current view.
root.createTree();
@@ -2676,8 +2660,7 @@ public class RemoteViews implements Parcelable, Filter {
}
return new RuntimeAction() {
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) throws ActionException {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
for (int i = targetVg.getChildCount() - 1; i >= 0; i--) {
if (!hasStableId(targetVg.getChildAt(i))) {
@@ -2736,8 +2719,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null || target == root) {
@@ -2752,7 +2734,7 @@ public class RemoteViews implements Parcelable, Filter {
@Override
public Action initActionAsync(ViewTree root, ViewGroup rootParent,
- InteractionHandler handler, ColorResources colorResources) {
+ ActionApplyParams params) {
// In the async implementation, update the view tree so that subsequent calls to
// findViewById return the correct view.
root.createTree();
@@ -2771,8 +2753,7 @@ public class RemoteViews implements Parcelable, Filter {
parent.mChildren.remove(target);
return new RuntimeAction() {
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) throws ActionException {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
parentVg.removeView(target.mRoot);
}
};
@@ -2851,8 +2832,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final TextView target = root.findViewById(viewId);
if (target == null) return;
if (drawablesLoaded) {
@@ -2883,7 +2863,7 @@ public class RemoteViews implements Parcelable, Filter {
@Override
public Action initActionAsync(ViewTree root, ViewGroup rootParent,
- InteractionHandler handler, ColorResources colorResources) {
+ ActionApplyParams params) {
final TextView target = root.findViewById(viewId);
if (target == null) return ACTION_NOOP;
@@ -2961,8 +2941,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final TextView target = root.findViewById(viewId);
if (target == null) return;
target.setTextSize(units, size);
@@ -3007,8 +2986,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null) return;
target.setPadding(left, top, right, bottom);
@@ -3084,8 +3062,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null) {
return;
@@ -3230,8 +3207,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -3266,8 +3242,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
// Let's traverse the viewtree and override all textColors!
Stack<View> viewsToProcess = new Stack<>();
viewsToProcess.add(root);
@@ -3317,8 +3292,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(mViewId);
if (target == null) return;
@@ -3352,8 +3326,7 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources)
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
throws ActionException {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -3404,8 +3377,8 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) throws ActionException {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
+ throws ActionException {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -3483,8 +3456,8 @@ public class RemoteViews implements Parcelable, Filter {
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) throws ActionException {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
+ throws ActionException {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -5578,54 +5551,41 @@ public class RemoteViews implements Parcelable, Filter {
/** @hide */
public View apply(@NonNull Context context, @NonNull ViewGroup parent,
@Nullable InteractionHandler handler, @Nullable SizeF size) {
- RemoteViews rvToApply = getRemoteViewsToApply(context, size);
-
- View result = inflateView(context, rvToApply, parent);
- rvToApply.performApply(result, parent, handler, null);
- return result;
+ return apply(context, parent, size, new ActionApplyParams()
+ .withInteractionHandler(handler));
}
/** @hide */
public View applyWithTheme(@NonNull Context context, @NonNull ViewGroup parent,
@Nullable InteractionHandler handler, @StyleRes int applyThemeResId) {
- return applyWithTheme(context, parent, handler, applyThemeResId, null);
- }
-
- /** @hide */
- public View applyWithTheme(@NonNull Context context, @NonNull ViewGroup parent,
- @Nullable InteractionHandler handler, @StyleRes int applyThemeResId,
- @Nullable SizeF size) {
- RemoteViews rvToApply = getRemoteViewsToApply(context, size);
-
- View result = inflateView(context, rvToApply, parent, applyThemeResId, null);
- rvToApply.performApply(result, parent, handler, null);
- return result;
+ return apply(context, parent, null, new ActionApplyParams()
+ .withInteractionHandler(handler)
+ .withThemeResId(applyThemeResId));
}
/** @hide */
public View apply(Context context, ViewGroup parent, InteractionHandler handler,
@Nullable SizeF size, @Nullable ColorResources colorResources) {
- RemoteViews rvToApply = getRemoteViewsToApply(context, size);
+ return apply(context, parent, size, new ActionApplyParams()
+ .withInteractionHandler(handler)
+ .withColorResources(colorResources));
+ }
- View result = inflateView(context, rvToApply, parent, 0, colorResources);
- rvToApply.performApply(result, parent, handler, colorResources);
- return result;
+ /** @hide **/
+ public View apply(Context context, ViewGroup parent, @Nullable SizeF size,
+ ActionApplyParams params) {
+ return apply(context, parent, parent, size, params);
}
- private View applyNestedViews(Context context, ViewGroup directParent,
- ViewGroup rootParent, InteractionHandler handler, SizeF size,
- ColorResources colorResources) {
+ private View apply(Context context, ViewGroup directParent, ViewGroup rootParent,
+ @Nullable SizeF size, ActionApplyParams params) {
RemoteViews rvToApply = getRemoteViewsToApply(context, size);
-
- View result = inflateView(context, rvToApply, directParent, 0, colorResources);
- rvToApply.performApply(result, rootParent, handler, colorResources);
+ View result = inflateView(context, rvToApply, directParent,
+ params.applyThemeResId, params.colorResources);
+ rvToApply.performApply(result, rootParent, params);
return result;
}
- private View inflateView(Context context, RemoteViews rv, ViewGroup parent) {
- return inflateView(context, rv, parent, 0, null);
- }
-
private View inflateView(Context context, RemoteViews rv, @Nullable ViewGroup parent,
@StyleRes int applyThemeResId, @Nullable ColorResources colorResources) {
// RemoteViews may be built by an application installed in another
@@ -5704,7 +5664,6 @@ public class RemoteViews implements Parcelable, Filter {
return applyAsync(context, parent, executor, listener, null /* handler */);
}
-
/** @hide */
public CancellationSignal applyAsync(Context context, ViewGroup parent,
Executor executor, OnViewAppliedListener listener, InteractionHandler handler) {
@@ -5723,16 +5682,19 @@ public class RemoteViews implements Parcelable, Filter {
public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor,
OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
ColorResources colorResources) {
+
+ ActionApplyParams params = new ActionApplyParams()
+ .withInteractionHandler(handler)
+ .withColorResources(colorResources)
+ .withExecutor(executor);
return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, listener,
- handler, colorResources, null /* result */,
- true /* topLevel */).startTaskOnExecutor(executor);
+ params, null /* result */, true /* topLevel */).startTaskOnExecutor(executor);
}
private AsyncApplyTask getInternalAsyncApplyTask(Context context, ViewGroup parent,
- OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
- ColorResources colorResources, View result) {
+ OnViewAppliedListener listener, ActionApplyParams params, SizeF size, View result) {
return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, listener,
- handler, colorResources, result, false /* topLevel */);
+ params, result, false /* topLevel */);
}
private class AsyncApplyTask extends AsyncTask<Void, Void, ViewTree>
@@ -5742,8 +5704,8 @@ public class RemoteViews implements Parcelable, Filter {
final ViewGroup mParent;
final Context mContext;
final OnViewAppliedListener mListener;
- final InteractionHandler mHandler;
- final ColorResources mColorResources;
+ final ActionApplyParams mApplyParams;
+
/**
* Whether the remote view is the top-level one (i.e. not within an action).
*
@@ -5758,16 +5720,13 @@ public class RemoteViews implements Parcelable, Filter {
private AsyncApplyTask(
RemoteViews rv, ViewGroup parent, Context context, OnViewAppliedListener listener,
- InteractionHandler handler, ColorResources colorResources,
- View result, boolean topLevel) {
+ ActionApplyParams applyParams, View result, boolean topLevel) {
mRV = rv;
mParent = parent;
mContext = context;
mListener = listener;
- mColorResources = colorResources;
- mHandler = handler;
mTopLevel = topLevel;
-
+ mApplyParams = applyParams;
mResult = result;
}
@@ -5776,17 +5735,18 @@ public class RemoteViews implements Parcelable, Filter {
protected ViewTree doInBackground(Void... params) {
try {
if (mResult == null) {
- mResult = inflateView(mContext, mRV, mParent, 0, mColorResources);
+ mResult = inflateView(mContext, mRV, mParent, 0, mApplyParams.colorResources);
}
mTree = new ViewTree(mResult);
+
if (mRV.mActions != null) {
int count = mRV.mActions.size();
mActions = new Action[count];
for (int i = 0; i < count && !isCancelled(); i++) {
// TODO: check if isCancelled in nested views.
- mActions[i] = mRV.mActions.get(i).initActionAsync(mTree, mParent, mHandler,
- mColorResources);
+ mActions[i] = mRV.mActions.get(i)
+ .initActionAsync(mTree, mParent, mApplyParams);
}
} else {
mActions = null;
@@ -5808,10 +5768,13 @@ public class RemoteViews implements Parcelable, Filter {
try {
if (mActions != null) {
- InteractionHandler handler = mHandler == null
- ? DEFAULT_INTERACTION_HANDLER : mHandler;
+
+ ActionApplyParams applyParams = mApplyParams.clone();
+ if (applyParams.handler == null) {
+ applyParams.handler = DEFAULT_INTERACTION_HANDLER;
+ }
for (Action a : mActions) {
- a.apply(viewTree.mRoot, mParent, handler, mColorResources);
+ a.apply(viewTree.mRoot, mParent, applyParams);
}
}
// If the parent of the view is has is a root, resolve the recycling.
@@ -5859,18 +5822,43 @@ public class RemoteViews implements Parcelable, Filter {
* the {@link #apply(Context,ViewGroup)} call.
*/
public void reapply(Context context, View v) {
- reapply(context, v, null /* handler */);
+ reapply(context, v, null /* size */, new ActionApplyParams());
}
/** @hide */
public void reapply(Context context, View v, InteractionHandler handler) {
- reapply(context, v, handler, null /* size */, null /* colorResources */);
+ reapply(context, v, null /* size */,
+ new ActionApplyParams().withInteractionHandler(handler));
}
/** @hide */
public void reapply(Context context, View v, InteractionHandler handler, SizeF size,
ColorResources colorResources) {
- reapply(context, v, handler, size, colorResources, true);
+ reapply(context, v, size, new ActionApplyParams()
+ .withInteractionHandler(handler).withColorResources(colorResources));
+ }
+
+ /** @hide */
+ public void reapply(Context context, View v, @Nullable SizeF size, ActionApplyParams params) {
+ reapply(context, v, (ViewGroup) v.getParent(), size, params, true);
+ }
+
+ private void reapplyNestedViews(Context context, View v, ViewGroup rootParent,
+ ActionApplyParams params) {
+ reapply(context, v, rootParent, null, params, false);
+ }
+
+ // Note: topLevel should be true only for calls on the topLevel RemoteViews, internal calls
+ // should set it to false.
+ private void reapply(Context context, View v, ViewGroup rootParent,
+ @Nullable SizeF size, ActionApplyParams params, boolean topLevel) {
+ RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
+ rvToApply.performApply(v, rootParent, params);
+
+ // If the parent of the view is has is a root, resolve the recycling.
+ if (topLevel && v instanceof ViewGroup) {
+ finalizeViewRecycling((ViewGroup) v);
+ }
}
/** @hide */
@@ -5922,27 +5910,6 @@ public class RemoteViews implements Parcelable, Filter {
return rvToApply;
}
- // Note: topLevel should be true only for calls on the topLevel RemoteViews, internal calls
- // should set it to false.
- private void reapply(Context context, View v, InteractionHandler handler, SizeF size,
- ColorResources colorResources, boolean topLevel) {
-
- RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
-
- rvToApply.performApply(v, (ViewGroup) v.getParent(), handler, colorResources);
-
- // If the parent of the view is has is a root, resolve the recycling.
- if (topLevel && v instanceof ViewGroup) {
- finalizeViewRecycling((ViewGroup) v);
- }
- }
-
- private void reapplyNestedViews(Context context, View v, ViewGroup rootParent,
- InteractionHandler handler, SizeF size, ColorResources colorResources) {
- RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
- rvToApply.performApply(v, rootParent, handler, colorResources);
- }
-
/**
* Applies all the actions to the provided view, moving as much of the task on the background
* thread as possible.
@@ -5973,19 +5940,25 @@ public class RemoteViews implements Parcelable, Filter {
ColorResources colorResources) {
RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
+ ActionApplyParams params = new ActionApplyParams()
+ .withColorResources(colorResources)
+ .withInteractionHandler(handler)
+ .withExecutor(executor);
+
return new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(),
- context, listener, handler, colorResources, v, true /* topLevel */)
+ context, listener, params, v, true /* topLevel */)
.startTaskOnExecutor(executor);
}
- private void performApply(View v, ViewGroup parent, InteractionHandler handler,
- ColorResources colorResources) {
+ private void performApply(View v, ViewGroup parent, ActionApplyParams params) {
+ params = params.clone();
+ if (params.handler == null) {
+ params.handler = DEFAULT_INTERACTION_HANDLER;
+ }
if (mActions != null) {
- handler = handler == null ? DEFAULT_INTERACTION_HANDLER : handler;
final int count = mActions.size();
for (int i = 0; i < count; i++) {
- Action a = mActions.get(i);
- a.apply(v, parent, handler, colorResources);
+ mActions.get(i).apply(v, parent, params);
}
}
}
@@ -6043,6 +6016,47 @@ public class RemoteViews implements Parcelable, Filter {
}
/**
+ * Utility class to hold all the options when applying the remote views
+ * @hide
+ */
+ public class ActionApplyParams {
+
+ public InteractionHandler handler;
+ public ColorResources colorResources;
+ public Executor executor;
+ @StyleRes public int applyThemeResId;
+
+ @Override
+ public ActionApplyParams clone() {
+ return new ActionApplyParams()
+ .withInteractionHandler(handler)
+ .withColorResources(colorResources)
+ .withExecutor(executor)
+ .withThemeResId(applyThemeResId);
+ }
+
+ public ActionApplyParams withInteractionHandler(InteractionHandler handler) {
+ this.handler = handler;
+ return this;
+ }
+
+ public ActionApplyParams withColorResources(ColorResources colorResources) {
+ this.colorResources = colorResources;
+ return this;
+ }
+
+ public ActionApplyParams withThemeResId(@StyleRes int themeResId) {
+ this.applyThemeResId = themeResId;
+ return this;
+ }
+
+ public ActionApplyParams withExecutor(Executor executor) {
+ this.executor = executor;
+ return this;
+ }
+ }
+
+ /**
* Object allowing the modification of a context to overload the system's dynamic colors.
*
* Only colors from {@link android.R.color#system_accent1_0} to
@@ -6056,10 +6070,12 @@ public class RemoteViews implements Parcelable, Filter {
// Size, in bytes, of an entry in the array of colors in an ARSC file.
private static final int ARSC_ENTRY_SIZE = 16;
- private ResourcesLoader mLoader;
+ private final ResourcesLoader mLoader;
+ private final SparseIntArray mColorMapping;
- private ColorResources(ResourcesLoader loader) {
+ private ColorResources(ResourcesLoader loader, SparseIntArray colorMapping) {
mLoader = loader;
+ mColorMapping = colorMapping;
}
/**
@@ -6071,6 +6087,10 @@ public class RemoteViews implements Parcelable, Filter {
context.getResources().addLoaders(mLoader);
}
+ public SparseIntArray getColorMapping() {
+ return mColorMapping;
+ }
+
private static ByteArrayOutputStream readFileContent(InputStream input) throws IOException {
ByteArrayOutputStream content = new ByteArrayOutputStream(2048);
byte[] buffer = new byte[4096];
@@ -6145,7 +6165,7 @@ public class RemoteViews implements Parcelable, Filter {
ResourcesLoader colorsLoader = new ResourcesLoader();
colorsLoader.addProvider(ResourcesProvider
.loadFromTable(pfd, null /* assetsProvider */));
- return new ColorResources(colorsLoader);
+ return new ColorResources(colorsLoader, colorMapping.clone());
}
}
} finally {
diff --git a/core/java/android/window/ITaskFragmentOrganizer.aidl b/core/java/android/window/ITaskFragmentOrganizer.aidl
index 3335c9c073e0..79ddadb74f82 100644
--- a/core/java/android/window/ITaskFragmentOrganizer.aidl
+++ b/core/java/android/window/ITaskFragmentOrganizer.aidl
@@ -16,54 +16,9 @@
package android.window;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.window.TaskFragmentInfo;
+import android.window.TaskFragmentTransaction;
/** @hide */
oneway interface ITaskFragmentOrganizer {
- void onTaskFragmentAppeared(in TaskFragmentInfo taskFragmentInfo);
- void onTaskFragmentInfoChanged(in TaskFragmentInfo taskFragmentInfo);
- void onTaskFragmentVanished(in TaskFragmentInfo taskFragmentInfo);
-
- /**
- * Called when the parent leaf Task of organized TaskFragments is changed.
- * When the leaf Task is changed, the organizer may want to update the TaskFragments in one
- * transaction.
- *
- * For case like screen size change, it will trigger onTaskFragmentParentInfoChanged with new
- * Task bounds, but may not trigger onTaskFragmentInfoChanged because there can be an override
- * bounds.
- */
- void onTaskFragmentParentInfoChanged(in IBinder fragmentToken, in Configuration parentConfig);
-
- /**
- * Called when the {@link WindowContainerTransaction} created with
- * {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)} failed on the server side.
- *
- * @param errorCallbackToken Token set through {@link
- * WindowContainerTransaction#setErrorCallbackToken(IBinder)}
- * @param errorBundle Bundle containing the exception, operation type and TaskFragmentInfo
- * if any. Should be created with
- * {@link TaskFragmentOrganizer#putErrorInfoInBundle}.
- */
- void onTaskFragmentError(in IBinder errorCallbackToken, in Bundle errorBundle);
-
- /**
- * Called when an Activity is reparented to the Task with organized TaskFragment. For example,
- * when an Activity enters and then exits Picture-in-picture, it will be reparented back to its
- * orginial Task. In this case, we need to notify the organizer so that it can check if the
- * Activity matches any split rule.
- *
- * @param taskId The Task that the activity is reparented to.
- * @param activityIntent The intent that the activity is original launched with.
- * @param activityToken If the activity belongs to the same process as the organizer, this
- * will be the actual activity token; if the activity belongs to a
- * different process, the server will generate a temporary token that
- * the organizer can use to reparent the activity through
- * {@link WindowContainerTransaction} if needed.
- */
- void onActivityReparentToTask(int taskId, in Intent activityIntent, in IBinder activityToken);
+ void onTransactionReady(in TaskFragmentTransaction transaction);
}
diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java
index 765cd8163cb3..e567cedb43cc 100644
--- a/core/java/android/window/TaskFragmentOrganizer.java
+++ b/core/java/android/window/TaskFragmentOrganizer.java
@@ -16,6 +16,13 @@
package android.window;
+import static android.window.TaskFragmentTransaction.TYPE_ACTIVITY_REPARENT_TO_TASK;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_APPEARED;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_ERROR;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_INFO_CHANGED;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED;
+
import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -25,8 +32,11 @@ import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.SparseArray;
import android.view.RemoteAnimationDefinition;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.Executor;
/**
@@ -64,6 +74,12 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
*/
private final Executor mExecutor;
+ // TODO(b/240519866): doing so to keep CTS compatibility. Remove in the next release.
+ /** Map from Task id to client tokens of TaskFragments in the Task. */
+ private final SparseArray<List<IBinder>> mTaskIdToFragmentTokens = new SparseArray<>();
+ /** Map from Task id to Task configuration. */
+ private final SparseArray<Configuration> mTaskIdToConfigurations = new SparseArray<>();
+
public TaskFragmentOrganizer(@NonNull Executor executor) {
mExecutor = executor;
}
@@ -153,6 +169,27 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
@NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) {}
/**
+ * Called when the parent leaf Task of organized TaskFragments is changed.
+ * When the leaf Task is changed, the organizer may want to update the TaskFragments in one
+ * transaction.
+ *
+ * For case like screen size change, it will trigger onTaskFragmentParentInfoChanged with new
+ * Task bounds, but may not trigger onTaskFragmentInfoChanged because there can be an override
+ * bounds.
+ * @hide
+ */
+ public void onTaskFragmentParentInfoChanged(int taskId, @NonNull Configuration parentConfig) {
+ // TODO(b/240519866): doing so to keep CTS compatibility. Remove in the next release.
+ final List<IBinder> tokens = mTaskIdToFragmentTokens.get(taskId);
+ if (tokens == null || tokens.isEmpty()) {
+ return;
+ }
+ for (int i = tokens.size() - 1; i >= 0; i--) {
+ onTaskFragmentParentInfoChanged(tokens.get(i), parentConfig);
+ }
+ }
+
+ /**
* Called when the {@link WindowContainerTransaction} created with
* {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)} failed on the server side.
*
@@ -186,6 +223,76 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
public void onActivityReparentToTask(int taskId, @NonNull Intent activityIntent,
@NonNull IBinder activityToken) {}
+ /**
+ * Called when the transaction is ready so that the organizer can update the TaskFragments based
+ * on the changes in transaction.
+ * @hide
+ */
+ public void onTransactionReady(@NonNull TaskFragmentTransaction transaction) {
+ final List<TaskFragmentTransaction.Change> changes = transaction.getChanges();
+ for (TaskFragmentTransaction.Change change : changes) {
+ // TODO(b/240519866): apply all changes in one WCT.
+ final int taskId = change.getTaskId();
+ switch (change.getType()) {
+ case TYPE_TASK_FRAGMENT_APPEARED:
+ // TODO(b/240519866): doing so to keep CTS compatibility. Remove in the next
+ // release.
+ if (!mTaskIdToFragmentTokens.contains(taskId)) {
+ mTaskIdToFragmentTokens.put(taskId, new ArrayList<>());
+ }
+ mTaskIdToFragmentTokens.get(taskId).add(change.getTaskFragmentToken());
+ onTaskFragmentParentInfoChanged(change.getTaskFragmentToken(),
+ mTaskIdToConfigurations.get(taskId));
+
+ onTaskFragmentAppeared(change.getTaskFragmentInfo());
+ break;
+ case TYPE_TASK_FRAGMENT_INFO_CHANGED:
+ onTaskFragmentInfoChanged(change.getTaskFragmentInfo());
+ break;
+ case TYPE_TASK_FRAGMENT_VANISHED:
+ // TODO(b/240519866): doing so to keep CTS compatibility. Remove in the next
+ // release.
+ if (mTaskIdToFragmentTokens.contains(taskId)) {
+ final List<IBinder> tokens = mTaskIdToFragmentTokens.get(taskId);
+ tokens.remove(change.getTaskFragmentToken());
+ if (tokens.isEmpty()) {
+ mTaskIdToFragmentTokens.remove(taskId);
+ mTaskIdToConfigurations.remove(taskId);
+ }
+ }
+
+ onTaskFragmentVanished(change.getTaskFragmentInfo());
+ break;
+ case TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED:
+ // TODO(b/240519866): doing so to keep CTS compatibility. Remove in the next
+ // release.
+ mTaskIdToConfigurations.put(taskId, change.getTaskConfiguration());
+
+ onTaskFragmentParentInfoChanged(taskId, change.getTaskConfiguration());
+ break;
+ case TYPE_TASK_FRAGMENT_ERROR:
+ final Bundle errorBundle = change.getErrorBundle();
+ onTaskFragmentError(
+ change.getErrorCallbackToken(),
+ errorBundle.getParcelable(
+ KEY_ERROR_CALLBACK_TASK_FRAGMENT_INFO, TaskFragmentInfo.class),
+ errorBundle.getInt(KEY_ERROR_CALLBACK_OP_TYPE),
+ errorBundle.getSerializable(KEY_ERROR_CALLBACK_EXCEPTION,
+ java.lang.Throwable.class));
+ break;
+ case TYPE_ACTIVITY_REPARENT_TO_TASK:
+ onActivityReparentToTask(
+ change.getTaskId(),
+ change.getActivityIntent(),
+ change.getActivityToken());
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Unknown TaskFragmentEvent=" + change.getType());
+ }
+ }
+ }
+
@Override
public void applyTransaction(@NonNull WindowContainerTransaction t) {
t.setTaskFragmentOrganizer(mInterface);
@@ -203,51 +310,8 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
private final ITaskFragmentOrganizer mInterface = new ITaskFragmentOrganizer.Stub() {
@Override
- public void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo) {
- mExecutor.execute(
- () -> TaskFragmentOrganizer.this.onTaskFragmentAppeared(taskFragmentInfo));
- }
-
- @Override
- public void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo) {
- mExecutor.execute(
- () -> TaskFragmentOrganizer.this.onTaskFragmentInfoChanged(taskFragmentInfo));
- }
-
- @Override
- public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {
- mExecutor.execute(
- () -> TaskFragmentOrganizer.this.onTaskFragmentVanished(taskFragmentInfo));
- }
-
- @Override
- public void onTaskFragmentParentInfoChanged(
- @NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) {
- mExecutor.execute(
- () -> TaskFragmentOrganizer.this.onTaskFragmentParentInfoChanged(
- fragmentToken, parentConfig));
- }
-
- @Override
- public void onTaskFragmentError(
- @NonNull IBinder errorCallbackToken, @NonNull Bundle errorBundle) {
- mExecutor.execute(() -> {
- final TaskFragmentInfo info = errorBundle.getParcelable(
- KEY_ERROR_CALLBACK_TASK_FRAGMENT_INFO, TaskFragmentInfo.class);
- TaskFragmentOrganizer.this.onTaskFragmentError(
- errorCallbackToken, info,
- errorBundle.getInt(KEY_ERROR_CALLBACK_OP_TYPE),
- (Throwable) errorBundle.getSerializable(KEY_ERROR_CALLBACK_EXCEPTION,
- java.lang.Throwable.class));
- });
- }
-
- @Override
- public void onActivityReparentToTask(int taskId, @NonNull Intent activityIntent,
- @NonNull IBinder activityToken) {
- mExecutor.execute(
- () -> TaskFragmentOrganizer.this.onActivityReparentToTask(
- taskId, activityIntent, activityToken));
+ public void onTransactionReady(@NonNull TaskFragmentTransaction transaction) {
+ mExecutor.execute(() -> TaskFragmentOrganizer.this.onTransactionReady(transaction));
}
};
diff --git a/core/java/android/window/TaskFragmentTransaction.aidl b/core/java/android/window/TaskFragmentTransaction.aidl
new file mode 100644
index 000000000000..aaa2db4b15b8
--- /dev/null
+++ b/core/java/android/window/TaskFragmentTransaction.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+parcelable TaskFragmentTransaction;
+parcelable TaskFragmentTransaction.Change;
diff --git a/core/java/android/window/TaskFragmentTransaction.java b/core/java/android/window/TaskFragmentTransaction.java
new file mode 100644
index 000000000000..755864f60f87
--- /dev/null
+++ b/core/java/android/window/TaskFragmentTransaction.java
@@ -0,0 +1,335 @@
+/*
+ * 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.window;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Used to communicate information about what are changing on embedded TaskFragments belonging to
+ * the same TaskFragmentOrganizer. A transaction can contain multiple changes.
+ * @see TaskFragmentTransaction.Change
+ * @hide
+ */
+public final class TaskFragmentTransaction implements Parcelable {
+
+ private final ArrayList<Change> mChanges = new ArrayList<>();
+
+ public TaskFragmentTransaction() {}
+
+ private TaskFragmentTransaction(Parcel in) {
+ in.readTypedList(mChanges, Change.CREATOR);
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeTypedList(mChanges);
+ }
+
+ /** Adds a {@link Change} to this transaction. */
+ public void addChange(@Nullable Change change) {
+ if (change != null) {
+ mChanges.add(change);
+ }
+ }
+
+ /** Whether this transaction contains any {@link Change}. */
+ public boolean isEmpty() {
+ return mChanges.isEmpty();
+ }
+
+ @NonNull
+ public List<Change> getChanges() {
+ return mChanges;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("TaskFragmentTransaction{changes=[");
+ for (int i = 0; i < mChanges.size(); ++i) {
+ if (i > 0) {
+ sb.append(',');
+ }
+ sb.append(mChanges.get(i));
+ }
+ sb.append("]}");
+ return sb.toString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<TaskFragmentTransaction> CREATOR = new Creator<>() {
+ @Override
+ public TaskFragmentTransaction createFromParcel(Parcel in) {
+ return new TaskFragmentTransaction(in);
+ }
+
+ @Override
+ public TaskFragmentTransaction[] newArray(int size) {
+ return new TaskFragmentTransaction[size];
+ }
+ };
+
+ /** Change type: the TaskFragment is attached to the hierarchy. */
+ public static final int TYPE_TASK_FRAGMENT_APPEARED = 1;
+
+ /** Change type: the status of the TaskFragment is changed. */
+ public static final int TYPE_TASK_FRAGMENT_INFO_CHANGED = 2;
+
+ /** Change type: the TaskFragment is removed form the hierarchy. */
+ public static final int TYPE_TASK_FRAGMENT_VANISHED = 3;
+
+ /** Change type: the status of the parent leaf Task is changed. */
+ public static final int TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED = 4;
+
+ /** Change type: the TaskFragment related operation failed on the server side. */
+ public static final int TYPE_TASK_FRAGMENT_ERROR = 5;
+
+ /**
+ * Change type: an Activity is reparented to the Task. For example, when an Activity enters and
+ * then exits Picture-in-picture, it will be reparented back to its original Task. In this case,
+ * we need to notify the organizer so that it can check if the Activity matches any split rule.
+ */
+ public static final int TYPE_ACTIVITY_REPARENT_TO_TASK = 6;
+
+ @IntDef(prefix = { "TYPE_" }, value = {
+ TYPE_TASK_FRAGMENT_APPEARED,
+ TYPE_TASK_FRAGMENT_INFO_CHANGED,
+ TYPE_TASK_FRAGMENT_VANISHED,
+ TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED,
+ TYPE_TASK_FRAGMENT_ERROR,
+ TYPE_ACTIVITY_REPARENT_TO_TASK
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface ChangeType {}
+
+ /** Represents the change an embedded TaskFragment undergoes. */
+ public static final class Change implements Parcelable {
+
+ /** @see ChangeType */
+ @ChangeType
+ private final int mType;
+
+ /** @see #setTaskFragmentToken(IBinder) */
+ @Nullable
+ private IBinder mTaskFragmentToken;
+
+ /** @see #setTaskFragmentInfo(TaskFragmentInfo) */
+ @Nullable
+ private TaskFragmentInfo mTaskFragmentInfo;
+
+ /** @see #setTaskId(int) */
+ private int mTaskId;
+
+ /** @see #setTaskConfiguration(Configuration) */
+ @Nullable
+ private Configuration mTaskConfiguration;
+
+ /** @see #setErrorCallbackToken(IBinder) */
+ @Nullable
+ private IBinder mErrorCallbackToken;
+
+ /** @see #setErrorBundle(Bundle) */
+ @Nullable
+ private Bundle mErrorBundle;
+
+ /** @see #setActivityIntent(Intent) */
+ @Nullable
+ private Intent mActivityIntent;
+
+ /** @see #setActivityToken(IBinder) */
+ @Nullable
+ private IBinder mActivityToken;
+
+ public Change(@ChangeType int type) {
+ mType = type;
+ }
+
+ private Change(Parcel in) {
+ mType = in.readInt();
+ mTaskFragmentToken = in.readStrongBinder();
+ mTaskFragmentInfo = in.readTypedObject(TaskFragmentInfo.CREATOR);
+ mTaskId = in.readInt();
+ mTaskConfiguration = in.readTypedObject(Configuration.CREATOR);
+ mErrorCallbackToken = in.readStrongBinder();
+ mErrorBundle = in.readBundle(TaskFragmentTransaction.class.getClassLoader());
+ mActivityIntent = in.readTypedObject(Intent.CREATOR);
+ mActivityToken = in.readStrongBinder();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mType);
+ dest.writeStrongBinder(mTaskFragmentToken);
+ dest.writeTypedObject(mTaskFragmentInfo, flags);
+ dest.writeInt(mTaskId);
+ dest.writeTypedObject(mTaskConfiguration, flags);
+ dest.writeStrongBinder(mErrorCallbackToken);
+ dest.writeBundle(mErrorBundle);
+ dest.writeTypedObject(mActivityIntent, flags);
+ dest.writeStrongBinder(mActivityToken);
+ }
+
+ /** The change is related to the TaskFragment created with this unique token. */
+ public Change setTaskFragmentToken(@NonNull IBinder taskFragmentToken) {
+ mTaskFragmentToken = requireNonNull(taskFragmentToken);
+ return this;
+ }
+
+ /** Info of the embedded TaskFragment. */
+ public Change setTaskFragmentInfo(@NonNull TaskFragmentInfo info) {
+ mTaskFragmentInfo = requireNonNull(info);
+ return this;
+ }
+
+ /** Task id the parent Task. */
+ public Change setTaskId(int taskId) {
+ mTaskId = taskId;
+ return this;
+ }
+
+ /** Configuration of the parent Task. */
+ public Change setTaskConfiguration(@NonNull Configuration configuration) {
+ mTaskConfiguration = requireNonNull(configuration);
+ return this;
+ }
+
+ /**
+ * If the {@link #TYPE_TASK_FRAGMENT_ERROR} is from a {@link WindowContainerTransaction}
+ * from the {@link TaskFragmentOrganizer}, it may come with an error callback token to
+ * report back.
+ */
+ public Change setErrorCallbackToken(@Nullable IBinder errorCallbackToken) {
+ mErrorCallbackToken = errorCallbackToken;
+ return this;
+ }
+
+ /**
+ * Bundle with necessary info about the failure operation of
+ * {@link #TYPE_TASK_FRAGMENT_ERROR}.
+ */
+ public Change setErrorBundle(@NonNull Bundle errorBundle) {
+ mErrorBundle = requireNonNull(errorBundle);
+ return this;
+ }
+
+ /**
+ * Intent of the activity that is reparented to the Task for
+ * {@link #TYPE_ACTIVITY_REPARENT_TO_TASK}.
+ */
+ public Change setActivityIntent(@NonNull Intent intent) {
+ mActivityIntent = requireNonNull(intent);
+ return this;
+ }
+
+ /**
+ * Token of the reparent activity for {@link #TYPE_ACTIVITY_REPARENT_TO_TASK}.
+ * If the activity belongs to the same process as the organizer, this will be the actual
+ * activity token; if the activity belongs to a different process, the server will generate
+ * a temporary token that the organizer can use to reparent the activity through
+ * {@link WindowContainerTransaction} if needed.
+ */
+ public Change setActivityToken(@NonNull IBinder activityToken) {
+ mActivityToken = requireNonNull(activityToken);
+ return this;
+ }
+
+ @ChangeType
+ public int getType() {
+ return mType;
+ }
+
+ @Nullable
+ public IBinder getTaskFragmentToken() {
+ return mTaskFragmentToken;
+ }
+
+ @Nullable
+ public TaskFragmentInfo getTaskFragmentInfo() {
+ return mTaskFragmentInfo;
+ }
+
+ public int getTaskId() {
+ return mTaskId;
+ }
+
+ @Nullable
+ public Configuration getTaskConfiguration() {
+ return mTaskConfiguration;
+ }
+
+ @Nullable
+ public IBinder getErrorCallbackToken() {
+ return mErrorCallbackToken;
+ }
+
+ @Nullable
+ public Bundle getErrorBundle() {
+ return mErrorBundle;
+ }
+
+ @Nullable
+ public Intent getActivityIntent() {
+ return mActivityIntent;
+ }
+
+ @Nullable
+ public IBinder getActivityToken() {
+ return mActivityToken;
+ }
+
+ @Override
+ public String toString() {
+ return "Change{ type=" + mType + " }";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<Change> CREATOR = new Creator<>() {
+ @Override
+ public Change createFromParcel(Parcel in) {
+ return new Change(in);
+ }
+
+ @Override
+ public Change[] newArray(int size) {
+ return new Change[size];
+ }
+ };
+ }
+}
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 1d396be6b478..0730f3ddf8ac 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -325,7 +325,8 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
public boolean checkApplicationCallbackRegistration(int priority,
OnBackInvokedCallback callback) {
if (!mApplicationCallBackEnabled
- && !(callback instanceof CompatOnBackInvokedCallback)) {
+ && !(callback instanceof CompatOnBackInvokedCallback)
+ && !ALWAYS_ENFORCE_PREDICTIVE_BACK) {
Log.w("OnBackInvokedCallback",
"OnBackInvokedCallback is not enabled for the application."
+ "\nSet 'android:enableOnBackInvokedCallback=\"true\"' in the"
diff --git a/core/java/com/android/internal/app/AssistUtils.java b/core/java/com/android/internal/app/AssistUtils.java
index c056e26e4674..f8433182db9e 100644
--- a/core/java/com/android/internal/app/AssistUtils.java
+++ b/core/java/com/android/internal/app/AssistUtils.java
@@ -17,6 +17,7 @@
package com.android.internal.app;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
@@ -67,12 +68,53 @@ public class AssistUtils {
ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
}
- public boolean showSessionForActiveService(Bundle args, int sourceFlags,
- IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken) {
+ /**
+ * Shows the session for the currently active service. Used to start a new session from system
+ * affordances.
+ *
+ * @param args the bundle to pass as arguments to the voice interaction session
+ * @param sourceFlags flags indicating the source of this show
+ * @param showCallback optional callback to be notified when the session was shown
+ * @param activityToken optional token of activity that needs to be on top
+ *
+ * @deprecated Use {@link #showSessionForActiveService(Bundle, int, String,
+ * IVoiceInteractionSessionShowCallback, IBinder)} instead
+ */
+ @Deprecated
+ public boolean showSessionForActiveService(@NonNull Bundle args, int sourceFlags,
+ @Nullable IVoiceInteractionSessionShowCallback showCallback,
+ @Nullable IBinder activityToken) {
+ return showSessionForActiveServiceInternal(args, sourceFlags, /* attributionTag */ null,
+ showCallback, activityToken);
+ }
+
+ /**
+ * Shows the session for the currently active service. Used to start a new session from system
+ * affordances.
+ *
+ * @param args the bundle to pass as arguments to the voice interaction session
+ * @param sourceFlags flags indicating the source of this show
+ * @param attributionTag the attribution tag of the calling context or {@code null} for default
+ * attribution
+ * @param showCallback optional callback to be notified when the session was shown
+ * @param activityToken optional token of activity that needs to be on top
+ */
+ public boolean showSessionForActiveService(@NonNull Bundle args, int sourceFlags,
+ @Nullable String attributionTag,
+ @Nullable IVoiceInteractionSessionShowCallback showCallback,
+ @Nullable IBinder activityToken) {
+ return showSessionForActiveServiceInternal(args, sourceFlags, attributionTag, showCallback,
+ activityToken);
+ }
+
+ private boolean showSessionForActiveServiceInternal(@NonNull Bundle args, int sourceFlags,
+ @Nullable String attributionTag,
+ @Nullable IVoiceInteractionSessionShowCallback showCallback,
+ @Nullable IBinder activityToken) {
try {
if (mVoiceInteractionManagerService != null) {
return mVoiceInteractionManagerService.showSessionForActiveService(args,
- sourceFlags, showCallback, activityToken);
+ sourceFlags, attributionTag, showCallback, activityToken);
}
} catch (RemoteException e) {
Log.w(TAG, "Failed to call showSessionForActiveService", e);
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index ff1afddbbb06..f6c647320159 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -254,7 +254,7 @@ public class ChooserActivity extends ResolverActivity implements
SystemUiDeviceConfigFlags.IS_NEARBY_SHARE_FIRST_TARGET_IN_RANKED_APP,
DEFAULT_IS_NEARBY_SHARE_FIRST_TARGET_IN_RANKED_APP);
- private static final int DEFAULT_LIST_VIEW_UPDATE_DELAY_MS = 125;
+ private static final int DEFAULT_LIST_VIEW_UPDATE_DELAY_MS = 0;
private static final int URI_PERMISSION_INTENT_FLAGS = Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 4b01357877ed..83bf801391f0 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -39,15 +39,16 @@ import android.service.voice.IVoiceInteractionSession;
import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
interface IVoiceInteractionManagerService {
- void showSession(in Bundle sessionArgs, int flags);
+ void showSession(in Bundle sessionArgs, int flags, String attributionTag);
boolean deliverNewSession(IBinder token, IVoiceInteractionSession session,
IVoiceInteractor interactor);
- boolean showSessionFromSession(IBinder token, in Bundle sessionArgs, int flags);
+ boolean showSessionFromSession(IBinder token, in Bundle sessionArgs, int flags,
+ String attributionTag);
boolean hideSessionFromSession(IBinder token);
int startVoiceActivity(IBinder token, in Intent intent, String resolvedType,
- String callingFeatureId);
+ String attributionTag);
int startAssistantActivity(IBinder token, in Intent intent, String resolvedType,
- String callingFeatureId);
+ String attributionTag);
void setKeepAwake(IBinder token, boolean keepAwake);
void closeSystemDialogs(IBinder token);
void finish(IBinder token);
@@ -125,12 +126,14 @@ interface IVoiceInteractionManagerService {
*
* @param args the bundle to pass as arguments to the voice interaction session
* @param sourceFlags flags indicating the source of this show
+ * @param attributionTag the attribution tag of the calling context or {@code null} for default
+ * attribution
* @param showCallback optional callback to be notified when the session was shown
* @param activityToken optional token of activity that needs to be on top
* @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE
*/
@EnforcePermission("ACCESS_VOICE_INTERACTION_SERVICE")
- boolean showSessionForActiveService(in Bundle args, int sourceFlags,
+ boolean showSessionForActiveService(in Bundle args, int sourceFlags, String attributionTag,
IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken);
/**
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index b32afb44852a..66fff5c13ab7 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -951,7 +951,7 @@ public class ResolverListAdapter extends BaseAdapter {
protected void onPostExecute(Drawable d) {
if (getOtherProfile() == mDisplayResolveInfo) {
mResolverListCommunicator.updateProfileViewButton();
- } else {
+ } else if (!mDisplayResolveInfo.hasDisplayIcon()) {
mDisplayResolveInfo.setDisplayIcon(d);
mHolder.bindIcon(mDisplayResolveInfo);
// Notify in case view is already bound to resolve the race conditions on
diff --git a/core/java/com/android/internal/inputmethod/IInputMethod.aidl b/core/java/com/android/internal/inputmethod/IInputMethod.aidl
index 8bab5c31217d..5db2e84845f5 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethod.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethod.aidl
@@ -74,6 +74,8 @@ oneway interface IInputMethod {
void hideSoftInput(in IBinder hideInputToken, int flags, in ResultReceiver resultReceiver);
+ void updateEditorToolType(int toolType);
+
void changeInputMethodSubtype(in InputMethodSubtype subtype);
void canStartStylusHandwriting(int requestId);
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/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index dbfa4d35974b..48343803b10e 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -66,6 +66,8 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
private static final long INVALID_ID = -1;
public static final int NANOS_IN_MILLISECOND = 1_000_000;
+ private static final int MAX_LENGTH_EVENT_DESC = 20;
+
static final int REASON_END_UNKNOWN = -1;
static final int REASON_END_NORMAL = 0;
static final int REASON_END_SURFACE_DESTROYED = 1;
@@ -394,7 +396,18 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
return true;
}
- private void markEvent(String desc) {
+ /**
+ * Mark the FrameTracker events in the trace.
+ *
+ * @param desc The description of the trace event,
+ * shouldn't exceed {@link #MAX_LENGTH_EVENT_DESC}.
+ */
+ private void markEvent(@NonNull String desc) {
+ if (desc.length() > MAX_LENGTH_EVENT_DESC) {
+ throw new IllegalArgumentException(TextUtils.formatSimple(
+ "The length of the trace event description <%s> exceeds %d",
+ desc, MAX_LENGTH_EVENT_DESC));
+ }
Trace.beginSection(TextUtils.formatSimple("%s#%s", mSession.getName(), desc));
Trace.endSection();
}
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 8f10a5e3f049..72de78c148f8 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -55,7 +55,6 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_CLEAR_ALL;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_DIALOG_OPEN;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_EXPAND_COLLAPSE_LOCK;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_APPEAR;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_DISAPPEAR;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_NOTIFICATION_ADD;
@@ -150,12 +149,15 @@ public class InteractionJankMonitor {
private static final int DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES = 3;
private static final int DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS = 64;
+ @VisibleForTesting
+ public static final int MAX_LENGTH_OF_CUJ_NAME = 80;
+ private static final int MAX_LENGTH_SESSION_NAME = 100;
+
public static final String ACTION_SESSION_END = ACTION_PREFIX + ".ACTION_SESSION_END";
public static final String ACTION_SESSION_CANCEL = ACTION_PREFIX + ".ACTION_SESSION_CANCEL";
// Every value must have a corresponding entry in CUJ_STATSD_INTERACTION_TYPE.
public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 0;
- public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE_LOCK = 1;
public static final int CUJ_NOTIFICATION_SHADE_SCROLL_FLING = 2;
public static final int CUJ_NOTIFICATION_SHADE_ROW_EXPAND = 3;
public static final int CUJ_NOTIFICATION_SHADE_ROW_SWIPE = 4;
@@ -226,7 +228,7 @@ public class InteractionJankMonitor {
public static final int[] CUJ_TO_STATSD_INTERACTION_TYPE = {
// This should be mapping to CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE.
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE,
- UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_EXPAND_COLLAPSE_LOCK,
+ NO_STATSD_LOGGING, // This is deprecated.
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_EXPAND,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_SWIPE,
@@ -310,7 +312,6 @@ public class InteractionJankMonitor {
/** @hide */
@IntDef({
CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE,
- CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE_LOCK,
CUJ_NOTIFICATION_SHADE_SCROLL_FLING,
CUJ_NOTIFICATION_SHADE_ROW_EXPAND,
CUJ_NOTIFICATION_SHADE_ROW_SWIPE,
@@ -735,11 +736,12 @@ public class InteractionJankMonitor {
* @return the name of the cuj type
*/
public static String getNameOfCuj(int cujType) {
+ // Please note:
+ // 1. The length of the returned string shouldn't exceed MAX_LENGTH_OF_CUJ_NAME.
+ // 2. The returned string should be the same with the name defined in atoms.proto.
switch (cujType) {
case CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE:
return "SHADE_EXPAND_COLLAPSE";
- case CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE_LOCK:
- return "SHADE_EXPAND_COLLAPSE_LOCK";
case CUJ_NOTIFICATION_SHADE_SCROLL_FLING:
return "SHADE_SCROLL_FLING";
case CUJ_NOTIFICATION_SHADE_ROW_EXPAND:
@@ -1111,9 +1113,27 @@ public class InteractionJankMonitor {
public Session(@CujType int cujType, @NonNull String postfix) {
mCujType = cujType;
mTimeStamp = System.nanoTime();
- mName = TextUtils.isEmpty(postfix)
- ? String.format("J<%s>", getNameOfCuj(mCujType))
- : String.format("J<%s::%s>", getNameOfCuj(mCujType), postfix);
+ mName = generateSessionName(getNameOfCuj(cujType), postfix);
+ }
+
+ private String generateSessionName(@NonNull String cujName, @NonNull String cujPostfix) {
+ final boolean hasPostfix = !TextUtils.isEmpty(cujPostfix);
+ // We assert that the cujName shouldn't exceed MAX_LENGTH_OF_CUJ_NAME.
+ if (cujName.length() > MAX_LENGTH_OF_CUJ_NAME) {
+ throw new IllegalArgumentException(TextUtils.formatSimple(
+ "The length of cuj name <%s> exceeds %d", cujName, MAX_LENGTH_OF_CUJ_NAME));
+ }
+ if (hasPostfix) {
+ final int remaining = MAX_LENGTH_SESSION_NAME - cujName.length();
+ if (cujPostfix.length() > remaining) {
+ cujPostfix = cujPostfix.substring(0, remaining - 3).concat("...");
+ }
+ }
+ // The max length of the whole string should be:
+ // 105 with postfix, 83 without postfix
+ return hasPostfix
+ ? TextUtils.formatSimple("J<%s::%s>", cujName, cujPostfix)
+ : TextUtils.formatSimple("J<%s>", cujName);
}
@CujType
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index 0a29fc5285a5..fa6fa55cbde9 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -1169,7 +1169,15 @@ public class BinderCallsStats implements BinderInternal.Observer {
}
+ /** @hide */
+ public static void startForWifi(Context context) {
+ new BinderCallsStats.SettingsObserver(
+ context,
+ new BinderCallsStats(
+ new BinderCallsStats.Injector(),
+ com.android.internal.os.BinderLatencyProto.Dims.WIFI));
+ }
/**
* Settings observer for other processes (not system_server).
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index 8e5474676bcc..b9243ece9158 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -219,8 +219,9 @@ public class ScreenshotHelper {
throw new IllegalArgumentException("Bundle does not contain a hardware bitmap");
}
- HardwareBuffer buffer = bundle.getParcelable(KEY_BUFFER, android.hardware.HardwareBuffer.class);
- ParcelableColorSpace colorSpace = bundle.getParcelable(KEY_COLOR_SPACE, android.graphics.ParcelableColorSpace.class);
+ HardwareBuffer buffer = bundle.getParcelable(KEY_BUFFER, HardwareBuffer.class);
+ ParcelableColorSpace colorSpace = bundle.getParcelable(KEY_COLOR_SPACE,
+ ParcelableColorSpace.class);
return Bitmap.wrapHardwareBuffer(Objects.requireNonNull(buffer),
colorSpace.getColorSpace());
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 5f5886e38afb..9471faec8ea1 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -55,7 +55,7 @@ interface IInputMethodManager {
InputMethodSubtype getLastInputMethodSubtype(int userId);
boolean showSoftInput(in IInputMethodClient client, @nullable IBinder windowToken, int flags,
- in @nullable ResultReceiver resultReceiver, int reason);
+ int lastClickToolType, in @nullable ResultReceiver resultReceiver, int reason);
boolean hideSoftInput(in IInputMethodClient client, @nullable IBinder windowToken, int flags,
in @nullable ResultReceiver resultReceiver, int reason);
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index 09ff4e0aa076..9ee9b8249493 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -828,6 +828,8 @@ public class PointerLocationView extends View implements InputDeviceListener,
mSystemGestureExclusionListener, mContext.getDisplayId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Failed to unregister window manager callbacks", e);
}
}
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 578b5880c5f0..2c9ef4f3c059 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -339,6 +339,8 @@ public class SystemConfig {
// A map from package name of vendor APEXes that can be updated to an installer package name
// allowed to install updates for it.
private final ArrayMap<String, String> mAllowedVendorApexes = new ArrayMap<>();
+ // A set of package names that are allowed to use <install-constraints> manifest tag.
+ private final Set<String> mInstallConstraintsAllowlist = new ArraySet<>();
private String mModulesInstallerPackageName;
@@ -535,6 +537,10 @@ public class SystemConfig {
return mAllowedVendorApexes;
}
+ public Set<String> getInstallConstraintsAllowlist() {
+ return mInstallConstraintsAllowlist;
+ }
+
public String getModulesInstallerPackageName() {
return mModulesInstallerPackageName;
}
@@ -1455,6 +1461,20 @@ public class SystemConfig {
}
XmlUtils.skipCurrentTag(parser);
} break;
+ case "install-constraints-allowed": {
+ if (allowAppConfigs) {
+ String packageName = parser.getAttributeValue(null, "package");
+ if (packageName == null) {
+ Slog.w(TAG, "<" + name + "> without package in " + permFile
+ + " at " + parser.getPositionDescription());
+ } else {
+ mInstallConstraintsAllowlist.add(packageName);
+ }
+ } else {
+ logNotAllowedInPartition(name, permFile, parser);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ } break;
default: {
Slog.w(TAG, "Tag " + name + " is unknown in "
+ permFile + " at " + parser.getPositionDescription());
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 22f84b6e961a..9ae16304b6c8 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1116,12 +1116,6 @@ static jlongArray nativeGetPhysicalDisplayIds(JNIEnv* env, jclass clazz) {
return array;
}
-static jlong nativeGetPrimaryPhysicalDisplayId(JNIEnv* env, jclass clazz) {
- PhysicalDisplayId displayId;
- SurfaceComposerClient::getPrimaryPhysicalDisplayId(&displayId);
- return static_cast<jlong>(displayId.value);
-}
-
static jobject nativeGetPhysicalDisplayToken(JNIEnv* env, jclass clazz, jlong physicalDisplayId) {
const auto id = DisplayId::fromValue<PhysicalDisplayId>(physicalDisplayId);
if (!id) return nullptr;
@@ -2210,8 +2204,6 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeSetDefaultFrameRateCompatibility},
{"nativeGetPhysicalDisplayIds", "()[J",
(void*)nativeGetPhysicalDisplayIds },
- {"nativeGetPrimaryPhysicalDisplayId", "()J",
- (void*)nativeGetPrimaryPhysicalDisplayId },
{"nativeGetPhysicalDisplayToken", "(J)Landroid/os/IBinder;",
(void*)nativeGetPhysicalDisplayToken },
{"nativeCreateDisplay", "(Ljava/lang/String;Z)Landroid/os/IBinder;",
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/values-sw600dp/config.xml b/core/res/res/values-sw600dp/config.xml
index d686dd2ea690..34b6a54be493 100644
--- a/core/res/res/values-sw600dp/config.xml
+++ b/core/res/res/values-sw600dp/config.xml
@@ -51,7 +51,5 @@
<!-- If true, show multiuser switcher by default unless the user specifically disables it. -->
<bool name="config_showUserSwitcherByDefault">true</bool>
-
- <integer name="config_chooser_max_targets_per_row">6</integer>
</resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 0acf7031e407..062523ed5f23 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -3607,4 +3607,19 @@
false, the application cannot be profiled at all. Defaults to true. -->
<attr name="enabled" format="boolean" />
</declare-styleable>
+
+ <!-- <code>install-constraints</code> tag rejects installs unless one the constraints defined by
+ its child elements is true.
+ It is possible to have multiple <code>install-constraints</code> tags in a single manifest,
+ where each tag is evaluated independently.
+ @hide -->
+ <declare-styleable name="AndroidManifestInstallConstraints" parent="AndroidManifest" />
+
+ <!-- A constraint for <code>install-constraints</code>. Checks that the device fingerprint
+ starts with the given prefix.
+ @hide -->
+ <declare-styleable name="AndroidManifestInstallConstraintsFingerprintPrefix"
+ parent="AndroidManifestInstallConstraints">
+ <attr name="value" />
+ </declare-styleable>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9d2c3d7b7e0e..d176a1d97494 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2629,6 +2629,10 @@
will be locked. -->
<bool name="config_multiuserDelayUserDataLocking">false</bool>
+ <!-- Whether the device allows users to start in background on secondary displays.
+ Should be false for most devices, except automotive vehicle with passenger displays. -->
+ <bool name="config_multiuserUsersOnSecondaryDisplays">false</bool>
+
<!-- Whether to automatically switch a non-primary user back to the primary user after a
timeout when the device is docked. -->
<bool name="config_enableTimeoutToUserZeroWhenDocked">false</bool>
@@ -5850,4 +5854,7 @@
<!-- Whether the wake screen on notifications feature is available. -->
<bool name="config_pulseOnNotificationsAvailable">true</bool>
+
+ <!-- The number of tasks to scan to get the visibility of Home -->
+ <integer name="config_maxScanTasksForHomeVisibility">10</integer>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index cb0393f5eed1..e6ae7b1c8b7a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -453,6 +453,7 @@
<java-symbol type="integer" name="config_multiuserMaximumUsers" />
<java-symbol type="integer" name="config_multiuserMaxRunningUsers" />
<java-symbol type="bool" name="config_multiuserDelayUserDataLocking" />
+ <java-symbol type="bool" name="config_multiuserUsersOnSecondaryDisplays" />
<java-symbol type="bool" name="config_enableTimeoutToUserZeroWhenDocked" />
<java-symbol type="integer" name="config_userTypePackageWhitelistMode"/>
<java-symbol type="xml" name="config_user_types" />
@@ -3977,6 +3978,8 @@
<java-symbol type="string" name="config_customCountryDetector" />
+ <java-symbol type="integer" name="config_maxScanTasksForHomeVisibility" />
+
<!-- For Foldables -->
<java-symbol type="array" name="config_foldedDeviceStates" />
<java-symbol type="array" name="config_deviceStatesOnWhichToWakeUp" />
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
index 8587a3579def..25bf93f72088 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
@@ -58,7 +58,7 @@ import java.util.List;
public class StartProgramListUpdatesFanoutTest {
private static final String TAG = "BroadcastRadioTests.hal2.StartProgramListUpdatesFanout";
- private static final VerificationWithTimeout CB_TIMEOUT = timeout(100);
+ private static final VerificationWithTimeout CB_TIMEOUT = timeout(500);
// Mocks
@Mock IBroadcastRadio mBroadcastRadioMock;
diff --git a/core/tests/coretests/src/android/content/res/ConfigurationTest.java b/core/tests/coretests/src/android/content/res/ConfigurationTest.java
index 669138c15698..0d5cd72a80e4 100644
--- a/core/tests/coretests/src/android/content/res/ConfigurationTest.java
+++ b/core/tests/coretests/src/android/content/res/ConfigurationTest.java
@@ -157,6 +157,22 @@ public class ConfigurationTest extends TestCase {
assertFalse(config.isNightModeActive());
}
+ @Test
+ public void testSequenceNumberWrapAround() {
+ Configuration config = new Configuration();
+ Configuration otherConfig = new Configuration();
+
+ // Verify the other configuration is not newer when this sequence number warp around.
+ config.seq = 1000;
+ otherConfig.seq = Integer.MAX_VALUE - 1000;
+ assertFalse(config.isOtherSeqNewer(otherConfig));
+
+ // Verify the other configuration is newer when the other sequence number warp around.
+ config.seq = Integer.MAX_VALUE - 1000;
+ otherConfig.seq = 1000;
+ assertTrue(config.isOtherSeqNewer(otherConfig));
+ }
+
private void dumpDebug(File f, Configuration config) throws Exception {
final AtomicFile af = new AtomicFile(f);
FileOutputStream fos = af.startWrite();
diff --git a/core/tests/coretests/src/android/graphics/PathOffsetTest.java b/core/tests/coretests/src/android/graphics/PathOffsetTest.java
deleted file mode 100644
index 6cc42f6a3efc..000000000000
--- a/core/tests/coretests/src/android/graphics/PathOffsetTest.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2008 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.graphics;
-
-import static org.junit.Assert.assertTrue;
-
-import android.graphics.Bitmap.Config;
-import android.graphics.Path.Direction;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class PathOffsetTest {
-
- private static final int SQUARE = 10;
- private static final int WIDTH = 100;
- private static final int HEIGHT = 100;
- private static final int START_X = 10;
- private static final int START_Y = 20;
- private static final int OFFSET_X = 30;
- private static final int OFFSET_Y = 40;
-
- @Test
- @SmallTest
- public void testPathOffset() {
- Path actualPath = new Path();
- actualPath.addRect(START_X, START_Y, START_X + SQUARE, START_Y + SQUARE, Direction.CW);
- assertTrue(actualPath.isSimplePath);
- actualPath.offset(OFFSET_X, OFFSET_Y);
- assertTrue(actualPath.isSimplePath);
-
- Path expectedPath = new Path();
- expectedPath.addRect(START_X + OFFSET_X, START_Y + OFFSET_Y, START_X + OFFSET_X + SQUARE,
- START_Y + OFFSET_Y + SQUARE, Direction.CW);
-
- assertPaths(actualPath, expectedPath);
- }
-
- @Test
- @SmallTest
- public void testPathOffsetWithDestination() {
- Path initialPath = new Path();
- initialPath.addRect(START_X, START_Y, START_X + SQUARE, START_Y + SQUARE, Direction.CW);
- Path actualPath = new Path();
- assertTrue(initialPath.isSimplePath);
- assertTrue(actualPath.isSimplePath);
- initialPath.offset(OFFSET_X, OFFSET_Y, actualPath);
- assertTrue(actualPath.isSimplePath);
-
- Path expectedPath = new Path();
- expectedPath.addRect(START_X + OFFSET_X, START_Y + OFFSET_Y, START_X + OFFSET_X + SQUARE,
- START_Y + OFFSET_Y + SQUARE, Direction.CW);
-
- assertPaths(actualPath, expectedPath);
- }
-
- private static void assertPaths(Path actual, Path expected) {
- Bitmap actualBitmap = drawAndGetBitmap(actual);
- Bitmap expectedBitmap = drawAndGetBitmap(expected);
- assertTrue(actualBitmap.sameAs(expectedBitmap));
- }
-
- private static Bitmap drawAndGetBitmap(Path path) {
- Bitmap bitmap = Bitmap.createBitmap(WIDTH, HEIGHT, Config.ARGB_8888);
- bitmap.eraseColor(Color.BLACK);
- Paint paint = new Paint();
- paint.setColor(Color.RED);
- Canvas canvas = new Canvas(bitmap);
- canvas.drawPath(path, paint);
- return bitmap;
- }
-
-}
diff --git a/core/tests/coretests/src/android/graphics/PathTest.java b/core/tests/coretests/src/android/graphics/PathTest.java
deleted file mode 100644
index b50792ca6b38..000000000000
--- a/core/tests/coretests/src/android/graphics/PathTest.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2013 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.graphics;
-
-import androidx.test.filters.SmallTest;
-
-import junit.framework.TestCase;
-
-public class PathTest extends TestCase {
-
- @SmallTest
- public void testResetPreservesFillType() throws Exception {
- Path path = new Path();
-
- final Path.FillType defaultFillType = path.getFillType();
- final Path.FillType fillType = Path.FillType.INVERSE_EVEN_ODD;
-
- // This test is only meaningful if it changes from the default.
- assertFalse(fillType.equals(defaultFillType));
-
- path.setFillType(fillType);
- path.reset();
- assertEquals(path.getFillType(), fillType);
- }
-}
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index c1e72fe75666..32c3a268153c 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -58,10 +58,10 @@ import android.provider.DocumentsContract.Document;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import libcore.io.Streams;
-
import com.google.android.collect.Sets;
+import libcore.io.Streams;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -477,6 +477,14 @@ public class FileUtilsTest {
new File(mTarget, "test (1).jpg").createNewFile();
assertNameEquals("test (2).jpg",
FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
+
+ assertNameEquals("test.mp3", FileUtils.buildUniqueFile(mTarget, "audio/mp3", "test.mp3"));
+ new File(mTarget, "test.mp3").createNewFile();
+ assertNameEquals("test (1).mp3",
+ FileUtils.buildUniqueFile(mTarget, "audio/mp3", "test.mp3"));
+ new File(mTarget, "test (1).mp3").createNewFile();
+ assertNameEquals("test (2).mp3",
+ FileUtils.buildUniqueFile(mTarget, "audio/mp3", "test.mp3"));
}
@Test
diff --git a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
index b867e4417439..9637de8f3126 100644
--- a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
@@ -42,6 +42,7 @@ import android.text.TextUtils;
import android.text.style.MaskFilterSpan;
import android.text.style.UnderlineSpan;
import android.util.StringBuilderPrinter;
+import android.view.MotionEvent;
import android.view.autofill.AutofillId;
import androidx.test.filters.SmallTest;
@@ -490,7 +491,8 @@ public class EditorInfoTest {
assertThat(sb.toString()).isEqualTo(
"prefix: inputType=0x0 imeOptions=0x0 privateImeOptions=null\n"
+ "prefix: actionLabel=null actionId=0\n"
- + "prefix: initialSelStart=-1 initialSelEnd=-1 initialCapsMode=0x0\n"
+ + "prefix: initialSelStart=-1 initialSelEnd=-1 initialToolType=0"
+ + " initialCapsMode=0x0\n"
+ "prefix: hintText=null label=null\n"
+ "prefix: packageName=null autofillId=null fieldId=0 fieldName=null\n"
+ "prefix: extras=null\n"
@@ -509,6 +511,7 @@ public class EditorInfoTest {
info.initialCapsMode = TextUtils.CAP_MODE_CHARACTERS; // 0x1000
info.hintText = "testHintText";
info.label = "testLabel";
+ info.setInitialToolType(MotionEvent.TOOL_TYPE_STYLUS);
info.packageName = "android.view.inputmethod";
info.autofillId = new AutofillId(123);
info.fieldId = 456;
@@ -523,7 +526,8 @@ public class EditorInfoTest {
assertThat(sb.toString()).isEqualTo(
"prefix2: inputType=0x1 imeOptions=0x2 privateImeOptions=testOptions\n"
+ "prefix2: actionLabel=null actionId=0\n"
- + "prefix2: initialSelStart=0 initialSelEnd=1 initialCapsMode=0x1000\n"
+ + "prefix2: initialSelStart=0 initialSelEnd=1 initialToolType=2"
+ + " initialCapsMode=0x1000\n"
+ "prefix2: hintText=testHintText label=testLabel\n"
+ "prefix2: packageName=android.view.inputmethod autofillId=123"
+ " fieldId=456 fieldName=testField\n"
@@ -543,7 +547,8 @@ public class EditorInfoTest {
assertThat(sb.toString()).isEqualTo(
"prefix: inputType=0x0 imeOptions=0x0 privateImeOptions=null\n"
+ "prefix: actionLabel=null actionId=0\n"
- + "prefix: initialSelStart=-1 initialSelEnd=-1 initialCapsMode=0x0\n"
+ + "prefix: initialSelStart=-1 initialSelEnd=-1 initialToolType=0"
+ + " initialCapsMode=0x0\n"
+ "prefix: hintText=null label=null\n"
+ "prefix: packageName=null autofillId=null fieldId=0 fieldName=null\n"
+ "prefix: hintLocales=null\n"
diff --git a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
index d96f041c13f8..1519e48adbe0 100644
--- a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
@@ -16,10 +16,26 @@
package com.android.internal.jank;
+import static android.text.TextUtils.formatSimple;
+
import static com.android.internal.jank.FrameTracker.REASON_CANCEL_TIMEOUT;
import static com.android.internal.jank.FrameTracker.REASON_END_NORMAL;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_LAUNCH_CAMERA;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_ADD;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_APP_START;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_HEADS_UP_APPEAR;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_REMOVE;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_ROW_EXPAND;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_ROW_SWIPE;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLIT_SCREEN_RESIZE;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_TO_STATSD_INTERACTION_TYPE;
+import static com.android.internal.jank.InteractionJankMonitor.MAX_LENGTH_OF_CUJ_NAME;
+import static com.android.internal.jank.InteractionJankMonitor.getNameOfCuj;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -38,6 +54,7 @@ import android.os.Handler;
import android.os.HandlerThread;
import android.os.SystemClock;
import android.provider.DeviceConfig;
+import android.util.SparseArray;
import android.view.View;
import android.view.ViewAttachTestActivity;
@@ -52,8 +69,12 @@ import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper;
import com.android.internal.jank.FrameTracker.ViewRootWrapper;
import com.android.internal.jank.InteractionJankMonitor.Configuration;
import com.android.internal.jank.InteractionJankMonitor.Session;
+import com.android.internal.util.FrameworkStatsLog;
+
+import com.google.common.truth.Expect;
import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
@@ -64,11 +85,17 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.stream.Collectors;
@SmallTest
public class InteractionJankMonitorTest {
private static final String CUJ_POSTFIX = "";
+ private static final SparseArray<String> ENUM_NAME_EXCEPTION_MAP = new SparseArray<>();
+ private static final SparseArray<String> CUJ_NAME_EXCEPTION_MAP = new SparseArray<>();
+ private static final String ENUM_NAME_PREFIX =
+ "UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__";
+
private ViewAttachTestActivity mActivity;
private View mView;
private HandlerThread mWorker;
@@ -77,6 +104,53 @@ public class InteractionJankMonitorTest {
public ActivityTestRule<ViewAttachTestActivity> mRule =
new ActivityTestRule<>(ViewAttachTestActivity.class);
+ @Rule
+ public final Expect mExpect = Expect.create();
+
+ @BeforeClass
+ public static void initialize() {
+ ENUM_NAME_EXCEPTION_MAP.put(CUJ_NOTIFICATION_ADD, getEnumName("SHADE_NOTIFICATION_ADD"));
+ ENUM_NAME_EXCEPTION_MAP.put(CUJ_NOTIFICATION_APP_START, getEnumName("SHADE_APP_LAUNCH"));
+ ENUM_NAME_EXCEPTION_MAP.put(
+ CUJ_NOTIFICATION_HEADS_UP_APPEAR, getEnumName("SHADE_HEADS_UP_APPEAR"));
+ ENUM_NAME_EXCEPTION_MAP.put(
+ CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR, getEnumName("SHADE_HEADS_UP_DISAPPEAR"));
+ ENUM_NAME_EXCEPTION_MAP.put(
+ CUJ_NOTIFICATION_REMOVE, getEnumName("SHADE_NOTIFICATION_REMOVE"));
+ ENUM_NAME_EXCEPTION_MAP.put(
+ CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, getEnumName("NOTIFICATION_SHADE_SWIPE"));
+ ENUM_NAME_EXCEPTION_MAP.put(
+ CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE, getEnumName("SHADE_QS_EXPAND_COLLAPSE"));
+ ENUM_NAME_EXCEPTION_MAP.put(
+ CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE, getEnumName("SHADE_QS_SCROLL_SWIPE"));
+ ENUM_NAME_EXCEPTION_MAP.put(
+ CUJ_NOTIFICATION_SHADE_ROW_EXPAND, getEnumName("SHADE_ROW_EXPAND"));
+ ENUM_NAME_EXCEPTION_MAP.put(
+ CUJ_NOTIFICATION_SHADE_ROW_SWIPE, getEnumName("SHADE_ROW_SWIPE"));
+ ENUM_NAME_EXCEPTION_MAP.put(
+ CUJ_NOTIFICATION_SHADE_SCROLL_FLING, getEnumName("SHADE_SCROLL_FLING"));
+
+ CUJ_NAME_EXCEPTION_MAP.put(
+ CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, "CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE");
+ CUJ_NAME_EXCEPTION_MAP.put(
+ CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE,
+ "CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE");
+ CUJ_NAME_EXCEPTION_MAP.put(
+ CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE, "CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE");
+ CUJ_NAME_EXCEPTION_MAP.put(
+ CUJ_NOTIFICATION_SHADE_ROW_EXPAND, "CUJ_NOTIFICATION_SHADE_ROW_EXPAND");
+ CUJ_NAME_EXCEPTION_MAP.put(
+ CUJ_NOTIFICATION_SHADE_ROW_SWIPE, "CUJ_NOTIFICATION_SHADE_ROW_SWIPE");
+ CUJ_NAME_EXCEPTION_MAP.put(
+ CUJ_NOTIFICATION_SHADE_SCROLL_FLING, "CUJ_NOTIFICATION_SHADE_SCROLL_FLING");
+ CUJ_NAME_EXCEPTION_MAP.put(CUJ_LOCKSCREEN_LAUNCH_CAMERA, "CUJ_LOCKSCREEN_LAUNCH_CAMERA");
+ CUJ_NAME_EXCEPTION_MAP.put(CUJ_SPLIT_SCREEN_RESIZE, "CUJ_SPLIT_SCREEN_RESIZE");
+ }
+
+ private static String getEnumName(String name) {
+ return formatSimple("%s%s", ENUM_NAME_PREFIX, name);
+ }
+
@Before
public void setup() {
// Prepare an activity for getting ThreadedRenderer later.
@@ -174,6 +248,82 @@ public class InteractionJankMonitorTest {
}
}
+ @Test
+ public void testCujsMapToEnumsCorrectly() {
+ List<Field> cujs = Arrays.stream(InteractionJankMonitor.class.getDeclaredFields())
+ .filter(f -> f.getName().startsWith("CUJ_")
+ && Modifier.isStatic(f.getModifiers())
+ && f.getType() == int.class)
+ .collect(Collectors.toList());
+
+ Map<Integer, String> enumsMap = Arrays.stream(FrameworkStatsLog.class.getDeclaredFields())
+ .filter(f -> f.getName().startsWith(ENUM_NAME_PREFIX)
+ && Modifier.isStatic(f.getModifiers())
+ && f.getType() == int.class)
+ .collect(Collectors.toMap(this::getIntFieldChecked, Field::getName));
+
+ cujs.forEach(f -> {
+ final int cuj = getIntFieldChecked(f);
+ final String cujName = f.getName();
+ final String expectedEnumName = ENUM_NAME_EXCEPTION_MAP.contains(cuj)
+ ? ENUM_NAME_EXCEPTION_MAP.get(cuj)
+ : formatSimple("%s%s", ENUM_NAME_PREFIX, cujName.substring(4));
+ final int enumKey = CUJ_TO_STATSD_INTERACTION_TYPE[cuj];
+ final String enumName = enumsMap.get(enumKey);
+ final String expectedNameOfCuj = CUJ_NAME_EXCEPTION_MAP.contains(cuj)
+ ? CUJ_NAME_EXCEPTION_MAP.get(cuj)
+ : formatSimple("CUJ_%s", getNameOfCuj(cuj));
+ mExpect
+ .withMessage(formatSimple(
+ "%s (%d) not matches %s (%d)", cujName, cuj, enumName, enumKey))
+ .that(expectedEnumName.equals(enumName))
+ .isTrue();
+ mExpect
+ .withMessage(formatSimple("getNameOfCuj(%d) not matches %s", cuj, cujName))
+ .that(cujName.equals(expectedNameOfCuj))
+ .isTrue();
+ });
+ }
+
+ @Test
+ public void testCujNameLimit() {
+ Arrays.stream(InteractionJankMonitor.class.getDeclaredFields())
+ .filter(f -> f.getName().startsWith("CUJ_")
+ && Modifier.isStatic(f.getModifiers())
+ && f.getType() == int.class)
+ .forEach(f -> {
+ final int cuj = getIntFieldChecked(f);
+ mExpect
+ .withMessage(formatSimple(
+ "Too long CUJ(%d) name: %s", cuj, getNameOfCuj(cuj)))
+ .that(getNameOfCuj(cuj).length())
+ .isAtMost(MAX_LENGTH_OF_CUJ_NAME);
+ });
+ }
+
+ @Test
+ public void testSessionNameLengthLimit() {
+ final int cujType = CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
+ final String cujName = getNameOfCuj(cujType);
+ final String cujTag = "ThisIsTheCujTag";
+ final String tooLongTag = cujTag.repeat(10);
+
+ // Normal case, no postfix.
+ Session noPostfix = new Session(cujType, "");
+ assertThat(noPostfix.getName()).isEqualTo(formatSimple("J<%s>", cujName));
+
+ // Normal case, with postfix.
+ Session withPostfix = new Session(cujType, cujTag);
+ assertThat(withPostfix.getName()).isEqualTo(formatSimple("J<%s::%s>", cujName, cujTag));
+
+ // Since the length of the cuj name is tested in another test, no need to test it here.
+ // Too long postfix case, should trim the postfix and keep the cuj name completed.
+ final String expectedTrimmedName = formatSimple("J<%s::%s>", cujName,
+ "ThisIsTheCujTagThisIsTheCujTagThisIsTheCujTagThisIsTheCujTagThisIsTheCujTagT...");
+ Session longPostfix = new Session(cujType, tooLongTag);
+ assertThat(longPostfix.getName()).isEqualTo(expectedTrimmedName);
+ }
+
private InteractionJankMonitor createMockedInteractionJankMonitor() {
InteractionJankMonitor monitor = spy(new InteractionJankMonitor(mWorker));
doReturn(true).when(monitor).shouldMonitor(anyInt());
@@ -217,4 +367,12 @@ public class InteractionJankMonitorTest {
return tracker;
}
+
+ private int getIntFieldChecked(Field field) {
+ try {
+ return field.getInt(null);
+ } catch (IllegalAccessException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java
deleted file mode 100644
index e8793a9f6097..000000000000
--- a/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.internal.util;
-
-import androidx.test.filters.SmallTest;
-
-import junit.framework.TestCase;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-
-public class ArrayUtilsTest extends TestCase {
-
- @SmallTest
- public void testUnstableRemoveIf() throws Exception {
- java.util.function.Predicate<Object> isNull = new java.util.function.Predicate<Object>() {
- @Override
- public boolean test(Object o) {
- return o == null;
- }
- };
-
- final Object a = new Object();
- final Object b = new Object();
- final Object c = new Object();
-
- ArrayList<Object> collection = null;
- assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
-
- collection = new ArrayList<>();
- assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
-
- collection = new ArrayList<>(Collections.singletonList(a));
- assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
- assertEquals(1, collection.size());
- assertTrue(collection.contains(a));
-
- collection = new ArrayList<>(Collections.singletonList(null));
- assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
- assertEquals(0, collection.size());
-
- collection = new ArrayList<>(Arrays.asList(a, b));
- assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
- assertEquals(2, collection.size());
- assertTrue(collection.contains(a));
- assertTrue(collection.contains(b));
-
- collection = new ArrayList<>(Arrays.asList(a, null));
- assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
- assertEquals(1, collection.size());
- assertTrue(collection.contains(a));
-
- collection = new ArrayList<>(Arrays.asList(null, a));
- assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
- assertEquals(1, collection.size());
- assertTrue(collection.contains(a));
-
- collection = new ArrayList<>(Arrays.asList(null, null));
- assertEquals(2, ArrayUtils.unstableRemoveIf(collection, isNull));
- assertEquals(0, collection.size());
-
- collection = new ArrayList<>(Arrays.asList(a, b, c));
- assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
- assertEquals(3, collection.size());
- assertTrue(collection.contains(a));
- assertTrue(collection.contains(b));
- assertTrue(collection.contains(c));
-
- collection = new ArrayList<>(Arrays.asList(a, b, null));
- assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
- assertEquals(2, collection.size());
- assertTrue(collection.contains(a));
- assertTrue(collection.contains(b));
-
- collection = new ArrayList<>(Arrays.asList(a, null, b));
- assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
- assertEquals(2, collection.size());
- assertTrue(collection.contains(a));
- assertTrue(collection.contains(b));
-
- collection = new ArrayList<>(Arrays.asList(null, a, b));
- assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
- assertEquals(2, collection.size());
- assertTrue(collection.contains(a));
- assertTrue(collection.contains(b));
-
- collection = new ArrayList<>(Arrays.asList(a, null, null));
- assertEquals(2, ArrayUtils.unstableRemoveIf(collection, isNull));
- assertEquals(1, collection.size());
- assertTrue(collection.contains(a));
-
- collection = new ArrayList<>(Arrays.asList(null, null, a));
- assertEquals(2, ArrayUtils.unstableRemoveIf(collection, isNull));
- assertEquals(1, collection.size());
- assertTrue(collection.contains(a));
-
- collection = new ArrayList<>(Arrays.asList(null, a, null));
- assertEquals(2, ArrayUtils.unstableRemoveIf(collection, isNull));
- assertEquals(1, collection.size());
- assertTrue(collection.contains(a));
-
- collection = new ArrayList<>(Arrays.asList(null, null, null));
- assertEquals(3, ArrayUtils.unstableRemoveIf(collection, isNull));
- assertEquals(0, collection.size());
- }
-
- @SmallTest
- public void testThrowsIfOutOfBounds_passesWhenRangeInsideArray() {
- ArrayUtils.throwsIfOutOfBounds(10, 2, 6);
- }
-
- @SmallTest
- public void testThrowsIfOutOfBounds_passesWhenRangeIsWholeArray() {
- ArrayUtils.throwsIfOutOfBounds(10, 0, 10);
- }
-
- @SmallTest
- public void testThrowsIfOutOfBounds_passesWhenEmptyRangeAtStart() {
- ArrayUtils.throwsIfOutOfBounds(10, 0, 0);
- }
-
- @SmallTest
- public void testThrowsIfOutOfBounds_passesWhenEmptyRangeAtEnd() {
- ArrayUtils.throwsIfOutOfBounds(10, 10, 0);
- }
-
- @SmallTest
- public void testThrowsIfOutOfBounds_passesWhenEmptyArray() {
- ArrayUtils.throwsIfOutOfBounds(0, 0, 0);
- }
-
- @SmallTest
- public void testThrowsIfOutOfBounds_failsWhenRangeStartNegative() {
- try {
- ArrayUtils.throwsIfOutOfBounds(10, -1, 5);
- fail();
- } catch (ArrayIndexOutOfBoundsException expected) {
- // expected
- }
- }
-
- @SmallTest
- public void testThrowsIfOutOfBounds_failsWhenCountNegative() {
- try {
- ArrayUtils.throwsIfOutOfBounds(10, 5, -1);
- fail();
- } catch (ArrayIndexOutOfBoundsException expected) {
- // expected
- }
- }
-
- @SmallTest
- public void testThrowsIfOutOfBounds_failsWhenRangeStartTooHigh() {
- try {
- ArrayUtils.throwsIfOutOfBounds(10, 11, 0);
- fail();
- } catch (ArrayIndexOutOfBoundsException expected) {
- // expected
- }
- }
-
- @SmallTest
- public void testThrowsIfOutOfBounds_failsWhenRangeEndTooHigh() {
- try {
- ArrayUtils.throwsIfOutOfBounds(10, 5, 6);
- fail();
- } catch (ArrayIndexOutOfBoundsException expected) {
- // expected
- }
- }
-
- @SmallTest
- public void testThrowsIfOutOfBounds_failsWhenLengthNegative() {
- try {
- ArrayUtils.throwsIfOutOfBounds(-1, 0, 0);
- fail();
- } catch (ArrayIndexOutOfBoundsException expected) {
- // expected
- }
- }
-
- @SmallTest
- public void testThrowsIfOutOfBounds_failsWhenOverflowRangeEndTooHigh() {
- try {
- ArrayUtils.throwsIfOutOfBounds(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
- fail();
- } catch (ArrayIndexOutOfBoundsException expected) {
- // expected
- }
- }
-}
diff --git a/core/tests/mockingcoretests/src/android/os/BundleRecyclingTest.java b/core/tests/mockingcoretests/src/android/os/BundleRecyclingTest.java
index 7c7649813824..c88ab903e86e 100644
--- a/core/tests/mockingcoretests/src/android/os/BundleRecyclingTest.java
+++ b/core/tests/mockingcoretests/src/android/os/BundleRecyclingTest.java
@@ -23,6 +23,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -34,7 +35,6 @@ import androidx.test.filters.SmallTest;
import com.android.dx.mockito.inline.extended.StaticMockitoSession;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.quality.Strictness;
@@ -56,16 +56,11 @@ public class BundleRecyclingTest {
private Parcel mParcelSpy;
private Bundle mBundle;
- @Before
- public void setUp() throws Exception {
- setUpBundle(/* hasLazy */ true);
- }
-
@Test
public void bundleClear_whenUnparcelledWithoutLazy_recyclesParcelOnce() {
- setUpBundle(/* hasLazy */ false);
+ setUpBundle(/* lazy */ 0, /* nonLazy */ 1);
// Will unparcel and immediately recycle parcel
- assertNotNull(mBundle.getString("key"));
+ assertNotNull(mBundle.getString("nonLazy0"));
verify(mParcelSpy, times(1)).recycle();
assertFalse(mBundle.isDefinitelyEmpty());
@@ -77,6 +72,7 @@ public class BundleRecyclingTest {
@Test
public void bundleClear_whenParcelled_recyclesParcel() {
+ setUpBundle(/* lazy */ 1);
assertTrue(mBundle.isParcelled());
verify(mParcelSpy, times(0)).recycle();
@@ -90,23 +86,9 @@ public class BundleRecyclingTest {
}
@Test
- public void bundleClear_whenUnparcelledWithLazyValueUnwrapped_recyclesParcel() {
- // Will unparcel with a lazy value, and immediately unwrap the lazy value,
- // with no lazy values left at the end of getParcelable
- assertNotNull(mBundle.getParcelable("key", CustomParcelable.class));
- verify(mParcelSpy, times(0)).recycle();
-
- mBundle.clear();
- verify(mParcelSpy, times(1)).recycle();
- assertTrue(mBundle.isDefinitelyEmpty());
-
- // Should not recycle again
- mBundle.clear();
- verify(mParcelSpy, times(1)).recycle();
- }
-
- @Test
public void bundleClear_whenUnparcelledWithLazy_recyclesParcel() {
+ setUpBundle(/* lazy */ 1);
+
// Will unparcel but keep the CustomParcelable lazy
assertFalse(mBundle.isEmpty());
verify(mParcelSpy, times(0)).recycle();
@@ -122,6 +104,8 @@ public class BundleRecyclingTest {
@Test
public void bundleClear_whenClearedWithSharedParcel_doesNotRecycleParcel() {
+ setUpBundle(/* lazy */ 1);
+
Bundle copy = new Bundle();
copy.putAll(mBundle);
@@ -136,6 +120,8 @@ public class BundleRecyclingTest {
@Test
public void bundleClear_whenClearedWithCopiedParcel_doesNotRecycleParcel() {
+ setUpBundle(/* lazy */ 1);
+
// Will unparcel but keep the CustomParcelable lazy
assertFalse(mBundle.isEmpty());
@@ -151,7 +137,101 @@ public class BundleRecyclingTest {
verify(mParcelSpy, never()).recycle();
}
- private void setUpBundle(boolean hasLazy) {
+ @Test
+ public void bundleGet_whenUnparcelledWithLazyValueUnwrapped_recyclesParcel() {
+ setUpBundle(/* lazy */ 1);
+
+ // Will unparcel with a lazy value, and immediately unwrap the lazy value,
+ // with no lazy values left at the end of getParcelable
+ // Ref counting should immediately recycle
+ assertNotNull(mBundle.getParcelable("lazy0", CustomParcelable.class));
+ verify(mParcelSpy, times(1)).recycle();
+
+ // Should not recycle again
+ assertNotNull(mBundle.getParcelable("lazy0", CustomParcelable.class));
+ mBundle.clear();
+ verify(mParcelSpy, times(1)).recycle();
+ }
+
+ @Test
+ public void bundleGet_whenMultipleLazy_recyclesParcelWhenAllUnwrapped() {
+ setUpBundle(/* lazy */ 2);
+
+ assertNotNull(mBundle.getParcelable("lazy0", CustomParcelable.class));
+ verify(mParcelSpy, times(0)).recycle();
+
+ assertNotNull(mBundle.getParcelable("lazy0", CustomParcelable.class));
+ verify(mParcelSpy, times(0)).recycle();
+
+ assertNotNull(mBundle.getParcelable("lazy1", CustomParcelable.class));
+ verify(mParcelSpy, times(1)).recycle();
+
+ // Should not recycle again
+ assertNotNull(mBundle.getParcelable("lazy0", CustomParcelable.class));
+ mBundle.clear();
+ verify(mParcelSpy, times(1)).recycle();
+ assertTrue(mBundle.isDefinitelyEmpty());
+ }
+
+ @Test
+ public void bundleGet_whenLazyAndNonLazy_recyclesParcelWhenAllUnwrapped() {
+ setUpBundle(/* lazy */ 1, /* nonLazy */ 1);
+
+ assertNotNull(mBundle.getParcelable("lazy0", CustomParcelable.class));
+ verify(mParcelSpy, times(1)).recycle();
+
+ // Should not recycle again
+ assertNotNull(mBundle.getString("nonLazy0"));
+ assertNotNull(mBundle.getParcelable("lazy0", CustomParcelable.class));
+ mBundle.clear();
+ verify(mParcelSpy, times(1)).recycle();
+ }
+
+ @Test
+ public void bundleGet_whenLazyAndNonLazy_doesNotRecycleWhenOnlyNonLazyRetrieved() {
+ setUpBundle(/* lazy */ 1, /* nonLazy */ 1);
+
+ assertNotNull(mBundle.getString("nonLazy0"));
+ verify(mParcelSpy, times(0)).recycle();
+
+ assertNotNull(mBundle.getString("nonLazy0"));
+ verify(mParcelSpy, times(0)).recycle();
+
+ assertNotNull(mBundle.getParcelable("lazy0", CustomParcelable.class));
+ verify(mParcelSpy, times(1)).recycle();
+ }
+
+ @Test
+ public void bundleGet_withWithSharedParcel_doesNotRecycleParcel() {
+ setUpBundle(/* lazy */ 1);
+
+ Bundle copy = new Bundle();
+ copy.putAll(mBundle);
+
+ assertNotNull(mBundle.getParcelable("lazy0", CustomParcelable.class));
+ mBundle.clear();
+
+ assertNotNull(copy.getParcelable("lazy0", CustomParcelable.class));
+ copy.clear();
+
+ verify(mParcelSpy, never()).recycle();
+ }
+
+ @Test
+ public void bundleGet_getAfterLazyCleared_doesNotRecycleAgain() {
+ setUpBundle(/* lazy */ 1);
+ mBundle.clear();
+ verify(mParcelSpy, times(1)).recycle();
+
+ assertNull(mBundle.getParcelable("lazy0", CustomParcelable.class));
+ verify(mParcelSpy, times(1)).recycle();
+ }
+
+ private void setUpBundle(int lazy) {
+ setUpBundle(lazy, /* nonLazy */ 0);
+ }
+
+ private void setUpBundle(int lazy, int nonLazy) {
AtomicReference<Parcel> parcel = new AtomicReference<>();
StaticMockitoSession session = mockitoSession()
.strictness(Strictness.STRICT_STUBS)
@@ -166,7 +246,7 @@ public class BundleRecyclingTest {
Bundle bundle = new Bundle();
bundle.setClassLoader(getClass().getClassLoader());
- Parcel p = createBundle(hasLazy);
+ Parcel p = createBundle(lazy, nonLazy);
bundle.readFromParcel(p);
p.recycle();
@@ -179,13 +259,17 @@ public class BundleRecyclingTest {
/**
* Create a test bundle, parcel it and return the parcel.
*/
- private Parcel createBundle(boolean hasLazy) {
+ private Parcel createBundle(int lazy, int nonLazy) {
final Bundle source = new Bundle();
- if (hasLazy) {
- source.putParcelable("key", new CustomParcelable(13, "Tiramisu"));
- } else {
- source.putString("key", "tiramisu");
+
+ for (int i = 0; i < nonLazy; i++) {
+ source.putString("nonLazy" + i, "Tiramisu");
}
+
+ for (int i = 0; i < lazy; i++) {
+ source.putParcelable("lazy" + i, new CustomParcelable(13, "Tiramisu"));
+ }
+
return getParcelledBundle(source);
}
diff --git a/core/tests/overlaytests/device/AndroidTest.xml b/core/tests/overlaytests/device/AndroidTest.xml
index 2d7d9b450c8c..4099ec1f819d 100644
--- a/core/tests/overlaytests/device/AndroidTest.xml
+++ b/core/tests/overlaytests/device/AndroidTest.xml
@@ -20,7 +20,7 @@
<option name="test-suite-tag" value="apct-instrumentation" />
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
<option name="remount-system" value="true" />
<option name="push" value="OverlayDeviceTests.apk->/system/app/OverlayDeviceTests.apk" />
diff --git a/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
index c66a743d8f53..72f3af640b67 100644
--- a/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
@@ -18,6 +18,12 @@ package com.android.internal.util;
import static org.junit.Assert.assertArrayEquals;
+import androidx.test.filters.SmallTest;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+
import junit.framework.TestCase;
/**
@@ -257,4 +263,182 @@ public class ArrayUtilsTest extends TestCase {
assertArrayEquals(expectation, ArrayUtils.concat(array1, array2, array3));
}
+
+ @SmallTest
+ public void testUnstableRemoveIf() throws Exception {
+ java.util.function.Predicate<Object> isNull = new java.util.function.Predicate<Object>() {
+ @Override
+ public boolean test(Object o) {
+ return o == null;
+ }
+ };
+
+ final Object a = new Object();
+ final Object b = new Object();
+ final Object c = new Object();
+
+ ArrayList<Object> collection = null;
+ assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
+
+ collection = new ArrayList<>();
+ assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
+
+ collection = new ArrayList<>(Collections.singletonList(a));
+ assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(1, collection.size());
+ assertTrue(collection.contains(a));
+
+ collection = new ArrayList<>(Collections.singletonList(null));
+ assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(0, collection.size());
+
+ collection = new ArrayList<>(Arrays.asList(a, b));
+ assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(2, collection.size());
+ assertTrue(collection.contains(a));
+ assertTrue(collection.contains(b));
+
+ collection = new ArrayList<>(Arrays.asList(a, null));
+ assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(1, collection.size());
+ assertTrue(collection.contains(a));
+
+ collection = new ArrayList<>(Arrays.asList(null, a));
+ assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(1, collection.size());
+ assertTrue(collection.contains(a));
+
+ collection = new ArrayList<>(Arrays.asList(null, null));
+ assertEquals(2, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(0, collection.size());
+
+ collection = new ArrayList<>(Arrays.asList(a, b, c));
+ assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(3, collection.size());
+ assertTrue(collection.contains(a));
+ assertTrue(collection.contains(b));
+ assertTrue(collection.contains(c));
+
+ collection = new ArrayList<>(Arrays.asList(a, b, null));
+ assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(2, collection.size());
+ assertTrue(collection.contains(a));
+ assertTrue(collection.contains(b));
+
+ collection = new ArrayList<>(Arrays.asList(a, null, b));
+ assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(2, collection.size());
+ assertTrue(collection.contains(a));
+ assertTrue(collection.contains(b));
+
+ collection = new ArrayList<>(Arrays.asList(null, a, b));
+ assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(2, collection.size());
+ assertTrue(collection.contains(a));
+ assertTrue(collection.contains(b));
+
+ collection = new ArrayList<>(Arrays.asList(a, null, null));
+ assertEquals(2, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(1, collection.size());
+ assertTrue(collection.contains(a));
+
+ collection = new ArrayList<>(Arrays.asList(null, null, a));
+ assertEquals(2, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(1, collection.size());
+ assertTrue(collection.contains(a));
+
+ collection = new ArrayList<>(Arrays.asList(null, a, null));
+ assertEquals(2, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(1, collection.size());
+ assertTrue(collection.contains(a));
+
+ collection = new ArrayList<>(Arrays.asList(null, null, null));
+ assertEquals(3, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(0, collection.size());
+ }
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_passesWhenRangeInsideArray() {
+ ArrayUtils.throwsIfOutOfBounds(10, 2, 6);
+ }
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_passesWhenRangeIsWholeArray() {
+ ArrayUtils.throwsIfOutOfBounds(10, 0, 10);
+ }
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_passesWhenEmptyRangeAtStart() {
+ ArrayUtils.throwsIfOutOfBounds(10, 0, 0);
+ }
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_passesWhenEmptyRangeAtEnd() {
+ ArrayUtils.throwsIfOutOfBounds(10, 10, 0);
+ }
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_passesWhenEmptyArray() {
+ ArrayUtils.throwsIfOutOfBounds(0, 0, 0);
+ }
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_failsWhenRangeStartNegative() {
+ try {
+ ArrayUtils.throwsIfOutOfBounds(10, -1, 5);
+ fail();
+ } catch (ArrayIndexOutOfBoundsException expected) {
+ // expected
+ }
+ }
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_failsWhenCountNegative() {
+ try {
+ ArrayUtils.throwsIfOutOfBounds(10, 5, -1);
+ fail();
+ } catch (ArrayIndexOutOfBoundsException expected) {
+ // expected
+ }
+ }
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_failsWhenRangeStartTooHigh() {
+ try {
+ ArrayUtils.throwsIfOutOfBounds(10, 11, 0);
+ fail();
+ } catch (ArrayIndexOutOfBoundsException expected) {
+ // expected
+ }
+ }
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_failsWhenRangeEndTooHigh() {
+ try {
+ ArrayUtils.throwsIfOutOfBounds(10, 5, 6);
+ fail();
+ } catch (ArrayIndexOutOfBoundsException expected) {
+ // expected
+ }
+ }
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_failsWhenLengthNegative() {
+ try {
+ ArrayUtils.throwsIfOutOfBounds(-1, 0, 0);
+ fail();
+ } catch (ArrayIndexOutOfBoundsException expected) {
+ // expected
+ }
+ }
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_failsWhenOverflowRangeEndTooHigh() {
+ try {
+ ArrayUtils.throwsIfOutOfBounds(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
+ fail();
+ } catch (ArrayIndexOutOfBoundsException expected) {
+ // expected
+ }
+ }
}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index ae1c94611355..505d2a3804d8 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1075,6 +1075,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-1075136930": {
+ "message": "startLockTaskMode: Can't lock due to auth",
+ "level": "WARN",
+ "group": "WM_DEBUG_LOCKTASK",
+ "at": "com\/android\/server\/wm\/LockTaskController.java"
+ },
"-1069336896": {
"message": "onRootTaskOrderChanged(): rootTask=%s",
"level": "DEBUG",
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index 425a37891afb..a8ab6d9e494e 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -330,11 +330,7 @@ public abstract class BaseCanvas {
public void drawPath(@NonNull Path path, @NonNull Paint paint) {
throwIfHasHwFeaturesInSwMode(paint);
- if (path.isSimplePath && path.rects != null) {
- nDrawRegion(mNativeCanvasWrapper, path.rects.mNativeRegion, paint.getNativeInstance());
- } else {
- nDrawPath(mNativeCanvasWrapper, path.readOnlyNI(), paint.getNativeInstance());
- }
+ nDrawPath(mNativeCanvasWrapper, path.readOnlyNI(), paint.getNativeInstance());
}
public void drawPoint(float x, float y, @NonNull Paint paint) {
diff --git a/graphics/java/android/graphics/BaseRecordingCanvas.java b/graphics/java/android/graphics/BaseRecordingCanvas.java
index a998ba870f74..d06f665631cc 100644
--- a/graphics/java/android/graphics/BaseRecordingCanvas.java
+++ b/graphics/java/android/graphics/BaseRecordingCanvas.java
@@ -286,11 +286,7 @@ public class BaseRecordingCanvas extends Canvas {
@Override
public final void drawPath(@NonNull Path path, @NonNull Paint paint) {
- if (path.isSimplePath && path.rects != null) {
- nDrawRegion(mNativeCanvasWrapper, path.rects.mNativeRegion, paint.getNativeInstance());
- } else {
- nDrawPath(mNativeCanvasWrapper, path.readOnlyNI(), paint.getNativeInstance());
- }
+ nDrawPath(mNativeCanvasWrapper, path.readOnlyNI(), paint.getNativeInstance());
}
@Override
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index 7cc22d753f69..0e67f1f4842a 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -1046,14 +1046,39 @@ public class HardwareRenderer {
}
/** @hide */
- public static int copySurfaceInto(Surface surface, Rect srcRect, Bitmap bitmap) {
- if (srcRect == null) {
- // Empty rect means entire surface
- return nCopySurfaceInto(surface, 0, 0, 0, 0, bitmap.getNativeInstance());
- } else {
- return nCopySurfaceInto(surface, srcRect.left, srcRect.top,
- srcRect.right, srcRect.bottom, bitmap.getNativeInstance());
+ public abstract static class CopyRequest {
+ protected Bitmap mDestinationBitmap;
+ final Rect mSrcRect;
+
+ protected CopyRequest(Rect srcRect, Bitmap destinationBitmap) {
+ mDestinationBitmap = destinationBitmap;
+ if (srcRect != null) {
+ mSrcRect = srcRect;
+ } else {
+ mSrcRect = new Rect();
+ }
+ }
+
+ /**
+ * Retrieve the bitmap in which to store the result of the copy request
+ */
+ public long getDestinationBitmap(int srcWidth, int srcHeight) {
+ if (mDestinationBitmap == null) {
+ mDestinationBitmap =
+ Bitmap.createBitmap(srcWidth, srcHeight, Bitmap.Config.ARGB_8888);
+ }
+ return mDestinationBitmap.getNativeInstance();
}
+
+ /** Called when the copy is completed */
+ public abstract void onCopyFinished(int result);
+ }
+
+ /** @hide */
+ public static void copySurfaceInto(Surface surface, CopyRequest copyRequest) {
+ final Rect srcRect = copyRequest.mSrcRect;
+ nCopySurfaceInto(surface, srcRect.left, srcRect.top, srcRect.right, srcRect.bottom,
+ copyRequest);
}
/**
@@ -1464,8 +1489,8 @@ public class HardwareRenderer {
private static native void nRemoveObserver(long nativeProxy, long nativeObserver);
- private static native int nCopySurfaceInto(Surface surface,
- int srcLeft, int srcTop, int srcRight, int srcBottom, long bitmapHandle);
+ private static native void nCopySurfaceInto(Surface surface,
+ int srcLeft, int srcTop, int srcRight, int srcBottom, CopyRequest request);
private static native Bitmap nCreateHardwareBitmap(long renderNode, int width, int height);
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index e5ef10d1d555..afcaabebb9fb 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -20,8 +20,6 @@ import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Build;
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
@@ -47,18 +45,6 @@ public class Path {
public final long mNativePath;
/**
- * @hide
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public boolean isSimplePath = true;
- /**
- * @hide
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public Region rects;
- private Direction mLastDirection = null;
-
- /**
* Create an empty path
*/
public Path() {
@@ -72,15 +58,7 @@ public class Path {
* @param src The path to copy from when initializing the new path
*/
public Path(@Nullable Path src) {
- long valNative = 0;
- if (src != null) {
- valNative = src.mNativePath;
- isSimplePath = src.isSimplePath;
- if (src.rects != null) {
- rects = new Region(src.rects);
- }
- }
- mNativePath = nInit(valNative);
+ mNativePath = nInit(src != null ? src.mNativePath : 0);
sRegistry.registerNativeAllocation(this, mNativePath);
}
@@ -89,9 +67,6 @@ public class Path {
* This does NOT change the fill-type setting.
*/
public void reset() {
- isSimplePath = true;
- mLastDirection = null;
- if (rects != null) rects.setEmpty();
// We promised not to change this, so preserve it around the native
// call, which does now reset fill type.
final FillType fillType = getFillType();
@@ -104,9 +79,6 @@ public class Path {
* keeps the internal data structure for faster reuse.
*/
public void rewind() {
- isSimplePath = true;
- mLastDirection = null;
- if (rects != null) rects.setEmpty();
nRewind(mNativePath);
}
@@ -116,19 +88,7 @@ public class Path {
if (this == src) {
return;
}
- isSimplePath = src.isSimplePath;
nSet(mNativePath, src.mNativePath);
- if (!isSimplePath) {
- return;
- }
-
- if (rects != null && src.rects != null) {
- rects.set(src.rects);
- } else if (rects != null && src.rects == null) {
- rects.setEmpty();
- } else if (src.rects != null) {
- rects = new Region(src.rects);
- }
}
/**
@@ -192,12 +152,7 @@ public class Path {
* @see #op(Path, android.graphics.Path.Op)
*/
public boolean op(@NonNull Path path1, @NonNull Path path2, @NonNull Op op) {
- if (nOp(path1.mNativePath, path2.mNativePath, op.ordinal(), this.mNativePath)) {
- isSimplePath = false;
- rects = null;
- return true;
- }
- return false;
+ return nOp(path1.mNativePath, path2.mNativePath, op.ordinal(), this.mNativePath);
}
/**
@@ -378,7 +333,6 @@ public class Path {
* @param y The y-coordinate of the end of a line
*/
public void lineTo(float x, float y) {
- isSimplePath = false;
nLineTo(mNativePath, x, y);
}
@@ -393,7 +347,6 @@ public class Path {
* this contour, to specify a line
*/
public void rLineTo(float dx, float dy) {
- isSimplePath = false;
nRLineTo(mNativePath, dx, dy);
}
@@ -408,7 +361,6 @@ public class Path {
* @param y2 The y-coordinate of the end point on a quadratic curve
*/
public void quadTo(float x1, float y1, float x2, float y2) {
- isSimplePath = false;
nQuadTo(mNativePath, x1, y1, x2, y2);
}
@@ -427,7 +379,6 @@ public class Path {
* this contour, for the end point of a quadratic curve
*/
public void rQuadTo(float dx1, float dy1, float dx2, float dy2) {
- isSimplePath = false;
nRQuadTo(mNativePath, dx1, dy1, dx2, dy2);
}
@@ -445,7 +396,6 @@ public class Path {
*/
public void cubicTo(float x1, float y1, float x2, float y2,
float x3, float y3) {
- isSimplePath = false;
nCubicTo(mNativePath, x1, y1, x2, y2, x3, y3);
}
@@ -456,7 +406,6 @@ public class Path {
*/
public void rCubicTo(float x1, float y1, float x2, float y2,
float x3, float y3) {
- isSimplePath = false;
nRCubicTo(mNativePath, x1, y1, x2, y2, x3, y3);
}
@@ -507,7 +456,6 @@ public class Path {
*/
public void arcTo(float left, float top, float right, float bottom, float startAngle,
float sweepAngle, boolean forceMoveTo) {
- isSimplePath = false;
nArcTo(mNativePath, left, top, right, bottom, startAngle, sweepAngle, forceMoveTo);
}
@@ -516,7 +464,6 @@ public class Path {
* first point of the contour, a line segment is automatically added.
*/
public void close() {
- isSimplePath = false;
nClose(mNativePath);
}
@@ -536,18 +483,6 @@ public class Path {
final int nativeInt;
}
- private void detectSimplePath(float left, float top, float right, float bottom, Direction dir) {
- if (mLastDirection == null) {
- mLastDirection = dir;
- }
- if (mLastDirection != dir) {
- isSimplePath = false;
- } else {
- if (rects == null) rects = new Region();
- rects.op((int) left, (int) top, (int) right, (int) bottom, Region.Op.UNION);
- }
- }
-
/**
* Add a closed rectangle contour to the path
*
@@ -568,7 +503,6 @@ public class Path {
* @param dir The direction to wind the rectangle's contour
*/
public void addRect(float left, float top, float right, float bottom, @NonNull Direction dir) {
- detectSimplePath(left, top, right, bottom, dir);
nAddRect(mNativePath, left, top, right, bottom, dir.nativeInt);
}
@@ -588,7 +522,6 @@ public class Path {
* @param dir The direction to wind the oval's contour
*/
public void addOval(float left, float top, float right, float bottom, @NonNull Direction dir) {
- isSimplePath = false;
nAddOval(mNativePath, left, top, right, bottom, dir.nativeInt);
}
@@ -601,7 +534,6 @@ public class Path {
* @param dir The direction to wind the circle's contour
*/
public void addCircle(float x, float y, float radius, @NonNull Direction dir) {
- isSimplePath = false;
nAddCircle(mNativePath, x, y, radius, dir.nativeInt);
}
@@ -624,7 +556,6 @@ public class Path {
*/
public void addArc(float left, float top, float right, float bottom, float startAngle,
float sweepAngle) {
- isSimplePath = false;
nAddArc(mNativePath, left, top, right, bottom, startAngle, sweepAngle);
}
@@ -649,7 +580,6 @@ public class Path {
*/
public void addRoundRect(float left, float top, float right, float bottom, float rx, float ry,
@NonNull Direction dir) {
- isSimplePath = false;
nAddRoundRect(mNativePath, left, top, right, bottom, rx, ry, dir.nativeInt);
}
@@ -682,7 +612,6 @@ public class Path {
if (radii.length < 8) {
throw new ArrayIndexOutOfBoundsException("radii[] needs 8 values");
}
- isSimplePath = false;
nAddRoundRect(mNativePath, left, top, right, bottom, radii, dir.nativeInt);
}
@@ -693,7 +622,6 @@ public class Path {
* @param dx The amount to translate the path in X as it is added
*/
public void addPath(@NonNull Path src, float dx, float dy) {
- isSimplePath = false;
nAddPath(mNativePath, src.mNativePath, dx, dy);
}
@@ -703,7 +631,6 @@ public class Path {
* @param src The path that is appended to the current path
*/
public void addPath(@NonNull Path src) {
- isSimplePath = false;
nAddPath(mNativePath, src.mNativePath);
}
@@ -713,7 +640,6 @@ public class Path {
* @param src The path to add as a new contour
*/
public void addPath(@NonNull Path src, @NonNull Matrix matrix) {
- if (!src.isSimplePath) isSimplePath = false;
nAddPath(mNativePath, src.mNativePath, matrix.ni());
}
@@ -741,15 +667,6 @@ public class Path {
* @param dy The amount in the Y direction to offset the entire path
*/
public void offset(float dx, float dy) {
- if (isSimplePath && rects == null) {
- // nothing to offset
- return;
- }
- if (isSimplePath && dx == Math.rint(dx) && dy == Math.rint(dy)) {
- rects.translate((int) dx, (int) dy);
- } else {
- isSimplePath = false;
- }
nOffset(mNativePath, dx, dy);
}
@@ -760,7 +677,6 @@ public class Path {
* @param dy The new Y coordinate for the last point
*/
public void setLastPoint(float dx, float dy) {
- isSimplePath = false;
nSetLastPoint(mNativePath, dx, dy);
}
@@ -773,12 +689,7 @@ public class Path {
* then the the original path is modified
*/
public void transform(@NonNull Matrix matrix, @Nullable Path dst) {
- long dstNative = 0;
- if (dst != null) {
- dst.isSimplePath = false;
- dstNative = dst.mNativePath;
- }
- nTransform(mNativePath, matrix.ni(), dstNative);
+ nTransform(mNativePath, matrix.ni(), dst != null ? dst.mNativePath : 0);
}
/**
@@ -787,7 +698,6 @@ public class Path {
* @param matrix The matrix to apply to the path
*/
public void transform(@NonNull Matrix matrix) {
- isSimplePath = false;
nTransform(mNativePath, matrix.ni());
}
@@ -797,7 +707,6 @@ public class Path {
}
final long mutateNI() {
- isSimplePath = false;
return mNativePath;
}
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index f4f5e1e792ec..25e1922fc38e 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -1244,14 +1244,16 @@ public class Typeface {
nativePtrs[i++] = entry.getValue().native_instance;
writeString(namesBytes, entry.getKey());
}
- int typefacesBytesCount = nativeWriteTypefaces(null, nativePtrs);
// int (typefacesBytesCount), typefaces, namesBytes
+ final int typefaceBytesCountSize = Integer.BYTES;
+ int typefacesBytesCount = nativeWriteTypefaces(null, typefaceBytesCountSize, nativePtrs);
SharedMemory sharedMemory = SharedMemory.create(
- "fontMap", Integer.BYTES + typefacesBytesCount + namesBytes.size());
+ "fontMap", typefaceBytesCountSize + typefacesBytesCount + namesBytes.size());
ByteBuffer writableBuffer = sharedMemory.mapReadWrite().order(ByteOrder.BIG_ENDIAN);
try {
writableBuffer.putInt(typefacesBytesCount);
- int writtenBytesCount = nativeWriteTypefaces(writableBuffer.slice(), nativePtrs);
+ int writtenBytesCount =
+ nativeWriteTypefaces(writableBuffer, writableBuffer.position(), nativePtrs);
if (writtenBytesCount != typefacesBytesCount) {
throw new IOException(String.format("Unexpected bytes written: %d, expected: %d",
writtenBytesCount, typefacesBytesCount));
@@ -1276,7 +1278,9 @@ public class Typeface {
@NonNull ByteBuffer buffer, @NonNull Map<String, Typeface> out)
throws IOException {
int typefacesBytesCount = buffer.getInt();
- long[] nativePtrs = nativeReadTypefaces(buffer.slice());
+ // Note: Do not call buffer.slice(), as nativeReadTypefaces() expects
+ // that buffer.address() is page-aligned.
+ long[] nativePtrs = nativeReadTypefaces(buffer, buffer.position());
if (nativePtrs == null) {
throw new IOException("Could not read typefaces");
}
@@ -1553,16 +1557,6 @@ public class Typeface {
return Arrays.binarySearch(mSupportedAxes, axis) >= 0;
}
- /** @hide */
- public List<FontFamily> getFallback() {
- ArrayList<FontFamily> families = new ArrayList<>();
- int familySize = nativeGetFamilySize(native_instance);
- for (int i = 0; i < familySize; ++i) {
- families.add(new FontFamily(nativeGetFamily(native_instance, i)));
- }
- return families;
- }
-
private static native long nativeCreateFromTypeface(long native_instance, int style);
private static native long nativeCreateFromTypefaceWithExactStyle(
long native_instance, int weight, boolean italic);
@@ -1588,19 +1582,13 @@ public class Typeface {
@CriticalNative
private static native long nativeGetReleaseFunc();
- @CriticalNative
- private static native int nativeGetFamilySize(long naitvePtr);
-
- @CriticalNative
- private static native long nativeGetFamily(long nativePtr, int index);
-
-
private static native void nativeRegisterGenericFamily(String str, long nativePtr);
private static native int nativeWriteTypefaces(
- @Nullable ByteBuffer buffer, @NonNull long[] nativePtrs);
+ @Nullable ByteBuffer buffer, int position, @NonNull long[] nativePtrs);
- private static native @Nullable long[] nativeReadTypefaces(@NonNull ByteBuffer buffer);
+ private static native
+ @Nullable long[] nativeReadTypefaces(@NonNull ByteBuffer buffer, int position);
private static native void nativeForceSetStaticFinalField(String fieldName, Typeface typeface);
diff --git a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
index 6939a72019e8..47de37d76167 100644
--- a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
+++ b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
@@ -359,10 +359,10 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
/** @hide */
@TestApi
public Region getSafeZone() {
- mMaskMatrix.reset();
+ Path mask = getIconMask();
mMaskMatrix.setScale(SAFEZONE_SCALE, SAFEZONE_SCALE, getBounds().centerX(), getBounds().centerY());
Path p = new Path();
- mMask.transform(mMaskMatrix, p);
+ mask.transform(mMaskMatrix, p);
Region safezoneRegion = new Region(getBounds());
safezoneRegion.setPath(p, safezoneRegion);
return safezoneRegion;
diff --git a/graphics/java/android/view/PixelCopy.java b/graphics/java/android/view/PixelCopy.java
index 2797a4daa925..0f82c8fe904a 100644
--- a/graphics/java/android/view/PixelCopy.java
+++ b/graphics/java/android/view/PixelCopy.java
@@ -20,12 +20,15 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Bitmap;
+import android.graphics.HardwareRenderer;
import android.graphics.Rect;
import android.os.Handler;
import android.view.ViewTreeObserver.OnDrawListener;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
* Provides a mechanisms to issue pixel copy requests to allow for copy
@@ -183,12 +186,10 @@ public final class PixelCopy {
if (srcRect != null && srcRect.isEmpty()) {
throw new IllegalArgumentException("sourceRect is empty");
}
- // TODO: Make this actually async and fast and cool and stuff
- int result = ThreadedRenderer.copySurfaceInto(source, srcRect, dest);
- listenerThread.post(new Runnable() {
+ HardwareRenderer.copySurfaceInto(source, new HardwareRenderer.CopyRequest(srcRect, dest) {
@Override
- public void run() {
- listener.onPixelCopyFinished(result);
+ public void onCopyFinished(int result) {
+ listenerThread.post(() -> listener.onPixelCopyFinished(result));
}
});
}
@@ -255,6 +256,26 @@ public final class PixelCopy {
@NonNull Bitmap dest, @NonNull OnPixelCopyFinishedListener listener,
@NonNull Handler listenerThread) {
validateBitmapDest(dest);
+ final Rect insets = new Rect();
+ final Surface surface = sourceForWindow(source, insets);
+ request(surface, adjustSourceRectForInsets(insets, srcRect), dest, listener,
+ listenerThread);
+ }
+
+ private static void validateBitmapDest(Bitmap bitmap) {
+ // TODO: Pre-check max texture dimens if we can
+ if (bitmap == null) {
+ throw new IllegalArgumentException("Bitmap cannot be null");
+ }
+ if (bitmap.isRecycled()) {
+ throw new IllegalArgumentException("Bitmap is recycled");
+ }
+ if (!bitmap.isMutable()) {
+ throw new IllegalArgumentException("Bitmap is immutable");
+ }
+ }
+
+ private static Surface sourceForWindow(Window source, Rect outInsets) {
if (source == null) {
throw new IllegalArgumentException("source is null");
}
@@ -267,32 +288,222 @@ public final class PixelCopy {
if (root != null) {
surface = root.mSurface;
final Rect surfaceInsets = root.mWindowAttributes.surfaceInsets;
- if (srcRect == null) {
- srcRect = new Rect(surfaceInsets.left, surfaceInsets.top,
- root.mWidth + surfaceInsets.left, root.mHeight + surfaceInsets.top);
- } else {
- srcRect.offset(surfaceInsets.left, surfaceInsets.top);
- }
+ outInsets.set(surfaceInsets.left, surfaceInsets.top,
+ root.mWidth + surfaceInsets.left, root.mHeight + surfaceInsets.top);
}
if (surface == null || !surface.isValid()) {
throw new IllegalArgumentException(
"Window doesn't have a backing surface!");
}
- request(surface, srcRect, dest, listener, listenerThread);
+ return surface;
}
- private static void validateBitmapDest(Bitmap bitmap) {
- // TODO: Pre-check max texture dimens if we can
- if (bitmap == null) {
- throw new IllegalArgumentException("Bitmap cannot be null");
+ private static Rect adjustSourceRectForInsets(Rect insets, Rect srcRect) {
+ if (srcRect == null) {
+ return insets;
}
- if (bitmap.isRecycled()) {
- throw new IllegalArgumentException("Bitmap is recycled");
+ if (insets != null) {
+ srcRect.offset(insets.left, insets.top);
}
- if (!bitmap.isMutable()) {
- throw new IllegalArgumentException("Bitmap is immutable");
+ return srcRect;
+ }
+
+ /**
+ * Contains the result of a PixelCopy request
+ */
+ public static final class CopyResult {
+ private int mStatus;
+ private Bitmap mBitmap;
+
+ private CopyResult(@CopyResultStatus int status, Bitmap bitmap) {
+ mStatus = status;
+ mBitmap = bitmap;
+ }
+
+ /**
+ * Returns the {@link CopyResultStatus} of the copy request.
+ */
+ public @CopyResultStatus int getStatus() {
+ return mStatus;
+ }
+
+ private void validateStatus() {
+ if (mStatus != SUCCESS) {
+ throw new IllegalStateException("Copy request didn't succeed, status = " + mStatus);
+ }
+ }
+
+ /**
+ * If the PixelCopy {@link Request} was given a destination bitmap with
+ * {@link Request#setDestinationBitmap(Bitmap)} then the returned bitmap will be the same
+ * as the one given. If no destination bitmap was provided, then this
+ * will contain the automatically allocated Bitmap to hold the result.
+ *
+ * @return the Bitmap the copy request was stored in.
+ * @throws IllegalStateException if {@link #getStatus()} is not SUCCESS
+ */
+ public @NonNull Bitmap getBitmap() {
+ validateStatus();
+ return mBitmap;
+ }
+ }
+
+ /**
+ * A builder to create the complete PixelCopy request, which is then executed by calling
+ * {@link #request()}
+ */
+ public static final class Request {
+ private Request(Surface source, Rect sourceInsets, Executor listenerThread,
+ Consumer<CopyResult> listener) {
+ this.mSource = source;
+ this.mSourceInsets = sourceInsets;
+ this.mListenerThread = listenerThread;
+ this.mListener = listener;
+ }
+
+ private final Surface mSource;
+ private final Consumer<CopyResult> mListener;
+ private final Executor mListenerThread;
+ private final Rect mSourceInsets;
+ private Rect mSrcRect;
+ private Bitmap mDest;
+
+ /**
+ * Sets the region of the source to copy from. By default, the entire source is copied to
+ * the output. If only a subset of the source is necessary to be copied, specifying a
+ * srcRect will improve performance by reducing
+ * the amount of data being copied.
+ *
+ * @param srcRect The area of the source to read from. Null or empty will be treated to
+ * mean the entire source
+ * @return this
+ */
+ public @NonNull Request setSourceRect(@Nullable Rect srcRect) {
+ this.mSrcRect = srcRect;
+ return this;
+ }
+
+ /**
+ * Specifies the output bitmap in which to store the result. By default, a Bitmap of format
+ * {@link android.graphics.Bitmap.Config#ARGB_8888} with a width & height matching that
+ * of the {@link #setSourceRect(Rect) source area} will be created to place the result.
+ *
+ * @param destination The bitmap to store the result, or null to have a bitmap
+ * automatically created of the appropriate size. If not null, must not
+ * be {@link Bitmap#isRecycled() recycled} and must be
+ * {@link Bitmap#isMutable() mutable}.
+ * @return this
+ */
+ public @NonNull Request setDestinationBitmap(@Nullable Bitmap destination) {
+ if (destination != null) {
+ validateBitmapDest(destination);
+ }
+ this.mDest = destination;
+ return this;
+ }
+
+ /**
+ * Executes the request.
+ */
+ public void request() {
+ if (!mSource.isValid()) {
+ mListenerThread.execute(() -> mListener.accept(
+ new CopyResult(ERROR_SOURCE_INVALID, null)));
+ return;
+ }
+ HardwareRenderer.copySurfaceInto(mSource, new HardwareRenderer.CopyRequest(
+ adjustSourceRectForInsets(mSourceInsets, mSrcRect), mDest) {
+ @Override
+ public void onCopyFinished(int result) {
+ mListenerThread.execute(() -> mListener.accept(
+ new CopyResult(result, mDestinationBitmap)));
+ }
+ });
}
}
+ /**
+ * Creates a PixelCopy request for the given {@link Window}
+ * @param source The Window to copy from
+ * @param callbackExecutor The executor to run the callback on
+ * @param listener The callback for when the copy request is completed
+ * @return A {@link Request} builder to set the optional params & execute the request
+ */
+ public static @NonNull Request ofWindow(@NonNull Window source,
+ @NonNull Executor callbackExecutor,
+ @NonNull Consumer<CopyResult> listener) {
+ final Rect insets = new Rect();
+ final Surface surface = sourceForWindow(source, insets);
+ return new Request(surface, insets, callbackExecutor, listener);
+ }
+
+ /**
+ * Creates a PixelCopy request for the {@link Window} that the given {@link View} is
+ * attached to.
+ *
+ * Note that this copy request is not cropped to the area the View occupies by default. If that
+ * behavior is desired, use {@link View#getLocationInWindow(int[])} combined with
+ * {@link Request#setSourceRect(Rect)} to set a crop area to restrict the copy operation.
+ *
+ * @param source A View that {@link View#isAttachedToWindow() is attached} to a window that
+ * will be used to retrieve the window to copy from.
+ * @param callbackExecutor The executor to run the callback on
+ * @param listener The callback for when the copy request is completed
+ * @return A {@link Request} builder to set the optional params & execute the request
+ */
+ public static @NonNull Request ofWindow(@NonNull View source,
+ @NonNull Executor callbackExecutor,
+ @NonNull Consumer<CopyResult> listener) {
+ if (source == null || !source.isAttachedToWindow()) {
+ throw new IllegalArgumentException(
+ "View must not be null & must be attached to window");
+ }
+ final Rect insets = new Rect();
+ Surface surface = null;
+ final ViewRootImpl root = source.getViewRootImpl();
+ if (root != null) {
+ surface = root.mSurface;
+ insets.set(root.mWindowAttributes.surfaceInsets);
+ }
+ if (surface == null || !surface.isValid()) {
+ throw new IllegalArgumentException(
+ "Window doesn't have a backing surface!");
+ }
+ return new Request(surface, insets, callbackExecutor, listener);
+ }
+
+ /**
+ * Creates a PixelCopy request for the given {@link Surface}
+ *
+ * @param source The Surface to copy from. Must be {@link Surface#isValid() valid}.
+ * @param callbackExecutor The executor to run the callback on
+ * @param listener The callback for when the copy request is completed
+ * @return A {@link Request} builder to set the optional params & execute the request
+ */
+ public static @NonNull Request ofSurface(@NonNull Surface source,
+ @NonNull Executor callbackExecutor,
+ @NonNull Consumer<CopyResult> listener) {
+ if (source == null || !source.isValid()) {
+ throw new IllegalArgumentException("Source must not be null & must be valid");
+ }
+ return new Request(source, null, callbackExecutor, listener);
+ }
+
+ /**
+ * Creates a PixelCopy request for the {@link Surface} belonging to the
+ * given {@link SurfaceView}
+ *
+ * @param source The SurfaceView to copy from. The backing surface must be
+ * {@link Surface#isValid() valid}
+ * @param callbackExecutor The executor to run the callback on
+ * @param listener The callback for when the copy request is completed
+ * @return A {@link Request} builder to set the optional params & execute the request
+ */
+ public static @NonNull Request ofSurface(@NonNull SurfaceView source,
+ @NonNull Executor callbackExecutor,
+ @NonNull Consumer<CopyResult> listener) {
+ return ofSurface(source.getHolder().getSurface(), callbackExecutor, listener);
+ }
+
private PixelCopy() {}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
index 0dba6b0049c0..d42fca244120 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -51,12 +51,6 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
@VisibleForTesting
final Map<IBinder, TaskFragmentInfo> mFragmentInfos = new ArrayMap<>();
- /**
- * Mapping from the client assigned unique token to the TaskFragment parent
- * {@link Configuration}.
- */
- final Map<IBinder, Configuration> mFragmentParentConfigs = new ArrayMap<>();
-
private final TaskFragmentCallback mCallback;
@VisibleForTesting
TaskFragmentAnimationController mAnimationController;
@@ -68,8 +62,7 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo);
- void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken,
- @NonNull Configuration parentConfig);
+ void onTaskFragmentParentInfoChanged(int taskId, @NonNull Configuration parentConfig);
void onActivityReparentToTask(int taskId, @NonNull Intent activityIntent,
@NonNull IBinder activityToken);
void onTaskFragmentError(@Nullable TaskFragmentInfo taskFragmentInfo, int opType);
@@ -300,7 +293,6 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
@Override
public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {
mFragmentInfos.remove(taskFragmentInfo.getFragmentToken());
- mFragmentParentConfigs.remove(taskFragmentInfo.getFragmentToken());
if (mCallback != null) {
mCallback.onTaskFragmentVanished(taskFragmentInfo);
@@ -308,12 +300,9 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
}
@Override
- public void onTaskFragmentParentInfoChanged(
- @NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) {
- mFragmentParentConfigs.put(fragmentToken, parentConfig);
-
+ public void onTaskFragmentParentInfoChanged(int taskId, @NonNull Configuration parentConfig) {
if (mCallback != null) {
- mCallback.onTaskFragmentParentInfoChanged(fragmentToken, parentConfig);
+ mCallback.onTaskFragmentParentInfoChanged(taskId, parentConfig);
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index c688080ad929..dad07394e3fb 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -155,6 +155,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
container.setInfo(taskFragmentInfo);
if (container.isFinished()) {
mPresenter.cleanupContainer(container, false /* shouldFinishDependent */);
+ } else {
+ // Update with the latest Task configuration.
+ mPresenter.updateContainer(container);
}
updateCallbackIfNecessary();
}
@@ -233,19 +236,30 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
@Override
- public void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken,
- @NonNull Configuration parentConfig) {
+ public void onTaskFragmentParentInfoChanged(int taskId, @NonNull Configuration parentConfig) {
synchronized (mLock) {
- final TaskFragmentContainer container = getContainer(fragmentToken);
- if (container != null) {
- onTaskConfigurationChanged(container.getTaskId(), parentConfig);
- if (isInPictureInPicture(parentConfig)) {
- // No need to update presentation in PIP until the Task exit PIP.
- return;
+ onTaskConfigurationChanged(taskId, parentConfig);
+ if (isInPictureInPicture(parentConfig)) {
+ // No need to update presentation in PIP until the Task exit PIP.
+ return;
+ }
+ final TaskContainer taskContainer = getTaskContainer(taskId);
+ if (taskContainer == null || taskContainer.isEmpty()) {
+ Log.e(TAG, "onTaskFragmentParentInfoChanged on empty Task id=" + taskId);
+ return;
+ }
+ // Update all TaskFragments in the Task. Make a copy of the list since some may be
+ // removed on updating.
+ final List<TaskFragmentContainer> containers =
+ new ArrayList<>(taskContainer.mContainers);
+ for (int i = containers.size() - 1; i >= 0; i--) {
+ final TaskFragmentContainer container = containers.get(i);
+ // Wait until onTaskFragmentAppeared to update new container.
+ if (!container.isFinished() && !container.isWaitingActivityAppear()) {
+ mPresenter.updateContainer(container);
}
- mPresenter.updateContainer(container);
- updateCallbackIfNecessary();
}
+ updateCallbackIfNecessary();
}
}
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
index f54ab08d8a8a..918e514f4c89 100644
--- a/libs/WindowManager/Jetpack/window-extensions-release.aar
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index 5cba3b4ec130..6ae0f9ba0485 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -55,6 +55,7 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.compatui.CompatUIController;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.startingsurface.StartingWindowController;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.unfold.UnfoldAnimationController;
@@ -72,6 +73,7 @@ import java.util.function.Consumer;
*/
public class ShellTaskOrganizer extends TaskOrganizer implements
CompatUIController.CompatUICallback {
+ private static final String TAG = "ShellTaskOrganizer";
// Intentionally using negative numbers here so the positive numbers can be used
// for task id specific listeners that will be added later.
@@ -90,8 +92,6 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
})
public @interface TaskListenerType {}
- private static final String TAG = "ShellTaskOrganizer";
-
/**
* Callbacks for when the tasks change in the system.
*/
@@ -177,6 +177,9 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
@Nullable
private final CompatUIController mCompatUI;
+ @NonNull
+ private final ShellCommandHandler mShellCommandHandler;
+
@Nullable
private final Optional<RecentTasksController> mRecentTasks;
@@ -187,29 +190,33 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
private RunningTaskInfo mLastFocusedTaskInfo;
public ShellTaskOrganizer(ShellExecutor mainExecutor) {
- this(null /* shellInit */, null /* taskOrganizerController */, null /* compatUI */,
+ this(null /* shellInit */, null /* shellCommandHandler */,
+ null /* taskOrganizerController */, null /* compatUI */,
Optional.empty() /* unfoldAnimationController */,
Optional.empty() /* recentTasksController */,
mainExecutor);
}
public ShellTaskOrganizer(ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
@Nullable CompatUIController compatUI,
Optional<UnfoldAnimationController> unfoldAnimationController,
Optional<RecentTasksController> recentTasks,
ShellExecutor mainExecutor) {
- this(shellInit, null /* taskOrganizerController */, compatUI,
+ this(shellInit, shellCommandHandler, null /* taskOrganizerController */, compatUI,
unfoldAnimationController, recentTasks, mainExecutor);
}
@VisibleForTesting
protected ShellTaskOrganizer(ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
ITaskOrganizerController taskOrganizerController,
@Nullable CompatUIController compatUI,
Optional<UnfoldAnimationController> unfoldAnimationController,
Optional<RecentTasksController> recentTasks,
ShellExecutor mainExecutor) {
super(taskOrganizerController, mainExecutor);
+ mShellCommandHandler = shellCommandHandler;
mCompatUI = compatUI;
mRecentTasks = recentTasks;
mUnfoldAnimationController = unfoldAnimationController.orElse(null);
@@ -219,6 +226,7 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
}
private void onInit() {
+ mShellCommandHandler.addDumpCallback(this::dump, this);
if (mCompatUI != null) {
mCompatUI.setCompatUICallback(this);
}
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 d3dc7e8c70a3..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
@@ -57,6 +57,7 @@ import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.annotations.ShellBackgroundThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
+import com.android.wm.shell.sysui.ShellInit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -91,13 +92,14 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
* Raw delta between {@link #mInitTouchLocation} and the last touch location.
*/
private final Point mTouchEventDelta = new Point();
- private final ShellExecutor mShellExecutor;
/** True when a back gesture is ongoing */
private boolean mBackGestureStarted = false;
/** 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;
@@ -106,6 +108,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
private final SurfaceControl.Transaction mTransaction;
private final IActivityTaskManager mActivityTaskManager;
private final Context mContext;
+ private final ContentResolver mContentResolver;
+ private final ShellExecutor mShellExecutor;
+ private final Handler mBgHandler;
@Nullable
private IOnBackInvokedCallback mBackToLauncherCallback;
private float mTriggerThreshold;
@@ -136,16 +141,19 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
};
public BackAnimationController(
+ @NonNull ShellInit shellInit,
@NonNull @ShellMainThread ShellExecutor shellExecutor,
@NonNull @ShellBackgroundThread Handler backgroundHandler,
Context context) {
- this(shellExecutor, backgroundHandler, new SurfaceControl.Transaction(),
+ this(shellInit, shellExecutor, backgroundHandler, new SurfaceControl.Transaction(),
ActivityTaskManager.getService(), context, context.getContentResolver());
}
@VisibleForTesting
- BackAnimationController(@NonNull @ShellMainThread ShellExecutor shellExecutor,
- @NonNull @ShellBackgroundThread Handler handler,
+ BackAnimationController(
+ @NonNull ShellInit shellInit,
+ @NonNull @ShellMainThread ShellExecutor shellExecutor,
+ @NonNull @ShellBackgroundThread Handler bgHandler,
@NonNull SurfaceControl.Transaction transaction,
@NonNull IActivityTaskManager activityTaskManager,
Context context, ContentResolver contentResolver) {
@@ -153,7 +161,13 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mTransaction = transaction;
mActivityTaskManager = activityTaskManager;
mContext = context;
- setupAnimationDeveloperSettingsObserver(contentResolver, handler);
+ mContentResolver = contentResolver;
+ mBgHandler = bgHandler;
+ shellInit.addInitCallback(this::onInit, this);
+ }
+
+ private void onInit() {
+ setupAnimationDeveloperSettingsObserver(mContentResolver, mBgHandler);
}
private void setupAnimationDeveloperSettingsObserver(
@@ -289,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) {
@@ -428,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.
@@ -543,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/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 31fc6a5be589..2c02006c8ca5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -452,6 +452,7 @@ public class Bubble implements BubbleViewProvider {
*/
void setEntry(@NonNull final BubbleEntry entry) {
Objects.requireNonNull(entry);
+ boolean showingDotPreviously = showDot();
mLastUpdated = entry.getStatusBarNotification().getPostTime();
mIsBubble = entry.getStatusBarNotification().getNotification().isBubbleNotification();
mPackageName = entry.getStatusBarNotification().getPackageName();
@@ -498,6 +499,10 @@ public class Bubble implements BubbleViewProvider {
mShouldSuppressNotificationDot = entry.shouldSuppressNotificationDot();
mShouldSuppressNotificationList = entry.shouldSuppressNotificationList();
mShouldSuppressPeek = entry.shouldSuppressPeek();
+ if (showingDotPreviously != showDot()) {
+ // This will update the UI if needed
+ setShowDot(showDot());
+ }
}
@Nullable
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java
index 4eeb20769e09..d6803e8052c6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java
@@ -19,14 +19,14 @@ package com.android.wm.shell.bubbles;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
-import android.graphics.Paint;
+import android.graphics.Color;
import android.graphics.Path;
+import android.graphics.Rect;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.Drawable;
import com.android.launcher3.icons.BaseIconFactory;
import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.icons.ShadowGenerator;
import com.android.wm.shell.R;
/**
@@ -44,78 +44,77 @@ public class BubbleBadgeIconFactory extends BaseIconFactory {
* will include the workprofile indicator on the badge if appropriate.
*/
BitmapInfo getBadgeBitmap(Drawable userBadgedAppIcon, boolean isImportantConversation) {
- ShadowGenerator shadowGenerator = new ShadowGenerator(mIconBitmapSize);
- Bitmap userBadgedBitmap = createIconBitmap(userBadgedAppIcon, 1f, mIconBitmapSize);
-
if (userBadgedAppIcon instanceof AdaptiveIconDrawable) {
- userBadgedBitmap = Bitmap.createScaledBitmap(
- getCircleBitmap((AdaptiveIconDrawable) userBadgedAppIcon, /* size */
- userBadgedAppIcon.getIntrinsicWidth()),
- mIconBitmapSize, mIconBitmapSize, /* filter */ true);
+ AdaptiveIconDrawable ad = (AdaptiveIconDrawable) userBadgedAppIcon;
+ userBadgedAppIcon = new CircularAdaptiveIcon(ad.getBackground(), ad.getForeground());
}
-
if (isImportantConversation) {
- final float ringStrokeWidth = mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.importance_ring_stroke_width);
- final int importantConversationColor = mContext.getResources().getColor(
+ userBadgedAppIcon = new CircularRingDrawable(userBadgedAppIcon);
+ }
+ Bitmap userBadgedBitmap = createIconBitmap(
+ userBadgedAppIcon, 1, BITMAP_GENERATION_MODE_WITH_SHADOW);
+ return createIconBitmap(userBadgedBitmap);
+ }
+
+ private class CircularRingDrawable extends CircularAdaptiveIcon {
+
+ final int mImportantConversationColor;
+ final Rect mTempBounds = new Rect();
+
+ final Drawable mDr;
+
+ CircularRingDrawable(Drawable dr) {
+ super(null, null);
+ mDr = dr;
+ mImportantConversationColor = mContext.getResources().getColor(
R.color.important_conversation, null);
- Bitmap badgeAndRing = Bitmap.createBitmap(userBadgedBitmap.getWidth(),
- userBadgedBitmap.getHeight(), userBadgedBitmap.getConfig());
- Canvas c = new Canvas(badgeAndRing);
-
- Paint ringPaint = new Paint();
- ringPaint.setStyle(Paint.Style.FILL);
- ringPaint.setColor(importantConversationColor);
- ringPaint.setAntiAlias(true);
- c.drawCircle(c.getWidth() / 2, c.getHeight() / 2, c.getWidth() / 2, ringPaint);
-
- final int bitmapTop = (int) ringStrokeWidth;
- final int bitmapLeft = (int) ringStrokeWidth;
- final int bitmapWidth = c.getWidth() - 2 * (int) ringStrokeWidth;
- final int bitmapHeight = c.getHeight() - 2 * (int) ringStrokeWidth;
-
- Bitmap scaledBitmap = Bitmap.createScaledBitmap(userBadgedBitmap, bitmapWidth,
- bitmapHeight, /* filter */ true);
- c.drawBitmap(scaledBitmap, bitmapTop, bitmapLeft, /* paint */null);
-
- shadowGenerator.recreateIcon(Bitmap.createBitmap(badgeAndRing), c);
- return createIconBitmap(badgeAndRing);
- } else {
- Canvas c = new Canvas();
- c.setBitmap(userBadgedBitmap);
- shadowGenerator.recreateIcon(Bitmap.createBitmap(userBadgedBitmap), c);
- return createIconBitmap(userBadgedBitmap);
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ int save = canvas.save();
+ canvas.clipPath(getIconMask());
+ canvas.drawColor(mImportantConversationColor);
+ int ringStrokeWidth = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.importance_ring_stroke_width);
+ mTempBounds.set(getBounds());
+ mTempBounds.inset(ringStrokeWidth, ringStrokeWidth);
+ mDr.setBounds(mTempBounds);
+ mDr.draw(canvas);
+ canvas.restoreToCount(save);
}
}
- private Bitmap getCircleBitmap(AdaptiveIconDrawable icon, int size) {
- Drawable foreground = icon.getForeground();
- Drawable background = icon.getBackground();
- Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas();
- canvas.setBitmap(bitmap);
-
- // Clip canvas to circle.
- Path circlePath = new Path();
- circlePath.addCircle(/* x */ size / 2f,
- /* y */ size / 2f,
- /* radius */ size / 2f,
- Path.Direction.CW);
- canvas.clipPath(circlePath);
-
- // Draw background.
- background.setBounds(0, 0, size, size);
- background.draw(canvas);
-
- // Draw foreground. The foreground and background drawables are derived from adaptive icons
- // Some icon shapes fill more space than others, so adaptive icons are normalized to about
- // the same size. This size is smaller than the original bounds, so we estimate
- // the difference in this offset.
- int offset = size / 5;
- foreground.setBounds(-offset, -offset, size + offset, size + offset);
- foreground.draw(canvas);
-
- canvas.setBitmap(null);
- return bitmap;
+ private static class CircularAdaptiveIcon extends AdaptiveIconDrawable {
+
+ final Path mPath = new Path();
+
+ CircularAdaptiveIcon(Drawable bg, Drawable fg) {
+ super(bg, fg);
+ }
+
+ @Override
+ public Path getIconMask() {
+ mPath.reset();
+ Rect bounds = getBounds();
+ mPath.addOval(bounds.left, bounds.top, bounds.right, bounds.bottom, Path.Direction.CW);
+ return mPath;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ int save = canvas.save();
+ canvas.clipPath(getIconMask());
+
+ canvas.drawColor(Color.BLACK);
+ Drawable d;
+ if ((d = getBackground()) != null) {
+ d.draw(canvas);
+ }
+ if ((d = getForeground()) != null) {
+ d.draw(canvas);
+ }
+ canvas.restoreToCount(save);
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 398261600dc7..8771ceb71d98 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -1055,18 +1055,28 @@ public class BubbleController implements ConfigurationChangeListener {
public void updateBubble(BubbleEntry notif, boolean suppressFlyout, boolean showInShade) {
// If this is an interruptive notif, mark that it's interrupted
mSysuiProxy.setNotificationInterruption(notif.getKey());
- if (!notif.getRanking().isTextChanged()
+ boolean isNonInterruptiveNotExpanding = !notif.getRanking().isTextChanged()
&& (notif.getBubbleMetadata() != null
- && !notif.getBubbleMetadata().getAutoExpandBubble())
+ && !notif.getBubbleMetadata().getAutoExpandBubble());
+ if (isNonInterruptiveNotExpanding
&& mBubbleData.hasOverflowBubbleWithKey(notif.getKey())) {
// Update the bubble but don't promote it out of overflow
Bubble b = mBubbleData.getOverflowBubbleWithKey(notif.getKey());
- b.setEntry(notif);
+ if (notif.isBubble()) {
+ notif.setFlagBubble(false);
+ }
+ updateNotNotifyingEntry(b, notif, showInShade);
+ } else if (mBubbleData.hasAnyBubbleWithKey(notif.getKey())
+ && isNonInterruptiveNotExpanding) {
+ Bubble b = mBubbleData.getAnyBubbleWithkey(notif.getKey());
+ if (b != null) {
+ updateNotNotifyingEntry(b, notif, showInShade);
+ }
} else if (mBubbleData.isSuppressedWithLocusId(notif.getLocusId())) {
// Update the bubble but don't promote it out of overflow
Bubble b = mBubbleData.getSuppressedBubbleWithKey(notif.getKey());
if (b != null) {
- b.setEntry(notif);
+ updateNotNotifyingEntry(b, notif, showInShade);
}
} else {
Bubble bubble = mBubbleData.getOrCreateBubble(notif, null /* persistedBubble */);
@@ -1076,13 +1086,25 @@ public class BubbleController implements ConfigurationChangeListener {
if (bubble.shouldAutoExpand()) {
bubble.setShouldAutoExpand(false);
}
+ mImpl.mCachedState.updateBubbleSuppressedState(bubble);
} else {
inflateAndAdd(bubble, suppressFlyout, showInShade);
}
}
}
- void inflateAndAdd(Bubble bubble, boolean suppressFlyout, boolean showInShade) {
+ void updateNotNotifyingEntry(Bubble b, BubbleEntry entry, boolean showInShade) {
+ boolean isBubbleSelected = Objects.equals(b, mBubbleData.getSelectedBubble());
+ boolean isBubbleExpandedAndSelected = isStackExpanded() && isBubbleSelected;
+ b.setEntry(entry);
+ boolean suppress = isBubbleExpandedAndSelected || !showInShade || !b.showInShade();
+ b.setSuppressNotification(suppress);
+ b.setShowDot(!isBubbleExpandedAndSelected);
+ mImpl.mCachedState.updateBubbleSuppressedState(b);
+ }
+
+ @VisibleForTesting
+ public void inflateAndAdd(Bubble bubble, boolean suppressFlyout, boolean showInShade) {
// Lazy init stack view when a bubble is created
ensureStackViewCreated();
bubble.setInflateSynchronously(mInflateSynchronously);
@@ -1111,7 +1133,10 @@ public class BubbleController implements ConfigurationChangeListener {
}
@VisibleForTesting
- public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp) {
+ public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp, boolean fromSystem) {
+ if (!fromSystem) {
+ return;
+ }
// shouldBubbleUp checks canBubble & for bubble metadata
boolean shouldBubble = shouldBubbleUp && canLaunchInTaskView(mContext, entry);
if (!shouldBubble && mBubbleData.hasAnyBubbleWithKey(entry.getKey())) {
@@ -1172,9 +1197,9 @@ public class BubbleController implements ConfigurationChangeListener {
// notification, so that the bubble will be re-created if shouldBubbleUp returns
// true.
mBubbleData.dismissBubbleWithKey(key, DISMISS_NO_BUBBLE_UP);
- } else if (entry != null && mTmpRanking.isBubble() && !isActive) {
+ } else if (entry != null && mTmpRanking.isBubble() && !isActiveOrInOverflow) {
entry.setFlagBubble(true);
- onEntryUpdated(entry, shouldBubbleUp);
+ onEntryUpdated(entry, shouldBubbleUp, /* fromSystem= */ true);
}
}
}
@@ -1773,9 +1798,9 @@ public class BubbleController implements ConfigurationChangeListener {
}
@Override
- public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp) {
+ public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp, boolean fromSystem) {
mMainExecutor.execute(() -> {
- BubbleController.this.onEntryUpdated(entry, shouldBubbleUp);
+ BubbleController.this.onEntryUpdated(entry, shouldBubbleUp, fromSystem);
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java
index 9d3bf34895d3..5dab8a071f76 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java
@@ -21,6 +21,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
+import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
@@ -65,4 +66,19 @@ public class BubbleIconFactory extends BaseIconFactory {
return null;
}
}
+
+ /**
+ * Creates the bitmap for the provided drawable and returns the scale used for
+ * drawing the actual drawable.
+ */
+ public Bitmap createIconBitmap(@NonNull Drawable icon, float[] outScale) {
+ if (outScale == null) {
+ outScale = new float[1];
+ }
+ icon = normalizeAndWrapToAdaptiveIcon(icon,
+ true /* shrinkNonAdaptiveIcons */,
+ null /* outscale */,
+ outScale);
+ return createIconBitmap(icon, outScale[0], BITMAP_GENERATION_MODE_WITH_SHADOW);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
index 69762c9bc06a..f437553337ef 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
@@ -195,15 +195,18 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
b.isImportantConversation());
info.badgeBitmap = badgeBitmapInfo.icon;
// Raw badge bitmap never includes the important conversation ring
- info.mRawBadgeBitmap = badgeIconFactory.getBadgeBitmap(badgedIcon, false).icon;
- info.bubbleBitmap = iconFactory.createBadgedIconBitmap(bubbleDrawable).icon;
+ info.mRawBadgeBitmap = b.isImportantConversation()
+ ? badgeIconFactory.getBadgeBitmap(badgedIcon, false).icon
+ : badgeBitmapInfo.icon;
+
+ float[] bubbleBitmapScale = new float[1];
+ info.bubbleBitmap = iconFactory.createIconBitmap(bubbleDrawable, bubbleBitmapScale);
// Dot color & placement
Path iconPath = PathParser.createPathFromPathData(
c.getResources().getString(com.android.internal.R.string.config_icon_mask));
Matrix matrix = new Matrix();
- float scale = iconFactory.getNormalizer().getScale(bubbleDrawable,
- null /* outBounds */, null /* path */, null /* outMaskShape */);
+ float scale = bubbleBitmapScale[0];
float radius = DEFAULT_PATH_SIZE / 2f;
matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
radius /* pivot y */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index cf792cda91b5..37b96ffe5cd1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -171,8 +171,9 @@ public interface Bubbles {
*
* @param entry the {@link BubbleEntry} by the notification.
* @param shouldBubbleUp {@code true} if this notification should bubble up.
+ * @param fromSystem {@code true} if this update is from NotificationManagerService.
*/
- void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp);
+ void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp, boolean fromSystem);
/**
* Called when new notification entry removed.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 40cf9a32d7a5..85c8ebf454c9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -1130,16 +1130,16 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
boolean adjusted = false;
if (mYOffsetForIme != 0) {
if (dividerLeash != null) {
- mTempRect.set(mDividerBounds);
+ getRefDividerBounds(mTempRect);
mTempRect.offset(0, mYOffsetForIme);
t.setPosition(dividerLeash, mTempRect.left, mTempRect.top);
}
- mTempRect.set(mBounds1);
+ getRefBounds1(mTempRect);
mTempRect.offset(0, mYOffsetForIme);
t.setPosition(leash1, mTempRect.left, mTempRect.top);
- mTempRect.set(mBounds2);
+ getRefBounds2(mTempRect);
mTempRect.offset(0, mYOffsetForIme);
t.setPosition(leash2, mTempRect.left, mTempRect.top);
adjusted = true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index db8d9d423aca..235fd9c469ea 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -43,6 +43,7 @@ import com.android.wm.shell.compatui.CompatUIWindowManager.CompatUIHintsState;
import com.android.wm.shell.compatui.letterboxedu.LetterboxEduWindowManager;
import com.android.wm.shell.sysui.KeyguardChangeListener;
import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import java.lang.ref.WeakReference;
@@ -119,6 +120,7 @@ public class CompatUIController implements OnDisplaysChangedListener,
private boolean mKeyguardShowing;
public CompatUIController(Context context,
+ ShellInit shellInit,
ShellController shellController,
DisplayController displayController,
DisplayInsetsController displayInsetsController,
@@ -134,10 +136,14 @@ public class CompatUIController implements OnDisplaysChangedListener,
mSyncQueue = syncQueue;
mMainExecutor = mainExecutor;
mTransitionsLazy = transitionsLazy;
+ mCompatUIHintsState = new CompatUIHintsState();
+ shellInit.addInitCallback(this::onInit, this);
+ }
+
+ private void onInit() {
+ mShellController.addKeyguardChangeListener(this);
mDisplayController.addDisplayWindowListener(this);
mImeController.addPositionProcessor(this);
- mCompatUIHintsState = new CompatUIHintsState();
- shellController.addKeyguardChangeListener(this);
}
/** Sets the callback for UI interactions. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
index 81904e291ad1..e22c9517f4ab 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
@@ -49,6 +49,7 @@ import com.android.wm.shell.pip.tv.TvPipTaskOrganizer;
import com.android.wm.shell.pip.tv.TvPipTransition;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import java.util.Optional;
@@ -138,12 +139,14 @@ public abstract class TvPipModule {
@WMSingleton
@Provides
static PipTransitionController provideTvPipTransition(
- Transitions transitions, ShellTaskOrganizer shellTaskOrganizer,
+ ShellInit shellInit,
+ ShellTaskOrganizer shellTaskOrganizer,
+ Transitions transitions,
PipAnimationController pipAnimationController,
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
TvPipBoundsState tvPipBoundsState, TvPipMenuController pipMenuController) {
- return new TvPipTransition(tvPipBoundsState, pipMenuController,
- tvPipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer);
+ return new TvPipTransition(shellInit, shellTaskOrganizer, transitions, tvPipBoundsState,
+ pipMenuController, tvPipBoundsAlgorithm, pipAnimationController);
}
@WMSingleton
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 3add4179ef82..bbaf51f7c54c 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
@@ -174,13 +174,14 @@ public abstract class WMShellBaseModule {
@Provides
static ShellTaskOrganizer provideShellTaskOrganizer(
ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
CompatUIController compatUI,
Optional<UnfoldAnimationController> unfoldAnimationController,
Optional<RecentTasksController> recentTasksOptional,
@ShellMainThread ShellExecutor mainExecutor
) {
- return new ShellTaskOrganizer(shellInit, compatUI, unfoldAnimationController,
- recentTasksOptional, mainExecutor);
+ return new ShellTaskOrganizer(shellInit, shellCommandHandler, compatUI,
+ unfoldAnimationController, recentTasksOptional, mainExecutor);
}
@WMSingleton
@@ -188,6 +189,7 @@ public abstract class WMShellBaseModule {
static KidsModeTaskOrganizer provideKidsModeTaskOrganizer(
Context context,
ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
SyncTransactionQueue syncTransactionQueue,
DisplayController displayController,
DisplayInsetsController displayInsetsController,
@@ -196,19 +198,20 @@ public abstract class WMShellBaseModule {
@ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler
) {
- return new KidsModeTaskOrganizer(context, shellInit, syncTransactionQueue,
- displayController, displayInsetsController, unfoldAnimationController,
- recentTasksOptional, mainExecutor, mainHandler);
+ return new KidsModeTaskOrganizer(context, shellInit, shellCommandHandler,
+ syncTransactionQueue, displayController, displayInsetsController,
+ unfoldAnimationController, recentTasksOptional, mainExecutor, mainHandler);
}
@WMSingleton
@Provides
static CompatUIController provideCompatUIController(Context context,
+ ShellInit shellInit,
ShellController shellController,
DisplayController displayController, DisplayInsetsController displayInsetsController,
DisplayImeController imeController, SyncTransactionQueue syncQueue,
@ShellMainThread ShellExecutor mainExecutor, Lazy<Transitions> transitionsLazy) {
- return new CompatUIController(context, shellController, displayController,
+ return new CompatUIController(context, shellInit, shellController, displayController,
displayInsetsController, imeController, syncQueue, mainExecutor, transitionsLazy);
}
@@ -268,12 +271,14 @@ public abstract class WMShellBaseModule {
@Provides
static Optional<BackAnimationController> provideBackAnimationController(
Context context,
+ ShellInit shellInit,
@ShellMainThread ShellExecutor shellExecutor,
@ShellBackgroundThread Handler backgroundHandler
) {
if (BackAnimationController.IS_ENABLED) {
return Optional.of(
- new BackAnimationController(shellExecutor, backgroundHandler, context));
+ new BackAnimationController(shellInit, shellExecutor, backgroundHandler,
+ context));
}
return Optional.empty();
}
@@ -384,11 +389,14 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
static Optional<HideDisplayCutoutController> provideHideDisplayCutoutController(Context context,
- ShellController shellController, DisplayController displayController,
+ ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
+ ShellController shellController,
+ DisplayController displayController,
@ShellMainThread ShellExecutor mainExecutor) {
return Optional.ofNullable(
- HideDisplayCutoutController.create(context, shellController, displayController,
- mainExecutor));
+ HideDisplayCutoutController.create(context, shellInit, shellCommandHandler,
+ shellController, displayController, mainExecutor));
}
//
@@ -466,12 +474,13 @@ public abstract class WMShellBaseModule {
static Optional<RecentTasksController> provideRecentTasksController(
Context context,
ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
TaskStackListenerImpl taskStackListener,
@ShellMainThread ShellExecutor mainExecutor
) {
return Optional.ofNullable(
- RecentTasksController.create(context, shellInit, taskStackListener,
- mainExecutor));
+ RecentTasksController.create(context, shellInit, shellCommandHandler,
+ taskStackListener, mainExecutor));
}
//
@@ -649,8 +658,9 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
static ShellController provideShellController(ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
@ShellMainThread ShellExecutor mainExecutor) {
- return new ShellController(shellInit, mainExecutor);
+ return new ShellController(shellInit, shellCommandHandler, mainExecutor);
}
//
@@ -676,12 +686,15 @@ public abstract class WMShellBaseModule {
KidsModeTaskOrganizer kidsModeTaskOrganizer,
Optional<BubbleController> bubblesOptional,
Optional<SplitScreenController> splitScreenOptional,
+ Optional<Pip> pipOptional,
Optional<PipTouchHandler> pipTouchHandlerOptional,
FullscreenTaskListener fullscreenTaskListener,
Optional<UnfoldAnimationController> unfoldAnimationController,
Optional<UnfoldTransitionHandler> unfoldTransitionHandler,
Optional<FreeformComponents> freeformComponents,
Optional<RecentTasksController> recentTasksOptional,
+ Optional<OneHandedController> oneHandedControllerOptional,
+ Optional<HideDisplayCutoutController> hideDisplayCutoutControllerOptional,
ActivityEmbeddingController activityEmbeddingOptional,
Transitions transitions,
StartingWindowController startingWindow,
@@ -697,18 +710,7 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
- static ShellCommandHandler provideShellCommandHandlerImpl(
- ShellController shellController,
- ShellTaskOrganizer shellTaskOrganizer,
- KidsModeTaskOrganizer kidsModeTaskOrganizer,
- Optional<SplitScreenController> splitScreenOptional,
- Optional<Pip> pipOptional,
- Optional<OneHandedController> oneHandedOptional,
- Optional<HideDisplayCutoutController> hideDisplayCutout,
- Optional<RecentTasksController> recentTasksOptional,
- @ShellMainThread ShellExecutor mainExecutor) {
- return new ShellCommandHandler(shellController, shellTaskOrganizer,
- kidsModeTaskOrganizer, splitScreenOptional, pipOptional, oneHandedOptional,
- hideDisplayCutout, recentTasksOptional, mainExecutor);
+ static ShellCommandHandler provideShellCommandHandler() {
+ return new ShellCommandHandler();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index e2bf7678f4b4..2ca9c3be8a69 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -74,6 +74,7 @@ import com.android.wm.shell.pip.phone.PipMotionHelper;
import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.SplitscreenPipMixedHandler;
@@ -248,14 +249,20 @@ public abstract class WMShellModule {
@Provides
@DynamicOverride
static OneHandedController provideOneHandedController(Context context,
+ ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
ShellController shellController,
- WindowManager windowManager, DisplayController displayController,
- DisplayLayout displayLayout, TaskStackListenerImpl taskStackListener,
- UiEventLogger uiEventLogger, InteractionJankMonitor jankMonitor,
- @ShellMainThread ShellExecutor mainExecutor, @ShellMainThread Handler mainHandler) {
- return OneHandedController.create(context, shellController, windowManager,
- displayController, displayLayout, taskStackListener, jankMonitor, uiEventLogger,
- mainExecutor, mainHandler);
+ WindowManager windowManager,
+ DisplayController displayController,
+ DisplayLayout displayLayout,
+ TaskStackListenerImpl taskStackListener,
+ UiEventLogger uiEventLogger,
+ InteractionJankMonitor jankMonitor,
+ @ShellMainThread ShellExecutor mainExecutor,
+ @ShellMainThread Handler mainHandler) {
+ return OneHandedController.create(context, shellInit, shellCommandHandler, shellController,
+ windowManager, displayController, displayLayout, taskStackListener, jankMonitor,
+ uiEventLogger, mainExecutor, mainHandler);
}
//
@@ -268,6 +275,7 @@ public abstract class WMShellModule {
static SplitScreenController provideSplitScreenController(
Context context,
ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
ShellController shellController,
ShellTaskOrganizer shellTaskOrganizer,
SyncTransactionQueue syncQueue,
@@ -281,10 +289,10 @@ public abstract class WMShellModule {
IconProvider iconProvider,
Optional<RecentTasksController> recentTasks,
@ShellMainThread ShellExecutor mainExecutor) {
- return new SplitScreenController(context, shellInit, shellController, shellTaskOrganizer,
- syncQueue, rootTaskDisplayAreaOrganizer, displayController, displayImeController,
- displayInsetsController, dragAndDropController, transitions, transactionPool,
- iconProvider, recentTasks, mainExecutor);
+ return new SplitScreenController(context, shellInit, shellCommandHandler, shellController,
+ shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer, displayController,
+ displayImeController, displayInsetsController, dragAndDropController, transitions,
+ transactionPool, iconProvider, recentTasks, mainExecutor);
}
//
@@ -294,24 +302,33 @@ public abstract class WMShellModule {
@WMSingleton
@Provides
static Optional<Pip> providePip(Context context,
- ShellController shellController, DisplayController displayController,
- PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm,
- PipKeepClearAlgorithm pipKeepClearAlgorithm, PipBoundsState pipBoundsState,
- PipMotionHelper pipMotionHelper, PipMediaController pipMediaController,
- PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer,
+ ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
+ ShellController shellController,
+ DisplayController displayController,
+ PipAppOpsListener pipAppOpsListener,
+ PipBoundsAlgorithm pipBoundsAlgorithm,
+ PipKeepClearAlgorithm pipKeepClearAlgorithm,
+ PipBoundsState pipBoundsState,
+ PipMotionHelper pipMotionHelper,
+ PipMediaController pipMediaController,
+ PhonePipMenuController phonePipMenuController,
+ PipTaskOrganizer pipTaskOrganizer,
PipTransitionState pipTransitionState,
- PipTouchHandler pipTouchHandler, PipTransitionController pipTransitionController,
+ PipTouchHandler pipTouchHandler,
+ PipTransitionController pipTransitionController,
WindowManagerShellWrapper windowManagerShellWrapper,
TaskStackListenerImpl taskStackListener,
PipParamsChangedForwarder pipParamsChangedForwarder,
Optional<OneHandedController> oneHandedController,
@ShellMainThread ShellExecutor mainExecutor) {
- return Optional.ofNullable(PipController.create(context, shellController, displayController,
- pipAppOpsListener, pipBoundsAlgorithm, pipKeepClearAlgorithm, pipBoundsState,
- pipMotionHelper,
- pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTransitionState,
- pipTouchHandler, pipTransitionController, windowManagerShellWrapper,
- taskStackListener, pipParamsChangedForwarder, oneHandedController, mainExecutor));
+ return Optional.ofNullable(PipController.create(
+ context, shellInit, shellCommandHandler, shellController,
+ displayController, pipAppOpsListener, pipBoundsAlgorithm, pipKeepClearAlgorithm,
+ pipBoundsState, pipMotionHelper, pipMediaController, phonePipMenuController,
+ pipTaskOrganizer, pipTransitionState, pipTouchHandler, pipTransitionController,
+ windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
+ oneHandedController, mainExecutor));
}
@WMSingleton
@@ -409,15 +426,15 @@ public abstract class WMShellModule {
@WMSingleton
@Provides
static PipTransitionController providePipTransitionController(Context context,
- Transitions transitions, ShellTaskOrganizer shellTaskOrganizer,
+ ShellInit shellInit, ShellTaskOrganizer shellTaskOrganizer, Transitions transitions,
PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm,
PipBoundsState pipBoundsState, PipTransitionState pipTransitionState,
PhonePipMenuController pipMenuController,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
Optional<SplitScreenController> splitScreenOptional) {
- return new PipTransition(context, pipBoundsState, pipTransitionState, pipMenuController,
- pipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer,
- pipSurfaceTransactionHelper, splitScreenOptional);
+ return new PipTransition(context, shellInit, shellTaskOrganizer, transitions,
+ pipBoundsState, pipTransitionState, pipMenuController, pipBoundsAlgorithm,
+ pipAnimationController, pipSurfaceTransactionHelper, splitScreenOptional);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
index 52f0c4222b64..99922fbc2d95 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
@@ -59,9 +59,9 @@ WMShell SysUI service:
adb shell dumpsys activity service SystemUIService WMShell
```
-If information should be added to the dump, make updates to:
-- `WMShell` if you are dumping SysUI state
-- `ShellCommandHandler` if you are dumping Shell state
+If information should be added to the dump, either:
+- Update `WMShell` if you are dumping SysUI state
+- Inject `ShellCommandHandler` into your Shell class, and add a dump callback
## Debugging in Android Studio
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/sysui.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/sysui.md
index 0dd50b1bee68..d6302e640ba7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/sysui.md
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/sysui.md
@@ -32,7 +32,7 @@ interfaces provided by the Shell and the rest of SystemUI.
More detail can be found in [go/wm-sysui-dagger](http://go/wm-sysui-dagger).
-## Interfaces to Shell components
+## Interfaces from SysUI to Shell components
Within the same process, the WM Shell components can be running on a different thread than the main
SysUI thread (disabled on certain products). This introduces challenges where we have to be
@@ -54,12 +54,30 @@ For example, you might have:
Adding an interface to a Shell component may seem like a lot of boiler plate, but is currently
necessary to maintain proper threading and logic isolation.
-## Configuration changes & other SysUI events
+## Listening for Configuration changes & other SysUI events
-Aside from direct calls into Shell controllers for exposed features, the Shell also receives
+Aside from direct calls into Shell controllers for exposed features, the Shell also receives
common event callbacks from SysUI via the `ShellController`. This includes things like:
- Configuration changes
-- TODO: Shell init
-- TODO: Shell command
-- TODO: Keyguard events \ No newline at end of file
+- Keyguard events
+- Shell init
+- Shell dumps & commands
+
+For other events which are specific to the Shell feature, then you can add callback methods on
+the Shell feature interface. Any such calls should <u>**never**</u> be synchronous calls as
+they will need to post to the Shell main thread to run.
+
+## Shell commands & Dumps
+
+Since the Shell library is a part of the SysUI process, it relies on SysUI to trigger commands
+on individual Shell components, or to dump individual shell components.
+
+```shell
+# Dump everything
+adb shell dumpsys activity service SystemUIService WMShell
+
+# Run a specific command
+adb shell dumpsys activity service SystemUIService WMShell help
+adb shell dumpsys activity service SystemUIService WMShell <cmd> <args> ...
+``` \ No newline at end of file
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 20d77259406a..af205ed051d7 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
@@ -122,7 +122,6 @@ public class FreeformTaskTransitionHandler
break;
case WindowManager.TRANSIT_TO_BACK:
case WindowManager.TRANSIT_TO_FRONT:
- transitionHandled = true;
break;
}
}
@@ -147,7 +146,9 @@ public class FreeformTaskTransitionHandler
return false;
}
mFreeformTaskListener.createWindowDecoration(change, startT, finishT);
- return true;
+
+ // Intercepted transition to manage the window decorations. Let other handlers animate.
+ return false;
}
private boolean startCloseTransition(
@@ -164,7 +165,8 @@ public class FreeformTaskTransitionHandler
windowDecors.add(windowDecor);
}
- return true;
+ // Intercepted transition to manage the window decorations. Let other handlers animate.
+ return false;
}
private boolean startChangeTransition(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java
index 665b035bc41c..32125fa44148 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java
@@ -27,7 +27,9 @@ import androidx.annotation.VisibleForTesting;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.sysui.ConfigurationChangeListener;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
import java.io.PrintWriter;
@@ -38,6 +40,7 @@ public class HideDisplayCutoutController implements ConfigurationChangeListener
private static final String TAG = "HideDisplayCutoutController";
private final Context mContext;
+ private final ShellCommandHandler mShellCommandHandler;
private final ShellController mShellController;
private final HideDisplayCutoutOrganizer mOrganizer;
@VisibleForTesting
@@ -49,7 +52,10 @@ public class HideDisplayCutoutController implements ConfigurationChangeListener
*/
@Nullable
public static HideDisplayCutoutController create(Context context,
- ShellController shellController, DisplayController displayController,
+ ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
+ ShellController shellController,
+ DisplayController displayController,
ShellExecutor mainExecutor) {
// The SystemProperty is set for devices that support this feature and is used to control
// whether to create the HideDisplayCutout instance.
@@ -60,14 +66,24 @@ public class HideDisplayCutoutController implements ConfigurationChangeListener
HideDisplayCutoutOrganizer organizer =
new HideDisplayCutoutOrganizer(context, displayController, mainExecutor);
- return new HideDisplayCutoutController(context, shellController, organizer);
+ return new HideDisplayCutoutController(context, shellInit, shellCommandHandler,
+ shellController, organizer);
}
- HideDisplayCutoutController(Context context, ShellController shellController,
+ HideDisplayCutoutController(Context context,
+ ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
+ ShellController shellController,
HideDisplayCutoutOrganizer organizer) {
mContext = context;
+ mShellCommandHandler = shellCommandHandler;
mShellController = shellController;
mOrganizer = organizer;
+ shellInit.addInitCallback(this::onInit, this);
+ }
+
+ private void onInit() {
+ mShellCommandHandler.addDumpCallback(this::dump, this);
updateStatus();
mShellController.addConfigurationChangeListener(this);
}
@@ -96,11 +112,11 @@ public class HideDisplayCutoutController implements ConfigurationChangeListener
updateStatus();
}
- public void dump(@NonNull PrintWriter pw) {
- final String prefix = " ";
+ public void dump(@NonNull PrintWriter pw, String prefix) {
+ final String innerPrefix = " ";
pw.print(TAG);
pw.println(" states: ");
- pw.print(prefix);
+ pw.print(innerPrefix);
pw.print("mEnabled=");
pw.println(mEnabled);
mOrganizer.dump(pw);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
index 73b9b89e6993..2fdd12185551 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
@@ -50,6 +50,7 @@ import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.recents.RecentTasksController;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.unfold.UnfoldAnimationController;
@@ -73,6 +74,7 @@ public class KidsModeTaskOrganizer extends ShellTaskOrganizer {
private final Handler mMainHandler;
private final Context mContext;
+ private final ShellCommandHandler mShellCommandHandler;
private final SyncTransactionQueue mSyncQueue;
private final DisplayController mDisplayController;
private final DisplayInsetsController mDisplayInsetsController;
@@ -141,6 +143,8 @@ public class KidsModeTaskOrganizer extends ShellTaskOrganizer {
@VisibleForTesting
KidsModeTaskOrganizer(
Context context,
+ ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
ITaskOrganizerController taskOrganizerController,
SyncTransactionQueue syncTransactionQueue,
DisplayController displayController,
@@ -150,19 +154,23 @@ public class KidsModeTaskOrganizer extends ShellTaskOrganizer {
KidsModeSettingsObserver kidsModeSettingsObserver,
ShellExecutor mainExecutor,
Handler mainHandler) {
- super(/* shellInit= */ null, taskOrganizerController, /* compatUI= */ null,
- unfoldAnimationController, recentTasks, mainExecutor);
+ // Note: we don't call super with the shell init because we will be initializing manually
+ super(/* shellInit= */ null, /* shellCommandHandler= */ null, taskOrganizerController,
+ /* compatUI= */ null, unfoldAnimationController, recentTasks, mainExecutor);
mContext = context;
+ mShellCommandHandler = shellCommandHandler;
mMainHandler = mainHandler;
mSyncQueue = syncTransactionQueue;
mDisplayController = displayController;
mDisplayInsetsController = displayInsetsController;
mKidsModeSettingsObserver = kidsModeSettingsObserver;
+ shellInit.addInitCallback(this::onInit, this);
}
public KidsModeTaskOrganizer(
Context context,
ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
SyncTransactionQueue syncTransactionQueue,
DisplayController displayController,
DisplayInsetsController displayInsetsController,
@@ -171,9 +179,10 @@ public class KidsModeTaskOrganizer extends ShellTaskOrganizer {
ShellExecutor mainExecutor,
Handler mainHandler) {
// Note: we don't call super with the shell init because we will be initializing manually
- super(/* shellInit= */ null, /* compatUI= */ null, unfoldAnimationController, recentTasks,
- mainExecutor);
+ super(/* shellInit= */ null, /* taskOrganizerController= */ null, /* compatUI= */ null,
+ unfoldAnimationController, recentTasks, mainExecutor);
mContext = context;
+ mShellCommandHandler = shellCommandHandler;
mMainHandler = mainHandler;
mSyncQueue = syncTransactionQueue;
mDisplayController = displayController;
@@ -185,6 +194,9 @@ public class KidsModeTaskOrganizer extends ShellTaskOrganizer {
* Initializes kids mode status.
*/
public void onInit() {
+ if (mShellCommandHandler != null) {
+ mShellCommandHandler.addDumpCallback(this::dump, this);
+ }
if (mKidsModeSettingsObserver == null) {
mKidsModeSettingsObserver = new KidsModeSettingsObserver(mMainHandler, mContext);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index 24f02ac1a6cf..9149204b94ce 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -56,7 +56,9 @@ import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.sysui.ConfigurationChangeListener;
import com.android.wm.shell.sysui.KeyguardChangeListener;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
import java.io.PrintWriter;
@@ -85,6 +87,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
private Context mContext;
+ private final ShellCommandHandler mShellCommandHandler;
private final ShellController mShellController;
private final AccessibilityManager mAccessibilityManager;
private final DisplayController mDisplayController;
@@ -192,8 +195,9 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
/**
* Creates {@link OneHandedController}, returns {@code null} if the feature is not supported.
*/
- public static OneHandedController create(
- Context context, ShellController shellController, WindowManager windowManager,
+ public static OneHandedController create(Context context,
+ ShellInit shellInit, ShellCommandHandler shellCommandHandler,
+ ShellController shellController, WindowManager windowManager,
DisplayController displayController, DisplayLayout displayLayout,
TaskStackListenerImpl taskStackListener,
InteractionJankMonitor jankMonitor, UiEventLogger uiEventLogger,
@@ -213,14 +217,16 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
context, displayLayout, settingsUtil, animationController, tutorialHandler,
jankMonitor, mainExecutor);
OneHandedUiEventLogger oneHandedUiEventsLogger = new OneHandedUiEventLogger(uiEventLogger);
- return new OneHandedController(context, shellController, displayController, organizer,
- touchHandler, tutorialHandler, settingsUtil, accessibilityUtil, timeoutHandler,
- oneHandedState, oneHandedUiEventsLogger, taskStackListener,
- mainExecutor, mainHandler);
+ return new OneHandedController(context, shellInit, shellCommandHandler, shellController,
+ displayController, organizer, touchHandler, tutorialHandler, settingsUtil,
+ accessibilityUtil, timeoutHandler, oneHandedState, oneHandedUiEventsLogger,
+ taskStackListener, mainExecutor, mainHandler);
}
@VisibleForTesting
OneHandedController(Context context,
+ ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
ShellController shellController,
DisplayController displayController,
OneHandedDisplayAreaOrganizer displayAreaOrganizer,
@@ -235,6 +241,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
ShellExecutor mainExecutor,
Handler mainHandler) {
mContext = context;
+ mShellCommandHandler = shellCommandHandler;
mShellController = shellController;
mOneHandedSettingsUtil = settingsUtil;
mOneHandedAccessibilityUtil = oneHandedAccessibilityUtil;
@@ -247,8 +254,8 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
mMainHandler = mainHandler;
mOneHandedUiEventLogger = uiEventsLogger;
mTaskStackListener = taskStackListener;
+ mAccessibilityManager = AccessibilityManager.getInstance(mContext);
- mDisplayController.addDisplayWindowListener(mDisplaysChangedListener);
final float offsetPercentageConfig = context.getResources().getFraction(
R.fraction.config_one_handed_offset, 1, 1);
final int sysPropPercentageConfig = SystemProperties.getInt(
@@ -268,6 +275,12 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
getObserver(this::onSwipeToNotificationEnabledChanged);
mShortcutEnabledObserver = getObserver(this::onShortcutEnabledChanged);
+ shellInit.addInitCallback(this::onInit, this);
+ }
+
+ private void onInit() {
+ mShellCommandHandler.addDumpCallback(this::dump, this);
+ mDisplayController.addDisplayWindowListener(mDisplaysChangedListener);
mDisplayController.addDisplayChangingController(this);
setupCallback();
registerSettingObservers(mUserId);
@@ -275,7 +288,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
updateSettings();
updateDisplayLayout(mContext.getDisplayId());
- mAccessibilityManager = AccessibilityManager.getInstance(context);
mAccessibilityManager.addAccessibilityStateChangeListener(
mAccessibilityStateChangeListener);
@@ -623,7 +635,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
updateOneHandedEnabled();
}
- public void dump(@NonNull PrintWriter pw) {
+ public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = " ";
pw.println();
pw.println(TAG);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
index 38631ce26cd1..93172f82edd1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
@@ -20,7 +20,6 @@ import android.graphics.Rect;
import com.android.wm.shell.common.annotations.ExternalThread;
-import java.io.PrintWriter;
import java.util.function.Consumer;
/**
@@ -99,12 +98,4 @@ public interface Pip {
* view hierarchy or destroyed.
*/
default void removePipExclusionBoundsChangeListener(Consumer<Rect> listener) { }
-
- /**
- * Dump the current state and information if need.
- *
- * @param pw The stream to dump information to.
- */
- default void dump(PrintWriter pw) {
- }
}
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 cf2734c375f2..81e49f884503 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
@@ -183,7 +183,7 @@ public class PipAnimationController {
return mCurrentAnimator;
}
- PipTransitionAnimator getCurrentAnimator() {
+ public PipTransitionAnimator getCurrentAnimator() {
return mCurrentAnimator;
}
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 1155ea174ed1..f747b5e00759 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
@@ -127,7 +127,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
private final PipBoundsAlgorithm mPipBoundsAlgorithm;
private final @NonNull PipMenuController mPipMenuController;
private final PipAnimationController mPipAnimationController;
- private final PipTransitionController mPipTransitionController;
+ protected final PipTransitionController mPipTransitionController;
protected final PipParamsChangedForwarder mPipParamsChangedForwarder;
private final PipUiEventLogger mPipUiEventLoggerLogger;
private final int mEnterAnimationDuration;
@@ -196,6 +196,26 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
}
};
+ @VisibleForTesting
+ final PipTransitionController.PipTransitionCallback mPipTransitionCallback =
+ new PipTransitionController.PipTransitionCallback() {
+ @Override
+ public void onPipTransitionStarted(int direction, Rect pipBounds) {}
+
+ @Override
+ public void onPipTransitionFinished(int direction) {
+ // Apply the deferred RunningTaskInfo if applicable after all proper callbacks
+ // are sent.
+ if (direction == TRANSITION_DIRECTION_TO_PIP && mDeferredTaskInfo != null) {
+ onTaskInfoChanged(mDeferredTaskInfo);
+ mDeferredTaskInfo = null;
+ }
+ }
+
+ @Override
+ public void onPipTransitionCanceled(int direction) {}
+ };
+
private final PipAnimationController.PipTransactionHandler mPipTransactionHandler =
new PipAnimationController.PipTransactionHandler() {
@Override
@@ -216,7 +236,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
private ActivityManager.RunningTaskInfo mDeferredTaskInfo;
private WindowContainerToken mToken;
private SurfaceControl mLeash;
- private PipTransitionState mPipTransitionState;
+ protected PipTransitionState mPipTransitionState;
private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
private long mLastOneShotAlphaAnimationTime;
private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
@@ -296,6 +316,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mTaskOrganizer.addFocusListener(this);
mPipTransitionController.setPipOrganizer(this);
displayController.addDisplayWindowListener(this);
+ pipTransitionController.registerPipTransitionCallback(mPipTransitionCallback);
}
public PipTransitionController getTransitionController() {
@@ -773,11 +794,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mPipTransitionState.setTransitionState(PipTransitionState.ENTERED_PIP);
}
mPipTransitionController.sendOnPipTransitionFinished(direction);
- // Apply the deferred RunningTaskInfo if applicable after all proper callbacks are sent.
- if (direction == TRANSITION_DIRECTION_TO_PIP && mDeferredTaskInfo != null) {
- onTaskInfoChanged(mDeferredTaskInfo);
- mDeferredTaskInfo = null;
- }
}
private void sendOnPipTransitionCancelled(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 51be2a534dd7..33761d23379d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -66,6 +66,7 @@ import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.CounterRotatorHelper;
import com.android.wm.shell.transition.Transitions;
@@ -107,17 +108,18 @@ public class PipTransition extends PipTransitionController {
private boolean mHasFadeOut;
public PipTransition(Context context,
+ @NonNull ShellInit shellInit,
+ @NonNull ShellTaskOrganizer shellTaskOrganizer,
+ @NonNull Transitions transitions,
PipBoundsState pipBoundsState,
PipTransitionState pipTransitionState,
PipMenuController pipMenuController,
PipBoundsAlgorithm pipBoundsAlgorithm,
PipAnimationController pipAnimationController,
- Transitions transitions,
- @NonNull ShellTaskOrganizer shellTaskOrganizer,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
Optional<SplitScreenController> splitScreenOptional) {
- super(pipBoundsState, pipMenuController, pipBoundsAlgorithm,
- pipAnimationController, transitions, shellTaskOrganizer);
+ super(shellInit, shellTaskOrganizer, transitions, pipBoundsState, pipMenuController,
+ pipBoundsAlgorithm, pipAnimationController);
mContext = context;
mPipTransitionState = pipTransitionState;
mEnterExitAnimationDuration = context.getResources()
@@ -315,7 +317,8 @@ public class PipTransition extends PipTransitionController {
}
@Override
- public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) {
+ public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+ @Nullable SurfaceControl.Transaction finishT) {
if (transition != mExitTransition) {
return;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 90a2695bdf90..f51e247fe112 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -38,6 +38,7 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import java.util.ArrayList;
@@ -131,10 +132,13 @@ public abstract class PipTransitionController implements Transitions.TransitionH
public void onFixedRotationStarted() {
}
- public PipTransitionController(PipBoundsState pipBoundsState,
+ public PipTransitionController(
+ @NonNull ShellInit shellInit,
+ @NonNull ShellTaskOrganizer shellTaskOrganizer,
+ @NonNull Transitions transitions,
+ PipBoundsState pipBoundsState,
PipMenuController pipMenuController, PipBoundsAlgorithm pipBoundsAlgorithm,
- PipAnimationController pipAnimationController, Transitions transitions,
- @android.annotation.NonNull ShellTaskOrganizer shellTaskOrganizer) {
+ PipAnimationController pipAnimationController) {
mPipBoundsState = pipBoundsState;
mPipMenuController = pipMenuController;
mShellTaskOrganizer = shellTaskOrganizer;
@@ -142,10 +146,14 @@ public abstract class PipTransitionController implements Transitions.TransitionH
mPipAnimationController = pipAnimationController;
mTransitions = transitions;
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- transitions.addHandler(this);
+ shellInit.addInitCallback(this::onInit, this);
}
}
+ private void onInit() {
+ mTransitions.addHandler(this);
+ }
+
void setPipOrganizer(PipTaskOrganizer pto) {
mPipOrganizer = pto;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 420d6067f420..fc97f310ad4e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -89,7 +89,9 @@ import com.android.wm.shell.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.sysui.ConfigurationChangeListener;
import com.android.wm.shell.sysui.KeyguardChangeListener;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
@@ -122,6 +124,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
private TaskStackListenerImpl mTaskStackListener;
private PipParamsChangedForwarder mPipParamsChangedForwarder;
private Optional<OneHandedController> mOneHandedController;
+ private final ShellCommandHandler mShellCommandHandler;
private final ShellController mShellController;
protected final PipImpl mImpl;
@@ -295,6 +298,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb
*/
@Nullable
public static Pip create(Context context,
+ ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
ShellController shellController,
DisplayController displayController,
PipAppOpsListener pipAppOpsListener,
@@ -319,16 +324,18 @@ public class PipController implements PipTransitionController.PipTransitionCallb
return null;
}
- return new PipController(context, shellController, displayController, pipAppOpsListener,
- pipBoundsAlgorithm, pipKeepClearAlgorithm, pipBoundsState, pipMotionHelper,
- pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTransitionState,
- pipTouchHandler, pipTransitionController,
+ return new PipController(context, shellInit, shellCommandHandler, shellController,
+ displayController, pipAppOpsListener, pipBoundsAlgorithm, pipKeepClearAlgorithm,
+ pipBoundsState, pipMotionHelper, pipMediaController, phonePipMenuController,
+ pipTaskOrganizer, pipTransitionState, pipTouchHandler, pipTransitionController,
windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
oneHandedController, mainExecutor)
.mImpl;
}
protected PipController(Context context,
+ ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
ShellController shellController,
DisplayController displayController,
PipAppOpsListener pipAppOpsListener,
@@ -355,6 +362,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
}
mContext = context;
+ mShellCommandHandler = shellCommandHandler;
mShellController = shellController;
mImpl = new PipImpl();
mWindowManagerShellWrapper = windowManagerShellWrapper;
@@ -378,11 +386,11 @@ public class PipController implements PipTransitionController.PipTransitionCallb
.getInteger(R.integer.config_pipEnterAnimationDuration);
mPipParamsChangedForwarder = pipParamsChangedForwarder;
- //TODO: move this to ShellInit when PipController can be injected
- mMainExecutor.execute(this::init);
+ shellInit.addInitCallback(this::onInit, this);
}
- public void init() {
+ private void onInit() {
+ mShellCommandHandler.addDumpCallback(this::dump, this);
mPipInputConsumer = new PipInputConsumer(WindowManagerGlobal.getWindowManagerService(),
INPUT_CONSUMER_PIP, mMainExecutor);
mPipTransitionController.registerPipTransitionCallback(this);
@@ -495,14 +503,21 @@ public class PipController implements PipTransitionController.PipTransitionCallb
mPipBoundsState.getBounds(),
mPipBoundsState.getAspectRatio());
Objects.requireNonNull(destinationBounds, "Missing destination bounds");
- mPipTaskOrganizer.scheduleAnimateResizePip(destinationBounds,
- mEnterAnimationDuration,
- null /* updateBoundsCallback */);
-
- mTouchHandler.onAspectRatioChanged();
- updateMovementBounds(null /* toBounds */, false /* fromRotation */,
- false /* fromImeAdjustment */, false /* fromShelfAdjustment */,
- null /* windowContainerTransaction */);
+ if (!destinationBounds.equals(mPipBoundsState.getBounds())) {
+ mPipTaskOrganizer.scheduleAnimateResizePip(destinationBounds,
+ mEnterAnimationDuration,
+ null /* updateBoundsCallback */);
+ mTouchHandler.onAspectRatioChanged();
+ updateMovementBounds(null /* toBounds */, false /* fromRotation */,
+ false /* fromImeAdjustment */, false /* fromShelfAdjustment */,
+ null /* windowContainerTransaction */);
+ } else {
+ // when we enter pip for the first time, the destination bounds and pip
+ // bounds will already match, since they are calculated prior to
+ // starting the animation, so we only need to update the min/max size
+ // that is used for e.g. double tap to maximized state
+ mTouchHandler.updateMinMaxSize(ratio);
+ }
}
@Override
@@ -621,7 +636,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb
mPipTaskOrganizer.scheduleAnimateResizePip(
postChangeBounds, duration, null /* updateBoundsCallback */);
} else {
- mTouchHandler.getMotionHelper().movePip(postChangeBounds);
+ // Directly move PiP to its final destination bounds without animation.
+ mPipTaskOrganizer.scheduleFinishResizePip(postChangeBounds);
}
} else {
updateDisplayLayout.run();
@@ -911,7 +927,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
return true;
}
- private void dump(PrintWriter pw) {
+ private void dump(PrintWriter pw, String prefix) {
final String innerPrefix = " ";
pw.println(TAG);
mMenuController.dump(pw, innerPrefix);
@@ -999,18 +1015,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb
PipController.this.showPictureInPictureMenu();
});
}
-
- @Override
- public void dump(PrintWriter pw) {
- try {
- mMainExecutor.executeBlocking(() -> {
- PipController.this.dump(pw);
- });
- } catch (InterruptedException e) {
- ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: Failed to dump PipController in 2s", TAG);
- }
- }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index c86c1368b88e..a2fa058e97b7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -412,13 +412,7 @@ public class PipTouchHandler {
mPipBoundsState.getExpandedBounds(), insetBounds, expandedMovementBounds,
bottomOffset);
- if (mPipResizeGestureHandler.isUsingPinchToZoom()) {
- updatePinchResizeSizeConstraints(insetBounds, normalBounds, aspectRatio);
- } else {
- mPipResizeGestureHandler.updateMinSize(normalBounds.width(), normalBounds.height());
- mPipResizeGestureHandler.updateMaxSize(mPipBoundsState.getExpandedBounds().width(),
- mPipBoundsState.getExpandedBounds().height());
- }
+ updatePipSizeConstraints(insetBounds, normalBounds, aspectRatio);
// The extra offset does not really affect the movement bounds, but are applied based on the
// current state (ime showing, or shelf offset) when we need to actually shift
@@ -487,6 +481,27 @@ public class PipTouchHandler {
}
}
+ /**
+ * Update the values for min/max allowed size of picture in picture window based on the aspect
+ * ratio.
+ * @param aspectRatio aspect ratio to use for the calculation of min/max size
+ */
+ public void updateMinMaxSize(float aspectRatio) {
+ updatePipSizeConstraints(mInsetBounds, mPipBoundsState.getNormalBounds(),
+ aspectRatio);
+ }
+
+ private void updatePipSizeConstraints(Rect insetBounds, Rect normalBounds,
+ float aspectRatio) {
+ if (mPipResizeGestureHandler.isUsingPinchToZoom()) {
+ updatePinchResizeSizeConstraints(insetBounds, normalBounds, aspectRatio);
+ } else {
+ mPipResizeGestureHandler.updateMinSize(normalBounds.width(), normalBounds.height());
+ mPipResizeGestureHandler.updateMaxSize(mPipBoundsState.getExpandedBounds().width(),
+ mPipBoundsState.getExpandedBounds().height());
+ }
+ }
+
private void updatePinchResizeSizeConstraints(Rect insetBounds, Rect normalBounds,
float aspectRatio) {
final int shorterLength = Math.min(mPipBoundsState.getDisplayBounds().width(),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
index 3a6ce81821ec..b212ea9a7156 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
@@ -122,9 +122,9 @@ public class TvPipBoundsController {
cancelScheduledPlacement();
applyPlacementBounds(placement.getUnstashedBounds(), animationDuration);
} else if (immediate) {
+ boolean shouldStash = mUnstashRunnable != null || placement.getTriggerStash();
cancelScheduledPlacement();
- applyPlacementBounds(placement.getBounds(), animationDuration);
- scheduleUnstashIfNeeded(placement);
+ applyPlacement(placement, shouldStash, animationDuration);
} else {
applyPlacementBounds(mCurrentPlacementBounds, animationDuration);
schedulePinnedStackPlacement(placement, animationDuration);
@@ -176,22 +176,21 @@ public class TvPipBoundsController {
"%s: applyPendingPlacement()", TAG);
}
if (mPendingPlacement != null) {
- if (mPendingStash) {
- mPendingStash = false;
- scheduleUnstashIfNeeded(mPendingPlacement);
- }
-
- if (mUnstashRunnable != null) {
- // currently stashed, use stashed pos
- applyPlacementBounds(mPendingPlacement.getBounds(),
- mPendingPlacementAnimationDuration);
- } else {
- applyPlacementBounds(mPendingPlacement.getUnstashedBounds(),
- mPendingPlacementAnimationDuration);
- }
+ applyPlacement(mPendingPlacement, mPendingStash, mPendingPlacementAnimationDuration);
+ mPendingStash = false;
+ mPendingPlacement = null;
}
+ }
- mPendingPlacement = null;
+ private void applyPlacement(@NonNull final Placement placement, boolean shouldStash,
+ int animationDuration) {
+ if (placement.getStashType() != STASH_TYPE_NONE && shouldStash) {
+ scheduleUnstashIfNeeded(placement);
+ }
+
+ Rect bounds =
+ mUnstashRunnable != null ? placement.getBounds() : placement.getUnstashedBounds();
+ applyPlacementBounds(bounds, animationDuration);
}
void onPipDismissed() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
index 5062cc436461..8ebcf63f36e9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
@@ -32,6 +32,7 @@ import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipMenuController;
import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
/**
@@ -39,14 +40,16 @@ import com.android.wm.shell.transition.Transitions;
* TODO: Implement animation once TV is using Transitions.
*/
public class TvPipTransition extends PipTransitionController {
- public TvPipTransition(PipBoundsState pipBoundsState,
+ public TvPipTransition(
+ @NonNull ShellInit shellInit,
+ @NonNull ShellTaskOrganizer shellTaskOrganizer,
+ @NonNull Transitions transitions,
+ PipBoundsState pipBoundsState,
PipMenuController pipMenuController,
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
- PipAnimationController pipAnimationController,
- Transitions transitions,
- @NonNull ShellTaskOrganizer shellTaskOrganizer) {
- super(pipBoundsState, pipMenuController, tvPipBoundsAlgorithm, pipAnimationController,
- transitions, shellTaskOrganizer);
+ PipAnimationController pipAnimationController) {
+ super(shellInit, shellTaskOrganizer, transitions, pipBoundsState, pipMenuController,
+ tvPipBoundsAlgorithm, pipAnimationController);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 3d1a7e98e20d..7b42350b1365 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -44,6 +44,7 @@ import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
import com.android.wm.shell.util.SplitBounds;
@@ -62,6 +63,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
private static final String TAG = RecentTasksController.class.getSimpleName();
private final Context mContext;
+ private final ShellCommandHandler mShellCommandHandler;
private final ShellExecutor mMainExecutor;
private final TaskStackListenerImpl mTaskStackListener;
private final RecentTasks mImpl = new RecentTasksImpl();
@@ -87,20 +89,24 @@ public class RecentTasksController implements TaskStackListenerCallback,
public static RecentTasksController create(
Context context,
ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
TaskStackListenerImpl taskStackListener,
@ShellMainThread ShellExecutor mainExecutor
) {
if (!context.getResources().getBoolean(com.android.internal.R.bool.config_hasRecents)) {
return null;
}
- return new RecentTasksController(context, shellInit, taskStackListener, mainExecutor);
+ return new RecentTasksController(context, shellInit, shellCommandHandler, taskStackListener,
+ mainExecutor);
}
RecentTasksController(Context context,
ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
TaskStackListenerImpl taskStackListener,
ShellExecutor mainExecutor) {
mContext = context;
+ mShellCommandHandler = shellCommandHandler;
mIsDesktopMode = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
mTaskStackListener = taskStackListener;
mMainExecutor = mainExecutor;
@@ -112,6 +118,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
}
private void onInit() {
+ mShellCommandHandler.addDumpCallback(this::dump, this);
mTaskStackListener.addListener(this);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 1be17f9bc5fd..2117b69ebc2e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -84,15 +84,14 @@ import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.splitscreen.SplitScreen.StageType;
import com.android.wm.shell.sysui.KeyguardChangeListener;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
-import com.android.wm.shell.transition.LegacyTransitions;
import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.Executor;
@@ -133,6 +132,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
@Retention(RetentionPolicy.SOURCE)
@interface ExitReason{}
+ private final ShellCommandHandler mShellCommandHandler;
private final ShellController mShellController;
private final ShellTaskOrganizer mTaskOrganizer;
private final SyncTransactionQueue mSyncQueue;
@@ -149,6 +149,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
private final SplitscreenEventLogger mLogger;
private final IconProvider mIconProvider;
private final Optional<RecentTasksController> mRecentTasksOptional;
+ private final SplitScreenShellCommandHandler mSplitScreenShellCommandHandler;
private StageCoordinator mStageCoordinator;
// Only used for the legacy recents animation from splitscreen to allow the tasks to be animated
@@ -157,6 +158,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
public SplitScreenController(Context context,
ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
ShellController shellController,
ShellTaskOrganizer shellTaskOrganizer,
SyncTransactionQueue syncQueue,
@@ -170,6 +172,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
IconProvider iconProvider,
Optional<RecentTasksController> recentTasks,
ShellExecutor mainExecutor) {
+ mShellCommandHandler = shellCommandHandler;
mShellController = shellController;
mTaskOrganizer = shellTaskOrganizer;
mSyncQueue = syncQueue;
@@ -185,6 +188,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
mLogger = new SplitscreenEventLogger();
mIconProvider = iconProvider;
mRecentTasksOptional = recentTasks;
+ mSplitScreenShellCommandHandler = new SplitScreenShellCommandHandler(this);
// TODO(b/238217847): Temporarily add this check here until we can remove the dynamic
// override for this controller from the base module
if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) {
@@ -202,6 +206,9 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
*/
@VisibleForTesting
void onInit() {
+ mShellCommandHandler.addDumpCallback(this::dump, this);
+ mShellCommandHandler.addCommandCallback("splitscreen", mSplitScreenShellCommandHandler,
+ this);
mShellController.addKeyguardChangeListener(this);
if (mStageCoordinator == null) {
// TODO: Multi-display
@@ -339,17 +346,39 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
public void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options) {
+ final int[] result = new int[1];
+ IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
+ @Override
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
+ final IRemoteAnimationFinishedCallback finishedCallback) {
+ try {
+ finishedCallback.onAnimationFinished();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to invoke onAnimationFinished", e);
+ }
+ if (result[0] == START_SUCCESS || result[0] == START_TASK_TO_FRONT) {
+ final WindowContainerTransaction evictWct = new WindowContainerTransaction();
+ mStageCoordinator.prepareEvictNonOpeningChildTasks(position, apps, evictWct);
+ mSyncQueue.queue(evictWct);
+ }
+ }
+ @Override
+ public void onAnimationCancelled(boolean isKeyguardOccluded) {
+ }
+ };
options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
null /* wct */);
+ RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter(wrapper,
+ 0 /* duration */, 0 /* statusBarTransitionDelay */);
+ ActivityOptions activityOptions = ActivityOptions.fromBundle(options);
+ activityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
try {
- final WindowContainerTransaction evictWct = new WindowContainerTransaction();
- mStageCoordinator.prepareEvictChildTasks(position, evictWct);
- final int result =
- ActivityTaskManager.getService().startActivityFromRecents(taskId, options);
- if (result == START_SUCCESS || result == START_TASK_TO_FRONT) {
- mSyncQueue.queue(evictWct);
- }
+ result[0] = ActivityTaskManager.getService().startActivityFromRecents(taskId,
+ activityOptions.toBundle());
} catch (RemoteException e) {
Slog.e(TAG, "Failed to launch task", e);
}
@@ -405,72 +434,22 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
// Flag with MULTIPLE_TASK if this is launching the same activity into both sides of the
// split.
- if (isLaunchingAdjacently(intent.getIntent(), position)) {
+ if (shouldAddMultipleTaskFlag(intent.getIntent(), position)) {
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
}
if (!ENABLE_SHELL_TRANSITIONS) {
- startIntentLegacy(intent, fillInIntent, position, options);
+ mStageCoordinator.startIntentLegacy(intent, fillInIntent, position, options);
return;
}
mStageCoordinator.startIntent(intent, fillInIntent, position, options);
}
- private void startIntentLegacy(PendingIntent intent, Intent fillInIntent,
- @SplitPosition int position, @Nullable Bundle options) {
- final WindowContainerTransaction evictWct = new WindowContainerTransaction();
- mStageCoordinator.prepareEvictChildTasks(position, evictWct);
-
- LegacyTransitions.ILegacyTransition transition = new LegacyTransitions.ILegacyTransition() {
- @Override
- public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
- RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
- IRemoteAnimationFinishedCallback finishedCallback,
- SurfaceControl.Transaction t) {
- if (apps == null || apps.length == 0) {
- // Switch the split position if launching as MULTIPLE_TASK failed.
- if ((fillInIntent.getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
- setSideStagePosition(SplitLayout.reversePosition(
- mStageCoordinator.getSideStagePosition()));
- }
-
- // Do nothing when the animation was cancelled.
- t.apply();
- return;
- }
-
- for (int i = 0; i < apps.length; ++i) {
- if (apps[i].mode == MODE_OPENING) {
- t.show(apps[i].leash);
- }
- }
- t.apply();
-
- if (finishedCallback != null) {
- try {
- finishedCallback.onAnimationFinished();
- } catch (RemoteException e) {
- Slog.e(TAG, "Error finishing legacy transition: ", e);
- }
- }
-
- mSyncQueue.queue(evictWct);
- }
- };
-
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, wct);
-
- wct.sendPendingIntent(intent, fillInIntent, options);
- mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
- }
-
/** Returns {@code true} if it's launching the same component on both sides of the split. */
@VisibleForTesting
- boolean isLaunchingAdjacently(@Nullable Intent startIntent,
- @SplitPosition int position) {
+ boolean shouldAddMultipleTaskFlag(@Nullable Intent startIntent, @SplitPosition int position) {
if (startIntent == null) {
return false;
}
@@ -481,6 +460,16 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
if (isSplitScreenVisible()) {
+ // To prevent users from constantly dropping the same app to the same side resulting in
+ // a large number of instances in the background.
+ final ActivityManager.RunningTaskInfo targetTaskInfo = getTaskInfo(position);
+ final ComponentName targetActivity = targetTaskInfo != null
+ ? targetTaskInfo.baseIntent.getComponent() : null;
+ if (Objects.equals(launchingActivity, targetActivity)) {
+ return false;
+ }
+
+ // Allow users to start a new instance the same to adjacent side.
final ActivityManager.RunningTaskInfo pairedTaskInfo =
getTaskInfo(SplitLayout.reversePosition(position));
final ComponentName pairedActivity = pairedTaskInfo != null
@@ -504,12 +493,12 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
mStageCoordinator.prepareEvictInvisibleChildTasks(wct);
mSyncQueue.queue(wct);
}
- return reparentSplitTasksForAnimation(apps, true /*splitExpectedToBeVisible*/);
+ return reparentSplitTasksForAnimation(apps, false /* enterSplitScreen */);
}
RemoteAnimationTarget[] onStartingSplitLegacy(RemoteAnimationTarget[] apps) {
try {
- return reparentSplitTasksForAnimation(apps, false /*splitExpectedToBeVisible*/);
+ return reparentSplitTasksForAnimation(apps, true /* enterSplitScreen */);
} finally {
for (RemoteAnimationTarget appTarget : apps) {
if (appTarget.leash != null) {
@@ -520,14 +509,23 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
private RemoteAnimationTarget[] reparentSplitTasksForAnimation(RemoteAnimationTarget[] apps,
- boolean splitExpectedToBeVisible) {
+ boolean enterSplitScreen) {
if (ENABLE_SHELL_TRANSITIONS) return null;
- // TODO(b/206487881): Integrate this with shell transition.
- if (splitExpectedToBeVisible && !isSplitScreenVisible()) return null;
- // Split not visible, but not enough apps to have split, also return null
- if (!splitExpectedToBeVisible && apps.length < 2) return null;
- SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+ if (enterSplitScreen) {
+ int openingApps = 0;
+ for (int i = 0; i < apps.length; ++i) {
+ if (apps[i].mode == MODE_OPENING) openingApps++;
+ }
+ if (openingApps < 2) {
+ // Not having enough apps to enter split screen
+ return null;
+ }
+ } else if (!isSplitScreenVisible()) {
+ return null;
+ }
+
+ final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
if (mSplitTasksContainerLayer != null) {
// Remove the previous layer before recreating
transaction.remove(mSplitTasksContainerLayer);
@@ -540,17 +538,14 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
mRootTDAOrganizer.attachToDisplayArea(DEFAULT_DISPLAY, builder);
mSplitTasksContainerLayer = builder.build();
- // Ensure that we order these in the parent in the right z-order as their previous order
- Arrays.sort(apps, (a1, a2) -> a1.prefixOrderIndex - a2.prefixOrderIndex);
- int layer = 1;
- for (RemoteAnimationTarget appTarget : apps) {
+ for (int i = 0; i < apps.length; ++i) {
+ final RemoteAnimationTarget appTarget = apps[i];
transaction.reparent(appTarget.leash, mSplitTasksContainerLayer);
transaction.setPosition(appTarget.leash, appTarget.screenSpaceBounds.left,
appTarget.screenSpaceBounds.top);
- transaction.setLayer(appTarget.leash, layer++);
}
transaction.apply();
- transaction.close();
+ mTransactionPool.release(transaction);
return new RemoteAnimationTarget[]{mStageCoordinator.getDividerBarLegacyTarget()};
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenShellCommandHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenShellCommandHandler.java
new file mode 100644
index 000000000000..681d9647d154
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenShellCommandHandler.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.wm.shell.splitscreen;
+
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
+
+import com.android.wm.shell.sysui.ShellCommandHandler;
+
+import java.io.PrintWriter;
+
+/**
+ * Handles the shell commands for the SplitscreenController.
+ */
+public class SplitScreenShellCommandHandler implements
+ ShellCommandHandler.ShellCommandActionHandler {
+
+ private final SplitScreenController mController;
+
+ public SplitScreenShellCommandHandler(SplitScreenController controller) {
+ mController = controller;
+ }
+
+ @Override
+ public boolean onShellCommand(String[] args, PrintWriter pw) {
+ switch (args[0]) {
+ case "moveToSideStage":
+ return runMoveToSideStage(args, pw);
+ case "removeFromSideStage":
+ return runRemoveFromSideStage(args, pw);
+ case "setSideStagePosition":
+ return runSetSideStagePosition(args, pw);
+ default:
+ pw.println("Invalid command: " + args[0]);
+ return false;
+ }
+ }
+
+ private boolean runMoveToSideStage(String[] args, PrintWriter pw) {
+ if (args.length < 3) {
+ // First argument is the action name.
+ pw.println("Error: task id should be provided as arguments");
+ return false;
+ }
+ final int taskId = new Integer(args[1]);
+ final int sideStagePosition = args.length > 3
+ ? new Integer(args[2]) : SPLIT_POSITION_BOTTOM_OR_RIGHT;
+ mController.moveToSideStage(taskId, sideStagePosition);
+ return true;
+ }
+
+ private boolean runRemoveFromSideStage(String[] args, PrintWriter pw) {
+ if (args.length < 2) {
+ // First argument is the action name.
+ pw.println("Error: task id should be provided as arguments");
+ return false;
+ }
+ final int taskId = new Integer(args[1]);
+ mController.removeFromSideStage(taskId);
+ return true;
+ }
+
+ private boolean runSetSideStagePosition(String[] args, PrintWriter pw) {
+ if (args.length < 2) {
+ // First argument is the action name.
+ pw.println("Error: side stage position should be provided as arguments");
+ return false;
+ }
+ final int position = new Integer(args[1]);
+ mController.setSideStagePosition(position);
+ return true;
+ }
+
+ @Override
+ public void printShellCommandHelp(PrintWriter pw, String prefix) {
+ pw.println(prefix + "moveToSideStage <taskId> <SideStagePosition>");
+ pw.println(prefix + " Move a task with given id in split-screen mode.");
+ pw.println(prefix + "removeFromSideStage <taskId>");
+ pw.println(prefix + " Remove a task with given id in split-screen mode.");
+ pw.println(prefix + "setSideStagePosition <SideStagePosition>");
+ pw.println(prefix + " Sets the position of the side-stage.");
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index 83bdf8bfb727..d7ca791e3863 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -94,6 +94,7 @@ class SplitScreenTransitions {
@NonNull WindowContainerToken topRoot) {
mFinishCallback = finishCallback;
mAnimatingTransition = transition;
+ mFinishTransaction = finishTransaction;
if (mPendingRemoteHandler != null) {
mPendingRemoteHandler.startAnimation(transition, info, startTransaction,
finishTransaction, mRemoteFinishCB);
@@ -107,8 +108,6 @@ class SplitScreenTransitions {
private void playInternalAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull WindowContainerToken mainRoot,
@NonNull WindowContainerToken sideRoot, @NonNull WindowContainerToken topRoot) {
- mFinishTransaction = mTransactionPool.acquire();
-
// Play some place-holder fade animations
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
@@ -287,16 +286,14 @@ class SplitScreenTransitions {
return true;
}
- void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) {
+ void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+ @Nullable SurfaceControl.Transaction finishT) {
if (isPendingEnter(transition)) {
if (!aborted) {
// An enter transition got merged, appends the rest operations to finish entering
// split screen.
- // TODO (b/238856352): Passed-in the proper finish transition to merge instead.
- if (mFinishTransaction == null) {
- mFinishTransaction = mTransactionPool.acquire();
- }
- mStageCoordinator.finishEnterSplitScreen(mFinishTransaction);
+ mStageCoordinator.finishEnterSplitScreen(finishT);
+ mPendingRemoteHandler = null;
}
mPendingEnter.mCallback.onTransitionConsumed(aborted);
@@ -339,11 +336,6 @@ class SplitScreenTransitions {
mAnimatingTransition = null;
mOnFinish.run();
- if (mFinishTransaction != null) {
- mFinishTransaction.apply();
- mTransactionPool.release(mFinishTransaction);
- mFinishTransaction = null;
- }
if (mFinishCallback != null) {
mFinishCallback.onTransitionFinished(wct /* wct */, wctCB /* wctCB */);
mFinishCallback = null;
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 2b3b61bbbd84..8e1ae397ea64 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
@@ -26,6 +26,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_TO_BACK;
@@ -118,6 +119,7 @@ import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.splitscreen.SplitScreen.StageType;
import com.android.wm.shell.splitscreen.SplitScreenController.ExitReason;
+import com.android.wm.shell.transition.LegacyTransitions;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.util.SplitBounds;
@@ -195,7 +197,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private boolean mExitSplitScreenOnHide;
private boolean mIsDividerRemoteAnimating;
private boolean mIsExiting;
- private boolean mResizingSplits;
/** The target stage to dismiss to when unlock after folded. */
@StageType
@@ -210,7 +211,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@Override
public void onLeashReady(SurfaceControl leash) {
- mSyncQueue.runInSync(t -> applyDividerVisibility(t));
+ // This is for avoiding divider invisible due to delay of creating so only need
+ // to do when divider should visible case.
+ if (mDividerVisible) {
+ mSyncQueue.runInSync(t -> applyDividerVisibility(t));
+ }
}
};
@@ -228,6 +233,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop()
&& (mMainStage.containsContainer(container)
|| mSideStage.containsContainer(container))) {
+ updateSurfaceBounds(mSplitLayout, finishT, false /* applyResizingOffset */);
setDividerVisibility(true, finishT);
return;
}
@@ -433,6 +439,69 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
});
}
+ /** Launches an activity into split by legacy transition. */
+ void startIntentLegacy(PendingIntent intent, Intent fillInIntent,
+ @SplitPosition int position, @androidx.annotation.Nullable Bundle options) {
+ final WindowContainerTransaction evictWct = new WindowContainerTransaction();
+ prepareEvictChildTasks(position, evictWct);
+
+ LegacyTransitions.ILegacyTransition transition = new LegacyTransitions.ILegacyTransition() {
+ @Override
+ public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback,
+ SurfaceControl.Transaction t) {
+ if (apps == null || apps.length == 0) {
+ if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) {
+ mMainExecutor.execute(() ->
+ exitSplitScreen(mMainStage.getChildCount() == 0
+ ? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
+ } else {
+ // Switch the split position if launching as MULTIPLE_TASK failed.
+ if ((fillInIntent.getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
+ setSideStagePosition(SplitLayout.reversePosition(
+ getSideStagePosition()), null);
+ }
+ }
+
+ // Do nothing when the animation was cancelled.
+ t.apply();
+ return;
+ }
+
+ for (int i = 0; i < apps.length; ++i) {
+ if (apps[i].mode == MODE_OPENING) {
+ t.show(apps[i].leash);
+ }
+ }
+ t.apply();
+
+ if (finishedCallback != null) {
+ try {
+ finishedCallback.onAnimationFinished();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error finishing legacy transition: ", e);
+ }
+ }
+
+ mSyncQueue.queue(evictWct);
+ }
+ };
+
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, wct);
+
+ // If split still not active, apply windows bounds first to avoid surface reset to
+ // wrong pos by SurfaceAnimator from wms.
+ // TODO(b/223325631): check is it still necessary after improve enter transition done.
+ if (!mMainStage.isActive()) {
+ updateWindowBounds(mSplitLayout, wct);
+ }
+
+ wct.sendPendingIntent(intent, fillInIntent, options);
+ mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
+ }
+
/** Starts 2 tasks in one transition. */
void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId,
@Nullable Bundle sideOptions, @SplitPosition int sidePosition, float splitRatio,
@@ -588,7 +657,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mShouldUpdateRecents = true;
// If any stage has no child after animation finished, it means that split will display
// nothing, such status will happen if task and intent is same app but not support
- // multi-instagce, we should exit split and expand that app as full screen.
+ // multi-instance, we should exit split and expand that app as full screen.
if (!cancel && (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0)) {
mMainExecutor.execute(() ->
exitSplitScreen(mMainStage.getChildCount() == 0
@@ -849,6 +918,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
.setWindowCrop(mSideStage.mRootLeash, null);
t.setPosition(mMainStage.mRootLeash, 0, 0)
.setPosition(mSideStage.mRootLeash, 0, 0);
+ t.hide(mMainStage.mDimLayer).hide(mSideStage.mDimLayer);
setDividerVisibility(false, t);
// In this case, exit still under progress, fade out the split decor after first WCT
@@ -858,7 +928,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
WindowContainerTransaction finishedWCT = new WindowContainerTransaction();
mIsExiting = false;
childrenToTop.dismiss(finishedWCT, true /* toTop */);
- wct.reorder(mRootTaskInfo.token, false /* toTop */);
+ finishedWCT.reorder(mRootTaskInfo.token, false /* toTop */);
mTaskOrganizer.applyTransaction(finishedWCT);
onTransitionAnimationComplete();
});
@@ -1679,8 +1749,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
@Override
- public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) {
- mSplitTransitions.onTransitionConsumed(transition, aborted);
+ public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+ @Nullable SurfaceControl.Transaction finishT) {
+ mSplitTransitions.onTransitionConsumed(transition, aborted, finishT);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index f414d69d37ec..1af9415fca3a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -33,6 +33,7 @@ import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.IBinder;
+import android.util.Slog;
import android.util.SparseArray;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
@@ -376,7 +377,13 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
SurfaceControl leash, boolean firstAppeared) {
final Point taskPositionInParent = taskInfo.positionInParent;
mSyncQueue.runInSync(t -> {
- t.setWindowCrop(leash, null);
+ // The task surface might be released before running in the sync queue for the case like
+ // trampoline launch, so check if the surface is valid before processing it.
+ if (!leash.isValid()) {
+ Slog.w(TAG, "Skip updating invalid child task surface of task#" + taskInfo.taskId);
+ return;
+ }
+ t.setCrop(leash, null);
t.setPosition(leash, taskPositionInParent.x, taskPositionInParent.y);
if (firstAppeared && !ENABLE_SHELL_TRANSITIONS) {
t.setAlpha(leash, 1f);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellCommandHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellCommandHandler.java
index f4fc0c4c36bc..2e6ddc363906 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellCommandHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellCommandHandler.java
@@ -16,19 +16,14 @@
package com.android.wm.shell.sysui;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_INIT;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController;
-import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer;
-import com.android.wm.shell.onehanded.OneHandedController;
-import com.android.wm.shell.pip.Pip;
-import com.android.wm.shell.recents.RecentTasksController;
-import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.internal.protolog.common.ProtoLog;
import java.io.PrintWriter;
-import java.util.Optional;
+import java.util.Arrays;
+import java.util.TreeMap;
+import java.util.function.BiConsumer;
/**
* An entry point into the shell for dumping shell internal state and running adb commands.
@@ -38,52 +33,61 @@ import java.util.Optional;
public final class ShellCommandHandler {
private static final String TAG = ShellCommandHandler.class.getSimpleName();
- private final Optional<SplitScreenController> mSplitScreenOptional;
- private final Optional<Pip> mPipOptional;
- private final Optional<OneHandedController> mOneHandedOptional;
- private final Optional<HideDisplayCutoutController> mHideDisplayCutout;
- private final Optional<RecentTasksController> mRecentTasks;
- private final ShellTaskOrganizer mShellTaskOrganizer;
- private final KidsModeTaskOrganizer mKidsModeTaskOrganizer;
-
- public ShellCommandHandler(
- ShellController shellController,
- ShellTaskOrganizer shellTaskOrganizer,
- KidsModeTaskOrganizer kidsModeTaskOrganizer,
- Optional<SplitScreenController> splitScreenOptional,
- Optional<Pip> pipOptional,
- Optional<OneHandedController> oneHandedOptional,
- Optional<HideDisplayCutoutController> hideDisplayCutout,
- Optional<RecentTasksController> recentTasks,
- ShellExecutor mainExecutor) {
- mShellTaskOrganizer = shellTaskOrganizer;
- mKidsModeTaskOrganizer = kidsModeTaskOrganizer;
- mRecentTasks = recentTasks;
- mSplitScreenOptional = splitScreenOptional;
- mPipOptional = pipOptional;
- mOneHandedOptional = oneHandedOptional;
- mHideDisplayCutout = hideDisplayCutout;
- // TODO(238217847): To be removed once the command handler dependencies are inverted
- shellController.setShellCommandHandler(this);
+ // We're using a TreeMap to keep them sorted by command name
+ private final TreeMap<String, BiConsumer<PrintWriter, String>> mDumpables = new TreeMap<>();
+ private final TreeMap<String, ShellCommandActionHandler> mCommands = new TreeMap<>();
+
+ public interface ShellCommandActionHandler {
+ /**
+ * Handles the given command.
+ *
+ * @param args the arguments starting with the action name, then the action arguments
+ * @param pw the write to print output to
+ */
+ boolean onShellCommand(String[] args, PrintWriter pw);
+
+ /**
+ * Prints the help for this class of commands. Implementations do not need to print the
+ * command class.
+ */
+ void printShellCommandHelp(PrintWriter pw, String prefix);
+ }
+
+
+ /**
+ * Adds a callback to run when the Shell is being dumped.
+ *
+ * @param callback the callback to be made when Shell is dumped, takes the print writer and
+ * a prefix
+ * @param instance used for debugging only
+ */
+ public <T> void addDumpCallback(BiConsumer<PrintWriter, String> callback, T instance) {
+ mDumpables.put(instance.getClass().getSimpleName(), callback);
+ ProtoLog.v(WM_SHELL_INIT, "Adding dump callback for %s",
+ instance.getClass().getSimpleName());
+ }
+
+ /**
+ * Adds an action callback to be invoked when the user runs that particular command from adb.
+ *
+ * @param commandClass the top level class of command to invoke
+ * @param actions the interface to callback when an action of this class is invoked
+ * @param instance used for debugging only
+ */
+ public <T> void addCommandCallback(String commandClass, ShellCommandActionHandler actions,
+ T instance) {
+ mCommands.put(commandClass, actions);
+ ProtoLog.v(WM_SHELL_INIT, "Adding command class %s for %s", commandClass,
+ instance.getClass().getSimpleName());
}
/** Dumps WM Shell internal state. */
public void dump(PrintWriter pw) {
- mShellTaskOrganizer.dump(pw, "");
- pw.println();
- pw.println();
- mPipOptional.ifPresent(pip -> pip.dump(pw));
- mOneHandedOptional.ifPresent(oneHanded -> oneHanded.dump(pw));
- mHideDisplayCutout.ifPresent(hideDisplayCutout -> hideDisplayCutout.dump(pw));
- pw.println();
- pw.println();
- mSplitScreenOptional.ifPresent(splitScreen -> splitScreen.dump(pw, ""));
- pw.println();
- pw.println();
- mRecentTasks.ifPresent(recentTasks -> recentTasks.dump(pw, ""));
- pw.println();
- pw.println();
- mKidsModeTaskOrganizer.dump(pw, "");
+ for (String key : mDumpables.keySet()) {
+ final BiConsumer<PrintWriter, String> r = mDumpables.get(key);
+ r.accept(pw, "");
+ pw.println();
+ }
}
@@ -93,72 +97,32 @@ public final class ShellCommandHandler {
// Argument at position 0 is "WMShell".
return false;
}
- switch (args[1]) {
- case "moveToSideStage":
- return runMoveToSideStage(args, pw);
- case "removeFromSideStage":
- return runRemoveFromSideStage(args, pw);
- case "setSideStagePosition":
- return runSetSideStagePosition(args, pw);
- case "help":
- return runHelp(pw);
- default:
- return false;
- }
- }
- private boolean runMoveToSideStage(String[] args, PrintWriter pw) {
- if (args.length < 3) {
- // First arguments are "WMShell" and command name.
- pw.println("Error: task id should be provided as arguments");
- return false;
+ final String cmdClass = args[1];
+ if (cmdClass.toLowerCase().equals("help")) {
+ return runHelp(pw);
}
- final int taskId = new Integer(args[2]);
- final int sideStagePosition = args.length > 3
- ? new Integer(args[3]) : SPLIT_POSITION_BOTTOM_OR_RIGHT;
- mSplitScreenOptional.ifPresent(split -> split.moveToSideStage(taskId, sideStagePosition));
- return true;
- }
-
- private boolean runRemoveFromSideStage(String[] args, PrintWriter pw) {
- if (args.length < 3) {
- // First arguments are "WMShell" and command name.
- pw.println("Error: task id should be provided as arguments");
+ if (!mCommands.containsKey(cmdClass)) {
return false;
}
- final int taskId = new Integer(args[2]);
- mSplitScreenOptional.ifPresent(split -> split.removeFromSideStage(taskId));
- return true;
- }
- private boolean runSetSideStagePosition(String[] args, PrintWriter pw) {
- if (args.length < 3) {
- // First arguments are "WMShell" and command name.
- pw.println("Error: side stage position should be provided as arguments");
- return false;
- }
- final int position = new Integer(args[2]);
- mSplitScreenOptional.ifPresent(split -> split.setSideStagePosition(position));
+ // Only pass the actions onwards as arguments to the callback
+ final ShellCommandActionHandler actions = mCommands.get(args[1]);
+ final String[] cmdClassArgs = Arrays.copyOfRange(args, 2, args.length);
+ actions.onShellCommand(cmdClassArgs, pw);
return true;
}
private boolean runHelp(PrintWriter pw) {
pw.println("Window Manager Shell commands:");
+ for (String commandClass : mCommands.keySet()) {
+ pw.println(" " + commandClass);
+ mCommands.get(commandClass).printShellCommandHelp(pw, " ");
+ }
pw.println(" help");
pw.println(" Print this help text.");
pw.println(" <no arguments provided>");
- pw.println(" Dump Window Manager Shell internal state");
- pw.println(" pair <taskId1> <taskId2>");
- pw.println(" unpair <taskId>");
- pw.println(" Pairs/unpairs tasks with given ids.");
- pw.println(" moveToSideStage <taskId> <SideStagePosition>");
- pw.println(" Move a task with given id in split-screen mode.");
- pw.println(" removeFromSideStage <taskId>");
- pw.println(" Remove a task with given id in split-screen mode.");
- pw.println(" setSideStageOutline <true/false>");
- pw.println(" Enable/Disable outline on the side-stage.");
- pw.println(" setSideStagePosition <SideStagePosition>");
- pw.println(" Sets the position of the side-stage.");
+ pw.println(" Dump all Window Manager Shell internal state");
return true;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java
index f1f317f65ba9..52ffb46bb39c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java
@@ -45,11 +45,10 @@ public class ShellController {
private static final String TAG = ShellController.class.getSimpleName();
private final ShellInit mShellInit;
+ private final ShellCommandHandler mShellCommandHandler;
private final ShellExecutor mMainExecutor;
private final ShellInterfaceImpl mImpl = new ShellInterfaceImpl();
- private ShellCommandHandler mShellCommandHandler;
-
private final CopyOnWriteArrayList<ConfigurationChangeListener> mConfigChangeListeners =
new CopyOnWriteArrayList<>();
private final CopyOnWriteArrayList<KeyguardChangeListener> mKeyguardChangeListeners =
@@ -57,8 +56,10 @@ public class ShellController {
private Configuration mLastConfiguration;
- public ShellController(ShellInit shellInit, ShellExecutor mainExecutor) {
+ public ShellController(ShellInit shellInit, ShellCommandHandler shellCommandHandler,
+ ShellExecutor mainExecutor) {
mShellInit = shellInit;
+ mShellCommandHandler = shellCommandHandler;
mMainExecutor = mainExecutor;
}
@@ -70,15 +71,6 @@ public class ShellController {
}
/**
- * Sets the command handler to call back to.
- * TODO(238217847): This is only exposed this way until we can remove the dependencies from the
- * command handler to other classes.
- */
- public void setShellCommandHandler(ShellCommandHandler shellCommandHandler) {
- mShellCommandHandler = shellCommandHandler;
- }
-
- /**
* Adds a new configuration listener. The configuration change callbacks are not made in any
* particular order.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java
index c250e0313255..ac52235375c4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java
@@ -52,6 +52,9 @@ public class ShellInit {
* Adds a callback to the ordered list of callbacks be made when Shell is first started. This
* can be used in class constructors when dagger is used to ensure that the initialization order
* matches the dependency order.
+ *
+ * @param r the callback to be made when Shell is initialized
+ * @param instance used for debugging only
*/
public <T extends Object> void addInitCallback(Runnable r, T instance) {
if (mHasInitialized) {
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 11b453cb24a2..5cce6b99fb11 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
@@ -274,7 +274,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
}
@Override
- public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) {
+ public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+ @Nullable SurfaceControl.Transaction finishT) {
MixedTransition mixed = null;
for (int i = mActiveTransitions.size() - 1; i >= 0; --i) {
if (mActiveTransitions.get(i).mTransition != transition) continue;
@@ -283,7 +284,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
}
if (mixed == null) return;
if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
- mPipHandler.onTransitionConsumed(transition, aborted);
+ mPipHandler.onTransitionConsumed(transition, aborted, finishT);
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index afc70a448aa8..08eb2c9b6bbe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -154,8 +154,6 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
private final int mCurrentUserId;
- private ScreenRotationAnimation mRotationAnimation;
-
private Drawable mEnterpriseThumbnailDrawable;
private BroadcastReceiver mEnterpriseResourceUpdatedReceiver = new BroadcastReceiver() {
@@ -340,12 +338,6 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
final Runnable onAnimFinish = () -> {
if (!animations.isEmpty()) return;
-
- if (mRotationAnimation != null) {
- mRotationAnimation.kill();
- mRotationAnimation = null;
- }
-
mAnimations.remove(transition);
finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
};
@@ -365,11 +357,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
isSeamlessDisplayChange = isRotationSeamless(info, mDisplayController);
final int anim = getRotationAnimation(info);
if (!(isSeamlessDisplayChange || anim == ROTATION_ANIMATION_JUMPCUT)) {
- mRotationAnimation = new ScreenRotationAnimation(mContext, mSurfaceSession,
- mTransactionPool, startTransaction, change, info.getRootLeash(),
- anim);
- mRotationAnimation.startAnimation(animations, onAnimFinish,
- mTransitionAnimationScaleSetting, mMainExecutor, mAnimExecutor);
+ startRotationAnimation(startTransaction, change, info, anim, animations,
+ onAnimFinish);
continue;
}
} else {
@@ -413,6 +402,13 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
startTransaction.setWindowCrop(change.getLeash(),
change.getEndAbsBounds().width(), change.getEndAbsBounds().height());
}
+ // Rotation change of independent non display window container.
+ if (change.getParent() == null
+ && change.getStartRotation() != change.getEndRotation()) {
+ startRotationAnimation(startTransaction, change, info,
+ ROTATION_ANIMATION_ROTATE, animations, onAnimFinish);
+ continue;
+ }
}
// Don't animate anything that isn't independent.
@@ -543,6 +539,31 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
}
}
+ private void startRotationAnimation(SurfaceControl.Transaction startTransaction,
+ TransitionInfo.Change change, TransitionInfo info, int animHint,
+ ArrayList<Animator> animations, Runnable onAnimFinish) {
+ final ScreenRotationAnimation anim = new ScreenRotationAnimation(mContext, mSurfaceSession,
+ mTransactionPool, startTransaction, change, info.getRootLeash(), animHint);
+ // The rotation animation may consist of 3 animations: fade-out screenshot, fade-in real
+ // content, and background color. The item of "animGroup" will be removed if the sub
+ // animation is finished. Then if the list becomes empty, the rotation animation is done.
+ final ArrayList<Animator> animGroup = new ArrayList<>(3);
+ final ArrayList<Animator> animGroupStore = new ArrayList<>(3);
+ final Runnable finishCallback = () -> {
+ if (!animGroup.isEmpty()) return;
+ anim.kill();
+ animations.removeAll(animGroupStore);
+ onAnimFinish.run();
+ };
+ anim.startAnimation(animGroup, finishCallback, mTransitionAnimationScaleSetting,
+ mMainExecutor, mAnimExecutor);
+ for (int i = animGroup.size() - 1; i >= 0; i--) {
+ final Animator animator = animGroup.get(i);
+ animGroupStore.add(animator);
+ animations.add(animator);
+ }
+ }
+
private void edgeExtendWindow(TransitionInfo.Change change,
Animation a, SurfaceControl.Transaction startTransaction,
SurfaceControl.Transaction finishTransaction) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index cedb340816ae..9469529de8f1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -82,7 +82,8 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
}
@Override
- public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) {
+ public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+ @Nullable SurfaceControl.Transaction finishT) {
mRequestedRemotes.remove(transition);
}
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 1005ef1705f0..a843b2a0ac39 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
@@ -84,7 +84,7 @@ class ScreenRotationAnimation {
private final Context mContext;
private final TransactionPool mTransactionPool;
private final float[] mTmpFloats = new float[9];
- /** The leash of display. */
+ /** The leash of the changing window container. */
private final SurfaceControl mSurfaceControl;
private final Rect mStartBounds = new Rect();
private final Rect mEndBounds = new Rect();
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 e6006c4fc6d0..279d57a23e6e 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
@@ -159,8 +159,10 @@ public class Transitions implements RemoteCallable<Transitions> {
private void onInit() {
// The very last handler (0 in the list) should be the default one.
mHandlers.add(mDefaultTransitionHandler);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Default");
// Next lowest priority is remote transitions.
mHandlers.add(mRemoteTransitionHandler);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Remote");
ContentResolver resolver = mContext.getContentResolver();
mTransitionAnimationScaleSetting = Settings.Global.getFloat(resolver,
@@ -206,7 +208,13 @@ public class Transitions implements RemoteCallable<Transitions> {
* @see TransitionHandler
*/
public void addHandler(@NonNull TransitionHandler handler) {
+ if (mHandlers.isEmpty()) {
+ throw new RuntimeException("Unexpected handler added prior to initialization, please "
+ + "use ShellInit callbacks to ensure proper ordering");
+ }
mHandlers.add(handler);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: %s",
+ handler.getClass().getSimpleName());
}
public ShellExecutor getMainExecutor() {
@@ -356,7 +364,7 @@ public class Transitions implements RemoteCallable<Transitions> {
// Put all the OPEN/SHOW on top
if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) {
// Wallpaper is always at the bottom.
- layer = 0;
+ layer = -zSplitLine;
} else if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) {
if (isOpening) {
// put on top
@@ -535,7 +543,8 @@ public class Transitions implements RemoteCallable<Transitions> {
active.mMerged = true;
active.mAborted = abort;
if (active.mHandler != null) {
- active.mHandler.onTransitionConsumed(active.mToken, abort);
+ active.mHandler.onTransitionConsumed(
+ active.mToken, abort, abort ? null : active.mFinishT);
}
return;
}
@@ -543,7 +552,8 @@ public class Transitions implements RemoteCallable<Transitions> {
active.mAborted = abort;
if (active.mAborted && active.mHandler != null) {
// Notifies to clean-up the aborted transition.
- active.mHandler.onTransitionConsumed(transition, true /* aborted */);
+ active.mHandler.onTransitionConsumed(
+ transition, true /* aborted */, null /* finishTransaction */);
}
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
"Transition animation finished (abort=%b), notifying core %s", abort, transition);
@@ -579,7 +589,8 @@ public class Transitions implements RemoteCallable<Transitions> {
ActiveTransition aborted = mActiveTransitions.remove(activeIdx);
// Notifies to clean-up the aborted transition.
if (aborted.mHandler != null) {
- aborted.mHandler.onTransitionConsumed(transition, true /* aborted */);
+ aborted.mHandler.onTransitionConsumed(
+ transition, true /* aborted */, null /* finishTransaction */);
}
mOrganizer.finishTransition(aborted.mToken, null /* wct */, null /* wctCB */);
}
@@ -765,8 +776,13 @@ public class Transitions implements RemoteCallable<Transitions> {
* Called when a transition which was already "claimed" by this handler has been merged
* into another animation or has been aborted. Gives this handler a chance to clean-up any
* expectations.
+ *
+ * @param transition The transition been consumed.
+ * @param aborted Whether the transition is aborted or not.
+ * @param finishTransaction The transaction to be applied after the transition animated.
*/
- default void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) { }
+ default void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+ @Nullable SurfaceControl.Transaction finishTransaction) { }
/**
* Sets transition animation scale settings value to handler.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 4380bdc36417..506a4c0f90f3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -66,6 +66,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
final DisplayController mDisplayController;
final ShellTaskOrganizer mTaskOrganizer;
final Supplier<SurfaceControl.Builder> mSurfaceControlBuilderSupplier;
+ final Supplier<WindowContainerTransaction> mWindowContainerTransactionSupplier;
final SurfaceControlViewHostFactory mSurfaceControlViewHostFactory;
private final DisplayController.OnDisplaysChangedListener mOnDisplaysChangedListener =
new DisplayController.OnDisplaysChangedListener() {
@@ -102,7 +103,8 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
RunningTaskInfo taskInfo,
SurfaceControl taskSurface) {
this(context, displayController, taskOrganizer, taskInfo, taskSurface,
- SurfaceControl.Builder::new, new SurfaceControlViewHostFactory() {});
+ SurfaceControl.Builder::new, WindowContainerTransaction::new,
+ new SurfaceControlViewHostFactory() {});
}
WindowDecoration(
@@ -112,6 +114,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
+ Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
SurfaceControlViewHostFactory surfaceControlViewHostFactory) {
mContext = context;
mDisplayController = displayController;
@@ -119,6 +122,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
mTaskInfo = taskInfo;
mTaskSurface = taskSurface;
mSurfaceControlBuilderSupplier = surfaceControlBuilderSupplier;
+ mWindowContainerTransactionSupplier = windowContainerTransactionSupplier;
mSurfaceControlViewHostFactory = surfaceControlViewHostFactory;
mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId);
@@ -229,7 +233,9 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
mTmpColor[2] = (float) Color.blue(backgroundColorInt) / 255.f;
startT.setWindowCrop(mTaskBackgroundSurface, taskBounds.width(), taskBounds.height())
.setShadowRadius(mTaskBackgroundSurface, shadowRadius)
- .setColor(mTaskBackgroundSurface, mTmpColor);
+ .setColor(mTaskBackgroundSurface, mTmpColor)
+ .setLayer(mTaskBackgroundSurface, -1)
+ .show(mTaskBackgroundSurface);
// Caption view
mCaptionWindowManager.setConfiguration(taskConfig);
@@ -301,6 +307,10 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
mTaskBackgroundSurface.release();
mTaskBackgroundSurface = null;
}
+
+ final WindowContainerTransaction wct = mWindowContainerTransactionSupplier.get();
+ wct.removeInsetsProvider(mTaskInfo.token, CAPTION_INSETS_TYPES);
+ mTaskOrganizer.applyTransaction(wct);
}
@Override
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 6c9ea7b8c43a..4877442bacf7 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
@@ -21,6 +21,7 @@ import android.graphics.Point
import android.os.SystemClock
import android.view.InputDevice
import android.view.MotionEvent
+import android.view.ViewConfiguration
import androidx.test.uiautomator.By
import androidx.test.uiautomator.BySelector
import androidx.test.uiautomator.UiDevice
@@ -43,12 +44,15 @@ class SplitScreenHelper(
const val TIMEOUT_MS = 3_000L
const val DRAG_DURATION_MS = 1_000L
const val NOTIFICATION_SCROLLER = "notification_stack_scroller"
+ const val DIVIDER_BAR = "docked_divider_handle"
const val GESTURE_STEP_MS = 16L
private val notificationScrollerSelector: BySelector
get() = By.res(SYSTEM_UI_PACKAGE_NAME, NOTIFICATION_SCROLLER)
private val notificationContentSelector: BySelector
get() = By.text("Notification content")
+ private val dividerBarSelector: BySelector
+ get() = By.res(SYSTEM_UI_PACKAGE_NAME, DIVIDER_BAR)
fun getPrimary(instrumentation: Instrumentation): SplitScreenHelper =
SplitScreenHelper(
@@ -79,16 +83,16 @@ class SplitScreenHelper(
)
fun waitForSplitComplete(
- wmHelper: WindowManagerStateHelper,
- primaryApp: IComponentMatcher,
- secondaryApp: IComponentMatcher,
+ wmHelper: WindowManagerStateHelper,
+ primaryApp: IComponentMatcher,
+ secondaryApp: IComponentMatcher,
) {
wmHelper.StateSyncBuilder()
- .withAppTransitionIdle()
- .withWindowSurfaceAppeared(primaryApp)
- .withWindowSurfaceAppeared(secondaryApp)
- .withSplitDividerVisible()
- .waitForAndVerify()
+ .withAppTransitionIdle()
+ .withWindowSurfaceAppeared(primaryApp)
+ .withWindowSurfaceAppeared(secondaryApp)
+ .withSplitDividerVisible()
+ .waitForAndVerify()
}
fun dragFromNotificationToSplit(
@@ -215,5 +219,26 @@ class SplitScreenHelper(
allApps.unfreeze()
}
}
+
+ fun dragDividerToDismissSplit(
+ 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 * 4 / 5, displayBounds.height * 4 / 5))
+ }
+
+ fun doubleTapDividerToSwitch(device: UiDevice) {
+ val dividerBar = device.wait(Until.findObject(dividerBarSelector), TIMEOUT_MS)
+ val interval = (ViewConfiguration.getDoubleTapTimeout() +
+ ViewConfiguration.getDoubleTapMinTime()) / 2
+ dividerBar.click()
+ SystemClock.sleep(interval.toLong())
+ dividerBar.click()
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
index 77dcbe0b489b..b71a9d85d8cc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
@@ -69,7 +69,7 @@ class ExitPipWithDismissButtonTest(testSpec: FlickerTestParameter) : ExitPipTran
*/
@Presubmit
@Test
- fun focusDoesNotChange() {
+ fun focusChanges() {
testSpec.assertEventLog {
this.focusChanges("PipMenuView", "NexusLauncherActivity")
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
new file mode 100644
index 000000000000..cd92db74af95
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
@@ -0,0 +1,204 @@
+/*
+ * 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.server.wm.flicker.helpers.WindowUtils
+import com.android.wm.shell.flicker.appWindowBecomesInvisible
+import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import com.android.wm.shell.flicker.layerBecomesInvisible
+import com.android.wm.shell.flicker.layerIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesInvisible
+import com.android.wm.shell.flicker.splitScreenDividerBecomesInvisible
+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 dismiss split screen by dragging the divider bar.
+ *
+ * To run this test: `atest WMShellFlickerTests:DismissSplitScreenByDivider`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+class DismissSplitScreenByDivider (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.dragDividerToDismissSplit(device, wmHelper)
+ wmHelper.StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withFullScreenApp(secondaryApp)
+ .waitForAndVerify()
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun splitScreenDividerBecomesInvisible() = testSpec.splitScreenDividerBecomesInvisible()
+
+ @Presubmit
+ @Test
+ fun primaryAppLayerBecomesInvisible() = testSpec.layerBecomesInvisible(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(secondaryApp)
+
+ @Presubmit
+ @Test
+ fun primaryAppBoundsBecomesInvisible() = testSpec.splitAppLayerBoundsBecomesInvisible(
+ primaryApp, splitLeftTop = false)
+
+ @Presubmit
+ @Test
+ fun secondaryAppBoundsIsFullscreenAtEnd() {
+ testSpec.assertLayers {
+ this.isVisible(secondaryApp)
+ .then()
+ .isInvisible(secondaryApp)
+ .then()
+ .isVisible(secondaryApp)
+ .invoke("secondaryAppBoundsIsFullscreenAtEnd") {
+ val displayBounds = WindowUtils.getDisplayBounds(testSpec.endRotation)
+ it.visibleRegion(secondaryApp).coversExactly(displayBounds)
+ }
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun primaryAppWindowBecomesInvisible() = testSpec.appWindowBecomesInvisible(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(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/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
index 4f48ff021bb2..127ac1e7162b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
@@ -66,24 +66,24 @@ class DismissSplitScreenByGoHome(
primaryApp.launchViaIntent(wmHelper)
// TODO(b/231399940): Use recent shortcut to enter split.
tapl.launchedAppState.taskbar
- .openAllApps()
- .getAppIcon(secondaryApp.appName)
- .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+ .openAllApps()
+ .getAppIcon(secondaryApp.appName)
+ .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
}
transitions {
tapl.goHome()
wmHelper.StateSyncBuilder()
- .withAppTransitionIdle()
- .withHomeActivityVisible()
- .waitForAndVerify()
+ .withAppTransitionIdle()
+ .withHomeActivityVisible()
+ .waitForAndVerify()
}
}
@Presubmit
@Test
- fun splitScreenDividerBecomesVisible() = testSpec.splitScreenDividerBecomesInvisible()
+ fun splitScreenDividerBecomesInvisible() = testSpec.splitScreenDividerBecomesInvisible()
@Presubmit
@Test
@@ -115,67 +115,67 @@ class DismissSplitScreenByGoHome(
@Postsubmit
@Test
override fun entireScreenCovered() =
- super.entireScreenCovered()
+ super.entireScreenCovered()
/** {@inheritDoc} */
@Postsubmit
@Test
override fun navBarLayerIsVisibleAtStartAndEnd() =
- super.navBarLayerIsVisibleAtStartAndEnd()
+ super.navBarLayerIsVisibleAtStartAndEnd()
/** {@inheritDoc} */
@Postsubmit
@Test
override fun navBarLayerPositionAtStartAndEnd() =
- super.navBarLayerPositionAtStartAndEnd()
+ super.navBarLayerPositionAtStartAndEnd()
/** {@inheritDoc} */
@Postsubmit
@Test
override fun navBarWindowIsAlwaysVisible() =
- super.navBarWindowIsAlwaysVisible()
+ super.navBarWindowIsAlwaysVisible()
/** {@inheritDoc} */
@Postsubmit
@Test
override fun statusBarLayerIsVisibleAtStartAndEnd() =
- super.statusBarLayerIsVisibleAtStartAndEnd()
+ super.statusBarLayerIsVisibleAtStartAndEnd()
/** {@inheritDoc} */
@Postsubmit
@Test
override fun statusBarLayerPositionAtStartAndEnd() =
- super.statusBarLayerPositionAtStartAndEnd()
+ super.statusBarLayerPositionAtStartAndEnd()
/** {@inheritDoc} */
@Postsubmit
@Test
override fun statusBarWindowIsAlwaysVisible() =
- super.statusBarWindowIsAlwaysVisible()
+ super.statusBarWindowIsAlwaysVisible()
/** {@inheritDoc} */
@Postsubmit
@Test
override fun taskBarLayerIsVisibleAtStartAndEnd() =
- super.taskBarLayerIsVisibleAtStartAndEnd()
+ super.taskBarLayerIsVisibleAtStartAndEnd()
/** {@inheritDoc} */
@Postsubmit
@Test
override fun taskBarWindowIsAlwaysVisible() =
- super.taskBarWindowIsAlwaysVisible()
+ super.taskBarWindowIsAlwaysVisible()
/** {@inheritDoc} */
@Postsubmit
@Test
override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
/** {@inheritDoc} */
@Postsubmit
@Test
override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
new file mode 100644
index 000000000000..38279a3dfd17
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.splitscreen
+
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
+import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import com.android.wm.shell.flicker.layerIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
+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 double tap the divider bar to switch the two apps.
+ *
+ * To run this test: `atest WMShellFlickerTests:SwitchAppByDoubleTapDivider`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+class SwitchAppByDoubleTapDivider (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)
+ }
+ }
+ transitions {
+ SplitScreenHelper.doubleTapDividerToSwitch(device)
+ wmHelper.StateSyncBuilder()
+ .withAppTransitionIdle()
+ .waitForAndVerify()
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun splitScreenDividerKeepVisible() {
+ testSpec.assertLayers {
+ this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun primaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(secondaryApp)
+
+ @Presubmit
+ @Test
+ fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
+ primaryApp, splitLeftTop = true)
+
+ @Presubmit
+ @Test
+ fun secondaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
+ secondaryApp, splitLeftTop = false)
+
+ @Presubmit
+ @Test
+ fun primaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(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
new file mode 100644
index 000000000000..c48f3f77858b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
@@ -0,0 +1,195 @@
+/*
+ * 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.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
+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 after go home
+ *
+ * To run this test: `atest WMShellFlickerTests:SwitchBackToSplitFromHome`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+class SwitchBackToSplitFromHome(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.quickSwitchToPreviousApp()
+ SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun splitScreenDividerBecomesVisible() = testSpec.splitScreenDividerBecomesVisible()
+
+ @Presubmit
+ @Test
+ fun primaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(secondaryApp)
+
+ @Presubmit
+ @Test
+ fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
+ primaryApp, splitLeftTop = false)
+
+ @Presubmit
+ @Test
+ fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisible(
+ secondaryApp, splitLeftTop = true)
+
+ @Presubmit
+ @Test
+ fun primaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(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/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index 7517e8ab6826..f865649b6bbc 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -59,6 +59,7 @@ import androidx.test.filters.SmallTest;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.compatui.CompatUIController;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellInit;
import org.junit.Before;
@@ -85,10 +86,12 @@ public class ShellTaskOrganizerTests extends ShellTestCase {
@Mock
private CompatUIController mCompatUI;
@Mock
- private ShellInit mShellInit;
+ private ShellExecutor mTestExecutor;
+ @Mock
+ private ShellCommandHandler mShellCommandHandler;
- ShellTaskOrganizer mOrganizer;
- private final ShellExecutor mTestExecutor = mock(ShellExecutor.class);
+ private ShellTaskOrganizer mOrganizer;
+ private ShellInit mShellInit;
private class TrackingTaskListener implements ShellTaskOrganizer.TaskListener {
final ArrayList<RunningTaskInfo> appeared = new ArrayList<>();
@@ -132,8 +135,11 @@ public class ShellTaskOrganizerTests extends ShellTestCase {
doReturn(ParceledListSlice.<TaskAppearedInfo>emptyList())
.when(mTaskOrganizerController).registerTaskOrganizer(any());
} catch (RemoteException e) {}
- mOrganizer = spy(new ShellTaskOrganizer(mShellInit, mTaskOrganizerController,
- mCompatUI, Optional.empty(), Optional.empty(), mTestExecutor));
+ mShellInit = spy(new ShellInit(mTestExecutor));
+ mOrganizer = spy(new ShellTaskOrganizer(mShellInit, mShellCommandHandler,
+ mTaskOrganizerController, mCompatUI, Optional.empty(), Optional.empty(),
+ mTestExecutor));
+ mShellInit.init();
}
@Test
@@ -142,9 +148,12 @@ public class ShellTaskOrganizerTests extends ShellTestCase {
}
@Test
- public void testRegisterOrganizer_sendRegisterTaskOrganizer() throws RemoteException {
- mOrganizer.registerOrganizer();
+ public void instantiate_addDumpCallback() {
+ verify(mShellCommandHandler, times(1)).addDumpCallback(any(), any());
+ }
+ @Test
+ public void testInit_sendRegisterTaskOrganizer() throws RemoteException {
verify(mTaskOrganizerController).registerTaskOrganizer(any(ITaskOrganizer.class));
}
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 ba81602054a8..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
@@ -29,6 +29,7 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -61,6 +62,7 @@ import androidx.test.platform.app.InstrumentationRegistry;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
+import com.android.wm.shell.sysui.ShellInit;
import org.junit.Before;
import org.junit.Ignore;
@@ -81,6 +83,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
private static final String ANIMATION_ENABLED = "1";
private final TestShellExecutor mShellExecutor = new TestShellExecutor();
+ private ShellInit mShellInit;
@Rule
public TestableContext mContext =
@@ -110,10 +113,12 @@ public class BackAnimationControllerTest extends ShellTestCase {
Settings.Global.putString(mContentResolver, Settings.Global.ENABLE_BACK_ANIMATION,
ANIMATION_ENABLED);
mTestableLooper = TestableLooper.get(this);
- mController = new BackAnimationController(
+ mShellInit = spy(new ShellInit(mShellExecutor));
+ mController = new BackAnimationController(mShellInit,
mShellExecutor, new Handler(mTestableLooper.getLooper()), mTransaction,
mActivityTaskManager, mContext,
mContentResolver);
+ mShellInit.init();
mEventTime = 0;
mShellExecutor.flushAll();
}
@@ -160,6 +165,11 @@ public class BackAnimationControllerTest extends ShellTestCase {
}
@Test
+ public void instantiateController_addInitCallback() {
+ verify(mShellInit, times(1)).addInitCallback(any(), any());
+ }
+
+ @Test
@Ignore("b/207481538")
public void crossActivity_screenshotAttachedAndVisible() {
SurfaceControl screenshotSurface = new SurfaceControl();
@@ -233,10 +243,12 @@ public class BackAnimationControllerTest extends ShellTestCase {
public void animationDisabledFromSettings() throws RemoteException {
// Toggle the setting off
Settings.Global.putString(mContentResolver, Settings.Global.ENABLE_BACK_ANIMATION, "0");
- mController = new BackAnimationController(
+ ShellInit shellInit = new ShellInit(mShellExecutor);
+ mController = new BackAnimationController(shellInit,
mShellExecutor, new Handler(mTestableLooper.getLooper()), mTransaction,
mActivityTaskManager, mContext,
mContentResolver);
+ shellInit.init();
mController.setBackToLauncherCallback(mIOnBackInvokedCallback);
RemoteAnimationTarget animationTarget = createAnimationTarget();
@@ -272,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/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index 828c13ecfda6..6292130ddec9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -29,6 +29,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -54,6 +55,7 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.compatui.letterboxedu.LetterboxEduWindowManager;
import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
@@ -79,6 +81,7 @@ public class CompatUIControllerTest extends ShellTestCase {
private static final int TASK_ID = 12;
private CompatUIController mController;
+ private ShellInit mShellInit;
private @Mock ShellController mMockShellController;
private @Mock DisplayController mMockDisplayController;
private @Mock DisplayInsetsController mMockDisplayInsetsController;
@@ -107,9 +110,10 @@ public class CompatUIControllerTest extends ShellTestCase {
doReturn(TASK_ID).when(mMockLetterboxEduLayout).getTaskId();
doReturn(true).when(mMockLetterboxEduLayout).createLayout(anyBoolean());
doReturn(true).when(mMockLetterboxEduLayout).updateCompatInfo(any(), any(), anyBoolean());
- mController = new CompatUIController(mContext, mMockShellController, mMockDisplayController,
- mMockDisplayInsetsController, mMockImeController, mMockSyncQueue, mMockExecutor,
- mMockTransitionsLazy) {
+ mShellInit = spy(new ShellInit(mMockExecutor));
+ mController = new CompatUIController(mContext, mShellInit, mMockShellController,
+ mMockDisplayController, mMockDisplayInsetsController, mMockImeController,
+ mMockSyncQueue, mMockExecutor, mMockTransitionsLazy) {
@Override
CompatUIWindowManager createCompatUiWindowManager(Context context, TaskInfo taskInfo,
ShellTaskOrganizer.TaskListener taskListener) {
@@ -122,10 +126,16 @@ public class CompatUIControllerTest extends ShellTestCase {
return mMockLetterboxEduLayout;
}
};
+ mShellInit.init();
spyOn(mController);
}
@Test
+ public void instantiateController_addInitCallback() {
+ verify(mShellInit, times(1)).addInitCallback(any(), any());
+ }
+
+ @Test
public void instantiateController_registerKeyguardChangeListener() {
verify(mMockShellController, times(1)).addKeyguardChangeListener(any());
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutControllerTest.java
index dcc504ad0cdb..6c301bbbc7f1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutControllerTest.java
@@ -17,7 +17,9 @@
package com.android.wm.shell.hidedisplaycutout;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -29,7 +31,10 @@ import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
import org.junit.Before;
import org.junit.Test;
@@ -45,17 +50,32 @@ public class HideDisplayCutoutControllerTest extends ShellTestCase {
InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
@Mock
+ private ShellCommandHandler mShellCommandHandler;
+ @Mock
private ShellController mShellController;
@Mock
private HideDisplayCutoutOrganizer mMockDisplayAreaOrganizer;
private HideDisplayCutoutController mHideDisplayCutoutController;
+ private ShellInit mShellInit;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mHideDisplayCutoutController = new HideDisplayCutoutController(
- mContext, mShellController, mMockDisplayAreaOrganizer);
+ mShellInit = spy(new ShellInit(mock(ShellExecutor.class)));
+ mHideDisplayCutoutController = new HideDisplayCutoutController(mContext, mShellInit,
+ mShellCommandHandler, mShellController, mMockDisplayAreaOrganizer);
+ mShellInit.init();
+ }
+
+ @Test
+ public void instantiateController_addInitCallback() {
+ verify(mShellInit, times(1)).addInitCallback(any(), any());
+ }
+
+ @Test
+ public void instantiateController_registerDumpCallback() {
+ verify(mShellCommandHandler, times(1)).addDumpCallback(any(), any());
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java
index a919ad0b4765..ecfb427dbced 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java
@@ -49,6 +49,7 @@ import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellInit;
import org.junit.Before;
@@ -74,6 +75,7 @@ public class KidsModeTaskOrganizerTest extends ShellTestCase {
@Mock private WindowContainerTransaction mTransaction;
@Mock private KidsModeSettingsObserver mObserver;
@Mock private ShellInit mShellInit;
+ @Mock private ShellCommandHandler mShellCommandHandler;
@Mock private DisplayInsetsController mDisplayInsetsController;
KidsModeTaskOrganizer mOrganizer;
@@ -87,14 +89,20 @@ public class KidsModeTaskOrganizerTest extends ShellTestCase {
} catch (RemoteException e) {
}
// NOTE: KidsModeTaskOrganizer should have a null CompatUIController.
- mOrganizer = spy(new KidsModeTaskOrganizer(mContext, mTaskOrganizerController,
- mSyncTransactionQueue, mDisplayController, mDisplayInsetsController,
- Optional.empty(), Optional.empty(), mObserver, mTestExecutor, mHandler));
+ mOrganizer = spy(new KidsModeTaskOrganizer(mContext, mShellInit, mShellCommandHandler,
+ mTaskOrganizerController, mSyncTransactionQueue, mDisplayController,
+ mDisplayInsetsController, Optional.empty(), Optional.empty(), mObserver,
+ mTestExecutor, mHandler));
doReturn(mTransaction).when(mOrganizer).getWindowContainerTransaction();
doReturn(new InsetsState()).when(mDisplayController).getInsetsState(DEFAULT_DISPLAY);
}
@Test
+ public void instantiateController_addInitCallback() {
+ verify(mShellInit, times(1)).addInitCallback(any(), any());
+ }
+
+ @Test
public void testKidsModeOn() {
doReturn(true).when(mObserver).isEnabled();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index dbf93ae35c18..90645ce4747d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -36,7 +36,6 @@ import static org.mockito.Mockito.when;
import android.graphics.Rect;
import android.os.Handler;
-import android.os.UserHandle;
import android.testing.AndroidTestingRunner;
import android.util.ArrayMap;
import android.view.Display;
@@ -49,7 +48,9 @@ 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.TaskStackListenerImpl;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
import org.junit.Before;
import org.junit.Test;
@@ -61,18 +62,20 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class OneHandedControllerTest extends OneHandedTestCase {
- private int mCurrentUser = UserHandle.myUserId();
Display mDisplay;
OneHandedAccessibilityUtil mOneHandedAccessibilityUtil;
OneHandedController mSpiedOneHandedController;
OneHandedTimeoutHandler mSpiedTimeoutHandler;
OneHandedState mSpiedTransitionState;
+ ShellInit mShellInit;
@Mock
+ ShellCommandHandler mMockShellCommandHandler;
+ @Mock
ShellController mMockShellController;
@Mock
- DisplayLayout mDisplayLayout;
+ DisplayLayout mMockDisplayLayout;
@Mock
DisplayController mMockDisplayController;
@Mock
@@ -102,7 +105,7 @@ public class OneHandedControllerTest extends OneHandedTestCase {
public void setUp() {
MockitoAnnotations.initMocks(this);
mDisplay = mContext.getDisplay();
- mDisplayLayout = Mockito.mock(DisplayLayout.class);
+ mMockDisplayLayout = Mockito.mock(DisplayLayout.class);
mSpiedTimeoutHandler = spy(new OneHandedTimeoutHandler(mMockShellMainExecutor));
mSpiedTransitionState = spy(new OneHandedState());
@@ -122,11 +125,14 @@ public class OneHandedControllerTest extends OneHandedTestCase {
when(mMockDisplayAreaOrganizer.getLastDisplayBounds()).thenReturn(
new Rect(0, 0, 1080, 2400));
- when(mMockDisplayAreaOrganizer.getDisplayLayout()).thenReturn(mDisplayLayout);
+ when(mMockDisplayAreaOrganizer.getDisplayLayout()).thenReturn(mMockDisplayLayout);
+ mShellInit = spy(new ShellInit(mMockShellMainExecutor));
mOneHandedAccessibilityUtil = new OneHandedAccessibilityUtil(mContext);
mSpiedOneHandedController = spy(new OneHandedController(
mContext,
+ mShellInit,
+ mMockShellCommandHandler,
mMockShellController,
mMockDisplayController,
mMockDisplayAreaOrganizer,
@@ -141,6 +147,17 @@ public class OneHandedControllerTest extends OneHandedTestCase {
mMockShellMainExecutor,
mMockShellMainHandler)
);
+ mShellInit.init();
+ }
+
+ @Test
+ public void instantiateController_addInitCallback() {
+ verify(mShellInit, times(1)).addInitCallback(any(), any());
+ }
+
+ @Test
+ public void instantiateController_registerDumpCallback() {
+ verify(mMockShellCommandHandler, times(1)).addDumpCallback(any(), any());
}
@Test
@@ -308,9 +325,9 @@ public class OneHandedControllerTest extends OneHandedTestCase {
@Test
public void testRotation90CanNotStartOneHanded() {
- mDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_90);
+ mMockDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_90);
mSpiedTransitionState.setState(STATE_NONE);
- when(mDisplayLayout.isLandscape()).thenReturn(true);
+ when(mMockDisplayLayout.isLandscape()).thenReturn(true);
mSpiedOneHandedController.setOneHandedEnabled(true);
mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */);
mSpiedOneHandedController.startOneHanded();
@@ -320,10 +337,10 @@ public class OneHandedControllerTest extends OneHandedTestCase {
@Test
public void testRotation180CanStartOneHanded() {
- mDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_180);
+ mMockDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_180);
mSpiedTransitionState.setState(STATE_NONE);
when(mMockDisplayAreaOrganizer.isReady()).thenReturn(true);
- when(mDisplayLayout.isLandscape()).thenReturn(false);
+ when(mMockDisplayLayout.isLandscape()).thenReturn(false);
mSpiedOneHandedController.setOneHandedEnabled(true);
mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */);
mSpiedOneHandedController.startOneHanded();
@@ -333,9 +350,9 @@ public class OneHandedControllerTest extends OneHandedTestCase {
@Test
public void testRotation270CanNotStartOneHanded() {
- mDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_270);
+ mMockDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_270);
mSpiedTransitionState.setState(STATE_NONE);
- when(mDisplayLayout.isLandscape()).thenReturn(true);
+ when(mMockDisplayLayout.isLandscape()).thenReturn(true);
mSpiedOneHandedController.setOneHandedEnabled(true);
mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */);
mSpiedOneHandedController.startOneHanded();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
index e6a8220e081b..a39bdf04bf56 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
@@ -41,7 +41,9 @@ 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.TaskStackListenerImpl;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
import org.junit.Before;
import org.junit.Test;
@@ -61,6 +63,10 @@ public class OneHandedStateTest extends OneHandedTestCase {
OneHandedState mSpiedState;
@Mock
+ ShellInit mMockShellInit;
+ @Mock
+ ShellCommandHandler mMockShellCommandHandler;
+ @Mock
ShellController mMockShellController;
@Mock
DisplayController mMockDisplayController;
@@ -111,6 +117,8 @@ public class OneHandedStateTest extends OneHandedTestCase {
mOneHandedAccessibilityUtil = new OneHandedAccessibilityUtil(mContext);
mSpiedOneHandedController = spy(new OneHandedController(
mContext,
+ mMockShellInit,
+ mMockShellCommandHandler,
mMockShellController,
mMockDisplayController,
mMockDisplayAreaOrganizer,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 857f578fd8ed..579638d28311 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -21,13 +21,13 @@ import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTI
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.app.ActivityManager;
@@ -70,7 +70,7 @@ import java.util.Optional;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class PipTaskOrganizerTest extends ShellTestCase {
- private PipTaskOrganizer mSpiedPipTaskOrganizer;
+ private PipTaskOrganizer mPipTaskOrganizer;
@Mock private DisplayController mMockDisplayController;
@Mock private SyncTransactionQueue mMockSyncTransactionQueue;
@@ -100,14 +100,15 @@ public class PipTaskOrganizerTest extends ShellTestCase {
mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState,
new PipSnapAlgorithm());
mMainExecutor = new TestShellExecutor();
- mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mContext,
+ mPipTaskOrganizer = new PipTaskOrganizer(mContext,
mMockSyncTransactionQueue, mPipTransitionState, mPipBoundsState,
mPipBoundsAlgorithm, mMockPhonePipMenuController, mMockPipAnimationController,
mMockPipSurfaceTransactionHelper, mMockPipTransitionController,
mMockPipParamsChangedForwarder, mMockOptionalSplitScreen, mMockDisplayController,
- mMockPipUiEventLogger, mMockShellTaskOrganizer, mMainExecutor));
+ mMockPipUiEventLogger, mMockShellTaskOrganizer, mMainExecutor);
mMainExecutor.flushAll();
preparePipTaskOrg();
+ preparePipSurfaceTransactionHelper();
}
@Test
@@ -124,14 +125,14 @@ public class PipTaskOrganizerTest extends ShellTestCase {
public void startSwipePipToHome_updatesAspectRatio() {
final Rational aspectRatio = new Rational(2, 1);
- mSpiedPipTaskOrganizer.startSwipePipToHome(mComponent1, null, createPipParams(aspectRatio));
+ mPipTaskOrganizer.startSwipePipToHome(mComponent1, null, createPipParams(aspectRatio));
assertEquals(aspectRatio.floatValue(), mPipBoundsState.getAspectRatio(), 0.01f);
}
@Test
public void startSwipePipToHome_updatesLastPipComponentName() {
- mSpiedPipTaskOrganizer.startSwipePipToHome(mComponent1, null, createPipParams(null));
+ mPipTaskOrganizer.startSwipePipToHome(mComponent1, null, createPipParams(null));
assertEquals(mComponent1, mPipBoundsState.getLastPipComponentName());
}
@@ -140,7 +141,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
public void startSwipePipToHome_updatesOverrideMinSize() {
final Size minSize = new Size(400, 320);
- mSpiedPipTaskOrganizer.startSwipePipToHome(mComponent1, createActivityInfo(minSize),
+ mPipTaskOrganizer.startSwipePipToHome(mComponent1, createActivityInfo(minSize),
createPipParams(null));
assertEquals(minSize, mPipBoundsState.getOverrideMinSize());
@@ -150,7 +151,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
public void onTaskAppeared_updatesAspectRatio() {
final Rational aspectRatio = new Rational(2, 1);
- mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
+ mPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
createPipParams(aspectRatio)), mock(SurfaceControl.class));
assertEquals(aspectRatio.floatValue(), mPipBoundsState.getAspectRatio(), 0.01f);
@@ -158,7 +159,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
@Test
public void onTaskAppeared_updatesLastPipComponentName() {
- mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1, createPipParams(null)),
+ mPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1, createPipParams(null)),
mock(SurfaceControl.class));
assertEquals(mComponent1, mPipBoundsState.getLastPipComponentName());
@@ -168,7 +169,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
public void onTaskAppeared_updatesOverrideMinSize() {
final Size minSize = new Size(400, 320);
- mSpiedPipTaskOrganizer.onTaskAppeared(
+ mPipTaskOrganizer.onTaskAppeared(
createTaskInfo(mComponent1, createPipParams(null), minSize),
mock(SurfaceControl.class));
@@ -179,16 +180,16 @@ public class PipTaskOrganizerTest extends ShellTestCase {
public void onTaskInfoChanged_notInPip_deferUpdatesAspectRatio() {
final Rational startAspectRatio = new Rational(2, 1);
final Rational newAspectRatio = new Rational(1, 2);
- mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
+ mPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
createPipParams(startAspectRatio)), mock(SurfaceControl.class));
// It is in entering transition, should defer onTaskInfoChanged callback in this case.
- mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent1,
+ mPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent1,
createPipParams(newAspectRatio)));
verify(mMockPipParamsChangedForwarder, never()).notifyAspectRatioChanged(anyFloat());
// Once the entering transition finishes, the new aspect ratio applies in a deferred manner
- mSpiedPipTaskOrganizer.sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
+ sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
verify(mMockPipParamsChangedForwarder)
.notifyAspectRatioChanged(newAspectRatio.floatValue());
}
@@ -197,11 +198,11 @@ public class PipTaskOrganizerTest extends ShellTestCase {
public void onTaskInfoChanged_inPip_updatesAspectRatioIfChanged() {
final Rational startAspectRatio = new Rational(2, 1);
final Rational newAspectRatio = new Rational(1, 2);
- mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
+ mPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
createPipParams(startAspectRatio)), mock(SurfaceControl.class));
- mSpiedPipTaskOrganizer.sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
+ sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
- mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent1,
+ mPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent1,
createPipParams(newAspectRatio)));
verify(mMockPipParamsChangedForwarder)
@@ -210,11 +211,11 @@ public class PipTaskOrganizerTest extends ShellTestCase {
@Test
public void onTaskInfoChanged_inPip_updatesLastPipComponentName() {
- mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
+ mPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
createPipParams(null)), mock(SurfaceControl.class));
- mSpiedPipTaskOrganizer.sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
+ sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
- mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent2,
+ mPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent2,
createPipParams(null)));
assertEquals(mComponent2, mPipBoundsState.getLastPipComponentName());
@@ -222,12 +223,12 @@ public class PipTaskOrganizerTest extends ShellTestCase {
@Test
public void onTaskInfoChanged_inPip_updatesOverrideMinSize() {
- mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
+ mPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
createPipParams(null)), mock(SurfaceControl.class));
- mSpiedPipTaskOrganizer.sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
+ sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
final Size minSize = new Size(400, 320);
- mSpiedPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent2,
+ mPipTaskOrganizer.onTaskInfoChanged(createTaskInfo(mComponent2,
createPipParams(null), minSize));
assertEquals(minSize, mPipBoundsState.getOverrideMinSize());
@@ -235,23 +236,42 @@ public class PipTaskOrganizerTest extends ShellTestCase {
@Test
public void onTaskVanished_clearsPipBounds() {
- mSpiedPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
+ mPipTaskOrganizer.onTaskAppeared(createTaskInfo(mComponent1,
createPipParams(null)), mock(SurfaceControl.class));
mPipBoundsState.setBounds(new Rect(100, 100, 200, 150));
- mSpiedPipTaskOrganizer.onTaskVanished(createTaskInfo(mComponent1, createPipParams(null)));
+ mPipTaskOrganizer.onTaskVanished(createTaskInfo(mComponent1, createPipParams(null)));
assertTrue(mPipBoundsState.getBounds().isEmpty());
}
+ private void sendOnPipTransitionFinished(
+ @PipAnimationController.TransitionDirection int direction) {
+ mPipTaskOrganizer.sendOnPipTransitionFinished(direction);
+ // PipTransitionController will call back into PipTaskOrganizer.
+ mPipTaskOrganizer.mPipTransitionCallback.onPipTransitionFinished(direction);
+ }
+
private void preparePipTaskOrg() {
final DisplayInfo info = new DisplayInfo();
mPipBoundsState.setDisplayLayout(new DisplayLayout(info,
mContext.getResources(), true, true));
- mSpiedPipTaskOrganizer.setOneShotAnimationType(PipAnimationController.ANIM_TYPE_ALPHA);
- mSpiedPipTaskOrganizer.setSurfaceControlTransactionFactory(
+ mPipTaskOrganizer.setOneShotAnimationType(PipAnimationController.ANIM_TYPE_ALPHA);
+ mPipTaskOrganizer.setSurfaceControlTransactionFactory(
MockSurfaceControlHelper::createMockSurfaceControlTransaction);
- doNothing().when(mSpiedPipTaskOrganizer).enterPipWithAlphaAnimation(any(), anyLong());
- doNothing().when(mSpiedPipTaskOrganizer).scheduleAnimateResizePip(any(), anyInt(), any());
+ }
+
+ private void preparePipSurfaceTransactionHelper() {
+ doReturn(mMockPipSurfaceTransactionHelper).when(mMockPipSurfaceTransactionHelper)
+ .crop(any(), any(), any());
+ doReturn(mMockPipSurfaceTransactionHelper).when(mMockPipSurfaceTransactionHelper)
+ .resetScale(any(), any(), any());
+ doReturn(mMockPipSurfaceTransactionHelper).when(mMockPipSurfaceTransactionHelper)
+ .round(any(), any(), anyBoolean());
+ doReturn(mMockPipSurfaceTransactionHelper).when(mMockPipSurfaceTransactionHelper)
+ .scale(any(), any(), any(), any(), anyFloat());
+ doReturn(mMockPipSurfaceTransactionHelper).when(mMockPipSurfaceTransactionHelper)
+ .alpha(any(), any(), anyFloat());
+ doNothing().when(mMockPipSurfaceTransactionHelper).onDensityOrFontScaleChanged(any());
}
private static ActivityManager.RunningTaskInfo createTaskInfo(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index f192514c37ab..9ed8d84d665f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -55,7 +55,9 @@ import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
import org.junit.Before;
import org.junit.Test;
@@ -74,7 +76,9 @@ import java.util.Set;
@TestableLooper.RunWithLooper
public class PipControllerTest extends ShellTestCase {
private PipController mPipController;
+ private ShellInit mShellInit;
+ @Mock private ShellCommandHandler mMockShellCommandHandler;
@Mock private ShellController mMockShellController;
@Mock private DisplayController mMockDisplayController;
@Mock private PhonePipMenuController mMockPhonePipMenuController;
@@ -105,19 +109,31 @@ public class PipControllerTest extends ShellTestCase {
((Runnable) invocation.getArgument(0)).run();
return null;
}).when(mMockExecutor).execute(any());
- mPipController = new PipController(mContext, mMockShellController, mMockDisplayController,
- mMockPipAppOpsListener, mMockPipBoundsAlgorithm,
- mMockPipKeepClearAlgorithm,
+ mShellInit = spy(new ShellInit(mMockExecutor));
+ mPipController = new PipController(mContext, mShellInit, mMockShellCommandHandler,
+ mMockShellController, mMockDisplayController, mMockPipAppOpsListener,
+ mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTransitionState,
mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper,
mMockTaskStackListener, mPipParamsChangedForwarder,
mMockOneHandedController, mMockExecutor);
+ mShellInit.init();
when(mMockPipBoundsAlgorithm.getSnapAlgorithm()).thenReturn(mMockPipSnapAlgorithm);
when(mMockPipTouchHandler.getMotionHelper()).thenReturn(mMockPipMotionHelper);
}
@Test
+ public void instantiatePipController_addInitCallback() {
+ verify(mShellInit, times(1)).addInitCallback(any(), any());
+ }
+
+ @Test
+ public void instantiateController_registerDumpCallback() {
+ verify(mMockShellCommandHandler, times(1)).addDumpCallback(any(), any());
+ }
+
+ @Test
public void instantiatePipController_registerConfigChangeListener() {
verify(mMockShellController, times(1)).addConfigurationChangeListener(any());
}
@@ -149,9 +165,10 @@ public class PipControllerTest extends ShellTestCase {
when(mockPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)).thenReturn(false);
when(spyContext.getPackageManager()).thenReturn(mockPackageManager);
- assertNull(PipController.create(spyContext, mMockShellController, mMockDisplayController,
- mMockPipAppOpsListener, mMockPipBoundsAlgorithm,
- mMockPipKeepClearAlgorithm,
+ ShellInit shellInit = new ShellInit(mMockExecutor);
+ assertNull(PipController.create(spyContext, shellInit, mMockShellCommandHandler,
+ mMockShellController, mMockDisplayController, mMockPipAppOpsListener,
+ mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTransitionState,
mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper,
@@ -217,7 +234,7 @@ public class PipControllerTest extends ShellTestCase {
mPipController.mDisplaysChangedListener.onDisplayConfigurationChanged(
displayId, new Configuration());
- verify(mMockPipMotionHelper).movePip(any(Rect.class));
+ verify(mMockPipTaskOrganizer).scheduleFinishResizePip(any(Rect.class));
}
@Test
@@ -233,7 +250,7 @@ public class PipControllerTest extends ShellTestCase {
mPipController.mDisplaysChangedListener.onDisplayConfigurationChanged(
displayId, new Configuration());
- verify(mMockPipMotionHelper, never()).movePip(any(Rect.class));
+ verify(mMockPipTaskOrganizer, never()).scheduleFinishResizePip(any(Rect.class));
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt
index 05e472245b4a..cc51efd7e16b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt
@@ -179,6 +179,16 @@ class TvPipBoundsControllerTest {
}
@Test
+ fun testImmediatePlacement_DoNotStashIfAlreadyUnstashed() {
+ triggerImmediatePlacement(STASHED_PLACEMENT_RESTASH)
+ assertMovement(STASHED_BOUNDS)
+ assertMovementAt(time + STASH_DURATION, ANCHOR_BOUNDS)
+
+ triggerImmediatePlacement(STASHED_PLACEMENT)
+ assertNoMovementUpTo(time + FAR_FUTURE)
+ }
+
+ @Test
fun testInMoveMode_KeepAtAnchor() {
startMoveMode()
triggerImmediatePlacement(STASHED_MOVED_PLACEMENT_RESTASH)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index d406a4ed3fd7..81bb609cc711 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -23,7 +23,9 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
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.anyInt;
+import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
@@ -48,6 +50,7 @@ import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
import com.android.wm.shell.util.SplitBounds;
@@ -73,21 +76,35 @@ public class RecentTasksControllerTest extends ShellTestCase {
@Mock
private TaskStackListenerImpl mTaskStackListener;
@Mock
- private ShellInit mShellInit;
+ private ShellCommandHandler mShellCommandHandler;
private ShellTaskOrganizer mShellTaskOrganizer;
private RecentTasksController mRecentTasksController;
+ private ShellInit mShellInit;
private ShellExecutor mMainExecutor;
@Before
public void setUp() {
mMainExecutor = new TestShellExecutor();
when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class));
+ mShellInit = spy(new ShellInit(mMainExecutor));
mRecentTasksController = spy(new RecentTasksController(mContext, mShellInit,
- mTaskStackListener, mMainExecutor));
- mShellTaskOrganizer = new ShellTaskOrganizer(mShellInit,
+ mShellCommandHandler, mTaskStackListener, mMainExecutor));
+ mShellTaskOrganizer = new ShellTaskOrganizer(mShellInit, mShellCommandHandler,
null /* sizeCompatUI */, Optional.empty(), Optional.of(mRecentTasksController),
mMainExecutor);
+ mShellInit.init();
+ }
+
+ @Test
+ public void instantiateController_addInitCallback() {
+ verify(mShellInit, times(1)).addInitCallback(any(), isA(RecentTasksController.class));
+ }
+
+ @Test
+ public void instantiateController_addDumpCallback() {
+ verify(mShellCommandHandler, times(1)).addDumpCallback(any(),
+ isA(RecentTasksController.class));
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index 10788f9df32f..5a68361c595c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -20,12 +20,14 @@ 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 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 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.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -53,6 +55,7 @@ import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.recents.RecentTasksController;
+import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
@@ -72,8 +75,9 @@ import java.util.Optional;
@RunWith(AndroidJUnit4.class)
public class SplitScreenControllerTests extends ShellTestCase {
- @Mock ShellController mShellController;
@Mock ShellInit mShellInit;
+ @Mock ShellController mShellController;
+ @Mock ShellCommandHandler mShellCommandHandler;
@Mock ShellTaskOrganizer mTaskOrganizer;
@Mock SyncTransactionQueue mSyncQueue;
@Mock RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
@@ -93,9 +97,10 @@ public class SplitScreenControllerTests extends ShellTestCase {
public void setup() {
MockitoAnnotations.initMocks(this);
mSplitScreenController = spy(new SplitScreenController(mContext, mShellInit,
- mShellController, mTaskOrganizer, mSyncQueue, mRootTDAOrganizer, mDisplayController,
- mDisplayImeController, mDisplayInsetsController, mDragAndDropController,
- mTransitions, mTransactionPool, mIconProvider, mRecentTasks, mMainExecutor));
+ mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
+ mRootTDAOrganizer, mDisplayController, mDisplayImeController,
+ mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
+ mIconProvider, mRecentTasks, mMainExecutor));
}
@Test
@@ -104,14 +109,31 @@ public class SplitScreenControllerTests extends ShellTestCase {
}
@Test
+ public void instantiateController_registerDumpCallback() {
+ doReturn(mMainExecutor).when(mTaskOrganizer).getExecutor();
+ when(mDisplayController.getDisplayLayout(anyInt())).thenReturn(new DisplayLayout());
+ mSplitScreenController.onInit();
+ verify(mShellCommandHandler, times(1)).addDumpCallback(any(), any());
+ }
+
+ @Test
+ public void instantiateController_registerCommandCallback() {
+ doReturn(mMainExecutor).when(mTaskOrganizer).getExecutor();
+ when(mDisplayController.getDisplayLayout(anyInt())).thenReturn(new DisplayLayout());
+ mSplitScreenController.onInit();
+ verify(mShellCommandHandler, times(1)).addCommandCallback(eq("splitscreen"), any(), any());
+ }
+
+ @Test
public void testControllerRegistersKeyguardChangeListener() {
+ doReturn(mMainExecutor).when(mTaskOrganizer).getExecutor();
when(mDisplayController.getDisplayLayout(anyInt())).thenReturn(new DisplayLayout());
mSplitScreenController.onInit();
verify(mShellController, times(1)).addKeyguardChangeListener(any());
}
@Test
- public void testIsLaunchingAdjacently_notInSplitScreen() {
+ public void testShouldAddMultipleTaskFlag_notInSplitScreen() {
doReturn(false).when(mSplitScreenController).isSplitScreenVisible();
doReturn(true).when(mSplitScreenController).isValidToEnterSplitScreen(any());
@@ -120,7 +142,7 @@ public class SplitScreenControllerTests extends ShellTestCase {
ActivityManager.RunningTaskInfo focusTaskInfo =
createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent);
doReturn(focusTaskInfo).when(mSplitScreenController).getFocusingTaskInfo();
- assertTrue(mSplitScreenController.isLaunchingAdjacently(
+ assertTrue(mSplitScreenController.shouldAddMultipleTaskFlag(
startIntent, SPLIT_POSITION_TOP_OR_LEFT));
// Verify launching different activity returns false.
@@ -128,28 +150,40 @@ public class SplitScreenControllerTests extends ShellTestCase {
focusTaskInfo =
createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, diffIntent);
doReturn(focusTaskInfo).when(mSplitScreenController).getFocusingTaskInfo();
- assertFalse(mSplitScreenController.isLaunchingAdjacently(
+ assertFalse(mSplitScreenController.shouldAddMultipleTaskFlag(
startIntent, SPLIT_POSITION_TOP_OR_LEFT));
}
@Test
- public void testIsLaunchingAdjacently_inSplitScreen() {
+ public void testShouldAddMultipleTaskFlag_inSplitScreen() {
doReturn(true).when(mSplitScreenController).isSplitScreenVisible();
-
- // Verify launching the same activity returns true.
Intent startIntent = createStartIntent("startActivity");
- ActivityManager.RunningTaskInfo pairingTaskInfo =
+ ActivityManager.RunningTaskInfo sameTaskInfo =
createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, startIntent);
- doReturn(pairingTaskInfo).when(mSplitScreenController).getTaskInfo(anyInt());
- assertTrue(mSplitScreenController.isLaunchingAdjacently(
- startIntent, SPLIT_POSITION_TOP_OR_LEFT));
-
- // Verify launching different activity returns false.
Intent diffIntent = createStartIntent("diffActivity");
- pairingTaskInfo =
+ ActivityManager.RunningTaskInfo differentTaskInfo =
createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, diffIntent);
- doReturn(pairingTaskInfo).when(mSplitScreenController).getTaskInfo(anyInt());
- assertFalse(mSplitScreenController.isLaunchingAdjacently(
+
+ // Verify launching the same activity return false.
+ doReturn(sameTaskInfo).when(mSplitScreenController)
+ .getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
+ assertFalse(mSplitScreenController.shouldAddMultipleTaskFlag(
+ startIntent, SPLIT_POSITION_TOP_OR_LEFT));
+
+ // Verify launching the same activity as adjacent returns true.
+ doReturn(differentTaskInfo).when(mSplitScreenController)
+ .getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
+ doReturn(sameTaskInfo).when(mSplitScreenController)
+ .getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT);
+ assertTrue(mSplitScreenController.shouldAddMultipleTaskFlag(
+ startIntent, SPLIT_POSITION_TOP_OR_LEFT));
+
+ // Verify launching different activity from adjacent returns false.
+ doReturn(differentTaskInfo).when(mSplitScreenController)
+ .getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
+ doReturn(differentTaskInfo).when(mSplitScreenController)
+ .getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT);
+ assertFalse(mSplitScreenController.shouldAddMultipleTaskFlag(
startIntent, SPLIT_POSITION_TOP_OR_LEFT));
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java
index 02311bab2e4d..39e58ffcf9c7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java
@@ -45,6 +45,8 @@ public class ShellControllerTest extends ShellTestCase {
@Mock
private ShellInit mShellInit;
@Mock
+ private ShellCommandHandler mShellCommandHandler;
+ @Mock
private ShellExecutor mExecutor;
private ShellController mController;
@@ -56,7 +58,7 @@ public class ShellControllerTest extends ShellTestCase {
MockitoAnnotations.initMocks(this);
mKeyguardChangeListener = new TestKeyguardChangeListener();
mConfigChangeListener = new TestConfigurationChangeListener();
- mController = new ShellController(mShellInit, mExecutor);
+ mController = new ShellController(mShellInit, mShellCommandHandler, mExecutor);
mController.onConfigurationChanged(getConfigurationCopy());
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index b318bb26a51d..226843eca64e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -204,6 +204,8 @@ public class WindowDecorationTests extends ShellTestCase {
verify(mMockSurfaceControlStartT)
.setColor(taskBackgroundSurface, new float[] {1.f, 1.f, 0.f});
verify(mMockSurfaceControlStartT).setShadowRadius(taskBackgroundSurface, 10);
+ verify(mMockSurfaceControlStartT).setLayer(taskBackgroundSurface, -1);
+ verify(mMockSurfaceControlStartT).show(taskBackgroundSurface);
verify(mMockSurfaceControlViewHostFactory)
.create(any(), eq(defaultDisplay), any(), anyBoolean());
@@ -233,6 +235,57 @@ public class WindowDecorationTests extends ShellTestCase {
}
@Test
+ public void testLayoutResultCalculation_visibleFocusedTaskToInvisible() {
+ final Display defaultDisplay = mock(Display.class);
+ doReturn(defaultDisplay).when(mMockDisplayController)
+ .getDisplay(Display.DEFAULT_DISPLAY);
+
+ final SurfaceControl decorContainerSurface = mock(SurfaceControl.class);
+ final SurfaceControl.Builder decorContainerSurfaceBuilder =
+ createMockSurfaceControlBuilder(decorContainerSurface);
+ mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
+ final SurfaceControl taskBackgroundSurface = mock(SurfaceControl.class);
+ final SurfaceControl.Builder taskBackgroundSurfaceBuilder =
+ createMockSurfaceControlBuilder(taskBackgroundSurface);
+ mMockSurfaceControlBuilders.add(taskBackgroundSurfaceBuilder);
+
+ final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
+ new ActivityManager.TaskDescription.Builder()
+ .setBackgroundColor(Color.YELLOW);
+ final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setDisplayId(Display.DEFAULT_DISPLAY)
+ .setTaskDescriptionBuilder(taskDescriptionBuilder)
+ .setBounds(TASK_BOUNDS)
+ .setPositionInParent(TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y)
+ .setVisible(true)
+ .build();
+ taskInfo.isFocused = true;
+ // Density is 2. Outsets are (20, 40, 60, 80) px. Shadow radius is 10px. Caption height is
+ // 64px.
+ taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
+ mOutsetsDp.set(10, 20, 30, 40);
+
+ final SurfaceControl taskSurface = mock(SurfaceControl.class);
+ final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface);
+
+ windowDecor.relayout(taskInfo);
+
+ verify(mMockSurfaceControlViewHost, never()).release();
+ verify(decorContainerSurface, never()).release();
+ verify(taskBackgroundSurface, never()).release();
+ verify(mMockWindowContainerTransaction, never())
+ .removeInsetsProvider(eq(taskInfo.token), any());
+
+ taskInfo.isVisible = false;
+ windowDecor.relayout(taskInfo);
+
+ verify(mMockSurfaceControlViewHost).release();
+ verify(decorContainerSurface).release();
+ verify(taskBackgroundSurface).release();
+ verify(mMockWindowContainerTransaction).removeInsetsProvider(eq(taskInfo.token), any());
+ }
+
+ @Test
public void testNotCrashWhenDisplayAppearsAfterTask() {
doReturn(mock(Display.class)).when(mMockDisplayController)
.getDisplay(Display.DEFAULT_DISPLAY);
@@ -282,7 +335,7 @@ public class WindowDecorationTests extends ShellTestCase {
ActivityManager.RunningTaskInfo taskInfo, SurfaceControl testSurface) {
return new TestWindowDecoration(mContext, mMockDisplayController, mMockShellTaskOrganizer,
taskInfo, testSurface, new MockSurfaceControlBuilderSupplier(),
- mMockSurfaceControlViewHostFactory);
+ () -> mMockWindowContainerTransaction, mMockSurfaceControlViewHostFactory);
}
private class MockSurfaceControlBuilderSupplier implements Supplier<SurfaceControl.Builder> {
@@ -313,9 +366,11 @@ public class WindowDecorationTests extends ShellTestCase {
ShellTaskOrganizer taskOrganizer, ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
+ Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
SurfaceControlViewHostFactory surfaceControlViewHostFactory) {
super(context, displayController, taskOrganizer, taskInfo, taskSurface,
- surfaceControlBuilderSupplier, surfaceControlViewHostFactory);
+ surfaceControlBuilderSupplier, windowContainerTransactionSupplier,
+ surfaceControlViewHostFactory);
}
@Override
diff --git a/libs/hwui/CopyRequest.h b/libs/hwui/CopyRequest.h
new file mode 100644
index 000000000000..5fbd5f900716
--- /dev/null
+++ b/libs/hwui/CopyRequest.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "Rect.h"
+#include "hwui/Bitmap.h"
+
+namespace android::uirenderer {
+
+// Keep in sync with PixelCopy.java codes
+enum class CopyResult {
+ Success = 0,
+ UnknownError = 1,
+ Timeout = 2,
+ SourceEmpty = 3,
+ SourceInvalid = 4,
+ DestinationInvalid = 5,
+};
+
+struct CopyRequest {
+ Rect srcRect;
+ CopyRequest(Rect srcRect) : srcRect(srcRect) {}
+ virtual ~CopyRequest() {}
+ virtual SkBitmap getDestinationBitmap(int srcWidth, int srcHeight) = 0;
+ virtual void onCopyFinished(CopyResult result) = 0;
+};
+
+} // namespace android::uirenderer
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index 1191b92a0aeb..02c2e67a319b 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -49,8 +49,7 @@ namespace uirenderer {
#define ARECT_ARGS(r) float((r).left), float((r).top), float((r).right), float((r).bottom)
-CopyResult Readback::copySurfaceInto(ANativeWindow* window, const Rect& inSrcRect,
- SkBitmap* bitmap) {
+void Readback::copySurfaceInto(ANativeWindow* window, const std::shared_ptr<CopyRequest>& request) {
ATRACE_CALL();
// Setup the source
AHardwareBuffer* rawSourceBuffer;
@@ -63,30 +62,33 @@ CopyResult Readback::copySurfaceInto(ANativeWindow* window, const Rect& inSrcRec
// Really this shouldn't ever happen, but better safe than sorry.
if (err == UNKNOWN_TRANSACTION) {
ALOGW("Readback failed to ANativeWindow_getLastQueuedBuffer2 - who are we talking to?");
- return copySurfaceIntoLegacy(window, inSrcRect, bitmap);
+ return request->onCopyFinished(CopyResult::SourceInvalid);
}
ALOGV("Using new path, cropRect=" RECT_STRING ", transform=%x", ARECT_ARGS(cropRect),
windowTransform);
if (err != NO_ERROR) {
ALOGW("Failed to get last queued buffer, error = %d", err);
- return CopyResult::UnknownError;
+ return request->onCopyFinished(CopyResult::SourceInvalid);
}
if (rawSourceBuffer == nullptr) {
ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
- return CopyResult::SourceEmpty;
+ return request->onCopyFinished(CopyResult::SourceEmpty);
}
UniqueAHardwareBuffer sourceBuffer{rawSourceBuffer};
AHardwareBuffer_Desc description;
AHardwareBuffer_describe(sourceBuffer.get(), &description);
if (description.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) {
ALOGW("Surface is protected, unable to copy from it");
- return CopyResult::SourceInvalid;
+ return request->onCopyFinished(CopyResult::SourceInvalid);
}
- if (sourceFence != -1 && sync_wait(sourceFence.get(), 500 /* ms */) != NO_ERROR) {
- ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
- return CopyResult::Timeout;
+ {
+ ATRACE_NAME("sync_wait");
+ if (sourceFence != -1 && sync_wait(sourceFence.get(), 500 /* ms */) != NO_ERROR) {
+ ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
+ return request->onCopyFinished(CopyResult::Timeout);
+ }
}
sk_sp<SkColorSpace> colorSpace = DataSpaceToColorSpace(
@@ -95,12 +97,12 @@ CopyResult Readback::copySurfaceInto(ANativeWindow* window, const Rect& inSrcRec
SkImage::MakeFromAHardwareBuffer(sourceBuffer.get(), kPremul_SkAlphaType, colorSpace);
if (!image.get()) {
- return CopyResult::UnknownError;
+ return request->onCopyFinished(CopyResult::UnknownError);
}
sk_sp<GrDirectContext> grContext = mRenderThread.requireGrContext();
- SkRect srcRect = inSrcRect.toSkRect();
+ SkRect srcRect = request->srcRect.toSkRect();
SkRect imageSrcRect = SkRect::MakeIWH(description.width, description.height);
SkISize imageWH = SkISize::Make(description.width, description.height);
@@ -148,10 +150,12 @@ CopyResult Readback::copySurfaceInto(ANativeWindow* window, const Rect& inSrcRec
ALOGV("intersecting " RECT_STRING " with " RECT_STRING, SK_RECT_ARGS(srcRect),
SK_RECT_ARGS(textureRect));
if (!srcRect.intersect(textureRect)) {
- return CopyResult::UnknownError;
+ return request->onCopyFinished(CopyResult::UnknownError);
}
}
+ SkBitmap skBitmap = request->getDestinationBitmap(srcRect.width(), srcRect.height());
+ SkBitmap* bitmap = &skBitmap;
sk_sp<SkSurface> tmpSurface =
SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes,
bitmap->info(), 0, kTopLeft_GrSurfaceOrigin, nullptr);
@@ -164,7 +168,7 @@ CopyResult Readback::copySurfaceInto(ANativeWindow* window, const Rect& inSrcRec
tmpInfo, 0, kTopLeft_GrSurfaceOrigin, nullptr);
if (!tmpSurface.get()) {
ALOGW("Unable to generate GPU buffer in a format compatible with the provided bitmap");
- return CopyResult::UnknownError;
+ return request->onCopyFinished(CopyResult::UnknownError);
}
}
@@ -235,52 +239,13 @@ CopyResult Readback::copySurfaceInto(ANativeWindow* window, const Rect& inSrcRec
!tmpBitmap.tryAllocPixels(tmpInfo) || !tmpSurface->readPixels(tmpBitmap, 0, 0) ||
!tmpBitmap.readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) {
ALOGW("Unable to convert content into the provided bitmap");
- return CopyResult::UnknownError;
+ return request->onCopyFinished(CopyResult::UnknownError);
}
}
bitmap->notifyPixelsChanged();
- return CopyResult::Success;
-}
-
-CopyResult Readback::copySurfaceIntoLegacy(ANativeWindow* window, const Rect& srcRect,
- SkBitmap* bitmap) {
- // Setup the source
- AHardwareBuffer* rawSourceBuffer;
- int rawSourceFence;
- Matrix4 texTransform;
- status_t err = ANativeWindow_getLastQueuedBuffer(window, &rawSourceBuffer, &rawSourceFence,
- texTransform.data);
- base::unique_fd sourceFence(rawSourceFence);
- texTransform.invalidateType();
- if (err != NO_ERROR) {
- ALOGW("Failed to get last queued buffer, error = %d", err);
- return CopyResult::UnknownError;
- }
- if (rawSourceBuffer == nullptr) {
- ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
- return CopyResult::SourceEmpty;
- }
-
- UniqueAHardwareBuffer sourceBuffer{rawSourceBuffer};
- AHardwareBuffer_Desc description;
- AHardwareBuffer_describe(sourceBuffer.get(), &description);
- if (description.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) {
- ALOGW("Surface is protected, unable to copy from it");
- return CopyResult::SourceInvalid;
- }
-
- if (sourceFence != -1 && sync_wait(sourceFence.get(), 500 /* ms */) != NO_ERROR) {
- ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
- return CopyResult::Timeout;
- }
-
- sk_sp<SkColorSpace> colorSpace = DataSpaceToColorSpace(
- static_cast<android_dataspace>(ANativeWindow_getBuffersDataSpace(window)));
- sk_sp<SkImage> image =
- SkImage::MakeFromAHardwareBuffer(sourceBuffer.get(), kPremul_SkAlphaType, colorSpace);
- return copyImageInto(image, srcRect, bitmap);
+ return request->onCopyFinished(CopyResult::Success);
}
CopyResult Readback::copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap) {
@@ -318,14 +283,14 @@ CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, SkBitmap* bitmap
CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, const Rect& srcRect,
SkBitmap* bitmap) {
ATRACE_CALL();
+ if (!image.get()) {
+ return CopyResult::UnknownError;
+ }
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
mRenderThread.requireGlContext();
} else {
mRenderThread.requireVkContext();
}
- if (!image.get()) {
- return CopyResult::UnknownError;
- }
int imgWidth = image->width();
int imgHeight = image->height();
sk_sp<GrDirectContext> grContext = sk_ref_sp(mRenderThread.getGrContext());
diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h
index aa6e43c3bc27..a092d472abf0 100644
--- a/libs/hwui/Readback.h
+++ b/libs/hwui/Readback.h
@@ -16,12 +16,13 @@
#pragma once
+#include <SkRefCnt.h>
+
+#include "CopyRequest.h"
#include "Matrix.h"
#include "Rect.h"
#include "renderthread/RenderThread.h"
-#include <SkRefCnt.h>
-
class SkBitmap;
class SkImage;
struct SkRect;
@@ -35,23 +36,13 @@ namespace uirenderer {
class DeferredLayerUpdater;
class Layer;
-// Keep in sync with PixelCopy.java codes
-enum class CopyResult {
- Success = 0,
- UnknownError = 1,
- Timeout = 2,
- SourceEmpty = 3,
- SourceInvalid = 4,
- DestinationInvalid = 5,
-};
-
class Readback {
public:
explicit Readback(renderthread::RenderThread& thread) : mRenderThread(thread) {}
/**
* Copies the surface's most recently queued buffer into the provided bitmap.
*/
- CopyResult copySurfaceInto(ANativeWindow* window, const Rect& srcRect, SkBitmap* bitmap);
+ void copySurfaceInto(ANativeWindow* window, const std::shared_ptr<CopyRequest>& request);
CopyResult copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap);
CopyResult copyImageInto(const sk_sp<SkImage>& image, SkBitmap* bitmap);
@@ -59,7 +50,6 @@ public:
CopyResult copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
private:
- CopyResult copySurfaceIntoLegacy(ANativeWindow* window, const Rect& srcRect, SkBitmap* bitmap);
CopyResult copyImageInto(const sk_sp<SkImage>& image, const Rect& srcRect, SkBitmap* bitmap);
bool copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* dstRect,
diff --git a/libs/hwui/jni/Typeface.cpp b/libs/hwui/jni/Typeface.cpp
index 19efc5f09d11..f5ed5689e4e8 100644
--- a/libs/hwui/jni/Typeface.cpp
+++ b/libs/hwui/jni/Typeface.cpp
@@ -140,15 +140,13 @@ static void Typeface_setDefault(CRITICAL_JNI_PARAMS_COMMA jlong faceHandle) {
static jobject Typeface_getSupportedAxes(JNIEnv *env, jobject, jlong faceHandle) {
Typeface* face = toTypeface(faceHandle);
- const std::unordered_set<minikin::AxisTag>& tagSet = face->fFontCollection->getSupportedTags();
- const size_t length = tagSet.size();
+ const size_t length = face->fFontCollection->getSupportedAxesCount();
if (length == 0) {
return nullptr;
}
std::vector<jint> tagVec(length);
- int index = 0;
- for (const auto& tag : tagSet) {
- tagVec[index++] = tag;
+ for (size_t i = 0; i < length; i++) {
+ tagVec[i] = face->fFontCollection->getSupportedAxisAt(i);
}
std::sort(tagVec.begin(), tagVec.end());
const jintArray result = env->NewIntArray(length);
@@ -305,7 +303,8 @@ void MinikinFontSkiaFactory::write(minikin::BufferWriter* writer,
}
}
-static jint Typeface_writeTypefaces(JNIEnv *env, jobject, jobject buffer, jlongArray faceHandles) {
+static jint Typeface_writeTypefaces(JNIEnv* env, jobject, jobject buffer, jint position,
+ jlongArray faceHandles) {
MinikinFontSkiaFactory::init();
ScopedLongArrayRO faces(env, faceHandles);
std::vector<Typeface*> typefaces;
@@ -314,7 +313,12 @@ static jint Typeface_writeTypefaces(JNIEnv *env, jobject, jobject buffer, jlongA
typefaces.push_back(toTypeface(faces[i]));
}
void* addr = buffer == nullptr ? nullptr : env->GetDirectBufferAddress(buffer);
- minikin::BufferWriter writer(addr);
+ if (addr != nullptr &&
+ reinterpret_cast<intptr_t>(addr) % minikin::BufferReader::kMaxAlignment != 0) {
+ ALOGE("addr (%p) must be aligned at kMaxAlignment, but it was not.", addr);
+ return 0;
+ }
+ minikin::BufferWriter writer(addr, position);
std::vector<std::shared_ptr<minikin::FontCollection>> fontCollections;
std::unordered_map<std::shared_ptr<minikin::FontCollection>, size_t> fcToIndex;
for (Typeface* typeface : typefaces) {
@@ -334,11 +338,18 @@ static jint Typeface_writeTypefaces(JNIEnv *env, jobject, jobject buffer, jlongA
return static_cast<jint>(writer.size());
}
-static jlongArray Typeface_readTypefaces(JNIEnv *env, jobject, jobject buffer) {
+static jlongArray Typeface_readTypefaces(JNIEnv* env, jobject, jobject buffer, jint position) {
MinikinFontSkiaFactory::init();
void* addr = buffer == nullptr ? nullptr : env->GetDirectBufferAddress(buffer);
- if (addr == nullptr) return nullptr;
- minikin::BufferReader reader(addr);
+ if (addr == nullptr) {
+ ALOGE("Passed a null buffer.");
+ return nullptr;
+ }
+ if (reinterpret_cast<intptr_t>(addr) % minikin::BufferReader::kMaxAlignment != 0) {
+ ALOGE("addr (%p) must be aligned at kMaxAlignment, but it was not.", addr);
+ return nullptr;
+ }
+ minikin::BufferReader reader(addr, position);
std::vector<std::shared_ptr<minikin::FontCollection>> fontCollections =
minikin::FontCollection::readVector(&reader);
uint32_t typefaceCount = reader.read<uint32_t>();
@@ -357,7 +368,6 @@ static jlongArray Typeface_readTypefaces(JNIEnv *env, jobject, jobject buffer) {
return result;
}
-
static void Typeface_forceSetStaticFinalField(JNIEnv *env, jclass cls, jstring fieldName,
jobject typeface) {
ScopedUtfChars fieldNameChars(env, fieldName);
@@ -370,18 +380,6 @@ static void Typeface_forceSetStaticFinalField(JNIEnv *env, jclass cls, jstring f
env->SetStaticObjectField(cls, fid, typeface);
}
-// Critical Native
-static jint Typeface_getFamilySize(CRITICAL_JNI_PARAMS_COMMA jlong faceHandle) {
- return toTypeface(faceHandle)->fFontCollection->getFamilyCount();
-}
-
-// Critical Native
-static jlong Typeface_getFamily(CRITICAL_JNI_PARAMS_COMMA jlong faceHandle, jint index) {
- std::shared_ptr<minikin::FontFamily> family =
- toTypeface(faceHandle)->fFontCollection->getFamilyAt(index);
- return reinterpret_cast<jlong>(new FontFamilyWrapper(std::move(family)));
-}
-
// Regular JNI
static void Typeface_warmUpCache(JNIEnv* env, jobject, jstring jFilePath) {
ScopedUtfChars filePath(env, jFilePath);
@@ -417,12 +415,10 @@ static const JNINativeMethod gTypefaceMethods[] = {
{"nativeGetSupportedAxes", "(J)[I", (void*)Typeface_getSupportedAxes},
{"nativeRegisterGenericFamily", "(Ljava/lang/String;J)V",
(void*)Typeface_registerGenericFamily},
- {"nativeWriteTypefaces", "(Ljava/nio/ByteBuffer;[J)I", (void*)Typeface_writeTypefaces},
- {"nativeReadTypefaces", "(Ljava/nio/ByteBuffer;)[J", (void*)Typeface_readTypefaces},
+ {"nativeWriteTypefaces", "(Ljava/nio/ByteBuffer;I[J)I", (void*)Typeface_writeTypefaces},
+ {"nativeReadTypefaces", "(Ljava/nio/ByteBuffer;I)[J", (void*)Typeface_readTypefaces},
{"nativeForceSetStaticFinalField", "(Ljava/lang/String;Landroid/graphics/Typeface;)V",
(void*)Typeface_forceSetStaticFinalField},
- {"nativeGetFamilySize", "(J)I", (void*)Typeface_getFamilySize},
- {"nativeGetFamily", "(JI)J", (void*)Typeface_getFamily},
{"nativeWarmUpCache", "(Ljava/lang/String;)V", (void*)Typeface_warmUpCache},
{"nativeAddFontCollections", "(J)V", (void*)Typeface_addFontCollection},
{"nativeRegisterLocaleList", "(Ljava/lang/String;)V", (void*)Typeface_registerLocaleList},
diff --git a/libs/hwui/jni/YuvToJpegEncoder.cpp b/libs/hwui/jni/YuvToJpegEncoder.cpp
index 87eda7e9f96f..1c5f126d8672 100644
--- a/libs/hwui/jni/YuvToJpegEncoder.cpp
+++ b/libs/hwui/jni/YuvToJpegEncoder.cpp
@@ -8,6 +8,8 @@
#include "graphics_jni_helpers.h"
+#include <csetjmp>
+
YuvToJpegEncoder* YuvToJpegEncoder::create(int format, int* strides) {
// Only ImageFormat.NV21 and ImageFormat.YUY2 are supported
// for now.
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index 55b1f23d294a..4f281fcde61d 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -88,6 +88,11 @@ struct {
jmethodID onFrameComplete;
} gFrameCompleteCallback;
+struct {
+ jmethodID onCopyFinished;
+ jmethodID getDestinationBitmap;
+} gCopyRequest;
+
static JNIEnv* getenv(JavaVM* vm) {
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
@@ -672,15 +677,41 @@ static void android_view_ThreadedRenderer_setFrameCompleteCallback(JNIEnv* env,
}
}
-static jint android_view_ThreadedRenderer_copySurfaceInto(JNIEnv* env,
- jobject clazz, jobject jsurface, jint left, jint top,
- jint right, jint bottom, jlong bitmapPtr) {
- SkBitmap bitmap;
- bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap);
+class CopyRequestAdapter : public CopyRequest {
+public:
+ CopyRequestAdapter(JavaVM* vm, jobject jCopyRequest, Rect srcRect)
+ : CopyRequest(srcRect), mRefHolder(vm, jCopyRequest) {}
+
+ virtual SkBitmap getDestinationBitmap(int srcWidth, int srcHeight) override {
+ JNIEnv* env = getenv(mRefHolder.vm());
+ jlong bitmapPtr = env->CallLongMethod(
+ mRefHolder.object(), gCopyRequest.getDestinationBitmap, srcWidth, srcHeight);
+ SkBitmap bitmap;
+ bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap);
+ return bitmap;
+ }
+
+ virtual void onCopyFinished(CopyResult result) override {
+ JNIEnv* env = getenv(mRefHolder.vm());
+ env->CallVoidMethod(mRefHolder.object(), gCopyRequest.onCopyFinished,
+ static_cast<jint>(result));
+ }
+
+private:
+ JGlobalRefHolder mRefHolder;
+};
+
+static void android_view_ThreadedRenderer_copySurfaceInto(JNIEnv* env, jobject clazz,
+ jobject jsurface, jint left, jint top,
+ jint right, jint bottom,
+ jobject jCopyRequest) {
+ JavaVM* vm = nullptr;
+ LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
+ auto copyRequest = std::make_shared<CopyRequestAdapter>(vm, env->NewGlobalRef(jCopyRequest),
+ Rect(left, top, right, bottom));
ANativeWindow* window = fromSurface(env, jsurface);
- jint result = RenderProxy::copySurfaceInto(window, left, top, right, bottom, &bitmap);
+ RenderProxy::copySurfaceInto(window, std::move(copyRequest));
ANativeWindow_release(window);
- return result;
}
class ContextFactory : public IContextFactory {
@@ -969,7 +1000,8 @@ static const JNINativeMethod gMethods[] = {
(void*)android_view_ThreadedRenderer_setFrameCompleteCallback},
{"nAddObserver", "(JJ)V", (void*)android_view_ThreadedRenderer_addObserver},
{"nRemoveObserver", "(JJ)V", (void*)android_view_ThreadedRenderer_removeObserver},
- {"nCopySurfaceInto", "(Landroid/view/Surface;IIIIJ)I",
+ {"nCopySurfaceInto",
+ "(Landroid/view/Surface;IIIILandroid/graphics/HardwareRenderer$CopyRequest;)V",
(void*)android_view_ThreadedRenderer_copySurfaceInto},
{"nCreateHardwareBitmap", "(JII)Landroid/graphics/Bitmap;",
(void*)android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode},
@@ -1042,6 +1074,11 @@ int register_android_view_ThreadedRenderer(JNIEnv* env) {
gFrameCompleteCallback.onFrameComplete =
GetMethodIDOrDie(env, frameCompleteClass, "onFrameComplete", "()V");
+ jclass copyRequest = FindClassOrDie(env, "android/graphics/HardwareRenderer$CopyRequest");
+ gCopyRequest.onCopyFinished = GetMethodIDOrDie(env, copyRequest, "onCopyFinished", "(I)V");
+ gCopyRequest.getDestinationBitmap =
+ GetMethodIDOrDie(env, copyRequest, "getDestinationBitmap", "(II)J");
+
void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
fromSurface = (ANW_fromSurface)dlsym(handle_, "ANativeWindow_fromSurface");
LOG_ALWAYS_FATAL_IF(fromSurface == nullptr,
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
index 2c421f8727a8..f17129c8d953 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -228,7 +228,7 @@ static jlong Font_getReleaseNativeFontFunc(CRITICAL_JNI_PARAMS) {
static jstring Font_getFontPath(JNIEnv* env, jobject, jlong fontPtr) {
FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
minikin::BufferReader reader = font->font->typefaceMetadataReader();
- if (reader.data() != nullptr) {
+ if (reader.current() != nullptr) {
std::string path = std::string(reader.readString());
if (path.empty()) {
return nullptr;
@@ -270,7 +270,7 @@ static jint Font_getPackedStyle(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
static jint Font_getIndex(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
minikin::BufferReader reader = font->font->typefaceMetadataReader();
- if (reader.data() != nullptr) {
+ if (reader.current() != nullptr) {
reader.skipString(); // fontPath
return reader.read<int>();
} else {
@@ -283,7 +283,7 @@ static jint Font_getIndex(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
static jint Font_getAxisCount(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
minikin::BufferReader reader = font->font->typefaceMetadataReader();
- if (reader.data() != nullptr) {
+ if (reader.current() != nullptr) {
reader.skipString(); // fontPath
reader.skip<int>(); // fontIndex
return reader.readArray<minikin::FontVariation>().second;
@@ -298,7 +298,7 @@ static jlong Font_getAxisInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr, jint inde
FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
minikin::BufferReader reader = font->font->typefaceMetadataReader();
minikin::FontVariation var;
- if (reader.data() != nullptr) {
+ if (reader.current() != nullptr) {
reader.skipString(); // fontPath
reader.skip<int>(); // fontIndex
var = reader.readArray<minikin::FontVariation>().first[index];
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index b2ba15cbe526..40a0bac3762d 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -364,12 +364,13 @@ void RenderProxy::setForceDark(bool enable) {
mRenderThread.queue().post([this, enable]() { mContext->setForceDark(enable); });
}
-int RenderProxy::copySurfaceInto(ANativeWindow* window, int left, int top, int right, int bottom,
- SkBitmap* bitmap) {
+void RenderProxy::copySurfaceInto(ANativeWindow* window, std::shared_ptr<CopyRequest>&& request) {
auto& thread = RenderThread::getInstance();
- return static_cast<int>(thread.queue().runSync([&]() -> auto {
- return thread.readback().copySurfaceInto(window, Rect(left, top, right, bottom), bitmap);
- }));
+ ANativeWindow_acquire(window);
+ thread.queue().post([&thread, window, request = std::move(request)] {
+ thread.readback().copySurfaceInto(window, request);
+ ANativeWindow_release(window);
+ });
}
void RenderProxy::prepareToDraw(Bitmap& bitmap) {
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index bbfeeac19d94..2a99a736180f 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -19,13 +19,14 @@
#include <SkRefCnt.h>
#include <android/native_window.h>
-#include <cutils/compiler.h>
#include <android/surface_control.h>
+#include <cutils/compiler.h>
#include <utils/Functor.h>
#include "../FrameMetricsObserver.h"
#include "../IContextFactory.h"
#include "ColorMode.h"
+#include "CopyRequest.h"
#include "DrawFrameTask.h"
#include "SwapBehavior.h"
#include "hwui/Bitmap.h"
@@ -137,8 +138,7 @@ public:
void removeFrameMetricsObserver(FrameMetricsObserver* observer);
void setForceDark(bool enable);
- static int copySurfaceInto(ANativeWindow* window, int left, int top, int right,
- int bottom, SkBitmap* bitmap);
+ static void copySurfaceInto(ANativeWindow* window, std::shared_ptr<CopyRequest>&& request);
static void prepareToDraw(Bitmap& bitmap);
static int copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap);
diff --git a/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
index 070a339a86a1..13a438199ae5 100644
--- a/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
@@ -25,17 +25,51 @@
class MagnifierAnimation;
+using Rect = android::uirenderer::Rect;
+
static TestScene::Registrar _Magnifier(TestScene::Info{
"magnifier", "A sample magnifier using Readback",
TestScene::simpleCreateScene<MagnifierAnimation>});
+class BlockingCopyRequest : public CopyRequest {
+ sk_sp<Bitmap> mDestination;
+ std::mutex mLock;
+ std::condition_variable mCondVar;
+ CopyResult mResult;
+
+public:
+ BlockingCopyRequest(::Rect rect, sk_sp<Bitmap> bitmap)
+ : CopyRequest(rect), mDestination(bitmap) {}
+
+ virtual SkBitmap getDestinationBitmap(int srcWidth, int srcHeight) override {
+ SkBitmap bitmap;
+ mDestination->getSkBitmap(&bitmap);
+ return bitmap;
+ }
+
+ virtual void onCopyFinished(CopyResult result) override {
+ std::unique_lock _lock{mLock};
+ mResult = result;
+ mCondVar.notify_all();
+ }
+
+ CopyResult waitForResult() {
+ std::unique_lock _lock{mLock};
+ mCondVar.wait(_lock);
+ return mResult;
+ }
+};
+
class MagnifierAnimation : public TestScene {
public:
sp<RenderNode> card;
sp<RenderNode> zoomImageView;
+ sk_sp<Bitmap> magnifier;
+ std::shared_ptr<BlockingCopyRequest> copyRequest;
void createContent(int width, int height, Canvas& canvas) override {
magnifier = TestUtils::createBitmap(200, 100);
+ setupCopyRequest();
SkBitmap temp;
magnifier->getSkBitmap(&temp);
temp.eraseColor(Color::White);
@@ -65,19 +99,20 @@ public:
canvas.enableZ(false);
}
+ void setupCopyRequest() {
+ constexpr int x = 90;
+ constexpr int y = 325;
+ copyRequest = std::make_shared<BlockingCopyRequest>(
+ ::Rect(x, y, x + magnifier->width(), y + magnifier->height()), magnifier);
+ }
+
void doFrame(int frameNr) override {
int curFrame = frameNr % 150;
card->mutateStagingProperties().setTranslationX(curFrame);
card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
if (renderTarget) {
- SkBitmap temp;
- magnifier->getSkBitmap(&temp);
- constexpr int x = 90;
- constexpr int y = 325;
- RenderProxy::copySurfaceInto(renderTarget.get(), x, y, x + magnifier->width(),
- y + magnifier->height(), &temp);
+ RenderProxy::copySurfaceInto(renderTarget.get(), copyRequest);
+ copyRequest->waitForResult();
}
}
-
- sk_sp<Bitmap> magnifier;
};
diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java
index c213fccb90d3..60c812ad048f 100644
--- a/media/java/android/media/AudioPlaybackConfiguration.java
+++ b/media/java/android/media/AudioPlaybackConfiguration.java
@@ -243,12 +243,23 @@ public final class AudioPlaybackConfiguration implements Parcelable {
* Flag used when playback is restricted by AppOps manager with OP_PLAY_AUDIO.
*/
public static final int PLAYER_MUTE_PLAYBACK_RESTRICTED = (1 << 3);
+ /**
+ * @hide
+ * Flag used when muted by client volume.
+ */
+ public static final int PLAYER_MUTE_CLIENT_VOLUME = (1 << 4);
+ /**
+ * @hide
+ * Flag used when muted by volume shaper.
+ */
+ public static final int PLAYER_MUTE_VOLUME_SHAPER = (1 << 5);
/** @hide */
@IntDef(
flag = true,
value = {PLAYER_MUTE_MASTER, PLAYER_MUTE_STREAM_VOLUME, PLAYER_MUTE_STREAM_MUTED,
- PLAYER_MUTE_PLAYBACK_RESTRICTED})
+ PLAYER_MUTE_PLAYBACK_RESTRICTED, PLAYER_MUTE_CLIENT_VOLUME,
+ PLAYER_MUTE_VOLUME_SHAPER})
@Retention(RetentionPolicy.SOURCE)
public @interface PlayerMuteEvent {
}
@@ -678,7 +689,9 @@ public final class AudioPlaybackConfiguration implements Parcelable {
+ " muteFromStreamVolume=" + ((mMutedState & PLAYER_MUTE_STREAM_VOLUME) != 0)
+ " muteFromStreamMuted=" + ((mMutedState & PLAYER_MUTE_STREAM_MUTED) != 0)
+ " muteFromPlaybackRestricted=" + ((mMutedState & PLAYER_MUTE_PLAYBACK_RESTRICTED)
- != 0);
+ != 0)
+ + " muteFromClientVolume=" + ((mMutedState & PLAYER_MUTE_CLIENT_VOLUME) != 0)
+ + " muteFromVolumeShaper=" + ((mMutedState & PLAYER_MUTE_VOLUME_SHAPER) != 0);
}
//=====================================================================
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 955bfcc902a4..444366adf265 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -65,6 +65,8 @@ public class AudioSystem
private static final String TAG = "AudioSystem";
+ private static final int SOURCE_CODEC_TYPE_OPUS = 6; // TODO remove in U
+
// private constructor to prevent instantiating AudioSystem
private AudioSystem() {
throw new UnsupportedOperationException("Trying to instantiate AudioSystem");
@@ -243,6 +245,8 @@ public class AudioSystem
public static final int AUDIO_FORMAT_LDAC = 0x23000000;
/** @hide */
public static final int AUDIO_FORMAT_LC3 = 0x2B000000;
+ /** @hide */
+ public static final int AUDIO_FORMAT_OPUS = 0x08000000;
/** @hide */
@@ -254,7 +258,9 @@ public class AudioSystem
AUDIO_FORMAT_APTX,
AUDIO_FORMAT_APTX_HD,
AUDIO_FORMAT_LDAC,
- AUDIO_FORMAT_LC3}
+ AUDIO_FORMAT_LC3,
+ AUDIO_FORMAT_OPUS
+ }
)
@Retention(RetentionPolicy.SOURCE)
public @interface AudioFormatNativeEnumForBtCodec {}
@@ -287,6 +293,7 @@ public class AudioSystem
case AUDIO_FORMAT_APTX_HD: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD;
case AUDIO_FORMAT_LDAC: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC;
case AUDIO_FORMAT_LC3: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3;
+ case AUDIO_FORMAT_OPUS: return SOURCE_CODEC_TYPE_OPUS; // TODO update in U
default:
Log.e(TAG, "Unknown audio format 0x" + Integer.toHexString(audioFormat)
+ " for conversion to BT codec");
@@ -329,6 +336,8 @@ public class AudioSystem
return AudioSystem.AUDIO_FORMAT_LDAC;
case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3:
return AudioSystem.AUDIO_FORMAT_LC3;
+ case SOURCE_CODEC_TYPE_OPUS: // TODO update in U
+ return AudioSystem.AUDIO_FORMAT_OPUS;
default:
Log.e(TAG, "Unknown BT codec 0x" + Integer.toHexString(btCodec)
+ " for conversion to audio format");
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index 472586b5e519..fde7afd21420 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -18,7 +18,11 @@ package android.media;
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SuppressLint;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.graphics.GraphicBuffer;
import android.graphics.ImageFormat;
import android.graphics.ImageFormat.Format;
@@ -29,6 +33,7 @@ import android.hardware.HardwareBuffer;
import android.hardware.HardwareBuffer.Usage;
import android.hardware.SyncFence;
import android.hardware.camera2.MultiResolutionImageReader;
+import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
@@ -87,6 +92,38 @@ public class ImageReader implements AutoCloseable {
/**
* <p>
+ * Flag to gate correct exception thrown by {@code #detachImage}.
+ * </p>
+ * <p>
+ * {@code #detachImage} is documented as throwing {@link java.lang.IllegalStateException} in
+ * the event of an error; a native helper method to this threw
+ * {@link java.lang.RuntimeException} if the surface was abandoned while detaching the
+ * {@code Image}.
+ * <p>
+ * This previously undocumented exception behavior continues through Android T.
+ * </p>
+ * <p>
+ * After Android T, the native helper method only throws {@code IllegalStateExceptions} in
+ * accordance with the documentation.
+ * </p>
+ * <p>
+ * {@code #detachImage} will now throw only ISEs if it runs into errors while detaching
+ * the image. Behavior on apps targeting API levels <= T remains unchanged.
+ * </p>
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ private static final long DETACH_THROWS_ISE_ONLY = 236825255L;
+
+ /**
+ * Cached value of {@link #DETACH_THROWS_ISE_ONLY} flag to prevent repeated calls when
+ * detaching image.
+ */
+ private final boolean mDetachThrowsIseOnly =
+ CompatChanges.isChangeEnabled(DETACH_THROWS_ISE_ONLY);
+
+ /**
+ * <p>
* Create a new reader for images of the desired size and format.
* </p>
* <p>
@@ -825,10 +862,10 @@ public class ImageReader implements AutoCloseable {
* </p>
* <p>
* After this call, the ImageReader no longer owns this image, and the image
- * ownership can be transfered to another entity like {@link ImageWriter}
+ * ownership can be transferred to another entity like {@link ImageWriter}
* via {@link ImageWriter#queueInputImage}. It's up to the new owner to
* release the resources held by this image. For example, if the ownership
- * of this image is transfered to an {@link ImageWriter}, the image will be
+ * of this image is transferred to an {@link ImageWriter}, the image will be
* freed by the ImageWriter after the image data consumption is done.
* </p>
* <p>
@@ -849,16 +886,22 @@ public class ImageReader implements AutoCloseable {
* @throws IllegalStateException If the ImageReader or image have been
* closed, or the has been detached, or has not yet been
* acquired.
+ * @throws RuntimeException If there is an error detaching {@code Image} from {@code Surface}.
+ * {@code RuntimeException} is only thrown for applications targeting SDK <=
+ * {@link android.os.Build.VERSION_CODES#TIRAMISU}.
+ * For applications targeting SDK >
+ * {@link android.os.Build.VERSION_CODES#TIRAMISU},
+ * this method only throws {@code IllegalStateException}.
* @hide
*/
- public void detachImage(Image image) {
- if (image == null) {
+ public void detachImage(@Nullable Image image) {
+ if (image == null) {
throw new IllegalArgumentException("input image must not be null");
- }
- if (!isImageOwnedbyMe(image)) {
+ }
+ if (!isImageOwnedbyMe(image)) {
throw new IllegalArgumentException("Trying to detach an image that is not owned by"
+ " this ImageReader");
- }
+ }
SurfaceImage si = (SurfaceImage) image;
si.throwISEIfImageIsInvalid();
@@ -867,7 +910,7 @@ public class ImageReader implements AutoCloseable {
throw new IllegalStateException("Image was already detached from this ImageReader");
}
- nativeDetachImage(image);
+ nativeDetachImage(image, mDetachThrowsIseOnly);
si.clearSurfacePlanes();
si.mPlanes = null;
si.setDetached(true);
@@ -1408,7 +1451,7 @@ public class ImageReader implements AutoCloseable {
private synchronized native void nativeClose();
private synchronized native void nativeReleaseImage(Image i);
private synchronized native Surface nativeGetSurface();
- private synchronized native int nativeDetachImage(Image i);
+ private synchronized native int nativeDetachImage(Image i, boolean throwISEOnly);
private synchronized native void nativeDiscardFreeBuffers();
/**
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 7b273580ebce..53d4efad5fc6 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -45,6 +45,7 @@ import java.nio.ByteOrder;
import java.nio.ReadOnlyBufferException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.BitSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -1811,7 +1812,7 @@ final public class MediaCodec {
synchronized(mBufferLock) {
switch (mBufferMode) {
case BUFFER_MODE_LEGACY:
- validateInputByteBuffer(mCachedInputBuffers, index);
+ validateInputByteBufferLocked(mCachedInputBuffers, index);
break;
case BUFFER_MODE_BLOCK:
while (mQueueRequests.size() <= index) {
@@ -1840,7 +1841,7 @@ final public class MediaCodec {
synchronized(mBufferLock) {
switch (mBufferMode) {
case BUFFER_MODE_LEGACY:
- validateOutputByteBuffer(mCachedOutputBuffers, index, info);
+ validateOutputByteBufferLocked(mCachedOutputBuffers, index, info);
break;
case BUFFER_MODE_BLOCK:
while (mOutputFrames.size() <= index) {
@@ -2328,10 +2329,6 @@ final public class MediaCodec {
*/
public final void start() {
native_start();
- synchronized(mBufferLock) {
- cacheBuffers(true /* input */);
- cacheBuffers(false /* input */);
- }
}
private native final void native_start();
@@ -2388,8 +2385,10 @@ final public class MediaCodec {
*/
public final void flush() {
synchronized(mBufferLock) {
- invalidateByteBuffers(mCachedInputBuffers);
- invalidateByteBuffers(mCachedOutputBuffers);
+ invalidateByteBuffersLocked(mCachedInputBuffers);
+ invalidateByteBuffersLocked(mCachedOutputBuffers);
+ mValidInputIndices.clear();
+ mValidOutputIndices.clear();
mDequeuedInputBuffers.clear();
mDequeuedOutputBuffers.clear();
}
@@ -2673,14 +2672,14 @@ final public class MediaCodec {
+ "is not compatible with CONFIGURE_FLAG_USE_BLOCK_MODEL. "
+ "Please use getQueueRequest() to queue buffers");
}
- invalidateByteBuffer(mCachedInputBuffers, index);
+ invalidateByteBufferLocked(mCachedInputBuffers, index, true /* input */);
mDequeuedInputBuffers.remove(index);
}
try {
native_queueInputBuffer(
index, offset, size, presentationTimeUs, flags);
} catch (CryptoException | IllegalStateException e) {
- revalidateByteBuffer(mCachedInputBuffers, index);
+ revalidateByteBuffer(mCachedInputBuffers, index, true /* input */);
throw e;
}
}
@@ -2943,14 +2942,14 @@ final public class MediaCodec {
+ "is not compatible with CONFIGURE_FLAG_USE_BLOCK_MODEL. "
+ "Please use getQueueRequest() to queue buffers");
}
- invalidateByteBuffer(mCachedInputBuffers, index);
+ invalidateByteBufferLocked(mCachedInputBuffers, index, true /* input */);
mDequeuedInputBuffers.remove(index);
}
try {
native_queueSecureInputBuffer(
index, offset, info, presentationTimeUs, flags);
} catch (CryptoException | IllegalStateException e) {
- revalidateByteBuffer(mCachedInputBuffers, index);
+ revalidateByteBuffer(mCachedInputBuffers, index, true /* input */);
throw e;
}
}
@@ -2984,7 +2983,7 @@ final public class MediaCodec {
int res = native_dequeueInputBuffer(timeoutUs);
if (res >= 0) {
synchronized(mBufferLock) {
- validateInputByteBuffer(mCachedInputBuffers, res);
+ validateInputByteBufferLocked(mCachedInputBuffers, res);
}
}
return res;
@@ -3581,10 +3580,10 @@ final public class MediaCodec {
int res = native_dequeueOutputBuffer(info, timeoutUs);
synchronized (mBufferLock) {
if (res == INFO_OUTPUT_BUFFERS_CHANGED) {
- cacheBuffers(false /* input */);
+ cacheBuffersLocked(false /* input */);
} else if (res >= 0) {
- validateOutputByteBuffer(mCachedOutputBuffers, res, info);
- if (mHasSurface) {
+ validateOutputByteBufferLocked(mCachedOutputBuffers, res, info);
+ if (mHasSurface || mCachedOutputBuffers == null) {
mDequeuedOutputInfos.put(res, info.dup());
}
}
@@ -3678,9 +3677,9 @@ final public class MediaCodec {
synchronized(mBufferLock) {
switch (mBufferMode) {
case BUFFER_MODE_LEGACY:
- invalidateByteBuffer(mCachedOutputBuffers, index);
+ invalidateByteBufferLocked(mCachedOutputBuffers, index, false /* input */);
mDequeuedOutputBuffers.remove(index);
- if (mHasSurface) {
+ if (mHasSurface || mCachedOutputBuffers == null) {
info = mDequeuedOutputInfos.remove(index);
}
break;
@@ -3832,15 +3831,24 @@ final public class MediaCodec {
private ByteBuffer[] mCachedInputBuffers;
private ByteBuffer[] mCachedOutputBuffers;
+ private BitSet mValidInputIndices = new BitSet();
+ private BitSet mValidOutputIndices = new BitSet();
+
private final BufferMap mDequeuedInputBuffers = new BufferMap();
private final BufferMap mDequeuedOutputBuffers = new BufferMap();
private final Map<Integer, BufferInfo> mDequeuedOutputInfos =
new HashMap<Integer, BufferInfo>();
final private Object mBufferLock;
- private final void invalidateByteBuffer(
- @Nullable ByteBuffer[] buffers, int index) {
- if (buffers != null && index >= 0 && index < buffers.length) {
+ private void invalidateByteBufferLocked(
+ @Nullable ByteBuffer[] buffers, int index, boolean input) {
+ if (buffers == null) {
+ if (index < 0) {
+ throw new IllegalStateException("index is negative (" + index + ")");
+ }
+ BitSet indices = input ? mValidInputIndices : mValidOutputIndices;
+ indices.clear(index);
+ } else if (index >= 0 && index < buffers.length) {
ByteBuffer buffer = buffers[index];
if (buffer != null) {
buffer.setAccessible(false);
@@ -3848,9 +3856,14 @@ final public class MediaCodec {
}
}
- private final void validateInputByteBuffer(
+ private void validateInputByteBufferLocked(
@Nullable ByteBuffer[] buffers, int index) {
- if (buffers != null && index >= 0 && index < buffers.length) {
+ if (buffers == null) {
+ if (index < 0) {
+ throw new IllegalStateException("index is negative (" + index + ")");
+ }
+ mValidInputIndices.set(index);
+ } else if (index >= 0 && index < buffers.length) {
ByteBuffer buffer = buffers[index];
if (buffer != null) {
buffer.setAccessible(true);
@@ -3859,10 +3872,16 @@ final public class MediaCodec {
}
}
- private final void revalidateByteBuffer(
- @Nullable ByteBuffer[] buffers, int index) {
+ private void revalidateByteBuffer(
+ @Nullable ByteBuffer[] buffers, int index, boolean input) {
synchronized(mBufferLock) {
- if (buffers != null && index >= 0 && index < buffers.length) {
+ if (buffers == null) {
+ if (index < 0) {
+ throw new IllegalStateException("index is negative (" + index + ")");
+ }
+ BitSet indices = input ? mValidInputIndices : mValidOutputIndices;
+ indices.set(index);
+ } else if (index >= 0 && index < buffers.length) {
ByteBuffer buffer = buffers[index];
if (buffer != null) {
buffer.setAccessible(true);
@@ -3871,9 +3890,14 @@ final public class MediaCodec {
}
}
- private final void validateOutputByteBuffer(
+ private void validateOutputByteBufferLocked(
@Nullable ByteBuffer[] buffers, int index, @NonNull BufferInfo info) {
- if (buffers != null && index >= 0 && index < buffers.length) {
+ if (buffers == null) {
+ if (index < 0) {
+ throw new IllegalStateException("index is negative (" + index + ")");
+ }
+ mValidOutputIndices.set(index);
+ } else if (index >= 0 && index < buffers.length) {
ByteBuffer buffer = buffers[index];
if (buffer != null) {
buffer.setAccessible(true);
@@ -3882,7 +3906,7 @@ final public class MediaCodec {
}
}
- private final void invalidateByteBuffers(@Nullable ByteBuffer[] buffers) {
+ private void invalidateByteBuffersLocked(@Nullable ByteBuffer[] buffers) {
if (buffers != null) {
for (ByteBuffer buffer: buffers) {
if (buffer != null) {
@@ -3892,27 +3916,29 @@ final public class MediaCodec {
}
}
- private final void freeByteBuffer(@Nullable ByteBuffer buffer) {
+ private void freeByteBufferLocked(@Nullable ByteBuffer buffer) {
if (buffer != null /* && buffer.isDirect() */) {
// all of our ByteBuffers are direct
java.nio.NioUtils.freeDirectBuffer(buffer);
}
}
- private final void freeByteBuffers(@Nullable ByteBuffer[] buffers) {
+ private void freeByteBuffersLocked(@Nullable ByteBuffer[] buffers) {
if (buffers != null) {
for (ByteBuffer buffer: buffers) {
- freeByteBuffer(buffer);
+ freeByteBufferLocked(buffer);
}
}
}
- private final void freeAllTrackedBuffers() {
+ private void freeAllTrackedBuffers() {
synchronized(mBufferLock) {
- freeByteBuffers(mCachedInputBuffers);
- freeByteBuffers(mCachedOutputBuffers);
+ freeByteBuffersLocked(mCachedInputBuffers);
+ freeByteBuffersLocked(mCachedOutputBuffers);
mCachedInputBuffers = null;
mCachedOutputBuffers = null;
+ mValidInputIndices.clear();
+ mValidOutputIndices.clear();
mDequeuedInputBuffers.clear();
mDequeuedOutputBuffers.clear();
mQueueRequests.clear();
@@ -3920,14 +3946,31 @@ final public class MediaCodec {
}
}
- private final void cacheBuffers(boolean input) {
+ private void cacheBuffersLocked(boolean input) {
ByteBuffer[] buffers = null;
try {
buffers = getBuffers(input);
- invalidateByteBuffers(buffers);
+ invalidateByteBuffersLocked(buffers);
} catch (IllegalStateException e) {
// we don't get buffers in async mode
}
+ if (buffers != null) {
+ BitSet indices = input ? mValidInputIndices : mValidOutputIndices;
+ for (int i = 0; i < buffers.length; ++i) {
+ ByteBuffer buffer = buffers[i];
+ if (buffer == null || !indices.get(i)) {
+ continue;
+ }
+ buffer.setAccessible(true);
+ if (!input) {
+ BufferInfo info = mDequeuedOutputInfos.get(i);
+ if (info != null) {
+ buffer.limit(info.offset + info.size).position(info.offset);
+ }
+ }
+ }
+ indices.clear();
+ }
if (input) {
mCachedInputBuffers = buffers;
} else {
@@ -3963,6 +4006,9 @@ final public class MediaCodec {
+ "objects and attach to QueueRequest objects.");
}
if (mCachedInputBuffers == null) {
+ cacheBuffersLocked(true /* input */);
+ }
+ if (mCachedInputBuffers == null) {
throw new IllegalStateException();
}
// FIXME: check codec status
@@ -4001,6 +4047,9 @@ final public class MediaCodec {
+ "Please use getOutputFrame to get output frames.");
}
if (mCachedOutputBuffers == null) {
+ cacheBuffersLocked(false /* input */);
+ }
+ if (mCachedOutputBuffers == null) {
throw new IllegalStateException();
}
// FIXME: check codec status
@@ -4038,7 +4087,7 @@ final public class MediaCodec {
}
ByteBuffer newBuffer = getBuffer(true /* input */, index);
synchronized (mBufferLock) {
- invalidateByteBuffer(mCachedInputBuffers, index);
+ invalidateByteBufferLocked(mCachedInputBuffers, index, true /* input */);
mDequeuedInputBuffers.put(index, newBuffer);
}
return newBuffer;
@@ -4075,7 +4124,7 @@ final public class MediaCodec {
}
Image newImage = getImage(true /* input */, index);
synchronized (mBufferLock) {
- invalidateByteBuffer(mCachedInputBuffers, index);
+ invalidateByteBufferLocked(mCachedInputBuffers, index, true /* input */);
mDequeuedInputBuffers.put(index, newImage);
}
return newImage;
@@ -4111,7 +4160,7 @@ final public class MediaCodec {
}
ByteBuffer newBuffer = getBuffer(false /* input */, index);
synchronized (mBufferLock) {
- invalidateByteBuffer(mCachedOutputBuffers, index);
+ invalidateByteBufferLocked(mCachedOutputBuffers, index, false /* input */);
mDequeuedOutputBuffers.put(index, newBuffer);
}
return newBuffer;
@@ -4146,7 +4195,7 @@ final public class MediaCodec {
}
Image newImage = getImage(false /* input */, index);
synchronized (mBufferLock) {
- invalidateByteBuffer(mCachedOutputBuffers, index);
+ invalidateByteBufferLocked(mCachedOutputBuffers, index, false /* input */);
mDequeuedOutputBuffers.put(index, newImage);
}
return newImage;
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 75fd64a4ccf5..bf30c5032132 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -1445,7 +1445,7 @@ public final class MediaCodecInfo {
sampleRates = new int[] { 8000, 12000, 16000, 24000, 48000 };
maxChannels = 255;
} else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_RAW)) {
- sampleRateRange = Range.create(1, 96000);
+ sampleRateRange = Range.create(1, 192000);
bitRates = Range.create(1, 10000000);
maxChannels = AudioSystem.OUT_CHANNEL_COUNT_MAX;
} else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_FLAC)) {
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index 9bf126b7a875..31fb8d03c4a0 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -35,7 +35,7 @@ interface ISession {
ISessionController getController();
void setFlags(int flags);
void setActive(boolean active);
- void setMediaButtonReceiver(in PendingIntent mbr, String sessionPackageName);
+ void setMediaButtonReceiver(in PendingIntent mbr);
void setMediaButtonBroadcastReceiver(in ComponentName broadcastReceiver);
void setLaunchPendingIntent(in PendingIntent pi);
void destroySession();
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 9e265d8339dd..1bd12afdc026 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -286,7 +286,7 @@ public final class MediaSession {
@Deprecated
public void setMediaButtonReceiver(@Nullable PendingIntent mbr) {
try {
- mBinder.setMediaButtonReceiver(mbr, mContext.getPackageName());
+ mBinder.setMediaButtonReceiver(mbr);
} catch (RemoteException e) {
Log.wtf(TAG, "Failure in setMediaButtonReceiver.", e);
}
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
new file mode 100644
index 000000000000..9f9288747185
--- /dev/null
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
@@ -0,0 +1,441 @@
+/*
+ * Copyright 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 android.media.tv.interactive;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Rect;
+import android.media.tv.AdResponse;
+import android.media.tv.BroadcastInfoResponse;
+import android.media.tv.TvContentRating;
+import android.media.tv.TvTrackInfo;
+import android.media.tv.interactive.TvInteractiveAppService.Session;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import android.view.InputChannel;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
+import android.view.Surface;
+
+import com.android.internal.os.HandlerCaller;
+import com.android.internal.os.SomeArgs;
+
+import java.util.List;
+
+/**
+ * Implements the internal ITvInteractiveAppSession interface.
+ * @hide
+ */
+public class ITvInteractiveAppSessionWrapper
+ extends ITvInteractiveAppSession.Stub implements HandlerCaller.Callback {
+ private static final String TAG = "ITvInteractiveAppSessionWrapper";
+
+ private static final int EXECUTE_MESSAGE_TIMEOUT_SHORT_MILLIS = 1000;
+ private static final int EXECUTE_MESSAGE_TIMEOUT_LONG_MILLIS = 5 * 1000;
+
+ private static final int DO_RELEASE = 1;
+ private static final int DO_START_INTERACTIVE_APP = 2;
+ private static final int DO_STOP_INTERACTIVE_APP = 3;
+ private static final int DO_RESET_INTERACTIVE_APP = 4;
+ private static final int DO_CREATE_BI_INTERACTIVE_APP = 5;
+ private static final int DO_DESTROY_BI_INTERACTIVE_APP = 6;
+ private static final int DO_SET_TELETEXT_APP_ENABLED = 7;
+ private static final int DO_SEND_CURRENT_CHANNEL_URI = 8;
+ private static final int DO_SEND_CURRENT_CHANNEL_LCN = 9;
+ private static final int DO_SEND_STREAM_VOLUME = 10;
+ private static final int DO_SEND_TRACK_INFO_LIST = 11;
+ private static final int DO_SEND_CURRENT_TV_INPUT_ID = 12;
+ private static final int DO_SEND_SIGNING_RESULT = 13;
+ private static final int DO_NOTIFY_ERROR = 14;
+ private static final int DO_NOTIFY_TUNED = 15;
+ private static final int DO_NOTIFY_TRACK_SELECTED = 16;
+ private static final int DO_NOTIFY_TRACKS_CHANGED = 17;
+ private static final int DO_NOTIFY_VIDEO_AVAILABLE = 18;
+ private static final int DO_NOTIFY_VIDEO_UNAVAILABLE = 19;
+ private static final int DO_NOTIFY_CONTENT_ALLOWED = 20;
+ private static final int DO_NOTIFY_CONTENT_BLOCKED = 21;
+ private static final int DO_NOTIFY_SIGNAL_STRENGTH = 22;
+ private static final int DO_SET_SURFACE = 23;
+ private static final int DO_DISPATCH_SURFACE_CHANGED = 24;
+ private static final int DO_NOTIFY_BROADCAST_INFO_RESPONSE = 25;
+ private static final int DO_NOTIFY_AD_RESPONSE = 26;
+ private static final int DO_CREATE_MEDIA_VIEW = 27;
+ private static final int DO_RELAYOUT_MEDIA_VIEW = 28;
+ private static final int DO_REMOVE_MEDIA_VIEW = 29;
+
+ private final HandlerCaller mCaller;
+ private Session mSessionImpl;
+ private InputChannel mChannel;
+ private TvInteractiveAppEventReceiver mReceiver;
+
+ public ITvInteractiveAppSessionWrapper(
+ Context context, Session mSessionImpl, InputChannel channel) {
+ this.mSessionImpl = mSessionImpl;
+ mCaller = new HandlerCaller(context, null, this, true /* asyncHandler */);
+ mChannel = channel;
+ if (channel != null) {
+ mReceiver = new TvInteractiveAppEventReceiver(channel, context.getMainLooper());
+ }
+ }
+
+ @Override
+ public void executeMessage(Message msg) {
+ if (mSessionImpl == null) {
+ return;
+ }
+
+ long startTime = System.nanoTime();
+ switch (msg.what) {
+ case DO_RELEASE: {
+ mSessionImpl.release();
+ mSessionImpl = null;
+ if (mReceiver != null) {
+ mReceiver.dispose();
+ mReceiver = null;
+ }
+ if (mChannel != null) {
+ mChannel.dispose();
+ mChannel = null;
+ }
+ break;
+ }
+ case DO_START_INTERACTIVE_APP: {
+ mSessionImpl.startInteractiveApp();
+ break;
+ }
+ case DO_STOP_INTERACTIVE_APP: {
+ mSessionImpl.stopInteractiveApp();
+ break;
+ }
+ case DO_RESET_INTERACTIVE_APP: {
+ mSessionImpl.resetInteractiveApp();
+ break;
+ }
+ case DO_CREATE_BI_INTERACTIVE_APP: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ mSessionImpl.createBiInteractiveApp((Uri) args.arg1, (Bundle) args.arg2);
+ args.recycle();
+ break;
+ }
+ case DO_DESTROY_BI_INTERACTIVE_APP: {
+ mSessionImpl.destroyBiInteractiveApp((String) msg.obj);
+ break;
+ }
+ case DO_SET_TELETEXT_APP_ENABLED: {
+ mSessionImpl.setTeletextAppEnabled((Boolean) msg.obj);
+ break;
+ }
+ case DO_SEND_CURRENT_CHANNEL_URI: {
+ mSessionImpl.sendCurrentChannelUri((Uri) msg.obj);
+ break;
+ }
+ case DO_SEND_CURRENT_CHANNEL_LCN: {
+ mSessionImpl.sendCurrentChannelLcn((Integer) msg.obj);
+ break;
+ }
+ case DO_SEND_STREAM_VOLUME: {
+ mSessionImpl.sendStreamVolume((Float) msg.obj);
+ break;
+ }
+ case DO_SEND_TRACK_INFO_LIST: {
+ mSessionImpl.sendTrackInfoList((List<TvTrackInfo>) msg.obj);
+ break;
+ }
+ case DO_SEND_CURRENT_TV_INPUT_ID: {
+ mSessionImpl.sendCurrentTvInputId((String) msg.obj);
+ break;
+ }
+ case DO_SEND_SIGNING_RESULT: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ mSessionImpl.sendSigningResult((String) args.arg1, (byte[]) args.arg2);
+ args.recycle();
+ break;
+ }
+ case DO_NOTIFY_ERROR: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ mSessionImpl.notifyError((String) args.arg1, (Bundle) args.arg2);
+ args.recycle();
+ break;
+ }
+ case DO_NOTIFY_TUNED: {
+ mSessionImpl.notifyTuned((Uri) msg.obj);
+ break;
+ }
+ case DO_NOTIFY_TRACK_SELECTED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ mSessionImpl.notifyTrackSelected((Integer) args.arg1, (String) args.arg2);
+ args.recycle();
+ break;
+ }
+ case DO_NOTIFY_TRACKS_CHANGED: {
+ mSessionImpl.notifyTracksChanged((List<TvTrackInfo>) msg.obj);
+ break;
+ }
+ case DO_NOTIFY_VIDEO_AVAILABLE: {
+ mSessionImpl.notifyVideoAvailable();
+ break;
+ }
+ case DO_NOTIFY_VIDEO_UNAVAILABLE: {
+ mSessionImpl.notifyVideoUnavailable((Integer) msg.obj);
+ break;
+ }
+ case DO_NOTIFY_CONTENT_ALLOWED: {
+ mSessionImpl.notifyContentAllowed();
+ break;
+ }
+ case DO_NOTIFY_CONTENT_BLOCKED: {
+ mSessionImpl.notifyContentBlocked((TvContentRating) msg.obj);
+ break;
+ }
+ case DO_NOTIFY_SIGNAL_STRENGTH: {
+ mSessionImpl.notifySignalStrength((Integer) msg.obj);
+ break;
+ }
+ case DO_SET_SURFACE: {
+ mSessionImpl.setSurface((Surface) msg.obj);
+ break;
+ }
+ case DO_DISPATCH_SURFACE_CHANGED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ mSessionImpl.dispatchSurfaceChanged(
+ (Integer) args.arg1, (Integer) args.arg2, (Integer) args.arg3);
+ args.recycle();
+ break;
+ }
+ case DO_NOTIFY_BROADCAST_INFO_RESPONSE: {
+ mSessionImpl.notifyBroadcastInfoResponse((BroadcastInfoResponse) msg.obj);
+ break;
+ }
+ case DO_NOTIFY_AD_RESPONSE: {
+ mSessionImpl.notifyAdResponse((AdResponse) msg.obj);
+ break;
+ }
+ case DO_CREATE_MEDIA_VIEW: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ mSessionImpl.createMediaView((IBinder) args.arg1, (Rect) args.arg2);
+ args.recycle();
+ break;
+ }
+ case DO_RELAYOUT_MEDIA_VIEW: {
+ mSessionImpl.relayoutMediaView((Rect) msg.obj);
+ break;
+ }
+ case DO_REMOVE_MEDIA_VIEW: {
+ mSessionImpl.removeMediaView(true);
+ break;
+ }
+ default: {
+ Log.w(TAG, "Unhandled message code: " + msg.what);
+ break;
+ }
+ }
+ long durationMs = (System.nanoTime() - startTime) / (1000 * 1000);
+ if (durationMs > EXECUTE_MESSAGE_TIMEOUT_SHORT_MILLIS) {
+ Log.w(TAG, "Handling message (" + msg.what + ") took too long time (duration="
+ + durationMs + "ms)");
+ if (durationMs > EXECUTE_MESSAGE_TIMEOUT_LONG_MILLIS) {
+ // TODO: handle timeout
+ }
+ }
+ }
+
+ @Override
+ public void startInteractiveApp() {
+ mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_START_INTERACTIVE_APP));
+ }
+
+ @Override
+ public void stopInteractiveApp() {
+ mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_STOP_INTERACTIVE_APP));
+ }
+
+ @Override
+ public void resetInteractiveApp() {
+ mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_RESET_INTERACTIVE_APP));
+ }
+
+ @Override
+ public void createBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageOO(DO_CREATE_BI_INTERACTIVE_APP, biIAppUri, params));
+ }
+
+ @Override
+ public void destroyBiInteractiveApp(@NonNull String biIAppId) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageO(DO_DESTROY_BI_INTERACTIVE_APP, biIAppId));
+ }
+
+ @Override
+ public void setTeletextAppEnabled(boolean enable) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageO(DO_SET_TELETEXT_APP_ENABLED, enable));
+ }
+
+ @Override
+ public void sendCurrentChannelUri(@Nullable Uri channelUri) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageO(DO_SEND_CURRENT_CHANNEL_URI, channelUri));
+ }
+
+ @Override
+ public void sendCurrentChannelLcn(int lcn) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageO(DO_SEND_CURRENT_CHANNEL_LCN, lcn));
+ }
+
+ @Override
+ public void sendStreamVolume(float volume) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageO(DO_SEND_STREAM_VOLUME, volume));
+ }
+
+ @Override
+ public void sendTrackInfoList(@NonNull List<TvTrackInfo> tracks) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageO(DO_SEND_TRACK_INFO_LIST, tracks));
+ }
+
+ @Override
+ public void sendCurrentTvInputId(@Nullable String inputId) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageO(DO_SEND_CURRENT_TV_INPUT_ID, inputId));
+ }
+
+ @Override
+ public void sendSigningResult(@NonNull String signingId, @NonNull byte[] result) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageOO(DO_SEND_SIGNING_RESULT, signingId, result));
+ }
+
+ @Override
+ public void notifyError(@NonNull String errMsg, @NonNull Bundle params) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageOO(DO_NOTIFY_ERROR, errMsg, params));
+ }
+
+ @Override
+ public void release() {
+ mSessionImpl.scheduleMediaViewCleanup();
+ mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_RELEASE));
+ }
+
+ @Override
+ public void notifyTuned(Uri channelUri) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_NOTIFY_TUNED, channelUri));
+ }
+
+ @Override
+ public void notifyTrackSelected(int type, final String trackId) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageOO(DO_NOTIFY_TRACK_SELECTED, type, trackId));
+ }
+
+ @Override
+ public void notifyTracksChanged(List<TvTrackInfo> tracks) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_NOTIFY_TRACKS_CHANGED, tracks));
+ }
+
+ @Override
+ public void notifyVideoAvailable() {
+ mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_NOTIFY_VIDEO_AVAILABLE));
+ }
+
+ @Override
+ public void notifyVideoUnavailable(int reason) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_NOTIFY_VIDEO_UNAVAILABLE, reason));
+ }
+
+ @Override
+ public void notifyContentAllowed() {
+ mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_NOTIFY_CONTENT_ALLOWED));
+ }
+
+ @Override
+ public void notifyContentBlocked(String rating) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_NOTIFY_CONTENT_BLOCKED, rating));
+ }
+
+ @Override
+ public void notifySignalStrength(int strength) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_NOTIFY_SIGNAL_STRENGTH, strength));
+ }
+
+ @Override
+ public void setSurface(Surface surface) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_SURFACE, surface));
+ }
+
+ @Override
+ public void dispatchSurfaceChanged(int format, int width, int height) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageIIII(DO_DISPATCH_SURFACE_CHANGED, format, width, height, 0));
+ }
+
+ @Override
+ public void notifyBroadcastInfoResponse(BroadcastInfoResponse response) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageO(DO_NOTIFY_BROADCAST_INFO_RESPONSE, response));
+ }
+
+ @Override
+ public void notifyAdResponse(AdResponse response) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_NOTIFY_AD_RESPONSE, response));
+ }
+
+ @Override
+ public void createMediaView(IBinder windowToken, Rect frame) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageOO(DO_CREATE_MEDIA_VIEW, windowToken, frame));
+ }
+
+ @Override
+ public void relayoutMediaView(Rect frame) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_RELAYOUT_MEDIA_VIEW, frame));
+ }
+
+ @Override
+ public void removeMediaView() {
+ mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_REMOVE_MEDIA_VIEW));
+ }
+
+ private final class TvInteractiveAppEventReceiver extends InputEventReceiver {
+ TvInteractiveAppEventReceiver(InputChannel inputChannel, Looper looper) {
+ super(inputChannel, looper);
+ }
+
+ @Override
+ public void onInputEvent(InputEvent event) {
+ if (mSessionImpl == null) {
+ // The session has been finished.
+ finishInputEvent(event, false);
+ return;
+ }
+
+ int handled = mSessionImpl.dispatchInputEvent(event, this);
+ if (handled != TvInteractiveAppManager.Session.DISPATCH_IN_PROGRESS) {
+ finishInputEvent(
+ event, handled == TvInteractiveAppManager.Session.DISPATCH_HANDLED);
+ }
+ }
+ }
+}
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
index 54700b6594f8..f5b432292b0f 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
@@ -45,7 +45,6 @@ import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.RemoteCallbackList;
@@ -1416,193 +1415,6 @@ public abstract class TvInteractiveAppService extends Service {
}
}
- /**
- * Implements the internal ITvInteractiveAppSession interface.
- * @hide
- */
- public static class ITvInteractiveAppSessionWrapper extends ITvInteractiveAppSession.Stub {
- // TODO: put ITvInteractiveAppSessionWrapper in a separate Java file
- private final Session mSessionImpl;
- private InputChannel mChannel;
- private TvInteractiveAppEventReceiver mReceiver;
-
- public ITvInteractiveAppSessionWrapper(
- Context context, Session mSessionImpl, InputChannel channel) {
- this.mSessionImpl = mSessionImpl;
- mChannel = channel;
- if (channel != null) {
- mReceiver = new TvInteractiveAppEventReceiver(channel, context.getMainLooper());
- }
- }
-
- @Override
- public void startInteractiveApp() {
- mSessionImpl.startInteractiveApp();
- }
-
- @Override
- public void stopInteractiveApp() {
- mSessionImpl.stopInteractiveApp();
- }
-
- @Override
- public void resetInteractiveApp() {
- mSessionImpl.resetInteractiveApp();
- }
-
- @Override
- public void createBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) {
- mSessionImpl.createBiInteractiveApp(biIAppUri, params);
- }
-
- @Override
- public void setTeletextAppEnabled(boolean enable) {
- mSessionImpl.setTeletextAppEnabled(enable);
- }
-
- @Override
- public void destroyBiInteractiveApp(@NonNull String biIAppId) {
- mSessionImpl.destroyBiInteractiveApp(biIAppId);
- }
-
- @Override
- public void sendCurrentChannelUri(@Nullable Uri channelUri) {
- mSessionImpl.sendCurrentChannelUri(channelUri);
- }
-
- @Override
- public void sendCurrentChannelLcn(int lcn) {
- mSessionImpl.sendCurrentChannelLcn(lcn);
- }
-
- @Override
- public void sendStreamVolume(float volume) {
- mSessionImpl.sendStreamVolume(volume);
- }
-
- @Override
- public void sendTrackInfoList(@NonNull List<TvTrackInfo> tracks) {
- mSessionImpl.sendTrackInfoList(tracks);
- }
-
- @Override
- public void sendCurrentTvInputId(@Nullable String inputId) {
- mSessionImpl.sendCurrentTvInputId(inputId);
- }
-
- @Override
- public void sendSigningResult(@NonNull String signingId, @NonNull byte[] result) {
- mSessionImpl.sendSigningResult(signingId, result);
- }
-
- @Override
- public void notifyError(@NonNull String errMsg, @NonNull Bundle params) {
- mSessionImpl.notifyError(errMsg, params);
- }
-
- @Override
- public void release() {
- mSessionImpl.scheduleMediaViewCleanup();
- mSessionImpl.release();
- }
-
- @Override
- public void notifyTuned(Uri channelUri) {
- mSessionImpl.notifyTuned(channelUri);
- }
-
- @Override
- public void notifyTrackSelected(int type, final String trackId) {
- mSessionImpl.notifyTrackSelected(type, trackId);
- }
-
- @Override
- public void notifyTracksChanged(List<TvTrackInfo> tracks) {
- mSessionImpl.notifyTracksChanged(tracks);
- }
-
- @Override
- public void notifyVideoAvailable() {
- mSessionImpl.notifyVideoAvailable();
- }
-
- @Override
- public void notifyVideoUnavailable(int reason) {
- mSessionImpl.notifyVideoUnavailable(reason);
- }
-
- @Override
- public void notifyContentAllowed() {
- mSessionImpl.notifyContentAllowed();
- }
-
- @Override
- public void notifyContentBlocked(String rating) {
- mSessionImpl.notifyContentBlocked(TvContentRating.unflattenFromString(rating));
- }
-
- @Override
- public void notifySignalStrength(int strength) {
- mSessionImpl.notifySignalStrength(strength);
- }
-
- @Override
- public void setSurface(Surface surface) {
- mSessionImpl.setSurface(surface);
- }
-
- @Override
- public void dispatchSurfaceChanged(int format, int width, int height) {
- mSessionImpl.dispatchSurfaceChanged(format, width, height);
- }
-
- @Override
- public void notifyBroadcastInfoResponse(BroadcastInfoResponse response) {
- mSessionImpl.notifyBroadcastInfoResponse(response);
- }
-
- @Override
- public void notifyAdResponse(AdResponse response) {
- mSessionImpl.notifyAdResponse(response);
- }
-
- @Override
- public void createMediaView(IBinder windowToken, Rect frame) {
- mSessionImpl.createMediaView(windowToken, frame);
- }
-
- @Override
- public void relayoutMediaView(Rect frame) {
- mSessionImpl.relayoutMediaView(frame);
- }
-
- @Override
- public void removeMediaView() {
- mSessionImpl.removeMediaView(true);
- }
-
- private final class TvInteractiveAppEventReceiver extends InputEventReceiver {
- TvInteractiveAppEventReceiver(InputChannel inputChannel, Looper looper) {
- super(inputChannel, looper);
- }
-
- @Override
- public void onInputEvent(InputEvent event) {
- if (mSessionImpl == null) {
- // The session has been finished.
- finishInputEvent(event, false);
- return;
- }
-
- int handled = mSessionImpl.dispatchInputEvent(event, this);
- if (handled != TvInteractiveAppManager.Session.DISPATCH_IN_PROGRESS) {
- finishInputEvent(
- event, handled == TvInteractiveAppManager.Session.DISPATCH_HANDLED);
- }
- }
- }
- }
-
@SuppressLint("HandlerLeak")
private final class ServiceHandler extends Handler {
private static final int DO_CREATE_SESSION = 1;
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 62c0d55951af..556f98cefca1 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -643,7 +643,8 @@ static jint ImageReader_imageSetup(JNIEnv* env, jobject thiz, jobject image,
return ACQUIRE_SUCCESS;
}
-static jint ImageReader_detachImage(JNIEnv* env, jobject thiz, jobject image) {
+static jint ImageReader_detachImage(JNIEnv* env, jobject thiz, jobject image,
+ jboolean throwISEOnly) {
ALOGV("%s:", __FUNCTION__);
JNIImageReaderContext* ctx = ImageReader_getContext(env, thiz);
if (ctx == NULL) {
@@ -666,8 +667,12 @@ static jint ImageReader_detachImage(JNIEnv* env, jobject thiz, jobject image) {
res = bufferConsumer->detachBuffer(buffer->mSlot);
if (res != OK) {
ALOGE("Image detach failed: %s (%d)!!!", strerror(-res), res);
- jniThrowRuntimeException(env,
- "nativeDetachImage failed for image!!!");
+ if ((bool) throwISEOnly) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "nativeDetachImage failed for image!!!");
+ } else {
+ jniThrowRuntimeException(env, "nativeDetachImage failed for image!!!");
+ }
return res;
}
return OK;
@@ -965,7 +970,7 @@ static const JNINativeMethod gImageReaderMethods[] = {
{"nativeReleaseImage", "(Landroid/media/Image;)V", (void*)ImageReader_imageRelease },
{"nativeImageSetup", "(Landroid/media/Image;Z)I", (void*)ImageReader_imageSetup },
{"nativeGetSurface", "()Landroid/view/Surface;", (void*)ImageReader_getSurface },
- {"nativeDetachImage", "(Landroid/media/Image;)I", (void*)ImageReader_detachImage },
+ {"nativeDetachImage", "(Landroid/media/Image;Z)I", (void*)ImageReader_detachImage },
{"nativeCreateImagePlanes",
"(ILandroid/graphics/GraphicBuffer;IIIIII)[Landroid/media/ImageReader$ImagePlane;",
(void*)ImageReader_createImagePlanes },
diff --git a/mime/java-res/android.mime.types b/mime/java-res/android.mime.types
index 60f0e9ef49cf..cb74cfc89cbe 100644
--- a/mime/java-res/android.mime.types
+++ b/mime/java-res/android.mime.types
@@ -86,6 +86,7 @@
?audio/x-matroska mka
?audio/x-pn-realaudio ra
?audio/x-mpeg mp3
+?audio/mp3 mp3
?image/bmp bmp
?image/gif gif
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml b/packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml
index a596a9a59ee8..7c3f5a566bdb 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml
@@ -28,8 +28,7 @@
style="@style/SettingsLibActionButton"
android:layout_width="0dp"
android:layout_height="match_parent"
- android:layout_weight="1"
- android:hyphenationFrequency="normalFast"/>
+ android:layout_weight="1"/>
<View
android:id="@+id/divider1"
@@ -44,8 +43,7 @@
style="@style/SettingsLibActionButton"
android:layout_width="0dp"
android:layout_height="match_parent"
- android:layout_weight="1"
- android:hyphenationFrequency="normalFast"/>
+ android:layout_weight="1"/>
<View
android:id="@+id/divider2"
@@ -60,8 +58,7 @@
style="@style/SettingsLibActionButton"
android:layout_width="0dp"
android:layout_height="match_parent"
- android:layout_weight="1"
- android:hyphenationFrequency="normalFast"/>
+ android:layout_weight="1"/>
<View
android:id="@+id/divider3"
@@ -76,6 +73,5 @@
style="@style/SettingsLibActionButton"
android:layout_width="0dp"
android:layout_height="match_parent"
- android:layout_weight="1"
- android:hyphenationFrequency="normalFast"/>
+ android:layout_weight="1"/>
</LinearLayout>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/layout-v33/settingslib_action_buttons.xml b/packages/SettingsLib/ActionButtonsPreference/res/layout-v33/settingslib_action_buttons.xml
new file mode 100644
index 000000000000..b4640eea77df
--- /dev/null
+++ b/packages/SettingsLib/ActionButtonsPreference/res/layout-v33/settingslib_action_buttons.xml
@@ -0,0 +1,85 @@
+<?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="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="8dp"
+ android:paddingHorizontal="8dp"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/button1"
+ style="@style/SettingsLibActionButton"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:lineBreakWordStyle="phrase"
+ android:hyphenationFrequency="normalFast"/>
+
+ <View
+ android:id="@+id/divider1"
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:paddingHorizontal="4dp"
+ android:visibility="gone"
+ android:background="?android:colorBackground" />
+
+ <Button
+ android:id="@+id/button2"
+ style="@style/SettingsLibActionButton"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:lineBreakWordStyle="phrase"
+ android:hyphenationFrequency="normalFast"/>
+
+ <View
+ android:id="@+id/divider2"
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:paddingHorizontal="4dp"
+ android:visibility="gone"
+ android:background="?android:colorBackground" />
+
+ <Button
+ android:id="@+id/button3"
+ style="@style/SettingsLibActionButton"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:lineBreakWordStyle="phrase"
+ android:hyphenationFrequency="normalFast"/>
+
+ <View
+ android:id="@+id/divider3"
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:paddingHorizontal="4dp"
+ android:visibility="gone"
+ android:background="?android:colorBackground" />
+
+ <Button
+ android:id="@+id/button4"
+ style="@style/SettingsLibActionButton"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:lineBreakWordStyle="phrase"
+ android:hyphenationFrequency="normalFast"/>
+</LinearLayout>
diff --git a/packages/SettingsLib/AppPreference/res/layout-v33/preference_app.xml b/packages/SettingsLib/AppPreference/res/layout-v33/preference_app.xml
new file mode 100644
index 000000000000..897585729596
--- /dev/null
+++ b/packages/SettingsLib/AppPreference/res/layout-v33/preference_app.xml
@@ -0,0 +1,100 @@
+<?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="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/selectableItemBackground"
+ android:gravity="center_vertical"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="start|center_vertical"
+ android:minWidth="@dimen/secondary_app_icon_size"
+ android:orientation="horizontal"
+ android:paddingEnd="16dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="@dimen/secondary_app_icon_size"
+ android:layout_height="@dimen/secondary_app_icon_size"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ android:maxLines="2"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="?android:attr/textAppearanceListItem"/>
+
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textDirection="locale"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"/>
+
+ <TextView
+ android:id="@+id/appendix"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"
+ android:visibility="gone"/>
+
+ <ProgressBar
+ android:id="@android:id/progress"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="4dp"
+ android:max="100"
+ android:visibility="gone"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical|end"
+ android:minWidth="@dimen/two_target_min_width"
+ android:orientation="vertical"/>
+
+</LinearLayout>
diff --git a/packages/SettingsLib/AppPreference/res/layout-v33/preference_app_header.xml b/packages/SettingsLib/AppPreference/res/layout-v33/preference_app_header.xml
new file mode 100644
index 000000000000..3a219b9045c8
--- /dev/null
+++ b/packages/SettingsLib/AppPreference/res/layout-v33/preference_app_header.xml
@@ -0,0 +1,38 @@
+<?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:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingBottom="16dp"
+ android:paddingTop="8dp"
+ android:clickable="false">
+
+ <TextView
+ android:id="@+id/apps_top_intro_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textDirection="locale"
+ android:clickable="false"
+ android:longClickable="false"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="@style/TextAppearance.TopIntroText" />
+
+</LinearLayout>
diff --git a/packages/SettingsLib/AppPreference/res/layout/preference_app.xml b/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
index 12db901bdb69..e65f7de2466a 100644
--- a/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
+++ b/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
@@ -55,7 +55,6 @@
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:maxLines="2"
- android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceListItem"/>
<TextView
@@ -63,7 +62,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textDirection="locale"
- android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary"/>
diff --git a/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java b/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java
index 56d296709914..5364783b4c68 100644
--- a/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java
+++ b/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java
@@ -132,6 +132,10 @@ public class ButtonPreference extends Preference {
}
}
+ @Override public CharSequence getTitle() {
+ return mTitle;
+ }
+
@Override
public void setIcon(Drawable icon) {
mIcon = icon;
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 ec091bf4de67..f4af9c7e0e78 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.graphics.text.LineBreakConfig;
import android.os.Build;
import android.view.LayoutInflater;
import android.view.View;
@@ -88,6 +89,12 @@ public class CollapsingToolbarDelegate {
mCollapsingToolbarLayout.setLineSpacingMultiplier(TOOLBAR_LINE_SPACING_MULTIPLIER);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
mCollapsingToolbarLayout.setHyphenationFrequency(HYPHENATION_FREQUENCY_NORMAL_FAST);
+ mCollapsingToolbarLayout.setStaticLayoutBuilderConfigurer(builder ->
+ builder.setLineBreakConfig(
+ new LineBreakConfig.Builder()
+ .setLineBreakWordStyle(
+ LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE)
+ .build()));
}
}
disableCollapsingToolbarLayoutScrollingBehavior();
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 a8c7a3f936ee..522de93be1ff 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
@@ -22,6 +22,7 @@ import android.app.ActionBar;
import android.app.Activity;
import android.content.Context;
import android.content.res.TypedArray;
+import android.graphics.text.LineBreakConfig;
import android.os.Build;
import android.text.TextUtils;
import android.util.AttributeSet;
@@ -110,6 +111,12 @@ public class CollapsingCoordinatorLayout extends CoordinatorLayout {
mCollapsingToolbarLayout.setLineSpacingMultiplier(TOOLBAR_LINE_SPACING_MULTIPLIER);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
mCollapsingToolbarLayout.setHyphenationFrequency(HYPHENATION_FREQUENCY_NORMAL_FAST);
+ mCollapsingToolbarLayout.setStaticLayoutBuilderConfigurer(builder ->
+ builder.setLineBreakConfig(
+ new LineBreakConfig.Builder()
+ .setLineBreakWordStyle(
+ LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE)
+ .build()));
}
if (!TextUtils.isEmpty(mToolbarTitle)) {
mCollapsingToolbarLayout.setTitle(mToolbarTitle);
diff --git a/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml b/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml
index 2c1fdd493c8a..42700b3ace07 100644
--- a/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml
+++ b/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml
@@ -53,7 +53,6 @@
android:paddingTop="16dp"
android:paddingBottom="8dp"
android:textColor="?android:attr/textColorSecondary"
- android:hyphenationFrequency="normalFast"
android:ellipsize="marquee" />
<com.android.settingslib.widget.LinkTextView
diff --git a/packages/SettingsLib/FooterPreference/res/layout-v33/preference_footer.xml b/packages/SettingsLib/FooterPreference/res/layout-v33/preference_footer.xml
new file mode 100644
index 000000000000..a2f25100edad
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/layout-v33/preference_footer.xml
@@ -0,0 +1,71 @@
+<?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="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:background="?android:attr/selectableItemBackground"
+ android:orientation="vertical"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minWidth="56dp"
+ android:gravity="start|top"
+ android:orientation="horizontal"
+ android:paddingEnd="12dp"
+ android:paddingTop="16dp"
+ android:paddingBottom="4dp">
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingTop="16dp"
+ android:paddingBottom="8dp"
+ android:textColor="?android:attr/textColorSecondary"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:ellipsize="marquee" />
+
+ <com.android.settingslib.widget.LinkTextView
+ android:id="@+id/settingslib_learn_more"
+ android:text="@string/settingslib_learn_more_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingBottom="8dp"
+ android:clickable="true"
+ android:visibility="gone"
+ style="@style/TextAppearance.Footer.Title.SettingsLib"/>
+ </LinearLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
new file mode 100644
index 000000000000..35d13230a3b7
--- /dev/null
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
@@ -0,0 +1,70 @@
+<?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_height="wrap_content"
+ android:layout_width="match_parent"
+ android:background="?android:attr/colorBackground"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/frame"
+ android:minHeight="@dimen/settingslib_min_switch_bar_height"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_margin="@dimen/settingslib_switchbar_margin"
+ android:paddingStart="@dimen/settingslib_switchbar_padding_left"
+ android:paddingEnd="@dimen/settingslib_switchbar_padding_right">
+
+ <TextView
+ android:id="@+id/switch_text"
+ android:layout_height="wrap_content"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_marginEnd="@dimen/settingslib_switch_title_margin"
+ android:layout_marginVertical="@dimen/settingslib_switch_title_margin"
+ android:layout_gravity="center_vertical"
+ android:ellipsize="end"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ style="@style/MainSwitchText.Settingslib" />
+
+ <ImageView
+ android:id="@+id/restricted_icon"
+ android:layout_width="@dimen/settingslib_restricted_icon_size"
+ android:layout_height="@dimen/settingslib_restricted_icon_size"
+ android:tint="?android:attr/colorAccent"
+ android:layout_gravity="center_vertical"
+ android:layout_marginEnd="@dimen/settingslib_restricted_icon_margin_end"
+ android:src="@drawable/settingslib_ic_info"
+ android:visibility="gone" />
+
+ <Switch
+ android:id="@android:id/switch_widget"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:layout_gravity="center_vertical"
+ android:focusable="false"
+ android:clickable="false"
+ android:theme="@style/Switch.SettingsLib"/>
+ </LinearLayout>
+
+</LinearLayout>
+
+
diff --git a/packages/SettingsLib/RadioButtonPreference/res/layout-v33/preference_radio.xml b/packages/SettingsLib/RadioButtonPreference/res/layout-v33/preference_radio.xml
new file mode 100644
index 000000000000..bb8ac286e38d
--- /dev/null
+++ b/packages/SettingsLib/RadioButtonPreference/res/layout-v33/preference_radio.xml
@@ -0,0 +1,127 @@
+<?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"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/selectableItemBackground"
+ android:gravity="center_vertical"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingHorizontal="20dp"
+ android:gravity="center"
+ android:minWidth="56dp"
+ android:orientation="vertical"/>
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:minWidth="32dp"
+ android:orientation="horizontal"
+ android:layout_marginEnd="16dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+ <androidx.preference.internal.PreferenceImageView
+ android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ settings:maxWidth="@dimen/secondary_app_icon_size"
+ settings:maxHeight="@dimen/secondary_app_icon_size"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="?android:attr/textAppearanceListItem"/>
+
+ <LinearLayout
+ android:id="@+id/summary_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone">
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textAlignment="viewStart"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textColor="?android:attr/textColorSecondary"/>
+
+ <TextView
+ android:id="@+id/appendix"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textAlignment="viewEnd"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="1"
+ android:visibility="gone"
+ android:ellipsize="end"/>
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/radio_extra_widget_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:gravity="center_vertical">
+ <View
+ android:layout_width=".75dp"
+ android:layout_height="32dp"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="16dp"
+ android:background="?android:attr/dividerVertical" />
+ <ImageView
+ android:id="@+id/radio_extra_widget"
+ android:layout_width="match_parent"
+ android:minWidth="@dimen/two_target_min_width"
+ android:layout_height="fill_parent"
+ android:src="@drawable/ic_settings_accent"
+ android:contentDescription="@string/settings_label"
+ android:paddingStart="24dp"
+ android:paddingEnd="24dp"
+ android:layout_gravity="center"
+ android:background="?android:attr/selectableItemBackground" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml b/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml
index 64d100a43e68..906ff2cc09d5 100644
--- a/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml
+++ b/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml
@@ -66,7 +66,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
- android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceListItem"/>
<LinearLayout
@@ -81,7 +80,6 @@
android:layout_weight="1"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textAlignment="viewStart"
- android:hyphenationFrequency="normalFast"
android:textColor="?android:attr/textColorSecondary"/>
<TextView
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/layout-v33/preference_selector_with_widget.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/layout-v33/preference_selector_with_widget.xml
new file mode 100644
index 000000000000..0b274646214a
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/layout-v33/preference_selector_with_widget.xml
@@ -0,0 +1,127 @@
+<?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"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/selectableItemBackground"
+ android:gravity="center_vertical"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingHorizontal="20dp"
+ android:gravity="center"
+ android:minWidth="56dp"
+ android:orientation="vertical"/>
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:minWidth="32dp"
+ android:orientation="horizontal"
+ android:layout_marginEnd="16dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+ <androidx.preference.internal.PreferenceImageView
+ android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ settings:maxWidth="@dimen/secondary_app_icon_size"
+ settings:maxHeight="@dimen/secondary_app_icon_size"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="?android:attr/textAppearanceListItem"/>
+
+ <LinearLayout
+ android:id="@+id/summary_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone">
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textAlignment="viewStart"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textColor="?android:attr/textColorSecondary"/>
+
+ <TextView
+ android:id="@+id/appendix"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textAlignment="viewEnd"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="1"
+ android:visibility="gone"
+ android:ellipsize="end"/>
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/selector_extra_widget_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:gravity="center_vertical">
+ <View
+ android:layout_width=".75dp"
+ android:layout_height="32dp"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="16dp"
+ android:background="?android:attr/dividerVertical" />
+ <ImageView
+ android:id="@+id/selector_extra_widget"
+ android:layout_width="match_parent"
+ android:minWidth="@dimen/two_target_min_width"
+ android:layout_height="fill_parent"
+ android:src="@drawable/ic_settings_accent"
+ android:contentDescription="@string/settings_label"
+ android:paddingStart="24dp"
+ android:paddingEnd="24dp"
+ android:layout_gravity="center"
+ android:background="?android:attr/selectableItemBackground" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_selector_with_widget.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_selector_with_widget.xml
index 2a550ef4e457..8bb56ff0a07d 100644
--- a/packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_selector_with_widget.xml
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_selector_with_widget.xml
@@ -66,7 +66,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
- android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceListItem"/>
<LinearLayout
@@ -81,7 +80,6 @@
android:layout_weight="1"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textAlignment="viewStart"
- android:hyphenationFrequency="normalFast"
android:textColor="?android:attr/textColorSecondary"/>
<TextView
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml b/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml
index d4d466a99301..23aa993cc8cc 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml
@@ -43,7 +43,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
- android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="marquee"/>
@@ -58,7 +57,6 @@
android:textAlignment="viewStart"
android:textColor="?android:attr/textColorSecondary"
android:maxLines="10"
- android:hyphenationFrequency="normalFast"
style="@style/PreferenceSummaryTextStyle"/>
</RelativeLayout>
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml b/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml
new file mode 100644
index 000000000000..70ce3749ea19
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml
@@ -0,0 +1,80 @@
+<?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="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:gravity="center_vertical"
+ android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingRight="?android:attr/listPreferredItemPaddingRight"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:background="?android:attr/selectableItemBackground"
+ android:clipToPadding="false"
+ android:baselineAligned="false">
+
+ <include layout="@layout/settingslib_icon_frame"/>
+
+ <RelativeLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:ellipsize="marquee"/>
+
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/title"
+ android:layout_alignLeft="@android:id/title"
+ android:layout_alignStart="@android:id/title"
+ android:layout_gravity="start"
+ android:textAlignment="viewStart"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="10"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ style="@style/PreferenceSummaryTextStyle"/>
+
+ </RelativeLayout>
+
+ <!-- Preference should place its actual preference widget here. -->
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="end|center_vertical"
+ android:paddingLeft="16dp"
+ android:paddingStart="16dp"
+ android:paddingRight="0dp"
+ android:paddingEnd="0dp"
+ android:orientation="vertical"/>
+
+</LinearLayout> \ No newline at end of file
diff --git a/packages/SettingsLib/Spa/.gitignore b/packages/SettingsLib/Spa/.gitignore
new file mode 100644
index 000000000000..b2ed2681b24c
--- /dev/null
+++ b/packages/SettingsLib/Spa/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/packages/SettingsLib/Spa/OWNERS b/packages/SettingsLib/Spa/OWNERS
new file mode 100644
index 000000000000..b352b045c352
--- /dev/null
+++ b/packages/SettingsLib/Spa/OWNERS
@@ -0,0 +1,6 @@
+chaohuiw@google.com
+hanxu@google.com
+kellyz@google.com
+pierreqian@google.com
+
+per-file *.xml = set noparent
diff --git a/packages/SettingsLib/Spa/TEST_MAPPING b/packages/SettingsLib/Spa/TEST_MAPPING
new file mode 100644
index 000000000000..ef3db4aef66d
--- /dev/null
+++ b/packages/SettingsLib/Spa/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "SpaLibTests"
+ }
+ ]
+}
diff --git a/packages/SettingsLib/Spa/build.gradle b/packages/SettingsLib/Spa/build.gradle
new file mode 100644
index 000000000000..8c97eca548b5
--- /dev/null
+++ b/packages/SettingsLib/Spa/build.gradle
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+buildscript {
+ ext {
+ minSdk_version = 31
+ compose_version = '1.2.0-alpha04'
+ compose_material3_version = '1.0.0-alpha06'
+ }
+}
+plugins {
+ id 'com.android.application' version '7.3.0-beta04' apply false
+ id 'com.android.library' version '7.3.0-beta04' apply false
+ id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
+}
diff --git a/packages/SettingsLib/Spa/codelab/Android.bp b/packages/SettingsLib/Spa/codelab/Android.bp
new file mode 100644
index 000000000000..8fbbf9aa03c6
--- /dev/null
+++ b/packages/SettingsLib/Spa/codelab/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_app {
+ name: "SpaLibCodelab",
+
+ srcs: ["src/**/*.kt"],
+
+ static_libs: [
+ "SpaLib",
+ "androidx.compose.runtime_runtime",
+ ],
+ kotlincflags: ["-Xjvm-default=all"],
+ platform_apis: true,
+ min_sdk_version: "31",
+}
diff --git a/packages/SettingsLib/Spa/codelab/AndroidManifest.xml b/packages/SettingsLib/Spa/codelab/AndroidManifest.xml
new file mode 100644
index 000000000000..9a89e5efdddb
--- /dev/null
+++ b/packages/SettingsLib/Spa/codelab/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.spa.codelab">
+
+ <application
+ android:label="@string/app_name"
+ android:supportsRtl="true"
+ android:theme="@style/Theme.SettingsLib.Compose.DayNight">
+ <activity
+ android:name="com.android.settingslib.spa.codelab.MainActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/packages/SettingsLib/Spa/codelab/build.gradle b/packages/SettingsLib/Spa/codelab/build.gradle
new file mode 100644
index 000000000000..5251ddd8c01d
--- /dev/null
+++ b/packages/SettingsLib/Spa/codelab/build.gradle
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+plugins {
+ id 'com.android.application'
+ id 'kotlin-android'
+}
+
+android {
+ namespace 'com.android.settingslib.spa.codelab'
+ compileSdk 33
+
+ defaultConfig {
+ applicationId "com.android.settingslib.spa.codelab"
+ minSdk minSdk_version
+ targetSdk 33
+ versionCode 1
+ versionName "1.0"
+ }
+
+ sourceSets {
+ main {
+ kotlin {
+ srcDir "src"
+ }
+ res.srcDirs = ["res"]
+ manifest.srcFile "AndroidManifest.xml"
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ freeCompilerArgs = ["-Xjvm-default=all"]
+ }
+ buildFeatures {
+ compose true
+ }
+ composeOptions {
+ kotlinCompilerExtensionVersion compose_version
+ }
+ packagingOptions {
+ resources {
+ excludes += '/META-INF/{AL2.0,LGPL2.1}'
+ }
+ }
+}
+
+dependencies {
+ implementation(project(":spa"))
+}
diff --git a/packages/SettingsLib/Spa/codelab/res/values/strings.xml b/packages/SettingsLib/Spa/codelab/res/values/strings.xml
new file mode 100644
index 000000000000..6fdbba09050e
--- /dev/null
+++ b/packages/SettingsLib/Spa/codelab/res/values/strings.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<resources>
+ <!-- Codelab App name. [DO NOT TRANSLATE] -->
+ <string name="app_name" translatable="false">SpaLib Codelab</string>
+</resources>
diff --git a/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/MainActivity.kt b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/MainActivity.kt
new file mode 100644
index 000000000000..eef81e0ad351
--- /dev/null
+++ b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/MainActivity.kt
@@ -0,0 +1,22 @@
+/*
+ * 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
+
+import com.android.settingslib.spa.codelab.page.codelabPageRepository
+import com.android.settingslib.spa.framework.SpaActivity
+
+class MainActivity : SpaActivity(codelabPageRepository)
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
new file mode 100644
index 000000000000..484b6045f443
--- /dev/null
+++ b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/ArgumentPage.kt
@@ -0,0 +1,87 @@
+/*
+ * 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.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.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+
+private const val STRING_PARAM_NAME = "stringParam"
+private const val INT_PARAM_NAME = "intParam"
+
+object ArgumentPageProvider : SettingsPageProvider {
+ override val name = Destinations.Argument
+
+ override val arguments = listOf(
+ navArgument(STRING_PARAM_NAME) { type = NavType.StringType },
+ navArgument(INT_PARAM_NAME) { type = NavType.IntType },
+ )
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ ArgumentPage(
+ stringParam = arguments!!.getString(STRING_PARAM_NAME, "default"),
+ intParam = arguments.getInt(INT_PARAM_NAME),
+ )
+ }
+
+ @Composable
+ fun EntryItem(stringParam: String, intParam: Int) {
+ Preference(object : PreferenceModel {
+ override val title = "Sample page with arguments"
+ override val summary =
+ "$STRING_PARAM_NAME=$stringParam, $INT_PARAM_NAME=$intParam".toState()
+ override val onClick = navigator("${Destinations.Argument}/$stringParam/$intParam")
+ })
+ }
+}
+
+@Composable
+fun ArgumentPage(stringParam: String, intParam: Int) {
+ Column {
+ Preference(object : PreferenceModel {
+ override val title = "String param value"
+ override val summary = stringParam.toState()
+ })
+
+ Preference(object : PreferenceModel {
+ override val title = "Int param value"
+ override val summary = intParam.toString().toState()
+ })
+
+ ArgumentPageProvider.EntryItem(stringParam = "foo", intParam = intParam + 1)
+
+ ArgumentPageProvider.EntryItem(stringParam = "bar", intParam = intParam + 1)
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun ArgumentPagePreview() {
+ SettingsTheme {
+ ArgumentPage(stringParam = "foo", intParam = 0)
+ }
+}
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
new file mode 100644
index 000000000000..27c70e450555
--- /dev/null
+++ b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/HomePage.kt
@@ -0,0 +1,64 @@
+/*
+ * 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.layout.padding
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+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
+
+object HomePageProvider : SettingsPageProvider {
+ override val name = Destinations.Home
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ HomePage()
+ }
+}
+
+@Composable
+private fun HomePage() {
+ Column {
+ Text(
+ text = stringResource(R.string.app_name),
+ modifier = Modifier.padding(SettingsDimension.itemPadding),
+ color = MaterialTheme.colorScheme.onSurface,
+ style = MaterialTheme.typography.headlineMedium,
+ )
+
+ PreferencePageProvider.EntryItem()
+
+ ArgumentPageProvider.EntryItem(stringParam = "foo", intParam = 0)
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun HomeScreenPreview() {
+ SettingsTheme {
+ HomePage()
+ }
+}
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
new file mode 100644
index 000000000000..862fc1ee3151
--- /dev/null
+++ b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PageRepository.kt
@@ -0,0 +1,30 @@
+/*
+ * 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 com.android.settingslib.spa.api.SettingsPageRepository
+
+object Destinations {
+ const val Home = "Home"
+ const val Preference = "Preference"
+ const val Argument = "Argument"
+}
+
+val codelabPageRepository = SettingsPageRepository(
+ allPages = listOf(HomePageProvider, PreferencePageProvider, ArgumentPageProvider),
+ 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
new file mode 100644
index 000000000000..81627f60168e
--- /dev/null
+++ b/packages/SettingsLib/Spa/codelab/src/com/android/settingslib/spa/codelab/page/PreferencePage.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.codelab.page
+
+import android.os.Bundle
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.produceState
+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.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import kotlinx.coroutines.delay
+
+object PreferencePageProvider : SettingsPageProvider {
+ override val name = Destinations.Preference
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ PreferencePage()
+ }
+
+ @Composable
+ fun EntryItem() {
+ Preference(object : PreferenceModel {
+ override val title = "Sample Preference"
+ override val onClick = navigator(Destinations.Preference)
+ })
+ }
+}
+
+@Composable
+private fun PreferencePage() {
+ Column(Modifier.verticalScroll(rememberScrollState())) {
+ Preference(object : PreferenceModel {
+ override val title = "Preference"
+ })
+
+ Preference(object : PreferenceModel {
+ override val title = "Preference"
+ override val summary = "With summary".toState()
+ })
+
+ Preference(object : PreferenceModel {
+ override val title = "Preference"
+ override val summary = produceState(initialValue = " ") {
+ delay(1000L)
+ value = "Async summary"
+ }
+ })
+
+ var count by remember { mutableStateOf(0) }
+ Preference(object : PreferenceModel {
+ override val title = "Click me"
+ override val summary = derivedStateOf { count.toString() }
+ override val onClick: (() -> Unit) = { count++ }
+ })
+
+ var ticks by remember { mutableStateOf(0) }
+ LaunchedEffect(ticks) {
+ delay(1000L)
+ ticks++
+ }
+ Preference(object : PreferenceModel {
+ override val title = "Ticker"
+ override val summary = derivedStateOf { ticks.toString() }
+ })
+
+ Preference(object : PreferenceModel {
+ override val title = "Disabled"
+ override val summary = "Disabled".toState()
+ override val enabled = false.toState()
+ })
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun PreferencePagePreview() {
+ SettingsTheme {
+ PreferencePage()
+ }
+}
diff --git a/packages/SettingsLib/Spa/gradle.properties b/packages/SettingsLib/Spa/gradle.properties
new file mode 100644
index 000000000000..82bd51292615
--- /dev/null
+++ b/packages/SettingsLib/Spa/gradle.properties
@@ -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.
+#
+
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true \ No newline at end of file
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000000..e708b1c023ec
--- /dev/null
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000000..52095e19cc0c
--- /dev/null
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+#Thu Jul 14 10:36:06 CST 2022
+distributionBase=GRADLE_USER_HOME
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
+distributionPath=wrapper/dists
+zipStorePath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
diff --git a/packages/SettingsLib/Spa/gradlew b/packages/SettingsLib/Spa/gradlew
new file mode 100755
index 000000000000..4f906e0c811f
--- /dev/null
+++ b/packages/SettingsLib/Spa/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# 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
+#
+# https://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.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/packages/SettingsLib/Spa/gradlew.bat b/packages/SettingsLib/Spa/gradlew.bat
new file mode 100644
index 000000000000..107acd32c4e6
--- /dev/null
+++ b/packages/SettingsLib/Spa/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/packages/SettingsLib/Spa/settings.gradle b/packages/SettingsLib/Spa/settings.gradle
new file mode 100644
index 000000000000..1b69c1eebe0b
--- /dev/null
+++ b/packages/SettingsLib/Spa/settings.gradle
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+pluginManagement {
+ repositories {
+ gradlePluginPortal()
+ google()
+ mavenCentral()
+ }
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+rootProject.name = "SpaLib"
+include ':spa'
+include ':codelab'
+include ':tests'
diff --git a/packages/SettingsLib/Spa/spa/Android.bp b/packages/SettingsLib/Spa/spa/Android.bp
new file mode 100644
index 000000000000..463c0765943f
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/Android.bp
@@ -0,0 +1,36 @@
+//
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_library {
+ name: "SpaLib",
+
+ srcs: ["src/**/*.kt"],
+
+ static_libs: [
+ "androidx.compose.material3_material3",
+ "androidx.compose.material_material-icons-extended",
+ "androidx.compose.runtime_runtime",
+ "androidx.compose.ui_ui-tooling-preview",
+ "androidx.navigation_navigation-compose",
+ "com.google.android.material_material",
+ ],
+ kotlincflags: ["-Xjvm-default=all"],
+ min_sdk_version: "31",
+}
diff --git a/packages/SettingsLib/Spa/spa/AndroidManifest.xml b/packages/SettingsLib/Spa/spa/AndroidManifest.xml
new file mode 100644
index 000000000000..410bcdb36782
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<manifest package="com.android.settingslib.spa" />
diff --git a/packages/SettingsLib/Spa/spa/build.gradle b/packages/SettingsLib/Spa/spa/build.gradle
new file mode 100644
index 000000000000..60794c88d00d
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/build.gradle
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+plugins {
+ id 'com.android.library'
+ id 'kotlin-android'
+}
+
+android {
+ namespace 'com.android.settingslib.spa'
+ compileSdk 33
+
+ defaultConfig {
+ minSdk minSdk_version
+ targetSdk 33
+ }
+
+ sourceSets {
+ main {
+ kotlin {
+ srcDir "src"
+ }
+ res.srcDirs = ["res"]
+ manifest.srcFile "AndroidManifest.xml"
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ freeCompilerArgs = ["-Xjvm-default=all"]
+ }
+ buildFeatures {
+ compose true
+ }
+ composeOptions {
+ kotlinCompilerExtensionVersion compose_version
+ }
+ packagingOptions {
+ resources {
+ excludes += '/META-INF/{AL2.0,LGPL2.1}'
+ }
+ }
+}
+
+dependencies {
+ api "androidx.compose.material3:material3:$compose_material3_version"
+ api "androidx.compose.material:material-icons-extended:$compose_version"
+ api "androidx.compose.runtime:runtime-livedata:$compose_version"
+ api "androidx.compose.ui:ui-tooling-preview:$compose_version"
+ api 'androidx.navigation:navigation-compose:2.5.0'
+ api 'com.google.android.material:material:1.6.1'
+ debugApi "androidx.compose.ui:ui-tooling:$compose_version"
+}
diff --git a/packages/SettingsLib/Spa/spa/res/values-night/themes.xml b/packages/SettingsLib/Spa/spa/res/values-night/themes.xml
new file mode 100644
index 000000000000..8b52b507bdd9
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/res/values-night/themes.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+<resources>
+
+ <style name="Theme.SettingsLib.Compose.DayNight" />
+</resources>
diff --git a/packages/SettingsLib/Spa/spa/res/values/themes.xml b/packages/SettingsLib/Spa/spa/res/values/themes.xml
new file mode 100644
index 000000000000..01f9ea592f6d
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/res/values/themes.xml
@@ -0,0 +1,27 @@
+<?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.
+ -->
+<resources>
+
+ <style name="Theme.SettingsLib.Compose" parent="Theme.Material3.DayNight.NoActionBar">
+ <item name="android:statusBarColor">@android:color/transparent</item>
+ <item name="android:navigationBarColor">@android:color/transparent</item>
+ </style>
+
+ <style name="Theme.SettingsLib.Compose.DayNight">
+ <item name="android:windowLightStatusBar">true</item>
+ </style>
+</resources>
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/api/SettingsPageProvider.kt
new file mode 100644
index 000000000000..0ad0003cca64
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/api/SettingsPageProvider.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.api
+
+import android.os.Bundle
+import androidx.compose.runtime.Composable
+import androidx.navigation.NamedNavArgument
+
+/**
+ * An SettingsPageProvider represent a Settings page.
+ */
+interface SettingsPageProvider {
+
+ /** The page name without arguments. */
+ val name: String
+
+ /** The page arguments, default is no arguments. */
+ val arguments: List<NamedNavArgument>
+ get() = emptyList()
+
+ /** The [Composable] used to render this page. */
+ @Composable
+ fun Page(arguments: Bundle?)
+}
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/api/SettingsPageRepository.kt
new file mode 100644
index 000000000000..ce39f4fbfa2e
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/api/SettingsPageRepository.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.api
+
+data class SettingsPageRepository(
+ val allPages: List<SettingsPageProvider>,
+ val startDestination: String,
+)
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/NavControllerWrapper.kt
new file mode 100644
index 000000000000..35112adf322c
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/NavControllerWrapper.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.framework
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.compositionLocalOf
+import androidx.compose.runtime.remember
+import androidx.navigation.NavHostController
+
+interface NavControllerWrapper {
+ fun navigate(route: String)
+ fun navigateUp()
+}
+
+@Composable
+fun NavHostController.localNavController() =
+ LocalNavController provides remember { NavControllerWrapperImpl(this) }
+
+val LocalNavController = compositionLocalOf<NavControllerWrapper> {
+ object : NavControllerWrapper {
+ override fun navigate(route: String) {}
+
+ override fun navigateUp() {}
+ }
+}
+
+@Composable
+fun navigator(route: String): () -> Unit {
+ val navController = LocalNavController.current
+ return { navController.navigate(route) }
+}
+
+internal class NavControllerWrapperImpl(
+ private val navController: NavHostController,
+) : NavControllerWrapper {
+ override fun navigate(route: String) {
+ navController.navigate(route)
+ }
+
+ override fun navigateUp() {
+ navController.navigateUp()
+ }
+}
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/RuntimeUtils.kt
new file mode 100644
index 000000000000..22d85e0cff88
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/RuntimeUtils.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.framework
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.remember
+
+/**
+ * Remember the [State] initialized with the [this].
+ */
+@Composable
+fun <T> T.toState(): State<T> = remember { stateOf(this) }
+
+/**
+ * Return a new [State] initialized with the passed in [value].
+ */
+fun <T> stateOf(value: T) = object : State<T> {
+ override val value = value
+}
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
new file mode 100644
index 000000000000..28a5899e0141
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/SpaActivity.kt
@@ -0,0 +1,62 @@
+/*
+ * 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
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.runtime.Composable
+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
+
+open class SpaActivity(
+ private val settingsPageRepository: SettingsPageRepository,
+) : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ MainContent()
+ }
+ }
+
+ @Composable
+ private fun MainContent() {
+ SettingsTheme {
+ val navController = rememberNavController()
+ CompositionLocalProvider(navController.localNavController()) {
+ NavHost(navController, settingsPageRepository.startDestination) {
+ for (page in settingsPageRepository.allPages) {
+ composable(
+ route = page.route,
+ arguments = page.arguments,
+ ) { navBackStackEntry ->
+ page.Page(navBackStackEntry.arguments)
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private val SettingsPageProvider.route: String
+ get() = name + arguments.joinToString("") { argument -> "/{${argument.name}}" }
+}
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/theme/MaterialColors.kt
new file mode 100644
index 000000000000..8cd9184317ca
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/MaterialColors.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.theme
+
+import androidx.compose.material3.ColorScheme
+import androidx.compose.material3.dynamicDarkColorScheme
+import androidx.compose.material3.dynamicLightColorScheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalContext
+
+@Composable
+internal fun materialColorScheme(isDarkTheme: Boolean): ColorScheme {
+ val context = LocalContext.current
+ return remember(isDarkTheme) {
+ when {
+ isDarkTheme -> dynamicDarkColorScheme(context)
+ else -> dynamicLightColorScheme(context)
+ }
+ }
+}
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/theme/SettingsDimension.kt
new file mode 100644
index 000000000000..51e75c53e046
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsDimension.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.theme
+
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.ui.unit.dp
+
+object SettingsDimension {
+ val itemIconContainerSize = 72.dp
+ val itemPaddingStart = 24.dp
+ val itemPaddingEnd = 16.dp
+ val itemPaddingVertical = 16.dp
+ val itemPadding = PaddingValues(
+ start = itemPaddingStart,
+ top = itemPaddingVertical,
+ end = itemPaddingEnd,
+ bottom = itemPaddingVertical,
+ )
+}
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/theme/SettingsOpacity.kt
new file mode 100644
index 000000000000..5f4da8af5cb4
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsOpacity.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.theme
+
+object SettingsOpacity {
+ const val Full = 1f
+ const val Disabled = 0.38f
+}
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/theme/SettingsTheme.kt
new file mode 100644
index 000000000000..fce9f2b19ee0
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/theme/SettingsTheme.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.theme
+
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+
+/**
+ * The Material 3 Theme for Settings.
+ */
+@Composable
+fun SettingsTheme(content: @Composable () -> Unit) {
+ val isDarkTheme = isSystemInDarkTheme()
+ val colorScheme = materialColorScheme(isDarkTheme)
+
+ MaterialTheme(colorScheme = colorScheme) {
+ content()
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt
new file mode 100644
index 000000000000..d415e9b262e3
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt
@@ -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.settingslib.spa.widget.preference
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.material3.Divider
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.alpha
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import com.android.settingslib.spa.framework.toState
+import com.android.settingslib.spa.theme.SettingsDimension
+import com.android.settingslib.spa.theme.SettingsOpacity
+import com.android.settingslib.spa.theme.SettingsTheme
+
+@Composable
+internal fun BaseLayout(
+ title: String,
+ subTitle: @Composable () -> Unit,
+ modifier: Modifier = Modifier,
+ icon: (@Composable () -> Unit)? = null,
+ enabled: State<Boolean> = true.toState(),
+ paddingStart: Dp = SettingsDimension.itemPaddingStart,
+ paddingEnd: Dp = SettingsDimension.itemPaddingEnd,
+ paddingVertical: Dp = SettingsDimension.itemPaddingVertical,
+ widget: @Composable () -> Unit = {},
+) {
+ Row(
+ modifier = modifier
+ .fillMaxWidth()
+ .padding(end = paddingEnd),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ val alphaModifier =
+ Modifier.alpha(if (enabled.value) SettingsOpacity.Full else SettingsOpacity.Disabled)
+ BaseIcon(icon, alphaModifier, paddingStart)
+ Titles(
+ title = title,
+ subTitle = subTitle,
+ modifier = alphaModifier
+ .weight(1f)
+ .padding(vertical = paddingVertical),
+ )
+ widget()
+ }
+}
+
+@Composable
+private fun BaseIcon(
+ icon: @Composable (() -> Unit)?,
+ modifier: Modifier,
+ paddingStart: Dp,
+) {
+ if (icon != null) {
+ Box(
+ modifier = modifier.size(SettingsDimension.itemIconContainerSize),
+ contentAlignment = Alignment.Center,
+ ) {
+ icon()
+ }
+ } else {
+ Spacer(modifier = Modifier.width(width = paddingStart))
+ }
+}
+
+// Extracts a scope to avoid frequent recompose outside scope.
+@Composable
+private fun Titles(title: String, subTitle: @Composable () -> Unit, modifier: Modifier) {
+ Column(modifier) {
+ Text(
+ text = title,
+ color = MaterialTheme.colorScheme.onSurface,
+ style = MaterialTheme.typography.titleMedium,
+ )
+ subTitle()
+ }
+}
+
+@Preview
+@Composable
+private fun BaseLayoutPreview() {
+ SettingsTheme {
+ BaseLayout(
+ title = "Title",
+ subTitle = {
+ Divider(thickness = 10.dp)
+ }
+ )
+ }
+}
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
new file mode 100644
index 000000000000..563a47a9a940
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BasePreference.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.widget.preference
+
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.BatteryChargingFull
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.Dp
+import com.android.settingslib.spa.framework.toState
+import com.android.settingslib.spa.theme.SettingsDimension
+import com.android.settingslib.spa.theme.SettingsTheme
+
+@Composable
+internal fun BasePreference(
+ title: String,
+ summary: State<String>,
+ modifier: Modifier = Modifier,
+ icon: (@Composable () -> Unit)? = null,
+ enabled: State<Boolean> = true.toState(),
+ paddingStart: Dp = SettingsDimension.itemPaddingStart,
+ paddingEnd: Dp = SettingsDimension.itemPaddingEnd,
+ paddingVertical: Dp = SettingsDimension.itemPaddingVertical,
+ widget: @Composable () -> Unit = {},
+) {
+ BaseLayout(
+ title = title,
+ subTitle = {
+ if (summary.value.isNotEmpty()) {
+ Text(
+ text = summary.value,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ style = MaterialTheme.typography.bodyMedium,
+ )
+ }
+ },
+ modifier = modifier,
+ icon = icon,
+ enabled = enabled,
+ paddingStart = paddingStart,
+ paddingEnd = paddingEnd,
+ paddingVertical = paddingVertical,
+ widget = widget,
+ )
+}
+
+@Preview
+@Composable
+private fun BasePreferencePreview() {
+ SettingsTheme {
+ BasePreference(
+ title = "Screen Saver",
+ summary = "Clock".toState(),
+ )
+ }
+}
+
+@Preview
+@Composable
+private fun BasePreferenceIconPreview() {
+ SettingsTheme {
+ BasePreference(
+ title = "Screen Saver",
+ summary = "Clock".toState(),
+ icon = {
+ Icon(imageVector = Icons.Outlined.BatteryChargingFull, contentDescription = null)
+ },
+ )
+ }
+}
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
new file mode 100644
index 000000000000..ee20280fb0cc
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.widget.preference
+
+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
+
+/**
+ * The widget model for [Preference] widget.
+ */
+interface PreferenceModel {
+ /**
+ * The title of this [Preference].
+ */
+ val title: String
+
+ /**
+ * The summary of this [Preference].
+ */
+ val summary: State<String>
+ get() = stateOf("")
+
+ /**
+ * Indicates whether this [Preference] is enabled.
+ *
+ * Disabled [Preference] will be displayed in disabled style.
+ */
+ val enabled: State<Boolean>
+ get() = stateOf(true)
+
+ /**
+ * The on click handler of this [Preference].
+ *
+ * This also indicates whether this [Preference] is clickable.
+ */
+ val onClick: (() -> Unit)?
+ get() = null
+}
+
+/**
+ * Preference widget.
+ *
+ * Data is provided through [PreferenceModel].
+ */
+@Composable
+fun Preference(model: PreferenceModel) {
+ val modifier = model.onClick?.let { onClick ->
+ Modifier.clickable(enabled = model.enabled.value) { onClick() }
+ } ?: Modifier
+ BasePreference(
+ title = model.title,
+ summary = model.summary,
+ modifier = modifier,
+ enabled = model.enabled,
+ )
+}
diff --git a/packages/SettingsLib/Spa/tests/Android.bp b/packages/SettingsLib/Spa/tests/Android.bp
new file mode 100644
index 000000000000..037d8c4e78b1
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/Android.bp
@@ -0,0 +1,36 @@
+//
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "SpaLibTests",
+ test_suites: ["device-tests"],
+
+ srcs: ["src/**/*.kt"],
+
+ static_libs: [
+ "SpaLib",
+ "androidx.test.runner",
+ "androidx.test.ext.junit",
+ "androidx.compose.runtime_runtime",
+ "androidx.compose.ui_ui-test-junit4",
+ "androidx.compose.ui_ui-test-manifest",
+ ],
+ kotlincflags: ["-Xjvm-default=all"],
+}
diff --git a/packages/SettingsLib/Spa/tests/AndroidManifest.xml b/packages/SettingsLib/Spa/tests/AndroidManifest.xml
new file mode 100644
index 000000000000..c224cafa4740
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.spa.tests">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:label="Tests for SpaLib"
+ android:targetPackage="com.android.settingslib.spa.tests">
+ </instrumentation>
+</manifest>
diff --git a/packages/SettingsLib/Spa/tests/build.gradle b/packages/SettingsLib/Spa/tests/build.gradle
new file mode 100644
index 000000000000..707017e7e17f
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/build.gradle
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+plugins {
+ id 'com.android.library'
+ id 'kotlin-android'
+}
+
+android {
+ namespace 'com.android.settingslib.spa.tests'
+ compileSdk 33
+
+ defaultConfig {
+ minSdk minSdk_version
+ targetSdk 33
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ sourceSets {
+ androidTest {
+ kotlin {
+ srcDir "src"
+ }
+ manifest.srcFile "AndroidManifest.xml"
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ freeCompilerArgs = ["-Xjvm-default=all"]
+ }
+ buildFeatures {
+ compose true
+ }
+ composeOptions {
+ kotlinCompilerExtensionVersion compose_version
+ }
+ packagingOptions {
+ resources {
+ excludes += '/META-INF/{AL2.0,LGPL2.1}'
+ }
+ }
+}
+
+dependencies {
+ androidTestImplementation(project(":spa"))
+ androidTestImplementation 'androidx.test.ext:junit-ktx:1.1.3'
+ androidTestImplementation("androidx.compose.ui:ui-test-junit4:$compose_version")
+ androidTestDebugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version"
+}
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
new file mode 100644
index 000000000000..4097946abc79
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/PreferenceTest.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.widget.preference
+
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.test.assertIsDisplayed
+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 org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class PreferenceTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Test
+ fun title_displayed() {
+ composeTestRule.setContent {
+ Preference(object : PreferenceModel {
+ override val title = "Preference"
+ })
+ }
+
+ composeTestRule.onNodeWithText("Preference").assertIsDisplayed()
+ }
+
+ @Test
+ fun click_enabled_withEffect() {
+ composeTestRule.setContent {
+ var count by remember { mutableStateOf(0) }
+ Preference(object : PreferenceModel {
+ override val title = "Preference"
+ override val summary = derivedStateOf { count.toString() }
+ override val onClick: (() -> Unit) = { count++ }
+ })
+ }
+
+ composeTestRule.onNodeWithText("Preference").performClick()
+ composeTestRule.onNodeWithText("1").assertIsDisplayed()
+ }
+
+ @Test
+ fun click_disabled_noEffect() {
+ composeTestRule.setContent {
+ var count by remember { mutableStateOf(0) }
+ Preference(object : PreferenceModel {
+ override val title = "Preference"
+ override val summary = derivedStateOf { count.toString() }
+ override val enabled = false.toState()
+ override val onClick: (() -> Unit) = { count++ }
+ })
+ }
+
+ composeTestRule.onNodeWithText("Preference").performClick()
+ composeTestRule.onNodeWithText("0").assertIsDisplayed()
+ }
+}
diff --git a/packages/SettingsLib/TopIntroPreference/res/layout-v33/top_intro_preference.xml b/packages/SettingsLib/TopIntroPreference/res/layout-v33/top_intro_preference.xml
new file mode 100644
index 000000000000..6046d911879e
--- /dev/null
+++ b/packages/SettingsLib/TopIntroPreference/res/layout-v33/top_intro_preference.xml
@@ -0,0 +1,39 @@
+<?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="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingBottom="16dp"
+ android:paddingTop="8dp"
+ android:background="?android:attr/selectableItemBackground"
+ android:clipToPadding="false">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clickable="false"
+ android:longClickable="false"
+ android:maxLines="10"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="@style/TextAppearance.TopIntroText"/>
+</LinearLayout> \ No newline at end of file
diff --git a/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml b/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
index b2a9037e3b79..4d6e1b7f0ac8 100644
--- a/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
+++ b/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
@@ -33,6 +33,5 @@
android:clickable="false"
android:longClickable="false"
android:maxLines="10"
- android:hyphenationFrequency="normalFast"
android:textAppearance="@style/TextAppearance.TopIntroText"/>
</LinearLayout> \ No newline at end of file
diff --git a/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target.xml b/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target.xml
index ac5807dc08de..2c35772de2ca 100644
--- a/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target.xml
+++ b/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target.xml
@@ -41,7 +41,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
- android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="marquee"/>
@@ -53,7 +52,6 @@
android:layout_alignStart="@android:id/title"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary"
- android:hyphenationFrequency="normalFast"
android:maxLines="10"/>
</RelativeLayout>
diff --git a/packages/SettingsLib/TwoTargetPreference/res/layout-v33/preference_two_target.xml b/packages/SettingsLib/TwoTargetPreference/res/layout-v33/preference_two_target.xml
new file mode 100644
index 000000000000..0bca9abfc6e2
--- /dev/null
+++ b/packages/SettingsLib/TwoTargetPreference/res/layout-v33/preference_two_target.xml
@@ -0,0 +1,74 @@
+<?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.
+ -->
+
+<!-- Based off preference_material_settings.xml except that ripple on only on the left side. -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:gravity="center_vertical"
+ android:background="?android:attr/selectableItemBackground"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:clipToPadding="false">
+
+ <include layout="@layout/settingslib_icon_frame"/>
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:ellipsize="marquee"/>
+
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/title"
+ android:layout_alignStart="@android:id/title"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:maxLines="10"/>
+
+ </RelativeLayout>
+
+ <include layout="@layout/preference_two_target_divider" />
+
+ <!-- Preference should place its actual preference widget here. -->
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/two_target_min_width"
+ android:gravity="center"
+ android:orientation="vertical" />
+
+</LinearLayout>
diff --git a/packages/SettingsLib/res/layout-v33/preference_access_point.xml b/packages/SettingsLib/res/layout-v33/preference_access_point.xml
new file mode 100644
index 000000000000..81bfeffdded9
--- /dev/null
+++ b/packages/SettingsLib/res/layout-v33/preference_access_point.xml
@@ -0,0 +1,110 @@
+<?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.
+ -->
+
+<!-- Based off preference_two_target.xml with Material ripple moved to parent for full ripple. -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:gravity="center_vertical"
+ android:background="?android:attr/selectableItemBackground"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="start|center_vertical"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="start|center_vertical"
+ android:minWidth="48dp"
+ android:orientation="horizontal"
+ android:clipToPadding="false"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+ <androidx.preference.internal.PreferenceImageView
+ android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ settings:maxWidth="48dp"
+ settings:maxHeight="48dp" />
+ </LinearLayout>
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:ellipsize="marquee" />
+
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/title"
+ android:layout_alignStart="@android:id/title"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:maxLines="10" />
+
+ </RelativeLayout>
+
+ </LinearLayout>
+
+ <include layout="@layout/preference_two_target_divider" />
+
+ <!-- Preference should place its actual preference widget here. -->
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/two_target_min_width"
+ android:gravity="center"
+ android:orientation="vertical" />
+
+ <ImageButton
+ android:id="@+id/icon_button"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/two_target_min_width"
+ android:minHeight="@dimen/min_tap_target_size"
+ android:layout_gravity="center"
+ android:background="?android:attr/selectableItemBackground"
+ android:visibility="gone">
+ </ImageButton>
+</LinearLayout>
diff --git a/packages/SettingsLib/res/layout-v33/preference_checkable_two_target.xml b/packages/SettingsLib/res/layout-v33/preference_checkable_two_target.xml
new file mode 100644
index 000000000000..7ad018cfbcf4
--- /dev/null
+++ b/packages/SettingsLib/res/layout-v33/preference_checkable_two_target.xml
@@ -0,0 +1,97 @@
+<?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.
+ -->
+
+<!-- Based off preference_material_settings.xml except that ripple on only on the left side. -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:gravity="center_vertical"
+ android:background="@android:color/transparent"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:background="?android:attr/selectableItemBackground"
+ android:gravity="start|center_vertical"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:id="@+id/checkbox_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="start|center_vertical"
+ android:minWidth="48dp"
+ android:minHeight="48dp"
+ android:orientation="horizontal"
+ android:clipToPadding="false"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+ <include layout="@layout/preference_widget_checkbox" />
+ </LinearLayout>
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:ellipsize="marquee" />
+
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/title"
+ android:layout_alignStart="@android:id/title"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:maxLines="10" />
+
+ </RelativeLayout>
+
+ </LinearLayout>
+
+ <include layout="@layout/preference_two_target_divider" />
+
+ <!-- Preference should place its actual preference widget here. -->
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/two_target_min_width"
+ android:gravity="center"
+ android:orientation="vertical" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/packages/SettingsLib/res/layout-v33/restricted_switch_preference.xml b/packages/SettingsLib/res/layout-v33/restricted_switch_preference.xml
new file mode 100644
index 000000000000..31e9696813af
--- /dev/null
+++ b/packages/SettingsLib/res/layout-v33/restricted_switch_preference.xml
@@ -0,0 +1,97 @@
+<?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="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:gravity="center_vertical"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:background="?android:attr/selectableItemBackground"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minWidth="56dp"
+ android:gravity="start|center_vertical"
+ android:orientation="horizontal"
+ android:paddingEnd="12dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+ <com.android.internal.widget.PreferenceImageView
+ android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxWidth="48dp"
+ android:maxHeight="48dp" />
+ </LinearLayout>
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:ellipsize="marquee" />
+
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/title"
+ android:layout_alignStart="@android:id/title"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:maxLines="10" />
+
+ <TextView
+ android:id="@+id/additional_summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/summary"
+ android:layout_alignStart="@android:id/summary"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="10"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:visibility="gone" />
+ </RelativeLayout>
+
+ <!-- Preference should place its actual preference widget here. -->
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="end|center_vertical"
+ android:paddingStart="16dp"
+ android:orientation="vertical" />
+
+</LinearLayout>
diff --git a/packages/SettingsLib/res/layout/preference_access_point.xml b/packages/SettingsLib/res/layout/preference_access_point.xml
index 4ad9d8040aaa..802d604a44f9 100644
--- a/packages/SettingsLib/res/layout/preference_access_point.xml
+++ b/packages/SettingsLib/res/layout/preference_access_point.xml
@@ -65,7 +65,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
- android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="marquee" />
@@ -77,7 +76,6 @@
android:layout_alignStart="@android:id/title"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary"
- android:hyphenationFrequency="normalFast"
android:maxLines="10" />
</RelativeLayout>
diff --git a/packages/SettingsLib/res/layout/preference_checkable_two_target.xml b/packages/SettingsLib/res/layout/preference_checkable_two_target.xml
index cbe49cd5dba7..f512f9b27131 100644
--- a/packages/SettingsLib/res/layout/preference_checkable_two_target.xml
+++ b/packages/SettingsLib/res/layout/preference_checkable_two_target.xml
@@ -62,7 +62,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
- android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="marquee" />
@@ -74,7 +73,6 @@
android:layout_alignStart="@android:id/title"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary"
- android:hyphenationFrequency="normalFast"
android:maxLines="10" />
</RelativeLayout>
diff --git a/packages/SettingsLib/res/layout/restricted_switch_preference.xml b/packages/SettingsLib/res/layout/restricted_switch_preference.xml
index edea1444e83a..169ae97c1a6d 100644
--- a/packages/SettingsLib/res/layout/restricted_switch_preference.xml
+++ b/packages/SettingsLib/res/layout/restricted_switch_preference.xml
@@ -52,7 +52,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
- android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="marquee" />
@@ -63,7 +62,6 @@
android:layout_alignStart="@android:id/title"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary"
- android:hyphenationFrequency="normalFast"
android:maxLines="10" />
<TextView android:id="@+id/additional_summary"
@@ -74,7 +72,6 @@
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary"
android:maxLines="10"
- android:hyphenationFrequency="normalFast"
android:visibility="gone" />
</RelativeLayout>
diff --git a/packages/SettingsLib/res/values-af/arrays.xml b/packages/SettingsLib/res/values-af/arrays.xml
index 7165c147e34f..1de76684d860 100644
--- a/packages/SettingsLib/res/values-af/arrays.xml
+++ b/packages/SettingsLib/res/values-af/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 264faad485c1..fe8030f9a68a 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Lêeroordrag"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Invoertoestel"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internettoegang"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Deling van kontakte en oproepgeskiedenis"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Gebruik vir deling van kontakte en oproepgeskiedenis"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Deling van internetverbinding"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Teksboodskappe"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM-toegang"</string>
@@ -662,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Weer"</string>
<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="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/arrays.xml b/packages/SettingsLib/res/values-am/arrays.xml
index 1108c82116ea..a900d1379027 100644
--- a/packages/SettingsLib/res/values-am/arrays.xml
+++ b/packages/SettingsLib/res/values-am/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_sample_rate_titles">
<item msgid="926809261293414607">"የስርዓቱን ምርጫ (ነባሪ) ተጠቀም"</item>
<item msgid="8003118270854840095">"44.1 ኪኸ"</item>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index b68cbd745e01..d3c034fa170c 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ፋይል ማስተላለፍ"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ግቤት መሣሪያ"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"የበይነመረብ ድረስ"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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">"የሲም መዳረሻ"</string>
@@ -662,6 +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) -->
+ <skip />
<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-ar/arrays.xml b/packages/SettingsLib/res/values-ar/arrays.xml
index eb4be38da93e..8f7d7d2ca399 100644
--- a/packages/SettingsLib/res/values-ar/arrays.xml
+++ b/packages/SettingsLib/res/values-ar/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_sample_rate_titles">
<item msgid="926809261293414607">"استخدام اختيار النظام (تلقائي)"</item>
<item msgid="8003118270854840095">"44.1 كيلو هرتز"</item>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index c8b263bcb196..6783654c32e5 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"نقل الملف"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"جهاز الإرسال"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"استخدام الإنترنت"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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>
@@ -662,6 +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="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-as/arrays.xml b/packages/SettingsLib/res/values-as/arrays.xml
index df23f672994c..4c879d0123d3 100644
--- a/packages/SettingsLib/res/values-as/arrays.xml
+++ b/packages/SettingsLib/res/values-as/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"ছিষ্টেমৰ বাছনি ব্যৱহাৰ কৰক (ডিফ\'ল্ট)"</item>
- <item msgid="4055460186095649420">"এছবিচি"</item>
- <item msgid="720249083677397051">"এএচি"</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>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"ছিষ্টেমৰ বাছনি ব্যৱহাৰ কৰক (ডিফ\'ল্ট)"</item>
- <item msgid="9024885861221697796">"এছবিচি"</item>
- <item msgid="4688890470703790013">"এএচি"</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>
- </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_sample_rate_titles">
<item msgid="926809261293414607">"ছিষ্টেমৰ বাছনি ব্যৱহাৰ কৰক (ডিফ\'ল্ট)"</item>
<item msgid="8003118270854840095">"৪৪.১ কিল\'হাৰ্টজ"</item>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index ea1aff001aa5..543c703e9003 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ফাইল স্থানান্তৰণ"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ইনপুট ডিভাইচ"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ইণ্টাৰনেট সংযোগ"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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">"ছিম প্ৰৱেশ"</string>
@@ -662,6 +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="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-az/arrays.xml b/packages/SettingsLib/res/values-az/arrays.xml
index 516379102177..48974a7bd40f 100644
--- a/packages/SettingsLib/res/values-az/arrays.xml
+++ b/packages/SettingsLib/res/values-az/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 88333e82782e..f3e5d95219fa 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Fayl transferi"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Daxiletmə cihazı"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"İnternetə giriş"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Kontaktlar və zəng tarixçəsi paylaşımı"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Kontaktlar və zəng tarixçəsi paylaşımı üçün istifadə edin"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"internet bağlantı paylaşımı"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mətn Mesajları"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM-karta giriş"</string>
@@ -662,6 +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="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 5cc43f6877ae..337da265711c 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 a84d0190607a..6a880aac349f 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Prenos datoteke"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Ulazni uređaj"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Pristup Internetu"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Deljenje kontakata i istorije poziva"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Koristite za deljenje kontakata i istorije poziva"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Deljenje internet veze"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS-ovi"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Pristup SIM kartici"</string>
@@ -662,6 +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="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/arrays.xml b/packages/SettingsLib/res/values-be/arrays.xml
index 6259c2d0f3a6..d843629a43c1 100644
--- a/packages/SettingsLib/res/values-be/arrays.xml
+++ b/packages/SettingsLib/res/values-be/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_sample_rate_titles">
<item msgid="926809261293414607">"Выбар сістэмы (стандартны)"</item>
<item msgid="8003118270854840095">"44,1 кГц"</item>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 37f8410dd87c..a90242667b56 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Перадача файлаў"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Прылада ўводу"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Доступ у інтэрнэт"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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>
@@ -662,6 +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="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-bg/arrays.xml b/packages/SettingsLib/res/values-bg/arrays.xml
index 49e66c0f74e6..1aad6ca714c4 100644
--- a/packages/SettingsLib/res/values-bg/arrays.xml
+++ b/packages/SettingsLib/res/values-bg/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 f2cf69ea5b1c..c5123667f2b2 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Прехвърляне на файл"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Входно устройство"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Достъп до интернет"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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>
@@ -662,6 +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="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 fdb611bf191d..5e6bb9527c29 100644
--- a/packages/SettingsLib/res/values-bn/arrays.xml
+++ b/packages/SettingsLib/res/values-bn/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 801fb347a4ac..23eed04b4fb6 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ফাইল স্থানান্তর"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ইনপুট ডিভাইস"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ইন্টারনেট অ্যাক্সেস"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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">"সিম অ্যাক্সেস"</string>
@@ -662,6 +660,7 @@
<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>
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"হোম কন্ট্রোল"</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 32edef143c24..262a35feab65 100644
--- a/packages/SettingsLib/res/values-bs/arrays.xml
+++ b/packages/SettingsLib/res/values-bs/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 61082f6dd7f5..78a484f28e56 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Prenošenje fajla"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Ulazni uređaj"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Pristup internetu"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Dijeljenje kontakata i historije poziva"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Upotrijebite za dijeljenje kontakata i historije poziva"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Dijeljenje internet veze"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS-ovi"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Pristup SIM-u"</string>
@@ -662,6 +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="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 a267af862daf..8c34a1f0f72a 100644
--- a/packages/SettingsLib/res/values-ca/arrays.xml
+++ b/packages/SettingsLib/res/values-ca/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 881f9d4ea193..606a379b1651 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferència de fitxers"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositiu d\'entrada"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Accés a Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Compartició de contactes i trucades"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Utilitza per compartir contactes i l\'historial de trucades"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Compartició de connexió d\'Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Missatges de text"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Accés a la SIM"</string>
@@ -662,6 +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="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/arrays.xml b/packages/SettingsLib/res/values-cs/arrays.xml
index 3eeae64c8667..90bcaa4ba386 100644
--- a/packages/SettingsLib/res/values-cs/arrays.xml
+++ b/packages/SettingsLib/res/values-cs/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Použít systémový výběr (výchozí)"</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>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Použít systémový výběr (výchozí)"</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>
- </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_sample_rate_titles">
<item msgid="926809261293414607">"Použít systémový výběr (výchozí)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 7f314bde25c4..37c0bb4ced79 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Přenos souborů"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Vstupní zařízení"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Přístup k internetu"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Sdílení kontaktů a historie volání"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Používat ke sdílení kontaktů a historie hovorů"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Sdílení internetového připojení"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Textové zprávy"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Přístup k SIM kartě"</string>
@@ -662,6 +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) -->
+ <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>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fyzická klávesnice"</string>
diff --git a/packages/SettingsLib/res/values-da/arrays.xml b/packages/SettingsLib/res/values-da/arrays.xml
index 58ca7225f549..155104ae81dc 100644
--- a/packages/SettingsLib/res/values-da/arrays.xml
+++ b/packages/SettingsLib/res/values-da/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 c470b8193fbd..d80e1ec4cbba 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Filoverførsel"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Inputenhed"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetadgang"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Deling af kontakter og opkaldshistorik"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Brug til deling af kontakter og opkaldshistorik"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Deling af internetforbindelse"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Sms-beskeder"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Adgang til SIM-kort"</string>
@@ -662,6 +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="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 a8eb3f6205ea..31126a803f8f 100644
--- a/packages/SettingsLib/res/values-de/arrays.xml
+++ b/packages/SettingsLib/res/values-de/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 34a622bcb925..d4fafe12e10c 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Dateiübertragung"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Eingabegerät"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetzugriff"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Teilen von Kontakten und der Anrufliste"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Zum Teilen von Kontakten und der Anrufliste verwenden"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Freigabe der Internetverbindung"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Zugriff auf SIM"</string>
@@ -662,6 +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="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 ccd06fa02451..70000e1917a7 100644
--- a/packages/SettingsLib/res/values-el/arrays.xml
+++ b/packages/SettingsLib/res/values-el/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 fdc7419359b5..aa613a5eeb17 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Μεταφορά αρχείου"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Συσκευή εισόδου"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Πρόσβαση στο Διαδίκτυο"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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>
@@ -662,6 +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="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 697e49afdc3d..fc6f791bf6ea 100644
--- a/packages/SettingsLib/res/values-en-rAU/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rAU/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 234109bd401c..62c8e21bf489 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"File transfer"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Input device"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internet access"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Contacts and call history sharing"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Use for contacts and call history sharing"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internet connection sharing"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Text messages"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM access"</string>
@@ -662,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Weather"</string>
<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="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 697e49afdc3d..fc6f791bf6ea 100644
--- a/packages/SettingsLib/res/values-en-rCA/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rCA/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 d88bb0e776cf..8650b77dec89 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"File transfer"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Input device"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internet access"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Contacts and call history sharing"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Use for contacts and call history sharing"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internet connection sharing"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Text messages"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM access"</string>
@@ -662,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Weather"</string>
<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="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 697e49afdc3d..fc6f791bf6ea 100644
--- a/packages/SettingsLib/res/values-en-rGB/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rGB/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 234109bd401c..62c8e21bf489 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"File transfer"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Input device"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internet access"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Contacts and call history sharing"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Use for contacts and call history sharing"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internet connection sharing"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Text messages"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM access"</string>
@@ -662,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Weather"</string>
<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="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 697e49afdc3d..fc6f791bf6ea 100644
--- a/packages/SettingsLib/res/values-en-rIN/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rIN/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 234109bd401c..62c8e21bf489 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"File transfer"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Input device"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internet access"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Contacts and call history sharing"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Use for contacts and call history sharing"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internet connection sharing"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Text messages"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM access"</string>
@@ -662,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Weather"</string>
<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="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 aca3eb420310..34db380b0cf6 100644
--- a/packages/SettingsLib/res/values-en-rXC/arrays.xml
+++ b/packages/SettingsLib/res/values-en-rXC/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‏‎‏‎‎‎‎‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‏‎‎‎‏‎‏‏‏‏‎‏‏‏‏‎‏‎‎‎map13‎‏‎‎‏‎"</item>
<item msgid="8147982633566548515">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‏‎‎‏‏‎‏‏‏‎‎‏‏‎‎‎‎‏‎‎‏‎‎‎‎‏‏‎‏‏‏‎‎‏‎‎‏‎‎‏‏‎‎‏‎‎‎‏‎‎‎‏‏‎map14‎‏‎‎‏‎"</item>
</string-array>
- <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>
- </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>
- </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_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 5d775d3a356b..21697b9a4fb5 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -660,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‎‎‎‎‏‎‎‏‏‏‎‏‎‏‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‏‎‏‎‏‎‎‎‎‎‏‎‎‎‎‎‎‏‏‎‎‎‎‏‎‏‏‏‏‎Weather‎‏‎‎‏‎"</string>
<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="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/arrays.xml b/packages/SettingsLib/res/values-es-rUS/arrays.xml
index 6a926d26cfb3..9b1aa3a274fc 100644
--- a/packages/SettingsLib/res/values-es-rUS/arrays.xml
+++ b/packages/SettingsLib/res/values-es-rUS/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Usar selección 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>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Usar selección 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>
- </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_sample_rate_titles">
<item msgid="926809261293414607">"Usar selección del sistema (predeterminado)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 98c140045b23..27cd0ab200f5 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferencia de archivos"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acceso a Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Compartir contactos e historial de llam."</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Uso para compartir contactos e historial de llamadas"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Compartir conexión a Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensajes de texto"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acceso a SIM"</string>
@@ -662,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Clima"</string>
<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>
<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 7c37fa5007e9..0677864d1f46 100644
--- a/packages/SettingsLib/res/values-es/arrays.xml
+++ b/packages/SettingsLib/res/values-es/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 90c4fb8e938a..d221c8dbe250 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferencia de archivos"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acceso a Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Compartir contactos e historial de llamadas"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Usar para compartir los contactos y el historial de llamadas"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Compartir conexión a Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensajes de texto"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acceso a tarjeta SIM"</string>
@@ -662,6 +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="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 14d0fd12d762..d986ecf47260 100644
--- a/packages/SettingsLib/res/values-et/arrays.xml
+++ b/packages/SettingsLib/res/values-et/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 a6c787af5146..f32ae2347ef6 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Failiedastus"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Sisendseade"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Juurdepääs internetile"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Kontaktide ja kõneajaloo jagamine"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Kasutage kontaktide ja kõneajaloo jagamiseks"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Interneti-ühenduse jagamine"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Tekstsõnumid"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Juurdepääs SIM-ile"</string>
@@ -662,6 +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="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 cc47e27fd485..d166e1b97a38 100644
--- a/packages/SettingsLib/res/values-eu/arrays.xml
+++ b/packages/SettingsLib/res/values-eu/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 87cfaa8dac95..bcb57e89b213 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Fitxategi-transferentzia"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Sarrerako gailua"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Interneteko konexioa"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Kontaktuak eta deien historia partekatzeko aukera"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Erabili kontaktuetarako eta deien historia partekatzeko"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Interneteko konexioa partekatzea"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Testu-mezuak"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIMerako sarbidea"</string>
@@ -662,6 +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="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 d76389b7b51f..b7761dd6ebcb 100644
--- a/packages/SettingsLib/res/values-fa/arrays.xml
+++ b/packages/SettingsLib/res/values-fa/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 51d74dd86ea9..6abe873d27eb 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"انتقال فایل"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"دستگاه ورودی"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"دسترسی به اینترنت"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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">"دسترسی سیم‌کارت"</string>
@@ -662,6 +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="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/arrays.xml b/packages/SettingsLib/res/values-fi/arrays.xml
index f8281866315b..296989299cfe 100644
--- a/packages/SettingsLib/res/values-fi/arrays.xml
+++ b/packages/SettingsLib/res/values-fi/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Käytä järjestelmän valintaa (oletus)"</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> ‑ääni"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ‑ääni"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Käytä järjestelmän valintaa (oletus)"</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> ‑ääni"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> ‑ääni"</item>
- <item msgid="2553206901068987657">"LDAC"</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_sample_rate_titles">
<item msgid="926809261293414607">"Käytä järjestelmän valintaa (oletus)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index c0f90b8e5a4c..2f3436e1e7c6 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Tiedostonsiirto"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Syöttölaite"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetyhteys"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Yhteystietojen ja soittohistorian jako"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Käytä yhteystiedoissa ja soittohistorian jakamiseen"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internetyhteyden jakaminen"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Tekstiviestit"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM-kortin käyttö"</string>
@@ -662,6 +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) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Valitse profiilikuva"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Oletuskäyttäjäkuvake"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fyysinen näppäimistö"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/arrays.xml b/packages/SettingsLib/res/values-fr-rCA/arrays.xml
index 50c1bcb2e6b3..12acbb6736b4 100644
--- a/packages/SettingsLib/res/values-fr-rCA/arrays.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 14515cd4ec7d..28b3cd96aed6 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transfert de fichier"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Périphérique d\'entrée"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Accès Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Partage des contacts et des appels"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Sert à partager des contacts et l\'historique des appels"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Partage de connexion Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Messages texte"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Accès à la carte SIM"</string>
@@ -662,6 +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="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 6343f0dc8845..80ac7e4e4752 100644
--- a/packages/SettingsLib/res/values-fr/arrays.xml
+++ b/packages/SettingsLib/res/values-fr/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 19ea6ddf2c9b..da1497aa980b 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transfert de fichiers"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Périphérique d\'entrée"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Accès Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Partage contacts/historique des appels"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"À utiliser pour partage des contacts/historique des appels"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Partage de connexion Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Accès à la carte SIM"</string>
@@ -662,6 +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="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 22fb2233ced9..b6cf48e54f72 100644
--- a/packages/SettingsLib/res/values-gl/arrays.xml
+++ b/packages/SettingsLib/res/values-gl/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 94fa72a260b1..7822749eea0f 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferencia de ficheiros"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acceso a Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Compartir contactos e hist. de chamadas"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Uso da opción de compartir contactos e historial de chamadas"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Uso compartido da conexión a Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensaxes de texto"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acceso á SIM"</string>
@@ -662,6 +660,8 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"O tempo"</string>
<string name="dream_complication_title_aqi" msgid="4587552608957834110">"Calidade do aire"</string>
<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="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 318b5f5714bd..7e668e71f91d 100644
--- a/packages/SettingsLib/res/values-gu/arrays.xml
+++ b/packages/SettingsLib/res/values-gu/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 a474d8226364..cebb1febc001 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ફાઇલ સ્થાનાંતરણ"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ઇનપુટ ડિવાઇસ"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ઇન્ટરનેટ ઍક્સેસ"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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">"સિમ ઍક્સેસ"</string>
@@ -662,6 +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="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 61a8f92b792d..13da75b9bdba 100644
--- a/packages/SettingsLib/res/values-hi/arrays.xml
+++ b/packages/SettingsLib/res/values-hi/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 55fc547444cd..417d2b282821 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"फ़ाइल स्थानांतरण"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"इनपुट डिवाइस"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"इंटरनेट ऐक्सेस"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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">"सिम ऐक्सेस"</string>
@@ -662,6 +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="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/arrays.xml b/packages/SettingsLib/res/values-hr/arrays.xml
index c979bc47ff6d..0e66858f2d3a 100644
--- a/packages/SettingsLib/res/values-hr/arrays.xml
+++ b/packages/SettingsLib/res/values-hr/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Upotreba odabira sustava (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>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Upotreba odabira sustava (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>
- </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_sample_rate_titles">
<item msgid="926809261293414607">"Upotreba odabira sustava (zadano)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 55a3e5ed7b4a..267000ff9d4b 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Prijenos datoteke"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Ulazni uređaj"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Pristup internetu"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Dijeljenje kontakata i povijesti poziva"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Upotreba za dijeljenje kontakata i povijesti poziva"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Dijeljenje internetske veze"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS-ovi"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Pristup SIM-u"</string>
@@ -662,6 +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) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Odabir profilne slike"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Ikona zadanog korisnika"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fizička tipkovnica"</string>
diff --git a/packages/SettingsLib/res/values-hu/arrays.xml b/packages/SettingsLib/res/values-hu/arrays.xml
index 230e5546fae5..a5f37ea4cab9 100644
--- a/packages/SettingsLib/res/values-hu/arrays.xml
+++ b/packages/SettingsLib/res/values-hu/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 2454d1cc5f90..69ddad82c712 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Fájlátvitel"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Beviteli eszköz"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetelérés"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Névjegyek és hívásnapló megosztása"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Névjegyek és hívásnapló megosztásához használható"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internetkapcsolat megosztása"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Szöveges üzenetek"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM-elérés"</string>
@@ -662,6 +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="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/arrays.xml b/packages/SettingsLib/res/values-hy/arrays.xml
index 2a9c544188c7..50d7c7f59f65 100644
--- a/packages/SettingsLib/res/values-hy/arrays.xml
+++ b/packages/SettingsLib/res/values-hy/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_sample_rate_titles">
<item msgid="926809261293414607">"Օգտագործել համակարգի կարգավորումը (կանխադրված)"</item>
<item msgid="8003118270854840095">"44,1 կՀց"</item>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 896655119ab7..d8efbd36bbeb 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Ֆայլերի փոխանցում"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Ներմուծման սարք"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Ինտերնետի հասանելիություն"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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">"SMS հաղորդագրություններ"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM քարտի հասանելիություն"</string>
@@ -662,6 +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="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-in/arrays.xml b/packages/SettingsLib/res/values-in/arrays.xml
index 6349e53465ee..5b0ad98aa2d5 100644
--- a/packages/SettingsLib/res/values-in/arrays.xml
+++ b/packages/SettingsLib/res/values-in/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 c525317c65ec..029ad8bcb196 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transfer file"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Perangkat masukan"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Akses Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Berbagi kontak dan histori panggilan"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Gunakan untuk berbagi kontak dan histori panggilan"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Berbagi koneksi internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Akses SIM"</string>
@@ -662,6 +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="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 730fcaf45c98..9d481f8993d8 100644
--- a/packages/SettingsLib/res/values-is/arrays.xml
+++ b/packages/SettingsLib/res/values-is/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 c06d2f0cc497..ccf06b027213 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Skráaflutningur"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Inntakstæki"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetaðgangur"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Tengiliðir, SMS-skilaboð og símtalaferill"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Nota til að deila tengiliðum og símtalaferli"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Deiling nettengingar"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Textaskilaboð"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Aðgangur að SIM-korti"</string>
@@ -662,6 +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="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 b7e114f025fb..62450da37ff7 100644
--- a/packages/SettingsLib/res/values-it/arrays.xml
+++ b/packages/SettingsLib/res/values-it/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 b3ac25aaeafc..3811152467f3 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -117,10 +117,8 @@
<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>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Condivis. 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>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Accesso alla SIM"</string>
@@ -662,6 +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="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 587016f99614..49f3fcf3cce6 100644
--- a/packages/SettingsLib/res/values-iw/arrays.xml
+++ b/packages/SettingsLib/res/values-iw/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 f021b3742399..3421fb5e7b83 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"העברת קבצים"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"מכשיר קלט"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"גישה לאינטרנט"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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>
@@ -662,6 +660,7 @@
<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>
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"בית חכם"</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 ad84d9ed6fba..d73cc4362a56 100644
--- a/packages/SettingsLib/res/values-ja/arrays.xml
+++ b/packages/SettingsLib/res/values-ja/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 2994e4d41559..e7d2cf8cccff 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ファイル転送"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"入力デバイス"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"インターネットアクセス"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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>
@@ -662,6 +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="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 01f1dcc7663b..c0d6f97eb552 100644
--- a/packages/SettingsLib/res/values-ka/arrays.xml
+++ b/packages/SettingsLib/res/values-ka/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 0c3277bacc31..09f547132420 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ფაილების გადაცემა"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"შეყვანის მოწყობილობა"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ინტერნეტზე წვდომა"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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>
@@ -662,6 +660,7 @@
<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>
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"სახლის მართვა"</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 33fd25bbdaf6..fc998e73a16c 100644
--- a/packages/SettingsLib/res/values-kk/arrays.xml
+++ b/packages/SettingsLib/res/values-kk/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 63c95759a1ba..3281303a6d33 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Файл жіберу"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Кіріс құрылғысы"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Интернетке қосылу"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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>
@@ -662,6 +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="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/arrays.xml b/packages/SettingsLib/res/values-km/arrays.xml
index ef3aa522163a..a005f4dc2bd0 100644
--- a/packages/SettingsLib/res/values-km/arrays.xml
+++ b/packages/SettingsLib/res/values-km/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_sample_rate_titles">
<item msgid="926809261293414607">"ប្រើ​ការ​ជ្រើសរើស​ប្រព័ន្ធ (លំនាំ​ដើម)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 9b9228a6d862..87c1aa22ff92 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ផ្ទេរ​ឯកសារ"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ឧបករណ៍​បញ្ចូល"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ការចូលប្រើ​អ៊ីនធឺណិត"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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">"ការចូលដំណើរការស៊ីម"</string>
@@ -662,6 +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="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-kn/arrays.xml b/packages/SettingsLib/res/values-kn/arrays.xml
index 6623564db76a..b6014ce72c65 100644
--- a/packages/SettingsLib/res/values-kn/arrays.xml
+++ b/packages/SettingsLib/res/values-kn/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_sample_rate_titles">
<item msgid="926809261293414607">"ಸಿಸ್ಟಂ ಆಯ್ಕೆಯನ್ನು ಬಳಸಿ (ಡಿಫಾಲ್ಟ್)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
@@ -184,7 +172,7 @@
<item msgid="7300881231043255746">"ಕೆರ್ನಲ್ ಮಾತ್ರ"</item>
</string-array>
<string-array name="select_logpersist_summaries">
- <item msgid="97587758561106269">"ಆಫ್ ಆಗಿದೆ"</item>
+ <item msgid="97587758561106269">"ಆಫ್"</item>
<item msgid="7126170197336963369">"ಎಲ್ಲಾ ಲಾಗ್ ಬಫರ್‌ಗಳು"</item>
<item msgid="7167543126036181392">"ಎಲ್ಲಾ ಆದರೆ ರೇಡಿಯೊ ಲಾಗ್ ಬಫರ್‌ಗಳು"</item>
<item msgid="5135340178556563979">"ಕೆರ್ನಲ್ ಲಾಗ್ ಬಫರ್ ಮಾತ್ರ"</item>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 7071598a6b0e..ab437e8a2d3a 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ಫೈಲ್ ವರ್ಗಾವಣೆ"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ಇನ್‌ಪುಟ್‌ ಸಾಧನ"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶ"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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">"ಸಿಮ್ ಪ್ರವೇಶ"</string>
@@ -662,6 +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="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-ko/arrays.xml b/packages/SettingsLib/res/values-ko/arrays.xml
index a20770609626..7138113c8b74 100644
--- a/packages/SettingsLib/res/values-ko/arrays.xml
+++ b/packages/SettingsLib/res/values-ko/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 6ba289da9ba7..2b8ab7558266 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"파일 전송"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"입력 장치"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"인터넷 액세스"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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>
@@ -662,6 +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="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 67cdc7cdb9e2..40271f71afdb 100644
--- a/packages/SettingsLib/res/values-ky/arrays.xml
+++ b/packages/SettingsLib/res/values-ky/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"карта13"</item>
<item msgid="8147982633566548515">"карта14"</item>
</string-array>
- <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>
- </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>
- </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_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 3eb43fac9151..e993e3f3049e 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Файл алмашуу"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Киргизүү түзмөгү"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Интернетке мүмкүнчүлүк алуу"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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">"SMS билдирүүлөрү"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM картаны пайдалануу мүмкүнчүлүгү"</string>
@@ -662,6 +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="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/arrays.xml b/packages/SettingsLib/res/values-lo/arrays.xml
index 56a9741d3419..792ca39a8bf4 100644
--- a/packages/SettingsLib/res/values-lo/arrays.xml
+++ b/packages/SettingsLib/res/values-lo/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_sample_rate_titles">
<item msgid="926809261293414607">"ໃຊ້ການເລືອກຂອງລະບົບ (ຄ່າເລີ່ມຕົ້ນ)"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 2dcad1fb774c..d295932053e2 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ການໂອນຍ້າຍໄຟລ໌"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ອຸປະກອນປ້ອນຂໍ້ມູນ"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ການເຂົ້າເຖິງອິນເຕີເນັດ"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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>
@@ -662,6 +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="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-lt/arrays.xml b/packages/SettingsLib/res/values-lt/arrays.xml
index a7aba78a51f0..946f69c3030b 100644
--- a/packages/SettingsLib/res/values-lt/arrays.xml
+++ b/packages/SettingsLib/res/values-lt/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 8aaec470c3bb..e38889bbf5ad 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Failo perkėlimas"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Įvesties įrenginys"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Prieiga prie interneto"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Kontaktų ir skambučių istorijos bendrinimas"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Naudoti kontaktams ir skambučių istorijai bendrinti"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Interneto ryšio bendrinimas"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Teksto pranešimai"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM prieiga"</string>
@@ -662,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Orai"</string>
<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="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/arrays.xml b/packages/SettingsLib/res/values-lv/arrays.xml
index 840d794d55ec..f4ae45234bf5 100644
--- a/packages/SettingsLib/res/values-lv/arrays.xml
+++ b/packages/SettingsLib/res/values-lv/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Sistēmas atlases izmantošana (nokl.)"</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>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Sistēmas atlases izmantošana (nokl.)"</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>
- </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_sample_rate_titles">
<item msgid="926809261293414607">"Sistēmas atlases izmantošana (nokl.)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index fcfb9b895313..d899adaa1f0e 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Failu pārsūtīšana"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Ievades ierīce"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Interneta piekļuve"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Kontaktpersonu un zvanu vēst. kopīgošana"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Paredzēts kontaktpersonu un zvanu vēstures kopīgošanai"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Interneta savienojuma koplietošana"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Īsziņas"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Piekļuve SIM kartei"</string>
@@ -662,6 +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) -->
+ <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>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Fiziskā tastatūra"</string>
diff --git a/packages/SettingsLib/res/values-mk/arrays.xml b/packages/SettingsLib/res/values-mk/arrays.xml
index 2f221653c5e7..9c46f216522c 100644
--- a/packages/SettingsLib/res/values-mk/arrays.xml
+++ b/packages/SettingsLib/res/values-mk/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 144de00caa6b..c8033e3e9ce9 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Пренос на датотека"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Влезен уред"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Пристап до интернет"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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>
@@ -662,6 +660,7 @@
<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>
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Контроли за домот"</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 3aa9472f0297..4715e2a32643 100644
--- a/packages/SettingsLib/res/values-ml/arrays.xml
+++ b/packages/SettingsLib/res/values-ml/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 59cd6bcee94f..089f6afd6271 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ഫയൽ കൈമാറൽ"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ഇൻപുട്ട് ഉപകരണം"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ഇന്‍റർനെറ്റ് ആക്‌സസ്"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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">"സിം ആക്സസ്"</string>
@@ -662,6 +660,7 @@
<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>
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"ഹോം കൺട്രോളുകൾ"</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 9bd10a78d402..63fa53b799d0 100644
--- a/packages/SettingsLib/res/values-mn/arrays.xml
+++ b/packages/SettingsLib/res/values-mn/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 1bd6beaacee5..cc350536e8d9 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Файл дамжуулалт"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Оруулах төхөөрөмж"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Интернэт хандалт"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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>
@@ -662,6 +660,7 @@
<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>
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Гэрийн удирдлага"</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 45bc5b8011e0..a54f99023a42 100644
--- a/packages/SettingsLib/res/values-mr/arrays.xml
+++ b/packages/SettingsLib/res/values-mr/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 c163219be81c..1907d009b896 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"फाइल स्थानांतरण"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"इनपुट डिव्हाइस"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"इंटरनेट अ‍ॅक्सेस"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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">"सिम अ‍ॅक्सेस"</string>
@@ -662,6 +660,7 @@
<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>
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"होम कंट्रोल"</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 d88ea764f56d..b26ed2317b5f 100644
--- a/packages/SettingsLib/res/values-ms/arrays.xml
+++ b/packages/SettingsLib/res/values-ms/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 55c881403bff..b3929c912e23 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Pemindahan fail"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Peranti input"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Akses Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Perkongsian kenalan &amp; sejarah panggilan"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Digunakan untuk perkongsian kenalan dan sejarah panggilan"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Perkongsian sambungan Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mesej Teks"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Akses SIM"</string>
@@ -662,6 +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="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 857a6aed8633..ed95dfe2e008 100644
--- a/packages/SettingsLib/res/values-my/arrays.xml
+++ b/packages/SettingsLib/res/values-my/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 38b1fe82456a..f7a33a95abf7 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ဖိုင်လွဲပြောင်းခြင်း"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ထည့်သွင်းသော စက်"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"အင်တာနက်ချိတ်ဆက်ခြင်း"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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>
@@ -662,6 +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="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 0f6e1fcb541c..317c2dbbce04 100644
--- a/packages/SettingsLib/res/values-nb/arrays.xml
+++ b/packages/SettingsLib/res/values-nb/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 ef2111d1c7d4..9b99631aa738 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Filoverføring"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Inndataenhet"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internett-tilgang"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Deling av kontakter og anropslogg"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Bruk for deling av kontakter og anropslogg"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Deling av internettilkobling"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Tekstmeldinger"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Tilgang til SIM-kortet"</string>
@@ -662,6 +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="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 611066efe9d2..b3c3ee2fc86f 100644
--- a/packages/SettingsLib/res/values-ne/arrays.xml
+++ b/packages/SettingsLib/res/values-ne/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 9fa7749359e8..1534cba6e1bd 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"फाइल स्थानान्तरण"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"इनपुट उपकरण"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"इन्टरनेट पहुँच"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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>
@@ -662,6 +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="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 e0690df63dc4..e8094521fcf7 100644
--- a/packages/SettingsLib/res/values-nl/arrays.xml
+++ b/packages/SettingsLib/res/values-nl/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 f4cbfd50ff69..ba11b4653b1c 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Bestandsoverdracht"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Invoerapparaat"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internettoegang"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Contacten en gespreksgeschiedenis delen"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Gebruiken om contacten en gespreksgeschiedenis te delen"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internetverbinding delen"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Sms-berichten"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Sim-toegang"</string>
@@ -662,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Weer"</string>
<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="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/arrays.xml b/packages/SettingsLib/res/values-or/arrays.xml
index dd25b3e7851f..d42aeaff755d 100644
--- a/packages/SettingsLib/res/values-or/arrays.xml
+++ b/packages/SettingsLib/res/values-or/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_sample_rate_titles">
<item msgid="926809261293414607">"ସିଷ୍ଟମ୍‌ର ଚୟନ (ଡିଫଲ୍ଟ୍) ବ୍ୟବହାର କରନ୍ତୁ"</item>
<item msgid="8003118270854840095">"44.1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 76c367195a9c..09fa59d60ba8 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ଫାଇଲ୍‌ ଟ୍ରାନ୍ସଫର୍‌"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ଇନ୍‌ପୁଟ୍‌ ଡିଭାଇସ୍"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ଇଣ୍ଟର୍‌ନେଟ୍‌ ଆକ୍ସେସ୍"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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>
@@ -662,6 +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="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-pa/arrays.xml b/packages/SettingsLib/res/values-pa/arrays.xml
index 6ba214504ea4..a3fae22d9439 100644
--- a/packages/SettingsLib/res/values-pa/arrays.xml
+++ b/packages/SettingsLib/res/values-pa/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 e34ece84d8ee..9df0fefd7ff8 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ਫਾਈਲ ਟ੍ਰਾਂਸਫਰ"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ਇਨਪੁੱਟ ਡੀਵਾਈਸ"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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">"ਸਿਮ ਪਹੁੰਚ"</string>
@@ -662,6 +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="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/arrays.xml b/packages/SettingsLib/res/values-pl/arrays.xml
index 5f91d35bb31b..f0453d1e7df1 100644
--- a/packages/SettingsLib/res/values-pl/arrays.xml
+++ b/packages/SettingsLib/res/values-pl/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Używaj wyboru systemu (domyślnie)"</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>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Używaj wyboru systemu (domyślnie)"</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>
- </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_sample_rate_titles">
<item msgid="926809261293414607">"Używaj wyboru systemu (domyślnie)"</item>
<item msgid="8003118270854840095">"44,1 kHz"</item>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index fe56c6170224..f4599fdf806a 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Przesyłanie pliku"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Urządzenie wejściowe"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Dostęp do internetu"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Udostępnianie kontaktów i historii połączeń"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Używaj w przypadku udostępniania kontaktów i historii połączeń"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Udostępnianie połączenia internetowego"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS-y"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Dostęp do karty SIM"</string>
@@ -228,7 +226,7 @@
<string name="category_personal" msgid="6236798763159385225">"Osobiste"</string>
<string name="category_work" msgid="4014193632325996115">"Służbowe"</string>
<string name="development_settings_title" msgid="140296922921597393">"Opcje programisty"</string>
- <string name="development_settings_enable" msgid="4285094651288242183">"Włącz opcje dla programistów"</string>
+ <string name="development_settings_enable" msgid="4285094651288242183">"Włącz opcje programisty"</string>
<string name="development_settings_summary" msgid="8718917813868735095">"Ustaw opcje związane z programowaniem aplikacji."</string>
<string name="development_settings_not_available" msgid="355070198089140951">"Opcje programisty są niedostępne dla tego użytkownika"</string>
<string name="vpn_settings_not_available" msgid="2894137119965668920">"Ustawienia VPN są niedostępne dla tego użytkownika"</string>
@@ -662,6 +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) -->
+ <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>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Klawiatura fizyczna"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/arrays.xml b/packages/SettingsLib/res/values-pt-rBR/arrays.xml
index c5a847ff29b2..1883ef355776 100644
--- a/packages/SettingsLib/res/values-pt-rBR/arrays.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 aa66c604304b..12507c5449df 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferência de arquivo"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acesso à Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Compartilhar contatos e histórico de chamadas"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Use para compartilhar contatos e o histórico de chamadas"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Compartilhamento de conexão à Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensagens de texto"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acesso ao chip"</string>
@@ -662,6 +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="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 3e7ee052ba1c..985bd515acd0 100644
--- a/packages/SettingsLib/res/values-pt-rPT/arrays.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 cf767809da05..4988136ba43a 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferência do ficheiro"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acesso à internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Part. histórico de chamadas e contactos"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Usar para partilha do histórico de chamadas e dos contactos"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Partilha da ligação à internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensagens de texto"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acesso ao SIM"</string>
@@ -662,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Meteorologia"</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>
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"Ctr. domésticos"</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 c5a847ff29b2..1883ef355776 100644
--- a/packages/SettingsLib/res/values-pt/arrays.xml
+++ b/packages/SettingsLib/res/values-pt/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 aa66c604304b..12507c5449df 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferência de arquivo"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acesso à Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Compartilhar contatos e histórico de chamadas"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Use para compartilhar contatos e o histórico de chamadas"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Compartilhamento de conexão à Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensagens de texto"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acesso ao chip"</string>
@@ -662,6 +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="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 454867e150ac..f1e99865c9a6 100644
--- a/packages/SettingsLib/res/values-ro/arrays.xml
+++ b/packages/SettingsLib/res/values-ro/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 946f4b7e1afa..f49fcdd96a05 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transfer de fișiere"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispozitiv de intrare"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acces la internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Acces la agendă și istoricul apelurilor"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Folosiți pentru accesul la agendă și istoricul apelurilor"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Distribuirea conexiunii la internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mesaje text"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acces la SIM"</string>
@@ -662,6 +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="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/arrays.xml b/packages/SettingsLib/res/values-ru/arrays.xml
index 28b4695d0463..b1211a5c376c 100644
--- a/packages/SettingsLib/res/values-ru/arrays.xml
+++ b/packages/SettingsLib/res/values-ru/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_sample_rate_titles">
<item msgid="926809261293414607">"Выбор системы (по умолчанию)"</item>
<item msgid="8003118270854840095">"44,1 кГц"</item>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index bcd83cf4cd79..38930a5117ca 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Профиль OPP"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Профиль HID"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Доступ к интернету"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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">"Профиль PAN"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Текстовые сообщения"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Доступ к SIM-карте"</string>
@@ -662,6 +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="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-si/arrays.xml b/packages/SettingsLib/res/values-si/arrays.xml
index c2dbf60f5906..8386c1aa5559 100644
--- a/packages/SettingsLib/res/values-si/arrays.xml
+++ b/packages/SettingsLib/res/values-si/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 7f5eb4921ed7..fa0296f80ff0 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ගොනු හුවමාරුව"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ආදාන උපාංගය"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"අන්තර්ජාල ප්‍රවේශය"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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>
@@ -662,6 +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="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 b28588832879..370b23f6cd58 100644
--- a/packages/SettingsLib/res/values-sk/arrays.xml
+++ b/packages/SettingsLib/res/values-sk/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 31925eaf0844..a7c69c8247d8 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Prenos súborov"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Vstupné zariadenie"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Prístup na internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Zdieľanie kontaktov a histórie hovorov"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Používané pri zdieľaní kontaktov a histórie hovorov"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Zdieľanie pripojenia na Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Textové správy"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Prístup k SIM karte"</string>
@@ -662,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Počasie"</string>
<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="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 8db3e99b77fc..6e33e386d897 100644
--- a/packages/SettingsLib/res/values-sl/arrays.xml
+++ b/packages/SettingsLib/res/values-sl/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 687ff38d4fc2..7334ceafc954 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Prenos datoteke"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Vnosna naprava"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetni dostop"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Deljenje stikov in zgodovine klicev"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Uporabite za deljenje stikov in zgodovine klicev."</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Deljenje internetne povezave"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Sporočila SMS"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Dostop do kartice SIM"</string>
@@ -662,6 +660,7 @@
<string name="dream_complication_title_weather" msgid="598609151677172783">"Vreme"</string>
<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="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 a82f71648298..8a6d853e7818 100644
--- a/packages/SettingsLib/res/values-sq/arrays.xml
+++ b/packages/SettingsLib/res/values-sq/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 7e96990af569..59359cdf74fd 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferimi i skedarëve"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Pajisja e hyrjes"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Qasje në internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Ndarje: kontakte e historik telefonatash"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Përdor për ndarje kontaktesh e të historikut të telefonatave"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Ndarja e lidhjes së internetit"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mesazhet me tekst"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Qasje në kartën SIM"</string>
@@ -662,6 +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="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 4cd83606f02d..7e198bfe3f6d 100644
--- a/packages/SettingsLib/res/values-sr/arrays.xml
+++ b/packages/SettingsLib/res/values-sr/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 2842223032c0..eee3f8917c50 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Пренос датотеке"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Улазни уређај"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Приступ Интернету"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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">"SMS-ови"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Приступ SIM картици"</string>
@@ -662,6 +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="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 1432ec21ea90..f99a85b84533 100644
--- a/packages/SettingsLib/res/values-sv/arrays.xml
+++ b/packages/SettingsLib/res/values-sv/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 b0f8df59be3c..1a8815c1164b 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Filöverföring"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Indataenhet"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetåtkomst"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Delning av kontakter och samtalshistorik"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Använd för delning av kontakter och samtalshistorik"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Delning av Internetanslutning"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Sms"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM-åtkomst"</string>
@@ -662,6 +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="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 cb74761aec58..dab4279f88dc 100644
--- a/packages/SettingsLib/res/values-sw/arrays.xml
+++ b/packages/SettingsLib/res/values-sw/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"ramani ya 13"</item>
<item msgid="8147982633566548515">"ramani ya 14"</item>
</string-array>
- <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>
- </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>
- </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_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 7e62d3fdb644..5f1d141a0256 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Uhamishaji wa faili"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Kifaa cha kuingiza"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Ufikiaji wa intaneti"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Kushiriki anwani na rekodi ya simu zilizopigwa"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Tumia kushiriki anwani na rekodi ya simu zilizopigwa"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Kushiriki muunganisho wa tovuti"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Ufikiaji wa SIM"</string>
@@ -662,6 +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="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 1f5380f9063c..a0f1fa6447c8 100644
--- a/packages/SettingsLib/res/values-ta/arrays.xml
+++ b/packages/SettingsLib/res/values-ta/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 abab3fb54361..b8c37b037d02 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ஃபைல் இடமாற்றம்"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"உள்ளீட்டுச் சாதனம்"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"இணைய அணுகல்"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"தொடர்புகள் &amp; அழைப்புப் பதிவைப் பகிர்தல்"</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">"சிம் அணுகல்"</string>
@@ -662,6 +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="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 513fb6e87a10..18a27585cc8c 100644
--- a/packages/SettingsLib/res/values-te/arrays.xml
+++ b/packages/SettingsLib/res/values-te/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 a362f24a75b6..504c827e9a9e 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ఫైల్ బదిలీ"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ఇన్‌పుట్ పరికరం"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ఇంటర్నెట్ యాక్సెస్"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Contacts and call history sharing"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Use for contacts and call history sharing"</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>
@@ -662,6 +660,7 @@
<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>
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"హోమ్ కంట్రోల్స్"</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 732124a15f7e..04a5f4dd86b9 100644
--- a/packages/SettingsLib/res/values-th/arrays.xml
+++ b/packages/SettingsLib/res/values-th/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 a4e050867f40..f33630dd0ea4 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"การถ่ายโอนไฟล์"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"อุปกรณ์อินพุต"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"การเข้าถึงอินเทอร์เน็ต"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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">"การเข้าถึงซิม"</string>
@@ -662,6 +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="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 bdd8706329cc..59cb1f370338 100644
--- a/packages/SettingsLib/res/values-tl/arrays.xml
+++ b/packages/SettingsLib/res/values-tl/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 e28fd961340c..f95f0e50f1be 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Paglilipat ng file"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Device sa pag-input"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Access sa internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Contacts at pagbabahagi ng call history"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Gamitin para sa contacts at pagbabahagi ng call history"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Pagbabahagi ng koneksyon sa internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mga Text Message"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Access sa SIM"</string>
@@ -662,6 +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="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 0d0c69a1215e..5ed35faee347 100644
--- a/packages/SettingsLib/res/values-tr/arrays.xml
+++ b/packages/SettingsLib/res/values-tr/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 05ec1cc1255b..2fa2bd18c5f3 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Dosya aktarımı"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Giriş cihazı"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"İnternet erişimi"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Kişi ve çağrı geçmişi paylaşımı"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Kişi ve çağrı geçmişi paylaşımı için kullanın"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"İnternet bağlantısı paylaşımı"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Kısa Mesajlar"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM Erişimi"</string>
@@ -662,6 +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="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/arrays.xml b/packages/SettingsLib/res/values-uk/arrays.xml
index 49a463519a38..0410bd6ab43d 100644
--- a/packages/SettingsLib/res/values-uk/arrays.xml
+++ b/packages/SettingsLib/res/values-uk/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_sample_rate_titles">
<item msgid="926809261293414607">"Використовувати вибір системи (за умовчанням)"</item>
<item msgid="8003118270854840095">"44,1 кГц"</item>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 184d0c118b22..9be0855b1d48 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Передавання файлів"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Пристрій введення"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Доступ до Інтернету"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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>
@@ -662,6 +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="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-ur/arrays.xml b/packages/SettingsLib/res/values-ur/arrays.xml
index 5dc21236a380..d0974580b5ee 100644
--- a/packages/SettingsLib/res/values-ur/arrays.xml
+++ b/packages/SettingsLib/res/values-ur/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 419833a457e3..8eb9a117e1a3 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"فائل کی منتقلی"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ان پٹ آلہ"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"انٹرنیٹ تک رسائی"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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>
@@ -662,6 +660,7 @@
<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>
+ <string name="dream_complication_title_home_controls" msgid="9153381632476738811">"ہوم کنٹرولز"</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/arrays.xml b/packages/SettingsLib/res/values-uz/arrays.xml
index 589c5927a5d2..7d09027983a3 100644
--- a/packages/SettingsLib/res/values-uz/arrays.xml
+++ b/packages/SettingsLib/res/values-uz/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="2494959071796102843">"Tizim tanlovi (birlamchi)"</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> audiokodeki"</item>
- <item msgid="2908219194098827570">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audiokodeki"</item>
- <item msgid="3825367753087348007">"LDAC"</item>
- </string-array>
- <string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="8868109554557331312">"Tizim tanlovi (birlamchi)"</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> audiokodeki"</item>
- <item msgid="3517061573669307965">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audiokodeki"</item>
- <item msgid="2553206901068987657">"LDAC"</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_sample_rate_titles">
<item msgid="926809261293414607">"Tizim tanlovi (birlamchi)"</item>
<item msgid="8003118270854840095">"44.1 kGs"</item>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index b1e22af84503..a5c868360eae 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Fayl uzatish"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Kiritish qurilmasi"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetga ulanish"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Kontakt va chaqiruvlar tarixiga kirish"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Kontaktlar va chaqiruvlar tarixiga kirish uchun foydalaning"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internet aloqasi ulashmasi"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS xabarlari"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM kartaga kirish"</string>
@@ -662,6 +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) -->
+ <skip />
<string name="avatar_picker_title" msgid="8492884172713170652">"Profil rasmini tanlash"</string>
<string name="default_user_icon_description" msgid="6554047177298972638">"Foydalanuvchining standart belgisi"</string>
<string name="physical_keyboard_title" msgid="4811935435315835220">"Tashqi klaviatura"</string>
diff --git a/packages/SettingsLib/res/values-vi/arrays.xml b/packages/SettingsLib/res/values-vi/arrays.xml
index 4cf8ff49b636..31867e2f18f7 100644
--- a/packages/SettingsLib/res/values-vi/arrays.xml
+++ b/packages/SettingsLib/res/values-vi/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 71bb08e94539..45a0465719ed 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Chuyển tệp"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Thiết bị đầu vào"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Truy cập Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Chia sẻ danh bạ và nhật ký cuộc gọi"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Dùng để chia sẻ danh bạ và nhật ký cuộc gọi"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Chia sẻ kết nối internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Tin nhắn văn bản"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Truy cập SIM"</string>
@@ -662,6 +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="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 af87f6fdab39..973d7d01fcc9 100644
--- a/packages/SettingsLib/res/values-zh-rCN/arrays.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 f9e087b9b212..2bc8a305d0fc 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"文件传输"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"输入设备"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"互联网连接"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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>
@@ -661,7 +659,9 @@
<string name="dream_complication_title_date" msgid="8661176085446135789">"日期"</string>
<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>
+ <string name="dream_complication_title_cast_info" msgid="4038776652841885084">"投放信息"</string>
+ <!-- no translation found for dream_complication_title_home_controls (9153381632476738811) -->
+ <skip />
<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 209941065755..87f382524a5c 100644
--- a/packages/SettingsLib/res/values-zh-rHK/arrays.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 cd93f71e1e3e..114bc6424db5 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"檔案傳輸"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"輸入裝置"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"互聯網連線"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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>
@@ -662,6 +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="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 24991e35e233..529287f4354f 100644
--- a/packages/SettingsLib/res/values-zh-rTW/arrays.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"map13"</item>
<item msgid="8147982633566548515">"map14"</item>
</string-array>
- <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>
- </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>
- </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_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 4f36742f59f2..a7ae213c26ba 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"檔案傳輸"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"輸入裝置"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"網際網路連線"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <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>
@@ -662,6 +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="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 8732b26817e8..59ead86f0f8f 100644
--- a/packages/SettingsLib/res/values-zu/arrays.xml
+++ b/packages/SettingsLib/res/values-zu/arrays.xml
@@ -85,22 +85,10 @@
<item msgid="7073042887003102964">"Imephu13"</item>
<item msgid="8147982633566548515">"Imephu14"</item>
</string-array>
- <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>
- </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>
- </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_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 88c41c580617..e037d1275ed1 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -117,10 +117,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Dlulisa ifayela"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Idivaysi yokufakwayo"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Ukufinyelela i-Inthanethi"</string>
- <!-- no translation found for bluetooth_profile_pbap (4262303387989406171) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (6466456791354759132) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Ukwabelana ngoxhumana nabo nomlando wekholi"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Ukusetshenziswa kokwabelana ngoxhumana nabo nomlando wekholi"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Ukwabelana ngoxhumano lwe-Inthanethi"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Imilayezo yombhalo"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Ukufinyelela kwe-SIM"</string>
@@ -662,6 +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="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/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index 29a1831d28cb..663e8e4b14d8 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -154,6 +154,8 @@
<item><xliff:g id="qualcomm">Qualcomm®</xliff:g> <xliff:g id="aptx">aptX™</xliff:g> audio</item>
<item><xliff:g id="qualcomm">Qualcomm®</xliff:g> <xliff:g id="aptx_hd">aptX™ HD</xliff:g> audio</item>
<item>LDAC</item>
+ <item>LC3</item>
+ <item>Opus</item>
</string-array>
<!-- Values for Bluetooth Audio Codec selection preference. -->
@@ -164,6 +166,8 @@
<item>2</item>
<item>3</item>
<item>4</item>
+ <item>5</item>
+ <item>6</item>
</string-array>
<!-- Summaries for Bluetooth Audio Codec selection preference. [CHAR LIMIT=50]-->
@@ -174,6 +178,8 @@
<item><xliff:g id="qualcomm">Qualcomm®</xliff:g> <xliff:g id="aptx">aptX™</xliff:g> audio</item>
<item><xliff:g id="qualcomm">Qualcomm®</xliff:g> <xliff:g id="aptx_hd">aptX™ HD</xliff:g> audio</item>
<item>LDAC</item>
+ <item>LC3</item>
+ <item>Opus</item>
</string-array>
<!-- Titles for Bluetooth Audio Codec Sample Rate selection preference. [CHAR LIMIT=50] -->
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index a79cbb6fab7a..b8792236d0d9 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1610,6 +1610,9 @@
<string name="dream_complication_title_cast_info">Cast Info</string>
<!-- Screensaver overlay which displays home controls. [CHAR LIMIT=20] -->
<string name="dream_complication_title_home_controls">Home Controls</string>
+ <!-- Screensaver overlay which displays smartspace. [CHAR LIMIT=20] -->
+ <string name="dream_complication_title_smartspace">Smartspace</string>
+
<!-- Title for a screen allowing the user to choose a profile picture. [CHAR LIMIT=NONE] -->
<string name="avatar_picker_title">Choose a profile picture</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index cf23a5446f91..7913c16b6b98 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -792,10 +792,8 @@ public class ApplicationsState {
// Rebuilding of app list. Synchronized on mRebuildSync.
final Object mRebuildSync = new Object();
boolean mRebuildRequested;
- boolean mRebuildAsync;
AppFilter mRebuildFilter;
Comparator<AppEntry> mRebuildComparator;
- ArrayList<AppEntry> mRebuildResult;
ArrayList<AppEntry> mLastAppList;
boolean mRebuildForeground;
@@ -901,11 +899,9 @@ public class ApplicationsState {
synchronized (mRebuildingSessions) {
mRebuildingSessions.add(this);
mRebuildRequested = true;
- mRebuildAsync = true;
mRebuildFilter = filter;
mRebuildComparator = comparator;
mRebuildForeground = foreground;
- mRebuildResult = null;
if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_REBUILD_LIST)) {
Message msg = mBackgroundHandler.obtainMessage(
BackgroundHandler.MSG_REBUILD_LIST);
@@ -985,15 +981,10 @@ public class ApplicationsState {
synchronized (mRebuildSync) {
if (!mRebuildRequested) {
mLastAppList = filteredApps;
- if (!mRebuildAsync) {
- mRebuildResult = filteredApps;
- mRebuildSync.notifyAll();
- } else {
- if (!mMainHandler.hasMessages(MainHandler.MSG_REBUILD_COMPLETE, this)) {
- Message msg = mMainHandler.obtainMessage(
- MainHandler.MSG_REBUILD_COMPLETE, this);
- mMainHandler.sendMessage(msg);
- }
+ if (!mMainHandler.hasMessages(MainHandler.MSG_REBUILD_COMPLETE, this)) {
+ Message msg = mMainHandler.obtainMessage(
+ MainHandler.MSG_REBUILD_COMPLETE, this);
+ mMainHandler.sendMessage(msg);
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index df19c67f00e5..19409865284c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -43,6 +43,8 @@ import java.util.List;
public class A2dpProfile implements LocalBluetoothProfile {
private static final String TAG = "A2dpProfile";
+ private static final int SOURCE_CODEC_TYPE_OPUS = 6; // TODO remove in U
+
private Context mContext;
private BluetoothA2dp mService;
@@ -328,6 +330,12 @@ public class A2dpProfile implements LocalBluetoothProfile {
case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC:
index = 5;
break;
+ case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3:
+ index = 6;
+ break;
+ case SOURCE_CODEC_TYPE_OPUS: // TODO update in U
+ index = 7;
+ break;
}
if (index < 0) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index a46e23235843..22586171e5cf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
@@ -92,7 +92,8 @@ public class DreamBackend {
COMPLICATION_TYPE_WEATHER,
COMPLICATION_TYPE_AIR_QUALITY,
COMPLICATION_TYPE_CAST_INFO,
- COMPLICATION_TYPE_HOME_CONTROLS
+ COMPLICATION_TYPE_HOME_CONTROLS,
+ COMPLICATION_TYPE_SMARTSPACE
})
@Retention(RetentionPolicy.SOURCE)
public @interface ComplicationType {}
@@ -103,6 +104,7 @@ public class DreamBackend {
public static final int COMPLICATION_TYPE_AIR_QUALITY = 4;
public static final int COMPLICATION_TYPE_CAST_INFO = 5;
public static final int COMPLICATION_TYPE_HOME_CONTROLS = 6;
+ public static final int COMPLICATION_TYPE_SMARTSPACE = 7;
private final Context mContext;
private final IDreamManager mDreamManager;
@@ -351,6 +353,9 @@ public class DreamBackend {
case COMPLICATION_TYPE_HOME_CONTROLS:
res = R.string.dream_complication_title_home_controls;
break;
+ case COMPLICATION_TYPE_SMARTSPACE:
+ res = R.string.dream_complication_title_smartspace;
+ break;
default:
return null;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
index 1a08366734bc..b416738ade4a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
@@ -44,6 +44,8 @@ public class MobileStatusTracker {
private final Handler mReceiverHandler;
private final MobileTelephonyCallback mTelephonyCallback;
+ private boolean mListening = false;
+
/**
* MobileStatusTracker constructors
*
@@ -76,6 +78,7 @@ public class MobileStatusTracker {
* Config the MobileStatusTracker to start or stop monitoring platform signals.
*/
public void setListening(boolean listening) {
+ mListening = listening;
if (listening) {
mPhone.registerTelephonyCallback(mReceiverHandler::post, mTelephonyCallback);
} else {
@@ -83,6 +86,10 @@ public class MobileStatusTracker {
}
}
+ public boolean isListening() {
+ return mListening;
+ }
+
private void updateDataSim() {
int activeDataSubId = mDefaults.getActiveDataSubId();
if (SubscriptionManager.isValidSubscriptionId(activeDataSubId)) {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ButtonPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ButtonPreferenceTest.java
index 625b214544f1..d78f8e7e4e9b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ButtonPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ButtonPreferenceTest.java
@@ -63,6 +63,7 @@ public class ButtonPreferenceTest {
final Button button = mPreference.getButton();
assertThat(button.getText().toString()).isEqualTo(testTitle);
+ assertThat(mPreference.getTitle().toString()).isEqualTo(testTitle);
}
@Test
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index 2f36ab9aa93d..8f9ced6956ca 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -23,7 +23,6 @@ import android.app.Dialog
import android.graphics.Color
import android.graphics.Rect
import android.os.Looper
-import android.service.dreams.IDreamManager
import android.util.Log
import android.util.MathUtils
import android.view.GhostView
@@ -54,7 +53,7 @@ private const val TAG = "DialogLaunchAnimator"
class DialogLaunchAnimator
@JvmOverloads
constructor(
- private val dreamManager: IDreamManager,
+ private val callback: Callback,
private val interactionJankMonitor: InteractionJankMonitor,
private val launchAnimator: LaunchAnimator = LaunchAnimator(TIMINGS, INTERPOLATORS),
private val isForTesting: Boolean = false
@@ -126,7 +125,7 @@ constructor(
val animatedDialog =
AnimatedDialog(
launchAnimator,
- dreamManager,
+ callback,
interactionJankMonitor,
animateFrom,
onDialogDismissed = { openedDialogs.remove(it) },
@@ -194,8 +193,12 @@ constructor(
val dialog = animatedDialog.dialog
- // Don't animate if the dialog is not showing.
- if (!dialog.isShowing) {
+ // Don't animate if the dialog is not showing or if we are locked and going to show the
+ // bouncer.
+ if (
+ !dialog.isShowing ||
+ (!callback.isUnlocked() && !callback.isShowingAlternateAuthOnUnlock())
+ ) {
return null
}
@@ -285,6 +288,23 @@ constructor(
?.let { it.touchSurface = it.prepareForStackDismiss() }
dialog.dismiss()
}
+
+ interface Callback {
+ /** Whether the device is currently in dreaming (screensaver) mode. */
+ fun isDreaming(): Boolean
+
+ /**
+ * Whether the device is currently unlocked, i.e. if it is *not* on the keyguard or if the
+ * keyguard can be dismissed.
+ */
+ fun isUnlocked(): Boolean
+
+ /**
+ * Whether we are going to show alternate authentication (like UDFPS) instead of the
+ * traditional bouncer when unlocking the device.
+ */
+ fun isShowingAlternateAuthOnUnlock(): Boolean
+ }
}
/**
@@ -296,7 +316,7 @@ data class DialogCuj(@CujType val cujType: Int, val tag: String? = null)
private class AnimatedDialog(
private val launchAnimator: LaunchAnimator,
- private val dreamManager: IDreamManager,
+ private val callback: DialogLaunchAnimator.Callback,
private val interactionJankMonitor: InteractionJankMonitor,
/** The view that triggered the dialog after being tapped. */
@@ -850,7 +870,7 @@ private class AnimatedDialog(
// If we are dreaming, the dialog was probably closed because of that so we don't animate
// into the touchSurface.
- if (dreamManager.isDreaming) {
+ if (callback.isDreaming()) {
return false
}
diff --git a/packages/SystemUI/docs/device-entry/keyguard.md b/packages/SystemUI/docs/device-entry/keyguard.md
index 337f73b79260..8634c950f96c 100644
--- a/packages/SystemUI/docs/device-entry/keyguard.md
+++ b/packages/SystemUI/docs/device-entry/keyguard.md
@@ -30,6 +30,10 @@ An indication to power off the device most likely comes from one of two signals:
### How the device locks
+### Quick Affordances
+
+These are interactive UI elements that appear on the lockscreen when the device is locked. They allow the user to perform quick actions without unlocking their device. To learn more about them, please see [this dedicated document](quickaffordance.md)
+
## Debugging Tips
Enable verbose keyguard logs that will print to logcat. Should only be used temporarily for debugging. See [KeyguardConstants][5].
```
diff --git a/packages/SystemUI/docs/device-entry/quickaffordance.md b/packages/SystemUI/docs/device-entry/quickaffordance.md
new file mode 100644
index 000000000000..a96e533933f4
--- /dev/null
+++ b/packages/SystemUI/docs/device-entry/quickaffordance.md
@@ -0,0 +1,25 @@
+# Keyguard Quick Affordances
+These are interactive UI elements that appear at the bottom of the lockscreen when the device is
+locked. They allow the user to perform quick actions without unlocking their device. For example:
+opening an screen that lets them control the smart devices in their home, access their touch-to-pay
+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
+* 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:
+ * 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)
+
+### 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)
+* 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
+
+### Step 3: manually verify the new quick affordance
+* Build and launch SysUI on a device
+* Verify that the quick affordance button for the new implementation is correctly visible and clicking it does the right thing
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/drawable-hdpi/textfield_default_filled.9.png b/packages/SystemUI/res/drawable-hdpi/textfield_default_filled.9.png
new file mode 100644
index 000000000000..3dd997fade6c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/textfield_default_filled.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/textfield_default_filled.9.png b/packages/SystemUI/res/drawable-mdpi/textfield_default_filled.9.png
new file mode 100644
index 000000000000..80aba01091fe
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/textfield_default_filled.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/textfield_default_filled.9.png b/packages/SystemUI/res/drawable-xhdpi/textfield_default_filled.9.png
new file mode 100644
index 000000000000..b3f89ed7ea7a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/textfield_default_filled.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/textfield_default_filled.9.png b/packages/SystemUI/res/drawable-xxhdpi/textfield_default_filled.9.png
new file mode 100644
index 000000000000..efa2cb988ac1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/textfield_default_filled.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/edit_text_filled.xml b/packages/SystemUI/res/drawable/edit_text_filled.xml
new file mode 100644
index 000000000000..cca34d456078
--- /dev/null
+++ b/packages/SystemUI/res/drawable/edit_text_filled.xml
@@ -0,0 +1,36 @@
+<?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.
+-->
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:insetLeft="4dp"
+ android:insetRight="4dp"
+ android:insetTop="10dp"
+ android:insetBottom="10dp">
+ <selector>
+ <item android:state_enabled="false">
+ <nine-patch android:src="@drawable/textfield_default_filled"
+ android:tint="?android:attr/colorControlNormal" />
+ </item>
+ <item android:state_pressed="false" android:state_focused="false">
+ <nine-patch android:src="@drawable/textfield_default_filled"
+ android:tint="?android:attr/colorControlNormal" />
+ </item>
+ <item>
+ <nine-patch android:src="@drawable/textfield_default_filled"
+ android:tint="?android:attr/colorControlActivated" />
+ </item>
+ </selector>
+</inset>
diff --git a/packages/SystemUI/res/layout-land/auth_credential_password_view.xml b/packages/SystemUI/res/layout-land/auth_credential_password_view.xml
index da76c8d0b11a..bc8e540cb612 100644
--- a/packages/SystemUI/res/layout-land/auth_credential_password_view.xml
+++ b/packages/SystemUI/res/layout-land/auth_credential_password_view.xml
@@ -18,44 +18,70 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical"
- android:gravity="center_horizontal"
+ android:orientation="horizontal"
android:elevation="@dimen/biometric_dialog_elevation">
- <TextView
- android:id="@+id/title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@style/TextAppearance.AuthCredential.Title"/>
+ <RelativeLayout
+ android:id="@+id/auth_credential_header"
+ style="@style/AuthCredentialHeaderStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent">
- <TextView
- android:id="@+id/subtitle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@style/TextAppearance.AuthCredential.Subtitle"/>
+ <ImageView
+ android:id="@+id/icon"
+ style="@style/TextAppearance.AuthNonBioCredential.Icon"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:contentDescription="@null"/>
- <TextView
- android:id="@+id/description"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@style/TextAppearance.AuthCredential.Description"/>
+ <TextView
+ android:id="@+id/title"
+ style="@style/TextAppearance.AuthNonBioCredential.Title"
+ android:layout_below="@id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
- <ImeAwareEditText
- android:id="@+id/lockPassword"
- android:layout_width="208dp"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:minHeight="48dp"
- android:gravity="center"
- android:inputType="textPassword"
- android:maxLength="500"
- android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
- style="@style/TextAppearance.AuthCredential.PasswordEntry"/>
-
- <TextView
- android:id="@+id/error"
- android:layout_width="match_parent"
+ <TextView
+ android:id="@+id/subtitle"
+ style="@style/TextAppearance.AuthNonBioCredential.Subtitle"
+ android:layout_below="@id/title"
+ android:layout_alignParentLeft="true"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <TextView
+ android:id="@+id/description"
+ style="@style/TextAppearance.AuthNonBioCredential.Description"
+ android:layout_below="@id/subtitle"
+ android:layout_alignParentLeft="true"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ </RelativeLayout>
+
+ <LinearLayout
+ android:id="@+id/auth_credential_input"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- style="@style/TextAppearance.AuthCredential.Error"/>
+ android:orientation="vertical">
+
+ <ImeAwareEditText
+ android:id="@+id/lockPassword"
+ style="@style/TextAppearance.AuthCredential.PasswordEntry"
+ android:layout_width="208dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
+ android:inputType="textPassword"
+ android:minHeight="48dp" />
+
+ <TextView
+ android:id="@+id/error"
+ style="@style/TextAppearance.AuthNonBioCredential.Error"
+ android:layout_gravity="center"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
</com.android.systemui.biometrics.AuthCredentialPasswordView> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_credential_password_view.xml b/packages/SystemUI/res/layout/auth_credential_password_view.xml
index 0ff1db2ef694..75a80bc39a1f 100644
--- a/packages/SystemUI/res/layout/auth_credential_password_view.xml
+++ b/packages/SystemUI/res/layout/auth_credential_password_view.xml
@@ -22,68 +22,63 @@
android:orientation="vertical">
<RelativeLayout
+ android:id="@+id/auth_credential_header"
+ style="@style/AuthCredentialHeaderStyle"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
+ android:layout_height="match_parent">
- <LinearLayout
- android:id="@+id/auth_credential_header"
- style="@style/AuthCredentialHeaderStyle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentTop="true">
+ <ImageView
+ android:id="@+id/icon"
+ style="@style/TextAppearance.AuthNonBioCredential.Icon"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:contentDescription="@null"/>
- <ImageView
- android:id="@+id/icon"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:contentDescription="@null" />
+ <TextView
+ android:id="@+id/title"
+ style="@style/TextAppearance.AuthNonBioCredential.Title"
+ android:layout_below="@id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
- <TextView
- android:id="@+id/title"
- style="@style/TextAppearance.AuthNonBioCredential.Title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
+ <TextView
+ android:id="@+id/subtitle"
+ style="@style/TextAppearance.AuthNonBioCredential.Subtitle"
+ android:layout_below="@id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
- <TextView
- android:id="@+id/subtitle"
- style="@style/TextAppearance.AuthNonBioCredential.Subtitle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
+ <TextView
+ android:id="@+id/description"
+ style="@style/TextAppearance.AuthNonBioCredential.Description"
+ android:layout_below="@id/subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ </RelativeLayout>
- <TextView
- android:id="@+id/description"
- style="@style/TextAppearance.AuthNonBioCredential.Description"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
+ <LinearLayout
+ android:id="@+id/auth_credential_input"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
- </LinearLayout>
+ <ImeAwareEditText
+ android:id="@+id/lockPassword"
+ style="@style/TextAppearance.AuthCredential.PasswordEntry"
+ android:layout_width="208dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
+ android:inputType="textPassword"
+ android:minHeight="48dp" />
- <LinearLayout
+ <TextView
+ android:id="@+id/error"
+ style="@style/TextAppearance.AuthNonBioCredential.Error"
+ android:layout_gravity="center_horizontal"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="center"
- android:orientation="vertical"
- android:layout_alignParentBottom="true">
-
- <ImeAwareEditText
- android:id="@+id/lockPassword"
- style="@style/TextAppearance.AuthCredential.PasswordEntry"
- android:layout_width="208dp"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
- android:inputType="textPassword"
- android:minHeight="48dp" />
-
- <TextView
- android:id="@+id/error"
- style="@style/TextAppearance.AuthNonBioCredential.Error"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content" />
- </LinearLayout>
-
- </RelativeLayout>
+ </LinearLayout>
</com.android.systemui.biometrics.AuthCredentialPasswordView> \ No newline at end of file
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-land/styles.xml b/packages/SystemUI/res/values-land/styles.xml
index 89191984b9e8..ac9a947f4417 100644
--- a/packages/SystemUI/res/values-land/styles.xml
+++ b/packages/SystemUI/res/values-land/styles.xml
@@ -18,4 +18,13 @@
<style name="BrightnessDialogContainer" parent="@style/BaseBrightnessDialogContainer">
<item name="android:layout_width">360dp</item>
</style>
+
+ <style name="AuthCredentialHeaderStyle">
+ <item name="android:paddingStart">48dp</item>
+ <item name="android:paddingEnd">24dp</item>
+ <item name="android:paddingTop">48dp</item>
+ <item name="android:paddingBottom">10dp</item>
+ <item name="android:gravity">top|center_horizontal</item>
+ </style>
+
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ddbe6d6a0f17..786b6b884605 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -940,6 +940,9 @@
<!-- Y translation for credential contents when animating in -->
<dimen name="biometric_dialog_credential_translation_offset">60dp</dimen>
+ <!-- Biometric Auth Credential values -->
+ <dimen name="biometric_auth_icon_size">48dp</dimen>
+
<!-- Starting text size in sp of batteryLevel for wireless charging animation -->
<item name="wireless_charging_anim_battery_level_text_size_start" format="float" type="dimen">
0
@@ -1447,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..1bf30377d7ff 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -887,7 +887,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/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 22279785a773..f7acf06350c6 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -198,6 +198,11 @@
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
+ <style name="TextAppearance.AuthNonBioCredential.Icon">
+ <item name="android:layout_width">@dimen/biometric_auth_icon_size</item>
+ <item name="android:layout_height">@dimen/biometric_auth_icon_size</item>
+ </style>
+
<style name="TextAppearance.AuthNonBioCredential.Title">
<item name="android:fontFamily">google-sans</item>
<item name="android:layout_marginTop">20dp</item>
@@ -227,14 +232,16 @@
<style name="TextAppearance.AuthCredential.PasswordEntry" parent="@android:style/TextAppearance.DeviceDefault">
<item name="android:gravity">center</item>
+ <item name="android:paddingTop">28dp</item>
<item name="android:singleLine">true</item>
<item name="android:textColor">?android:attr/colorForeground</item>
<item name="android:textSize">24sp</item>
+ <item name="android:background">@drawable/edit_text_filled</item>
</style>
<style name="AuthCredentialHeaderStyle">
<item name="android:paddingStart">48dp</item>
- <item name="android:paddingEnd">24dp</item>
+ <item name="android:paddingEnd">48dp</item>
<item name="android:paddingTop">28dp</item>
<item name="android:paddingBottom">20dp</item>
<item name="android:orientation">vertical</item>
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/flags/Flag.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
index a8a526a33229..f7049cf8f4f2 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
@@ -48,9 +48,13 @@ interface SysPropFlag<T> : Flag<T> {
val default: T
}
+/**
+ * Base class for most common boolean flags.
+ *
+ * See [UnreleasedFlag] and [ReleasedFlag] for useful implementations.
+ */
// Consider using the "parcelize" kotlin library.
-
-data class BooleanFlag @JvmOverloads constructor(
+abstract class BooleanFlag constructor(
override val id: Int,
override val default: Boolean = false,
override val teamfood: Boolean = false,
@@ -60,7 +64,7 @@ data class BooleanFlag @JvmOverloads constructor(
companion object {
@JvmField
val CREATOR = object : Parcelable.Creator<BooleanFlag> {
- override fun createFromParcel(parcel: Parcel) = BooleanFlag(parcel)
+ override fun createFromParcel(parcel: Parcel) = object : BooleanFlag(parcel) {}
override fun newArray(size: Int) = arrayOfNulls<BooleanFlag>(size)
}
}
@@ -80,12 +84,46 @@ data class BooleanFlag @JvmOverloads constructor(
}
}
+/**
+ * A Flag that is is false by default.
+ *
+ * It can be changed or overridden in debug builds but not in release builds.
+ */
+data class UnreleasedFlag @JvmOverloads constructor(
+ override val id: Int,
+ override val teamfood: Boolean = false,
+ override val overridden: Boolean = false
+) : BooleanFlag(id, false, teamfood, overridden)
+
+/**
+ * A Flag that is is true by default.
+ *
+ * It can be changed or overridden in any build, meaning it can be turned off if needed.
+ */
+data class ReleasedFlag @JvmOverloads constructor(
+ override val id: Int,
+ override val teamfood: Boolean = false,
+ override val overridden: Boolean = false
+) : BooleanFlag(id, true, teamfood, overridden)
+
+/**
+ * A Flag that reads its default values from a resource overlay instead of code.
+ *
+ * Prefer [UnreleasedFlag] and [ReleasedFlag].
+ */
data class ResourceBooleanFlag @JvmOverloads constructor(
override val id: Int,
@BoolRes override val resourceId: Int,
override val teamfood: Boolean = false
) : ResourceFlag<Boolean>
+/**
+ * A Flag that can reads its overrides from DeviceConfig.
+ *
+ * This is generally useful for flags that come from or are used _outside_ of SystemUI.
+ *
+ * Prefer [UnreleasedFlag] and [ReleasedFlag].
+ */
data class DeviceConfigBooleanFlag @JvmOverloads constructor(
override val id: Int,
override val name: String,
@@ -94,6 +132,13 @@ data class DeviceConfigBooleanFlag @JvmOverloads constructor(
override val teamfood: Boolean = false
) : DeviceConfigFlag<Boolean>
+/**
+ * A Flag that can reads its overrides from System Properties.
+ *
+ * This is generally useful for flags that come from or are used _outside_ of SystemUI.
+ *
+ * Prefer [UnreleasedFlag] and [ReleasedFlag].
+ */
data class SysPropBooleanFlag @JvmOverloads constructor(
override val id: Int,
override val name: String,
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index 01497516e0b1..4613e8b1060f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -100,4 +100,14 @@ oneway interface IOverviewProxy {
* Sent when the desired dark intensity of the nav buttons has changed
*/
void onNavButtonsDarkIntensityChanged(float darkIntensity) = 22;
+
+ /**
+ * Sent when screen started turning on.
+ */
+ void onScreenTurningOn() = 23;
+
+ /**
+ * Sent when screen started turning off.
+ */
+ void onScreenTurningOff() = 24;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 916526d0efac..ee30972296fb 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -19,7 +19,6 @@ package com.android.systemui.shared.system;
import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
-import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
import static android.app.ActivityTaskManager.getService;
import android.annotation.NonNull;
@@ -27,7 +26,6 @@ import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityClient;
import android.app.ActivityManager;
-import android.app.ActivityManager.RecentTaskInfo;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
@@ -337,7 +335,8 @@ public class ActivityManagerWrapper {
* Shows a voice session identified by {@code token}
* @return true if the session was shown, false otherwise
*/
- public boolean showVoiceSession(IBinder token, Bundle args, int flags) {
+ public boolean showVoiceSession(@NonNull IBinder token, @NonNull Bundle args, int flags,
+ @Nullable String attributionTag) {
IVoiceInteractionManagerService service = IVoiceInteractionManagerService.Stub.asInterface(
ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
if (service == null) {
@@ -346,7 +345,7 @@ public class ActivityManagerWrapper {
args.putLong(INVOCATION_TIME_MS_KEY, SystemClock.elapsedRealtime());
try {
- return service.showSessionFromSession(token, args, flags);
+ return service.showSessionFromSession(token, args, flags, attributionTag);
} catch (RemoteException e) {
return false;
}
diff --git a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
index 256028417a1d..74bd9c6c287d 100644
--- a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
+++ b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
@@ -27,7 +27,8 @@ import dagger.Provides
import javax.inject.Named
@Module(includes = [
- SettingsUtilModule::class
+ ServerFlagReaderModule::class,
+ SettingsUtilModule::class,
])
abstract class FlagsModule {
@Binds
@@ -46,4 +47,4 @@ abstract class FlagsModule {
@Named(ALL_FLAGS)
fun providesAllFlags(): Map<Int, Flag<*>> = Flags.collectFlags()
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt
index ab9e01eebaf7..38b5c9a9fa79 100644
--- a/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt
+++ b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt
@@ -19,8 +19,8 @@ package com.android.systemui.flags
import dagger.Binds
import dagger.Module
-@Module
+@Module(includes = [ServerFlagReaderModule::class])
abstract class FlagsModule {
@Binds
abstract fun bindsFeatureFlagRelease(impl: FeatureFlagsRelease): FeatureFlags
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
index 23195af8bdea..00f1c0108d0b 100644
--- a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
@@ -33,6 +33,7 @@ import android.view.SurfaceView;
import android.view.ViewGroup;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.keyguard.dagger.KeyguardBouncerScope;
import com.android.systemui.dagger.qualifiers.Main;
@@ -208,7 +209,7 @@ public class AdminSecondaryLockScreenController {
hide();
if (mKeyguardCallback != null) {
mKeyguardCallback.dismiss(/* securityVerified= */ true, userId,
- /* bypassSecondaryLockScreen= */true);
+ /* bypassSecondaryLockScreen= */true, SecurityMode.Invalid);
}
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index eb418ff3b142..b8fcb103402d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -179,7 +179,7 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey
if (dismissKeyguard) {
mDismissing = true;
mLatencyTracker.onActionStart(LatencyTracker.ACTION_LOCKSCREEN_UNLOCK);
- getKeyguardSecurityCallback().dismiss(true, userId);
+ getKeyguardSecurityCallback().dismiss(true, userId, getSecurityMode());
}
} else {
if (isValidPassword) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
index d32219a9817f..db64f05ccbea 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
@@ -90,7 +90,7 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView>
Log.i(TAG, "TrustAgent dismissed Keyguard.");
}
mSecurityCallback.dismiss(false /* authenticated */, userId,
- /* bypassSecondaryLockScreen */ false);
+ /* bypassSecondaryLockScreen */ false, SecurityMode.Invalid);
} else {
mViewMediatorCallback.playTrustedSound();
}
@@ -102,9 +102,9 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView>
@Override
public boolean dismiss(boolean authenticated, int targetUserId,
- boolean bypassSecondaryLockScreen) {
+ boolean bypassSecondaryLockScreen, SecurityMode expectedSecurityMode) {
return mKeyguardSecurityContainerController.showNextSecurityScreenOrFinish(
- authenticated, targetUserId, bypassSecondaryLockScreen);
+ authenticated, targetUserId, bypassSecondaryLockScreen, expectedSecurityMode);
}
@Override
@@ -212,7 +212,8 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView>
* @return True if the keyguard is done.
*/
public boolean dismiss(int targetUserId) {
- return mSecurityCallback.dismiss(false, targetUserId, false);
+ return mSecurityCallback.dismiss(false, targetUserId, false,
+ getCurrentSecurityMode());
}
/**
@@ -360,10 +361,10 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView>
}
public boolean handleBackKey() {
- if (mKeyguardSecurityContainerController.getCurrentSecurityMode()
- != SecurityMode.None) {
+ SecurityMode securityMode = mKeyguardSecurityContainerController.getCurrentSecurityMode();
+ if (securityMode != SecurityMode.None) {
mKeyguardSecurityContainerController.dismiss(
- false, KeyguardUpdateMonitor.getCurrentUser());
+ false, KeyguardUpdateMonitor.getCurrentUser(), securityMode);
return true;
}
return false;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index 98ac640bf703..87300c3f0504 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -59,10 +59,11 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
return false;
}
@Override
- public void dismiss(boolean securityVerified, int targetUserId) { }
+ public void dismiss(boolean securityVerified, int targetUserId,
+ SecurityMode expectedSecurityMode) { }
@Override
public void dismiss(boolean authenticated, int targetId,
- boolean bypassSecondaryLockScreen) { }
+ boolean bypassSecondaryLockScreen, SecurityMode expectedSecurityMode) { }
@Override
public void onUserInput() { }
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
index 58e0fb968204..0ea196537e72 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
@@ -55,10 +55,11 @@ data class KeyguardFaceListenModel(
val faceAuthenticated: Boolean,
val faceDisabled: Boolean,
val goingToSleep: Boolean,
- val keyguardAwake: Boolean,
+ val keyguardAwakeExcludingBouncerShowing: Boolean,
val keyguardGoingAway: Boolean,
val listeningForFaceAssistant: Boolean,
val occludingAppRequestingFaceAuth: Boolean,
+ val onlyFaceEnrolled: Boolean,
val primaryUser: Boolean,
val scanningAllowedByStrongAuth: Boolean,
val secureCameraLaunched: Boolean,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index b97d9671eb25..9aa6f0320f50 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -171,7 +171,7 @@ public class KeyguardPatternViewController
if (dismissKeyguard) {
mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct);
mLatencyTracker.onActionStart(LatencyTracker.ACTION_LOCKSCREEN_UNLOCK);
- getKeyguardSecurityCallback().dismiss(true, userId);
+ getKeyguardSecurityCallback().dismiss(true, userId, SecurityMode.Pattern);
}
} else {
mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java
index e38472745234..bc72f7979a74 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java
@@ -15,14 +15,17 @@
*/
package com.android.keyguard;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+
public interface KeyguardSecurityCallback {
/**
* Dismiss the given security screen.
* @param securityVerified true if the user correctly entered credentials for the given screen.
* @param targetUserId a user that needs to be the foreground user at the dismissal completion.
+ * @param expectedSecurityMode The security mode that is invoking this dismiss.
*/
- void dismiss(boolean securityVerified, int targetUserId);
+ void dismiss(boolean securityVerified, int targetUserId, SecurityMode expectedSecurityMode);
/**
* Dismiss the given security screen.
@@ -30,8 +33,10 @@ public interface KeyguardSecurityCallback {
* @param targetUserId a user that needs to be the foreground user at the dismissal completion.
* @param bypassSecondaryLockScreen true if the user can bypass the secondary lock screen,
* if any, during this dismissal.
+ * @param expectedSecurityMode The security mode that is invoking this dismiss.
*/
- void dismiss(boolean securityVerified, int targetUserId, boolean bypassSecondaryLockScreen);
+ void dismiss(boolean securityVerified, int targetUserId, boolean bypassSecondaryLockScreen,
+ SecurityMode expectedSecurityMode);
/**
* Manually report user activity to keep the device awake.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 8fb622a8c55e..3517d22ae50d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -236,7 +236,12 @@ public class KeyguardSecurityContainer extends FrameLayout {
// Used to notify the container when something interesting happens.
public interface SecurityCallback {
- boolean dismiss(boolean authenticated, int targetUserId, boolean bypassSecondaryLockScreen);
+ /**
+ * Potentially dismiss the current security screen, after validating that all device
+ * security has been unlocked. Otherwise show the next screen.
+ */
+ boolean dismiss(boolean authenticated, int targetUserId, boolean bypassSecondaryLockScreen,
+ SecurityMode expectedSecurityMode);
void userActivity();
@@ -1124,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) {
@@ -1208,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/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 0b3fe46007e5..e1957c09fef5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -156,14 +156,17 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
}
@Override
- public void dismiss(boolean authenticated, int targetId) {
- dismiss(authenticated, targetId, /* bypassSecondaryLockScreen */ false);
+ public void dismiss(boolean authenticated, int targetId,
+ SecurityMode expectedSecurityMode) {
+ dismiss(authenticated, targetId, /* bypassSecondaryLockScreen */ false,
+ expectedSecurityMode);
}
@Override
public void dismiss(boolean authenticated, int targetId,
- boolean bypassSecondaryLockScreen) {
- mSecurityCallback.dismiss(authenticated, targetId, bypassSecondaryLockScreen);
+ boolean bypassSecondaryLockScreen, SecurityMode expectedSecurityMode) {
+ mSecurityCallback.dismiss(authenticated, targetId, bypassSecondaryLockScreen,
+ expectedSecurityMode);
}
public boolean isVerifyUnlockOnly() {
@@ -385,8 +388,13 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
return mCurrentSecurityMode;
}
- public void dismiss(boolean authenticated, int targetUserId) {
- mKeyguardSecurityCallback.dismiss(authenticated, targetUserId);
+ /**
+ * Potentially dismiss the current security screen, after validating that all device
+ * security has been unlocked. Otherwise show the next screen.
+ */
+ public void dismiss(boolean authenticated, int targetUserId,
+ SecurityMode expectedSecurityMode) {
+ mKeyguardSecurityCallback.dismiss(authenticated, targetUserId, expectedSecurityMode);
}
public void reset() {
@@ -456,12 +464,21 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
* completion.
* @param bypassSecondaryLockScreen true if the user is allowed to bypass the secondary
* secondary lock screen requirement, if any.
+ * @param expectedSecurityMode SecurityMode that is invoking this request. SecurityMode.Invalid
+ * indicates that no check should be done
* @return true if keyguard is done
*/
public boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId,
- boolean bypassSecondaryLockScreen) {
+ boolean bypassSecondaryLockScreen, SecurityMode expectedSecurityMode) {
if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
+ if (expectedSecurityMode != SecurityMode.Invalid
+ && expectedSecurityMode != getCurrentSecurityMode()) {
+ Log.w(TAG, "Attempted to invoke showNextSecurityScreenOrFinish with securityMode "
+ + expectedSecurityMode + ", but current mode is " + getCurrentSecurityMode());
+ return false;
+ }
+
boolean finish = false;
boolean strongAuth = false;
int eventSubtype = -1;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index 2f6fa145664e..2a2e9bf54bb2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -142,7 +142,9 @@ public class KeyguardSimPinViewController
protected void verifyPasswordAndUnlock() {
String entry = mPasswordEntry.getText();
- if (entry.length() < 4) {
+ // A SIM PIN is 4 to 8 decimal digits according to
+ // GSM 02.17 version 5.0.1, Section 5.6 PIN Management
+ if ((entry.length() < 4) || (entry.length() > 8)) {
// otherwise, display a message to the user, and don't submit.
mMessageAreaController.setMessage(
com.android.systemui.R.string.kg_invalid_sim_pin_hint);
@@ -170,7 +172,8 @@ public class KeyguardSimPinViewController
mRemainingAttempts = -1;
mShowDefaultMessage = true;
getKeyguardSecurityCallback().dismiss(
- true, KeyguardUpdateMonitor.getCurrentUser());
+ true, KeyguardUpdateMonitor.getCurrentUser(),
+ SecurityMode.SimPin);
} else {
mShowDefaultMessage = false;
if (result.getResult() == PinResult.PIN_RESULT_TYPE_INCORRECT) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index 47aa43b86599..203f9b660536 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -69,7 +69,8 @@ public class KeyguardSimPukViewController
if (simState == TelephonyManager.SIM_STATE_READY) {
mRemainingAttempts = -1;
mShowDefaultMessage = true;
- getKeyguardSecurityCallback().dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
+ getKeyguardSecurityCallback().dismiss(true, KeyguardUpdateMonitor.getCurrentUser(),
+ SecurityMode.SimPuk);
} else {
resetState();
}
@@ -278,7 +279,8 @@ public class KeyguardSimPukViewController
mShowDefaultMessage = true;
getKeyguardSecurityCallback().dismiss(
- true, KeyguardUpdateMonitor.getCurrentUser());
+ true, KeyguardUpdateMonitor.getCurrentUser(),
+ SecurityMode.SimPuk);
} else {
mShowDefaultMessage = false;
if (result.getResult() == PinResult.PIN_RESULT_TYPE_INCORRECT) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
index 89d6fb5f062f..acbea1beeae3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
@@ -29,7 +29,7 @@ import javax.inject.Inject
/**
* Translates items away/towards the hinge when the device is opened/closed. This is controlled by
- * the set of ids, which also dictact which direction to move and when, via a filter function.
+ * the set of ids, which also dictate which direction to move and when, via a filter function.
*/
@SysUIUnfoldScope
class KeyguardUnfoldTransition
@@ -55,7 +55,9 @@ constructor(
ViewIdToTranslate(R.id.lockscreen_clock_view, LEFT, filterNever),
ViewIdToTranslate(
R.id.notification_stack_scroller, RIGHT, filterSplitShadeOnly),
- ViewIdToTranslate(R.id.wallet_button, RIGHT, filterNever)),
+ ViewIdToTranslate(R.id.wallet_button, RIGHT, filterNever),
+ ViewIdToTranslate(R.id.start_button, LEFT, filterNever),
+ ViewIdToTranslate(R.id.end_button, RIGHT, filterNever)),
progressProvider = unfoldProgressProvider)
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 489b4be51566..8b5e3c11b2f6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -213,7 +213,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
* If no cancel signal has been received after this amount of time, set the biometric running
* state to stopped to allow Keyguard to retry authentication.
*/
- private static final int DEFAULT_CANCEL_SIGNAL_TIMEOUT = 3000;
+ @VisibleForTesting
+ protected static final int DEFAULT_CANCEL_SIGNAL_TIMEOUT = 3000;
private static final ComponentName FALLBACK_HOME_COMPONENT = new ComponentName(
"com.android.settings", "com.android.settings.FallbackHome");
@@ -316,10 +317,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private static final int HAL_ERROR_RETRY_TIMEOUT = 500; // ms
private static final int HAL_ERROR_RETRY_MAX = 20;
- private final Runnable mFpCancelNotReceived = this::onFingerprintCancelNotReceived;
+ @VisibleForTesting
+ protected final Runnable mFpCancelNotReceived = this::onFingerprintCancelNotReceived;
private final Runnable mFaceCancelNotReceived = this::onFaceCancelNotReceived;
+ @VisibleForTesting
+ protected Handler getHandler() {
+ return mHandler;
+ }
private final Handler mHandler;
private SparseBooleanArray mBiometricEnabledForUser = new SparseBooleanArray();
@@ -707,6 +713,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private void handleFingerprintAuthFailed() {
Assert.isMainThread();
+ if (mHandler.hasCallbacks(mFpCancelNotReceived)) {
+ Log.d(TAG, "handleFingerprintAuthFailed()"
+ + " triggered while waiting for cancellation, removing watchdog");
+ mHandler.removeCallbacks(mFpCancelNotReceived);
+ }
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -737,6 +748,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private void handleFingerprintAuthenticated(int authUserId, boolean isStrongBiometric) {
Trace.beginSection("KeyGuardUpdateMonitor#handlerFingerPrintAuthenticated");
+ if (mHandler.hasCallbacks(mFpCancelNotReceived)) {
+ Log.d(TAG, "handleFingerprintAuthenticated()"
+ + " triggered while waiting for cancellation, removing watchdog");
+ mHandler.removeCallbacks(mFpCancelNotReceived);
+ }
try {
final int userId;
try {
@@ -2458,8 +2474,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
final boolean statusBarShadeLocked = mStatusBarState == StatusBarState.SHADE_LOCKED;
- final boolean awakeKeyguard = mKeyguardIsVisible && mDeviceInteractive && !mGoingToSleep
- && !statusBarShadeLocked;
+ // mKeyguardIsVisible is true even when the bouncer is shown, we don't want to run face auth
+ // on bouncer if both fp and fingerprint are enrolled.
+ final boolean awakeKeyguardExcludingBouncerShowing = mKeyguardIsVisible
+ && mDeviceInteractive && !mGoingToSleep
+ && !statusBarShadeLocked && !mBouncerFullyShown;
final int user = getCurrentUser();
final int strongAuth = mStrongAuthTracker.getStrongAuthForUser(user);
final boolean isLockDown =
@@ -2499,14 +2518,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
final boolean faceDisabledForUser = isFaceDisabled(user);
final boolean biometricEnabledForUser = mBiometricEnabledForUser.get(user);
final boolean shouldListenForFaceAssistant = shouldListenForFaceAssistant();
+ final boolean onlyFaceEnrolled = isOnlyFaceEnrolled();
// Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
// instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
final boolean shouldListen =
- (mBouncerFullyShown && !mGoingToSleep
+ ((mBouncerFullyShown && !mGoingToSleep && onlyFaceEnrolled)
|| mAuthInterruptActive
|| mOccludingAppRequestingFace
- || awakeKeyguard
+ || awakeKeyguardExcludingBouncerShowing
|| shouldListenForFaceAssistant
|| mAuthController.isUdfpsFingerDown()
|| mUdfpsBouncerShowing)
@@ -2530,10 +2550,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
faceAuthenticated,
faceDisabledForUser,
mGoingToSleep,
- awakeKeyguard,
+ awakeKeyguardExcludingBouncerShowing,
mKeyguardGoingAway,
shouldListenForFaceAssistant,
mOccludingAppRequestingFace,
+ onlyFaceEnrolled,
mIsPrimaryUser,
strongAuthAllowsScanning,
mSecureCameraLaunched,
@@ -2543,6 +2564,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
return shouldListen;
}
+ private boolean isOnlyFaceEnrolled() {
+ return isFaceAuthEnabledForUser(getCurrentUser())
+ && !isUnlockWithFingerprintPossible(getCurrentUser());
+ }
+
private void maybeLogListenerModelData(KeyguardListenModel model) {
mLogger.logKeyguardListenerModel(model);
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/analytics/DataCollector.java b/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java
deleted file mode 100644
index 0db5bef783c0..000000000000
--- a/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java
+++ /dev/null
@@ -1,506 +0,0 @@
-/*
- * Copyright (C) 2015 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.analytics;
-
-import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session;
-import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session.PhoneEvent;
-
-import android.content.Context;
-import android.database.ContentObserver;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.widget.Toast;
-
-import com.android.systemui.Dependency;
-import com.android.systemui.plugins.FalsingPlugin;
-import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.shared.plugins.PluginManager;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-/**
- * Tracks touch, sensor and phone events when the lockscreen is on. If the phone is unlocked
- * the data containing these events is saved to a file. This data is collected
- * to analyze how a human interaction looks like.
- *
- * A session starts when the screen is turned on.
- * A session ends when the screen is turned off or user unlocks the phone.
- */
-public class DataCollector implements SensorEventListener {
- private static final String TAG = "DataCollector";
- private static final String COLLECTOR_ENABLE = "data_collector_enable";
- private static final String COLLECT_BAD_TOUCHES = "data_collector_collect_bad_touches";
- private static final String ALLOW_REJECTED_TOUCH_REPORTS =
- "data_collector_allow_rejected_touch_reports";
- private static final String DISABLE_UNLOCKING_FOR_FALSING_COLLECTION =
- "data_collector_disable_unlocking";
-
- private static final long TIMEOUT_MILLIS = 11000; // 11 seconds.
- public static final boolean DEBUG = false;
-
- private final Handler mHandler = new Handler(Looper.getMainLooper());
- private final Context mContext;
-
- // Err on the side of caution, so logging is not started after a crash even tough the screen
- // is off.
- private SensorLoggerSession mCurrentSession = null;
-
- private boolean mEnableCollector = false;
- private boolean mCollectBadTouches = false;
- private boolean mCornerSwiping = false;
- private boolean mTrackingStarted = false;
- private boolean mAllowReportRejectedTouch = false;
- private boolean mDisableUnlocking = false;
-
- private static DataCollector sInstance = null;
-
- private FalsingPlugin mFalsingPlugin = null;
-
- protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
- @Override
- public void onChange(boolean selfChange) {
- updateConfiguration();
- }
- };
-
- private final PluginListener mPluginListener = new PluginListener<FalsingPlugin>() {
- public void onPluginConnected(FalsingPlugin plugin, Context context) {
- mFalsingPlugin = plugin;
- }
-
- public void onPluginDisconnected(FalsingPlugin plugin) {
- mFalsingPlugin = null;
- }
- };
-
- private DataCollector(Context context) {
- mContext = context;
-
- mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(COLLECTOR_ENABLE), false,
- mSettingsObserver,
- UserHandle.USER_ALL);
-
- mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(COLLECT_BAD_TOUCHES), false,
- mSettingsObserver,
- UserHandle.USER_ALL);
-
- mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(ALLOW_REJECTED_TOUCH_REPORTS), false,
- mSettingsObserver,
- UserHandle.USER_ALL);
-
- mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(DISABLE_UNLOCKING_FOR_FALSING_COLLECTION), false,
- mSettingsObserver,
- UserHandle.USER_ALL);
-
- updateConfiguration();
-
- Dependency.get(PluginManager.class).addPluginListener(mPluginListener, FalsingPlugin.class);
- }
-
- public static DataCollector getInstance(Context context) {
- if (sInstance == null) {
- sInstance = new DataCollector(context);
- }
- return sInstance;
- }
-
- private void updateConfiguration() {
- mEnableCollector = Build.IS_DEBUGGABLE && 0 != Settings.Secure.getInt(
- mContext.getContentResolver(),
- COLLECTOR_ENABLE, 0);
- mCollectBadTouches = mEnableCollector && 0 != Settings.Secure.getInt(
- mContext.getContentResolver(),
- COLLECT_BAD_TOUCHES, 0);
- mAllowReportRejectedTouch = Build.IS_DEBUGGABLE && 0 != Settings.Secure.getInt(
- mContext.getContentResolver(),
- ALLOW_REJECTED_TOUCH_REPORTS, 0);
- mDisableUnlocking = mEnableCollector && Build.IS_DEBUGGABLE && 0 != Settings.Secure.getInt(
- mContext.getContentResolver(),
- DISABLE_UNLOCKING_FOR_FALSING_COLLECTION, 0);
- }
-
- private boolean sessionEntrypoint() {
- if (isEnabled() && mCurrentSession == null) {
- onSessionStart();
- return true;
- }
- return false;
- }
-
- private void sessionExitpoint(int result) {
- if (mCurrentSession != null) {
- onSessionEnd(result);
- }
- }
-
- private void onSessionStart() {
- mCornerSwiping = false;
- mTrackingStarted = false;
- mCurrentSession = new SensorLoggerSession(System.currentTimeMillis(), System.nanoTime());
- }
-
- private void onSessionEnd(int result) {
- SensorLoggerSession session = mCurrentSession;
- mCurrentSession = null;
-
- if (mEnableCollector || mDisableUnlocking) {
- session.end(System.currentTimeMillis(), result);
- queueSession(session);
- }
- }
-
- public Uri reportRejectedTouch() {
- if (mCurrentSession == null) {
- Toast.makeText(mContext, "Generating rejected touch report failed: session timed out.",
- Toast.LENGTH_LONG).show();
- return null;
- }
- SensorLoggerSession currentSession = mCurrentSession;
-
- currentSession.setType(Session.REJECTED_TOUCH_REPORT);
- currentSession.end(System.currentTimeMillis(), Session.SUCCESS);
- Session proto = currentSession.toProto();
-
- byte[] b = Session.toByteArray(proto);
- File dir = new File(mContext.getExternalCacheDir(), "rejected_touch_reports");
- dir.mkdir();
- File touch = new File(dir, "rejected_touch_report_" + System.currentTimeMillis());
-
- try {
- new FileOutputStream(touch).write(b);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
-
- return Uri.fromFile(touch);
- }
-
- private void queueSession(final SensorLoggerSession currentSession) {
- AsyncTask.execute(new Runnable() {
- @Override
- public void run() {
- byte[] b = Session.toByteArray(currentSession.toProto());
-
- if (mFalsingPlugin != null) {
- mFalsingPlugin.dataCollected(currentSession.getResult() == Session.SUCCESS, b);
- } else {
- String dir = mContext.getFilesDir().getAbsolutePath();
- if (currentSession.getResult() != Session.SUCCESS) {
- if (!mDisableUnlocking && !mCollectBadTouches) {
- return;
- }
- dir += "/bad_touches";
- } else if (!mDisableUnlocking) {
- dir += "/good_touches";
- }
-
- File file = new File(dir);
- file.mkdir();
- File touch = new File(file, "trace_" + System.currentTimeMillis());
- try {
- new FileOutputStream(touch).write(b);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- }
- });
- }
-
- @Override
- public synchronized void onSensorChanged(SensorEvent event) {
- if (isEnabled() && mCurrentSession != null) {
- mCurrentSession.addSensorEvent(event, System.nanoTime());
- }
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- }
-
- /**
- * @return true if data is being collected - either for data gathering or creating a
- * rejected touch report.
- */
- public boolean isEnabled() {
- return mEnableCollector || mAllowReportRejectedTouch || mDisableUnlocking;
- }
-
- public boolean isUnlockingDisabled() {
- return mDisableUnlocking;
- }
- /**
- * @return true if the full data set for data gathering should be collected - including
- * extensive sensor data, which is is not normally included with rejected touch reports.
- */
- public boolean isEnabledFull() {
- return mEnableCollector;
- }
-
- public void onScreenTurningOn() {
- if (sessionEntrypoint()) {
- if (DEBUG) {
- Log.d(TAG, "onScreenTurningOn");
- }
- addEvent(PhoneEvent.ON_SCREEN_ON);
- }
- }
-
- public void onScreenOnFromTouch() {
- if (sessionEntrypoint()) {
- if (DEBUG) {
- Log.d(TAG, "onScreenOnFromTouch");
- }
- addEvent(PhoneEvent.ON_SCREEN_ON_FROM_TOUCH);
- }
- }
-
- public void onScreenOff() {
- if (DEBUG) {
- Log.d(TAG, "onScreenOff");
- }
- addEvent(PhoneEvent.ON_SCREEN_OFF);
- sessionExitpoint(Session.FAILURE);
- }
-
- public void onSucccessfulUnlock() {
- if (DEBUG) {
- Log.d(TAG, "onSuccessfulUnlock");
- }
- addEvent(PhoneEvent.ON_SUCCESSFUL_UNLOCK);
- sessionExitpoint(Session.SUCCESS);
- }
-
- public void onBouncerShown() {
- if (DEBUG) {
- Log.d(TAG, "onBouncerShown");
- }
- addEvent(PhoneEvent.ON_BOUNCER_SHOWN);
- }
-
- public void onBouncerHidden() {
- if (DEBUG) {
- Log.d(TAG, "onBouncerHidden");
- }
- addEvent(PhoneEvent.ON_BOUNCER_HIDDEN);
- }
-
- public void onQsDown() {
- if (DEBUG) {
- Log.d(TAG, "onQsDown");
- }
- addEvent(PhoneEvent.ON_QS_DOWN);
- }
-
- public void setQsExpanded(boolean expanded) {
- if (DEBUG) {
- Log.d(TAG, "setQsExpanded = " + expanded);
- }
- if (expanded) {
- addEvent(PhoneEvent.SET_QS_EXPANDED_TRUE);
- } else {
- addEvent(PhoneEvent.SET_QS_EXPANDED_FALSE);
- }
- }
-
- public void onTrackingStarted() {
- if (DEBUG) {
- Log.d(TAG, "onTrackingStarted");
- }
- mTrackingStarted = true;
- addEvent(PhoneEvent.ON_TRACKING_STARTED);
- }
-
- public void onTrackingStopped() {
- if (mTrackingStarted) {
- if (DEBUG) {
- Log.d(TAG, "onTrackingStopped");
- }
- mTrackingStarted = false;
- addEvent(PhoneEvent.ON_TRACKING_STOPPED);
- }
- }
-
- public void onNotificationActive() {
- if (DEBUG) {
- Log.d(TAG, "onNotificationActive");
- }
- addEvent(PhoneEvent.ON_NOTIFICATION_ACTIVE);
- }
-
-
- public void onNotificationDoubleTap() {
- if (DEBUG) {
- Log.d(TAG, "onNotificationDoubleTap");
- }
- addEvent(PhoneEvent.ON_NOTIFICATION_DOUBLE_TAP);
- }
-
- public void setNotificationExpanded() {
- if (DEBUG) {
- Log.d(TAG, "setNotificationExpanded");
- }
- addEvent(PhoneEvent.SET_NOTIFICATION_EXPANDED);
- }
-
- public void onNotificatonStartDraggingDown() {
- if (DEBUG) {
- Log.d(TAG, "onNotificationStartDraggingDown");
- }
- addEvent(PhoneEvent.ON_NOTIFICATION_START_DRAGGING_DOWN);
- }
-
- public void onStartExpandingFromPulse() {
- if (DEBUG) {
- Log.d(TAG, "onStartExpandingFromPulse");
- }
- // TODO: maybe add event
- }
-
- public void onExpansionFromPulseStopped() {
- if (DEBUG) {
- Log.d(TAG, "onExpansionFromPulseStopped");
- }
- // TODO: maybe add event
- }
-
- public void onNotificatonStopDraggingDown() {
- if (DEBUG) {
- Log.d(TAG, "onNotificationStopDraggingDown");
- }
- addEvent(PhoneEvent.ON_NOTIFICATION_STOP_DRAGGING_DOWN);
- }
-
- public void onNotificationDismissed() {
- if (DEBUG) {
- Log.d(TAG, "onNotificationDismissed");
- }
- addEvent(PhoneEvent.ON_NOTIFICATION_DISMISSED);
- }
-
- public void onNotificatonStartDismissing() {
- if (DEBUG) {
- Log.d(TAG, "onNotificationStartDismissing");
- }
- addEvent(PhoneEvent.ON_NOTIFICATION_START_DISMISSING);
- }
-
- public void onNotificatonStopDismissing() {
- if (DEBUG) {
- Log.d(TAG, "onNotificationStopDismissing");
- }
- addEvent(PhoneEvent.ON_NOTIFICATION_STOP_DISMISSING);
- }
-
- public void onCameraOn() {
- if (DEBUG) {
- Log.d(TAG, "onCameraOn");
- }
- addEvent(PhoneEvent.ON_CAMERA_ON);
- }
-
- public void onLeftAffordanceOn() {
- if (DEBUG) {
- Log.d(TAG, "onLeftAffordanceOn");
- }
- addEvent(PhoneEvent.ON_LEFT_AFFORDANCE_ON);
- }
-
- public void onAffordanceSwipingStarted(boolean rightCorner) {
- if (DEBUG) {
- Log.d(TAG, "onAffordanceSwipingStarted");
- }
- mCornerSwiping = true;
- if (rightCorner) {
- addEvent(PhoneEvent.ON_RIGHT_AFFORDANCE_SWIPING_STARTED);
- } else {
- addEvent(PhoneEvent.ON_LEFT_AFFORDANCE_SWIPING_STARTED);
- }
- }
-
- public void onAffordanceSwipingAborted() {
- if (mCornerSwiping) {
- if (DEBUG) {
- Log.d(TAG, "onAffordanceSwipingAborted");
- }
- mCornerSwiping = false;
- addEvent(PhoneEvent.ON_AFFORDANCE_SWIPING_ABORTED);
- }
- }
-
- public void onUnlockHintStarted() {
- if (DEBUG) {
- Log.d(TAG, "onUnlockHintStarted");
- }
- addEvent(PhoneEvent.ON_UNLOCK_HINT_STARTED);
- }
-
- public void onCameraHintStarted() {
- if (DEBUG) {
- Log.d(TAG, "onCameraHintStarted");
- }
- addEvent(PhoneEvent.ON_CAMERA_HINT_STARTED);
- }
-
- public void onLeftAffordanceHintStarted() {
- if (DEBUG) {
- Log.d(TAG, "onLeftAffordanceHintStarted");
- }
- addEvent(PhoneEvent.ON_LEFT_AFFORDANCE_HINT_STARTED);
- }
-
- public void onTouchEvent(MotionEvent event, int width, int height) {
- if (mCurrentSession != null) {
- if (DEBUG) {
- Log.v(TAG, "onTouchEvent(ev.action="
- + MotionEvent.actionToString(event.getAction()) + ")");
- }
- mCurrentSession.addMotionEvent(event);
- mCurrentSession.setTouchArea(width, height);
- }
- }
-
- private void addEvent(int eventType) {
- if (isEnabled() && mCurrentSession != null) {
- mCurrentSession.addPhoneEvent(eventType, System.nanoTime());
- }
- }
-
- public boolean isReportingEnabled() {
- return mAllowReportRejectedTouch;
- }
-
- public void onFalsingSessionStarted() {
- sessionEntrypoint();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java b/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java
deleted file mode 100644
index d294012a7527..000000000000
--- a/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright (C) 2015 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.analytics;
-
-import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session;
-import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session.PhoneEvent;
-import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session.SensorEvent;
-import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session.TouchEvent;
-
-import android.os.Build;
-import android.util.Log;
-import android.view.MotionEvent;
-
-import java.util.ArrayList;
-
-/**
- * Collects touch, sensor and phone events and converts the data to
- * TouchAnalyticsProto.Session.
- */
-public class SensorLoggerSession {
- private static final String TAG = "SensorLoggerSession";
-
- private final long mStartTimestampMillis;
- private final long mStartSystemTimeNanos;
-
- private long mEndTimestampMillis;
- private int mType;
-
- private ArrayList<TouchEvent> mMotionEvents = new ArrayList<>();
- private ArrayList<SensorEvent> mSensorEvents = new ArrayList<>();
- private ArrayList<PhoneEvent> mPhoneEvents = new ArrayList<>();
- private int mTouchAreaHeight;
- private int mTouchAreaWidth;
- private int mResult = Session.UNKNOWN;
-
- public SensorLoggerSession(long startTimestampMillis, long startSystemTimeNanos) {
- mStartTimestampMillis = startTimestampMillis;
- mStartSystemTimeNanos = startSystemTimeNanos;
- mType = Session.REAL;
- }
-
- public void setType(int type) {
- mType = type;
- }
-
- public void end(long endTimestampMillis, int result) {
- mResult = result;
- mEndTimestampMillis = endTimestampMillis;
-
- if (DataCollector.DEBUG) {
- Log.d(TAG, "Ending session result=" + result + " it lasted for " +
- (float) (mEndTimestampMillis - mStartTimestampMillis) / 1000f + "s");
- }
- }
-
- public void addMotionEvent(MotionEvent motionEvent) {
- TouchEvent event = motionEventToProto(motionEvent);
- mMotionEvents.add(event);
- }
-
- public void addSensorEvent(android.hardware.SensorEvent eventOrig, long systemTimeNanos) {
- SensorEvent event = sensorEventToProto(eventOrig, systemTimeNanos);
- mSensorEvents.add(event);
- }
-
- public void addPhoneEvent(int eventType, long systemTimeNanos) {
- PhoneEvent event = phoneEventToProto(eventType, systemTimeNanos);
- mPhoneEvents.add(event);
- }
-
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder("Session{");
- sb.append("mStartTimestampMillis=").append(mStartTimestampMillis);
- sb.append(", mStartSystemTimeNanos=").append(mStartSystemTimeNanos);
- sb.append(", mEndTimestampMillis=").append(mEndTimestampMillis);
- sb.append(", mResult=").append(mResult);
- sb.append(", mTouchAreaHeight=").append(mTouchAreaHeight);
- sb.append(", mTouchAreaWidth=").append(mTouchAreaWidth);
- sb.append(", mMotionEvents=[size=").append(mMotionEvents.size()).append("]");
- sb.append(", mSensorEvents=[size=").append(mSensorEvents.size()).append("]");
- sb.append(", mPhoneEvents=[size=").append(mPhoneEvents.size()).append("]");
- sb.append('}');
- return sb.toString();
- }
-
- public Session toProto() {
- Session proto = new Session();
- proto.startTimestampMillis = mStartTimestampMillis;
- proto.durationMillis = mEndTimestampMillis - mStartTimestampMillis;
- proto.build = Build.FINGERPRINT;
- proto.deviceId = Build.DEVICE;
- proto.result = mResult;
- proto.type = mType;
- proto.sensorEvents = mSensorEvents.toArray(proto.sensorEvents);
- proto.touchEvents = mMotionEvents.toArray(proto.touchEvents);
- proto.phoneEvents = mPhoneEvents.toArray(proto.phoneEvents);
- proto.touchAreaWidth = mTouchAreaWidth;
- proto.touchAreaHeight = mTouchAreaHeight;
- return proto;
- }
-
- private PhoneEvent phoneEventToProto(int eventType, long sysTimeNanos) {
- PhoneEvent proto = new PhoneEvent();
- proto.type = eventType;
- proto.timeOffsetNanos = sysTimeNanos - mStartSystemTimeNanos;
- return proto;
- }
-
- private SensorEvent sensorEventToProto(android.hardware.SensorEvent ev, long sysTimeNanos) {
- SensorEvent proto = new SensorEvent();
- proto.type = ev.sensor.getType();
- proto.timeOffsetNanos = sysTimeNanos - mStartSystemTimeNanos;
- proto.timestamp = ev.timestamp;
- proto.values = ev.values.clone();
- return proto;
- }
-
- private TouchEvent motionEventToProto(MotionEvent ev) {
- int count = ev.getPointerCount();
- TouchEvent proto = new TouchEvent();
- proto.timeOffsetNanos = ev.getEventTimeNano() - mStartSystemTimeNanos;
- proto.action = ev.getActionMasked();
- proto.actionIndex = ev.getActionIndex();
- proto.pointers = new TouchEvent.Pointer[count];
- for (int i = 0; i < count; i++) {
- TouchEvent.Pointer p = new TouchEvent.Pointer();
- p.x = ev.getX(i);
- p.y = ev.getY(i);
- p.size = ev.getSize(i);
- p.pressure = ev.getPressure(i);
- p.id = ev.getPointerId(i);
- proto.pointers[i] = p;
- }
- return proto;
- }
-
- public void setTouchArea(int width, int height) {
- mTouchAreaWidth = width;
- mTouchAreaHeight = height;
- }
-
- public int getResult() {
- return mResult;
- }
-
- public long getStartTimestampMillis() {
- return mStartTimestampMillis;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index 7c2673c31bf5..57ffdab50b25 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -310,7 +310,8 @@ public class AssistManager {
private void startVoiceInteractor(Bundle args) {
mAssistUtils.showSessionForActiveService(args,
- VoiceInteractionSession.SHOW_SOURCE_ASSIST_GESTURE, null, null);
+ VoiceInteractionSession.SHOW_SOURCE_ASSIST_GESTURE, mContext.getAttributionTag(),
+ null, null);
}
public void launchVoiceAssistFromKeyguard() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
index 0892612d1825..5ed898682883 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
@@ -16,12 +16,20 @@
package com.android.systemui.biometrics;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.view.WindowInsets.Type.ime;
+
import android.annotation.NonNull;
import android.content.Context;
+import android.graphics.Insets;
import android.os.UserHandle;
import android.text.InputType;
import android.util.AttributeSet;
import android.view.KeyEvent;
+import android.view.View;
+import android.view.View.OnApplyWindowInsetsListener;
+import android.view.ViewGroup;
+import android.view.WindowInsets;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.ImeAwareEditText;
@@ -31,18 +39,24 @@ import com.android.internal.widget.LockPatternChecker;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.VerifyCredentialResponse;
+import com.android.systemui.Dumpable;
import com.android.systemui.R;
+import java.io.PrintWriter;
+
/**
* Pin and Password UI
*/
public class AuthCredentialPasswordView extends AuthCredentialView
- implements TextView.OnEditorActionListener {
+ implements TextView.OnEditorActionListener, OnApplyWindowInsetsListener, Dumpable {
private static final String TAG = "BiometricPrompt/AuthCredentialPasswordView";
private final InputMethodManager mImm;
private ImeAwareEditText mPasswordField;
+ private ViewGroup mAuthCredentialHeader;
+ private ViewGroup mAuthCredentialInput;
+ private int mBottomInset = 0;
public AuthCredentialPasswordView(Context context,
AttributeSet attrs) {
@@ -53,6 +67,9 @@ public class AuthCredentialPasswordView extends AuthCredentialView
@Override
protected void onFinishInflate() {
super.onFinishInflate();
+
+ mAuthCredentialHeader = findViewById(R.id.auth_credential_header);
+ mAuthCredentialInput = findViewById(R.id.auth_credential_input);
mPasswordField = findViewById(R.id.lockPassword);
mPasswordField.setOnEditorActionListener(this);
// TODO: De-dupe the logic with AuthContainerView
@@ -66,6 +83,8 @@ public class AuthCredentialPasswordView extends AuthCredentialView
}
return true;
});
+
+ setOnApplyWindowInsetsListener(this);
}
@Override
@@ -127,4 +146,65 @@ public class AuthCredentialPasswordView extends AuthCredentialView
mPasswordField.setText("");
}
}
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+
+ if (mAuthCredentialInput == null || mAuthCredentialHeader == null
+ || mSubtitleView == null || mPasswordField == null || mErrorView == null) {
+ return;
+ }
+
+ // b/157910732 In AuthContainerView#getLayoutParams() we used to prevent jank risk when
+ // resizing by IME show or hide, we used to setFitInsetsTypes `~WindowInsets.Type.ime()` to
+ // LP. As a result this view needs to listen onApplyWindowInsets() and handle onLayout.
+ int inputLeftBound;
+ int inputTopBound;
+ int headerRightBound = right;
+ if (getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE) {
+ inputTopBound = (bottom - (mPasswordField.getHeight() + mErrorView.getHeight())) / 2;
+ inputLeftBound = (right - left) / 2;
+ headerRightBound = inputLeftBound;
+ } else {
+ inputTopBound = mSubtitleView.getBottom() + (bottom - mSubtitleView.getBottom()) / 2;
+ inputLeftBound = (right - left - mAuthCredentialInput.getWidth()) / 2;
+ }
+
+ mAuthCredentialHeader.layout(left, top, headerRightBound, bottom);
+ mAuthCredentialInput.layout(inputLeftBound, inputTopBound, right, bottom);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ final int newHeight = MeasureSpec.getSize(heightMeasureSpec) - mBottomInset;
+
+ setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), newHeight);
+
+ measureChildren(widthMeasureSpec,
+ MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.AT_MOST));
+ }
+
+ @NonNull
+ @Override
+ public WindowInsets onApplyWindowInsets(@NonNull View v, WindowInsets insets) {
+
+ final Insets bottomInset = insets.getInsets(ime());
+ if (v instanceof AuthCredentialPasswordView && mBottomInset != bottomInset.bottom) {
+ mBottomInset = bottomInset.bottom;
+ requestLayout();
+ }
+ return insets;
+ }
+
+ @Override
+ public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
+ pw.println(TAG + "State:");
+ pw.println(" mBottomInset=" + mBottomInset);
+ pw.println(" mAuthCredentialHeader size=(" + mAuthCredentialHeader.getWidth() + ","
+ + mAuthCredentialHeader.getHeight());
+ pw.println(" mAuthCredentialInput size=(" + mAuthCredentialInput.getWidth() + ","
+ + mAuthCredentialInput.getHeight());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
index 4fa835e038ec..d4176acf10f9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
@@ -87,7 +87,7 @@ public abstract class AuthCredentialView extends LinearLayout {
private boolean mShouldAnimateContents;
private TextView mTitleView;
- private TextView mSubtitleView;
+ protected TextView mSubtitleView;
private TextView mDescriptionView;
private ImageView mIconView;
protected TextView mErrorView;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
index fef7383634f0..1c574808e0e9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
@@ -75,12 +75,12 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
}
private var radius: Float = 0f
set(value) {
- rippleShader.radius = value
+ rippleShader.setMaxSize(value * 2f, value * 2f)
field = value
}
private var origin: PointF = PointF()
set(value) {
- rippleShader.origin = value
+ rippleShader.setCenter(value.x, value.y)
field = value
}
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
index 8292e52b1ffd..da675de2b9ec 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
@@ -19,7 +19,6 @@ package com.android.systemui.charging
import android.content.Context
import android.content.res.Configuration
import android.graphics.PixelFormat
-import android.graphics.PointF
import android.os.SystemProperties
import android.util.DisplayMetrics
import android.view.View
@@ -85,7 +84,7 @@ class WiredChargingRippleController @Inject constructor(
private var debounceLevel = 0
@VisibleForTesting
- var rippleView: RippleView = RippleView(context, attrs = null)
+ var rippleView: RippleView = RippleView(context, attrs = null).also { it.setupShader() }
init {
pluggedIn = batteryController.isPluggedIn
@@ -177,20 +176,25 @@ class WiredChargingRippleController @Inject constructor(
context.display.getRealMetrics(displayMetrics)
val width = displayMetrics.widthPixels
val height = displayMetrics.heightPixels
- rippleView.radius = Integer.max(width, height).toFloat()
- rippleView.origin = when (RotationUtils.getExactRotation(context)) {
+ val maxDiameter = Integer.max(width, height) * 2f
+ rippleView.setMaxSize(maxDiameter, maxDiameter)
+ when (RotationUtils.getExactRotation(context)) {
RotationUtils.ROTATION_LANDSCAPE -> {
- PointF(width * normalizedPortPosY, height * (1 - normalizedPortPosX))
+ rippleView.setCenter(
+ width * normalizedPortPosY, height * (1 - normalizedPortPosX))
}
RotationUtils.ROTATION_UPSIDE_DOWN -> {
- PointF(width * (1 - normalizedPortPosX), height * (1 - normalizedPortPosY))
+ rippleView.setCenter(
+ width * (1 - normalizedPortPosX), height * (1 - normalizedPortPosY))
}
RotationUtils.ROTATION_SEASCAPE -> {
- PointF(width * (1 - normalizedPortPosY), height * normalizedPortPosX)
+ rippleView.setCenter(
+ width * (1 - normalizedPortPosY), height * normalizedPortPosX)
}
else -> {
// ROTATION_NONE
- PointF(width * normalizedPortPosX, height * normalizedPortPosY)
+ rippleView.setCenter(
+ width * normalizedPortPosX, height * normalizedPortPosY)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
index 835025bbfc88..e82d0ea85490 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
@@ -16,8 +16,6 @@
package com.android.systemui.charging;
-import static com.android.systemui.charging.WirelessChargingLayout.UNKNOWN_BATTERY_LEVEL;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -32,13 +30,14 @@ import android.view.WindowManager;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.ripple.RippleShader.RippleShape;
/**
* A WirelessChargingAnimation is a view containing view + animation for wireless charging.
* @hide
*/
public class WirelessChargingAnimation {
-
+ public static final int UNKNOWN_BATTERY_LEVEL = -1;
public static final long DURATION = 1500;
private static final String TAG = "WirelessChargingView";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -58,11 +57,12 @@ public class WirelessChargingAnimation {
* before calling {@link #show} - can be done through {@link #makeWirelessChargingAnimation}.
* @hide
*/
- public WirelessChargingAnimation(@NonNull Context context, @Nullable Looper looper,
+ private WirelessChargingAnimation(@NonNull Context context, @Nullable Looper looper,
int transmittingBatteryLevel, int batteryLevel, Callback callback, boolean isDozing,
- UiEventLogger uiEventLogger) {
+ RippleShape rippleShape, UiEventLogger uiEventLogger) {
mCurrentWirelessChargingView = new WirelessChargingView(context, looper,
- transmittingBatteryLevel, batteryLevel, callback, isDozing, uiEventLogger);
+ transmittingBatteryLevel, batteryLevel, callback, isDozing,
+ rippleShape, uiEventLogger);
}
/**
@@ -72,9 +72,10 @@ public class WirelessChargingAnimation {
*/
public static WirelessChargingAnimation makeWirelessChargingAnimation(@NonNull Context context,
@Nullable Looper looper, int transmittingBatteryLevel, int batteryLevel,
- Callback callback, boolean isDozing, UiEventLogger uiEventLogger) {
+ Callback callback, boolean isDozing, RippleShape rippleShape,
+ UiEventLogger uiEventLogger) {
return new WirelessChargingAnimation(context, looper, transmittingBatteryLevel,
- batteryLevel, callback, isDozing, uiEventLogger);
+ batteryLevel, callback, isDozing, rippleShape, uiEventLogger);
}
/**
@@ -82,9 +83,10 @@ public class WirelessChargingAnimation {
* battery level without charging number shown.
*/
public static WirelessChargingAnimation makeChargingAnimationWithNoBatteryLevel(
- @NonNull Context context, UiEventLogger uiEventLogger) {
+ @NonNull Context context, RippleShape rippleShape, UiEventLogger uiEventLogger) {
return makeWirelessChargingAnimation(context, null,
- UNKNOWN_BATTERY_LEVEL, UNKNOWN_BATTERY_LEVEL, null, false, uiEventLogger);
+ UNKNOWN_BATTERY_LEVEL, UNKNOWN_BATTERY_LEVEL, null, false,
+ rippleShape, uiEventLogger);
}
/**
@@ -121,10 +123,10 @@ public class WirelessChargingAnimation {
public WirelessChargingView(Context context, @Nullable Looper looper,
int transmittingBatteryLevel, int batteryLevel, Callback callback,
- boolean isDozing, UiEventLogger uiEventLogger) {
+ boolean isDozing, RippleShape rippleShape, UiEventLogger uiEventLogger) {
mCallback = callback;
mNextView = new WirelessChargingLayout(context, transmittingBatteryLevel, batteryLevel,
- isDozing);
+ isDozing, rippleShape);
mGravity = Gravity.CENTER_HORIZONTAL | Gravity.CENTER;
mUiEventLogger = uiEventLogger;
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
index f6368ee19e8f..47ea27ff8ccb 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
@@ -21,7 +21,6 @@ import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Color;
-import android.graphics.PointF;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.ContextThemeWrapper;
@@ -34,6 +33,7 @@ import android.widget.TextView;
import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
+import com.android.systemui.ripple.RippleShader.RippleShape;
import com.android.systemui.ripple.RippleView;
import java.text.NumberFormat;
@@ -41,37 +41,36 @@ import java.text.NumberFormat;
/**
* @hide
*/
-public class WirelessChargingLayout extends FrameLayout {
- public static final int UNKNOWN_BATTERY_LEVEL = -1;
+final class WirelessChargingLayout extends FrameLayout {
private static final long RIPPLE_ANIMATION_DURATION = 1500;
private static final int SCRIM_COLOR = 0x4C000000;
private static final int SCRIM_FADE_DURATION = 300;
private RippleView mRippleView;
- public WirelessChargingLayout(Context context) {
+ WirelessChargingLayout(Context context, int transmittingBatteryLevel, int batteryLevel,
+ boolean isDozing, RippleShape rippleShape) {
super(context);
- init(context, null, false);
+ init(context, null, transmittingBatteryLevel, batteryLevel, isDozing, rippleShape);
}
- public WirelessChargingLayout(Context context, int transmittingBatteryLevel, int batteryLevel,
- boolean isDozing) {
+ private WirelessChargingLayout(Context context) {
super(context);
- init(context, null, transmittingBatteryLevel, batteryLevel, isDozing);
+ init(context, null, /* isDozing= */ false, RippleShape.CIRCLE);
}
- public WirelessChargingLayout(Context context, AttributeSet attrs) {
+ private WirelessChargingLayout(Context context, AttributeSet attrs) {
super(context, attrs);
- init(context, attrs, false);
+ init(context, attrs, /* isDozing= */false, RippleShape.CIRCLE);
}
- private void init(Context c, AttributeSet attrs, boolean isDozing) {
- init(c, attrs, -1, -1, false);
+ private void init(Context c, AttributeSet attrs, boolean isDozing, RippleShape rippleShape) {
+ init(c, attrs, -1, -1, isDozing, rippleShape);
}
private void init(Context context, AttributeSet attrs, int transmittingBatteryLevel,
- int batteryLevel, boolean isDozing) {
+ int batteryLevel, boolean isDozing, RippleShape rippleShape) {
final boolean showTransmittingBatteryLevel =
- (transmittingBatteryLevel != UNKNOWN_BATTERY_LEVEL);
+ (transmittingBatteryLevel != WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL);
// set style based on background
int style = R.style.ChargingAnim_WallpaperBackground;
@@ -84,7 +83,7 @@ public class WirelessChargingLayout extends FrameLayout {
// amount of battery:
final TextView percentage = findViewById(R.id.wireless_charging_percentage);
- if (batteryLevel != UNKNOWN_BATTERY_LEVEL) {
+ if (batteryLevel != WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL) {
percentage.setText(NumberFormat.getPercentInstance().format(batteryLevel / 100f));
percentage.setAlpha(0);
}
@@ -138,6 +137,7 @@ public class WirelessChargingLayout extends FrameLayout {
animatorSetScrim.start();
mRippleView = findViewById(R.id.wireless_charging_ripple);
+ mRippleView.setupShader(rippleShape);
OnAttachStateChangeListener listener = new OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View view) {
@@ -230,11 +230,15 @@ public class WirelessChargingLayout extends FrameLayout {
if (mRippleView != null) {
int width = getMeasuredWidth();
int height = getMeasuredHeight();
- mRippleView.setColor(
- Utils.getColorAttr(mRippleView.getContext(),
- android.R.attr.colorAccent).getDefaultColor());
- mRippleView.setOrigin(new PointF(width / 2, height / 2));
- mRippleView.setRadius(Math.max(width, height) * 0.5f);
+ mRippleView.setCenter(width * 0.5f, height * 0.5f);
+ if (mRippleView.getRippleShape() == RippleShape.ROUNDED_BOX) {
+ mRippleView.setMaxSize(width * 1.5f, height * 1.5f);
+ } else {
+ float maxSize = Math.max(width, height);
+ mRippleView.setMaxSize(maxSize, maxSize);
+ }
+ mRippleView.setColor(Utils.getColorAttr(mRippleView.getContext(),
+ android.R.attr.colorAccent).getDefaultColor());
}
super.onLayout(changed, left, top, right, bottom);
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index e16ac08c88f8..c21e36ab6ecc 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -97,6 +97,7 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.annotation.NonNull;
import androidx.core.view.ViewCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
@@ -132,7 +133,7 @@ public class ClipboardOverlayController {
private static final int FONT_SEARCH_STEP_PX = 4;
private final Context mContext;
- private final UiEventLogger mUiEventLogger;
+ private final ClipboardLogger mClipboardLogger;
private final BroadcastDispatcher mBroadcastDispatcher;
private final DisplayManager mDisplayManager;
private final DisplayMetrics mDisplayMetrics;
@@ -181,7 +182,7 @@ public class ClipboardOverlayController {
final Context displayContext = context.createDisplayContext(getDefaultDisplay());
mContext = displayContext.createWindowContext(TYPE_SCREENSHOT, null);
- mUiEventLogger = uiEventLogger;
+ mClipboardLogger = new ClipboardLogger(uiEventLogger);
mAccessibilityManager = AccessibilityManager.getInstance(mContext);
mTextClassifier = requireNonNull(context.getSystemService(TextClassificationManager.class))
@@ -231,7 +232,7 @@ public class ClipboardOverlayController {
@Override
public void onSwipeDismissInitiated(Animator animator) {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_SWIPE_DISMISSED);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_SWIPE_DISMISSED);
mExitAnimator = animator;
}
@@ -249,7 +250,7 @@ public class ClipboardOverlayController {
});
mDismissButton.setOnClickListener(view -> {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_DISMISS_TAPPED);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISS_TAPPED);
animateOut();
});
@@ -285,7 +286,8 @@ public class ClipboardOverlayController {
int newDisplayId) {
if (mContext.getResources().getConfiguration().orientation
!= mOrientation) {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
+ mClipboardLogger.logSessionComplete(
+ CLIPBOARD_OVERLAY_DISMISSED_OTHER);
hideImmediate();
}
}
@@ -300,7 +302,7 @@ public class ClipboardOverlayController {
});
mTimeoutHandler.setOnTimeoutRunnable(() -> {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_TIMED_OUT);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_TIMED_OUT);
animateOut();
});
@@ -308,7 +310,7 @@ public class ClipboardOverlayController {
@Override
public void onReceive(Context context, Intent intent) {
if (ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
animateOut();
}
}
@@ -320,7 +322,7 @@ public class ClipboardOverlayController {
@Override
public void onReceive(Context context, Intent intent) {
if (SCREENSHOT_ACTION.equals(intent.getAction())) {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
animateOut();
}
}
@@ -390,7 +392,7 @@ public class ClipboardOverlayController {
mContext.getString(R.string.clipboard_send_nearby_description));
mRemoteCopyChip.setVisibility(View.VISIBLE);
mRemoteCopyChip.setOnClickListener((v) -> {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_REMOTE_COPY_TAPPED);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_REMOTE_COPY_TAPPED);
mContext.startActivity(remoteCopyIntent);
animateOut();
});
@@ -450,7 +452,7 @@ public class ClipboardOverlayController {
chip.setContentDescription(action.getTitle());
chip.setIcon(action.getIcon(), false);
chip.setPendingIntent(action.getActionIntent(), () -> {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_ACTION_TAPPED);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_ACTION_TAPPED);
animateOut();
});
chip.setAlpha(1);
@@ -486,7 +488,7 @@ public class ClipboardOverlayController {
touchRegion.op(tmpRect, Region.Op.UNION);
if (!touchRegion.contains(
(int) motionEvent.getRawX(), (int) motionEvent.getRawY())) {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_TAP_OUTSIDE);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_TAP_OUTSIDE);
animateOut();
}
}
@@ -497,7 +499,7 @@ public class ClipboardOverlayController {
}
private void editImage(Uri uri) {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_EDIT_TAPPED);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
String editorPackage = mContext.getString(R.string.config_screenshotEditor);
Intent editIntent = new Intent(Intent.ACTION_EDIT);
if (!TextUtils.isEmpty(editorPackage)) {
@@ -512,7 +514,7 @@ public class ClipboardOverlayController {
}
private void editText() {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_EDIT_TAPPED);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
Intent editIntent = new Intent(mContext, EditTextActivity.class);
editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
mContext.startActivity(editIntent);
@@ -520,13 +522,15 @@ public class ClipboardOverlayController {
}
private void shareContent(ClipData clip) {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_SHARE_TAPPED);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_SHARE_TAPPED);
Intent shareIntent = new Intent(Intent.ACTION_SEND);
- shareIntent.putExtra(Intent.EXTRA_TEXT, clip.getItemAt(0).getText().toString());
shareIntent.setDataAndType(
clip.getItemAt(0).getUri(), clip.getDescription().getMimeType(0));
- shareIntent.putExtra(Intent.EXTRA_STREAM, clip.getItemAt(0).getUri());
- shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ shareIntent.putExtra(Intent.EXTRA_TEXT, clip.getItemAt(0).getText().toString());
+ if (clip.getItemAt(0).getUri() != null) {
+ shareIntent.putExtra(Intent.EXTRA_STREAM, clip.getItemAt(0).getUri());
+ shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ }
Intent chooserIntent = Intent.createChooser(shareIntent, null)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
@@ -864,6 +868,7 @@ public class ClipboardOverlayController {
mRemoteCopyChip.setVisibility(View.GONE);
resetActionChips();
mTimeoutHandler.cancelTimeout();
+ mClipboardLogger.reset();
}
@MainThread
@@ -969,4 +974,24 @@ public class ClipboardOverlayController {
mWindowManager.updateViewLayout(decorView, mWindowLayoutParams);
}
}
+
+ static class ClipboardLogger {
+ private final UiEventLogger mUiEventLogger;
+ private boolean mGuarded = false;
+
+ ClipboardLogger(UiEventLogger uiEventLogger) {
+ mUiEventLogger = uiEventLogger;
+ }
+
+ void logSessionComplete(@NonNull UiEventLogger.UiEventEnum event) {
+ if (!mGuarded) {
+ mGuarded = true;
+ mUiEventLogger.log(event);
+ }
+ }
+
+ void reset() {
+ mGuarded = false;
+ }
+ }
}
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 de7bf28c01d6..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;
@@ -215,16 +230,15 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
final boolean cameraBlocked = mSensorPrivacyController
.isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA);
@DreamOverlayStatusBarView.StatusIconType int iconType = Resources.ID_NULL;
- if (micBlocked && cameraBlocked) {
- iconType = DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED;
- } else if (!micBlocked && cameraBlocked) {
- iconType = DreamOverlayStatusBarView.STATUS_ICON_CAMERA_DISABLED;
- } else if (micBlocked && !cameraBlocked) {
- iconType = DreamOverlayStatusBarView.STATUS_ICON_MIC_DISABLED;
- }
- if (iconType != Resources.ID_NULL) {
- showIcon(iconType, true);
- }
+ showIcon(
+ DreamOverlayStatusBarView.STATUS_ICON_CAMERA_DISABLED,
+ !micBlocked && cameraBlocked);
+ showIcon(
+ DreamOverlayStatusBarView.STATUS_ICON_MIC_DISABLED,
+ micBlocked && !cameraBlocked);
+ showIcon(
+ DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED,
+ micBlocked && cameraBlocked);
}
private String buildNotificationsContentDescription(int notificationCount) {
@@ -272,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/dreams/complication/Complication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java
index 54571448c981..29bb2f42cca5 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java
@@ -163,7 +163,8 @@ public interface Complication {
COMPLICATION_TYPE_WEATHER,
COMPLICATION_TYPE_AIR_QUALITY,
COMPLICATION_TYPE_CAST_INFO,
- COMPLICATION_TYPE_HOME_CONTROLS
+ COMPLICATION_TYPE_HOME_CONTROLS,
+ COMPLICATION_TYPE_SMARTSPACE
})
@Retention(RetentionPolicy.SOURCE)
@interface ComplicationType {}
@@ -175,6 +176,7 @@ public interface Complication {
int COMPLICATION_TYPE_AIR_QUALITY = 1 << 3;
int COMPLICATION_TYPE_CAST_INFO = 1 << 4;
int COMPLICATION_TYPE_HOME_CONTROLS = 1 << 5;
+ int COMPLICATION_TYPE_SMARTSPACE = 1 << 6;
/**
* The {@link Host} interface specifies a way a {@link Complication} to communicate with its
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java
index dcab90fe7ab9..d5db63dc9093 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java
@@ -21,6 +21,7 @@ import static com.android.systemui.dreams.complication.Complication.COMPLICATION
import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_DATE;
import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_HOME_CONTROLS;
import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_NONE;
+import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_SMARTSPACE;
import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_TIME;
import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_WEATHER;
@@ -51,6 +52,8 @@ public class ComplicationUtils {
return COMPLICATION_TYPE_CAST_INFO;
case DreamBackend.COMPLICATION_TYPE_HOME_CONTROLS:
return COMPLICATION_TYPE_HOME_CONTROLS;
+ case DreamBackend.COMPLICATION_TYPE_SMARTSPACE:
+ return COMPLICATION_TYPE_SMARTSPACE;
default:
return COMPLICATION_TYPE_NONE;
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java
index ac6edba6b3fa..567bdbc01170 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java
@@ -52,6 +52,11 @@ public class SmartSpaceComplication implements Complication {
return mViewHolderProvider.get();
}
+ @Override
+ public int getRequiredTypeAvailability() {
+ return COMPLICATION_TYPE_SMARTSPACE;
+ }
+
/**
* {@link CoreStartable} responsbile for registering {@link SmartSpaceComplication} with
* SystemUI.
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
index 2cee2520ce55..dfa3bcda7d72 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
@@ -23,7 +23,10 @@ package com.android.systemui.flags
*/
interface FeatureFlags : FlagListenable {
/** Returns a boolean value for the given flag. */
- fun isEnabled(flag: BooleanFlag): Boolean
+ fun isEnabled(flag: UnreleasedFlag): Boolean
+
+ /** Returns a boolean value for the given flag. */
+ fun isEnabled(flag: ReleasedFlag): Boolean
/** Returns a boolean value for the given flag. */
fun isEnabled(flag: ResourceBooleanFlag): Boolean
@@ -39,4 +42,4 @@ interface FeatureFlags : FlagListenable {
/** Returns a string value for the given flag. */
fun getString(flag: ResourceStringFlag): String
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
index f804325e5aec..b4b87952980b 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
@@ -48,6 +48,8 @@ import com.android.systemui.statusbar.commandline.CommandRegistry;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.settings.SecureSettings;
+import org.jetbrains.annotations.NotNull;
+
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.util.ArrayList;
@@ -84,6 +86,7 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
private final Resources mResources;
private final SystemPropertiesHelper mSystemProperties;
private final DeviceConfigProxy mDeviceConfigProxy;
+ private final ServerFlagReader mServerFlagReader;
private final Map<Integer, Flag<?>> mAllFlags;
private final Map<Integer, Boolean> mBooleanFlagCache = new TreeMap<>();
private final Map<Integer, String> mStringFlagCache = new TreeMap<>();
@@ -98,6 +101,7 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
@Main Resources resources,
DumpManager dumpManager,
DeviceConfigProxy deviceConfigProxy,
+ ServerFlagReader serverFlagReader,
@Named(ALL_FLAGS) Map<Integer, Flag<?>> allFlags,
CommandRegistry commandRegistry,
IStatusBarService barService) {
@@ -106,6 +110,7 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
mResources = resources;
mSystemProperties = systemProperties;
mDeviceConfigProxy = deviceConfigProxy;
+ mServerFlagReader = serverFlagReader;
mAllFlags = allFlags;
mBarService = barService;
@@ -121,7 +126,16 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
}
@Override
- public boolean isEnabled(@NonNull BooleanFlag flag) {
+ public boolean isEnabled(@NotNull UnreleasedFlag flag) {
+ return isEnabledInternal(flag);
+ }
+
+ @Override
+ public boolean isEnabled(@NotNull ReleasedFlag flag) {
+ return isEnabledInternal(flag);
+ }
+
+ private boolean isEnabledInternal(@NotNull BooleanFlag flag) {
int id = flag.getId();
if (!mBooleanFlagCache.containsKey(id)) {
mBooleanFlagCache.put(id,
@@ -198,14 +212,17 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
/** Specific override for Boolean flags that checks against the teamfood list.*/
private boolean readFlagValue(int id, boolean defaultValue) {
Boolean result = readBooleanFlagOverride(id);
- // Only check for teamfood if the default is false.
- if (!defaultValue && result == null && id != Flags.TEAMFOOD.getId()) {
+ boolean hasServerOverride = mServerFlagReader.hasOverride(id);
+
+ // Only check for teamfood if the default is false
+ // and there is no server override.
+ if (!hasServerOverride && !defaultValue && result == null && id != Flags.TEAMFOOD.getId()) {
if (mAllFlags.containsKey(id) && mAllFlags.get(id).getTeamfood()) {
return isEnabled(Flags.TEAMFOOD);
}
}
- return result == null ? defaultValue : result;
+ return result == null ? mServerFlagReader.readServerOverride(id, defaultValue) : result;
}
private Boolean readBooleanFlagOverride(int id) {
@@ -410,36 +427,38 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
*/
@Nullable
private ParcelableFlag<?> toParcelableFlag(Flag<?> f) {
- if (f instanceof BooleanFlag) {
- return new BooleanFlag(
- f.getId(),
- isEnabled((BooleanFlag) f),
- f.getTeamfood(),
- readBooleanFlagOverride(f.getId()) != null);
- }
- if (f instanceof ResourceBooleanFlag) {
- return new BooleanFlag(
- f.getId(),
- isEnabled((ResourceBooleanFlag) f),
- f.getTeamfood(),
- readBooleanFlagOverride(f.getId()) != null);
- }
- if (f instanceof DeviceConfigBooleanFlag) {
- return new BooleanFlag(
- f.getId(), isEnabled((DeviceConfigBooleanFlag) f), f.getTeamfood());
- }
- if (f instanceof SysPropBooleanFlag) {
+ boolean enabled;
+ boolean teamfood = f.getTeamfood();
+ boolean overridden;
+
+ if (f instanceof ReleasedFlag) {
+ enabled = isEnabled((ReleasedFlag) f);
+ overridden = readBooleanFlagOverride(f.getId()) != null;
+ } else if (f instanceof UnreleasedFlag) {
+ enabled = isEnabled((UnreleasedFlag) f);
+ overridden = readBooleanFlagOverride(f.getId()) != null;
+ } else if (f instanceof ResourceBooleanFlag) {
+ enabled = isEnabled((ResourceBooleanFlag) f);
+ overridden = readBooleanFlagOverride(f.getId()) != null;
+ } else if (f instanceof DeviceConfigBooleanFlag) {
+ enabled = isEnabled((DeviceConfigBooleanFlag) f);
+ overridden = false;
+ } else if (f instanceof SysPropBooleanFlag) {
// TODO(b/223379190): Teamfood not supported for sysprop flags yet.
- return new BooleanFlag(
- f.getId(),
- ((SysPropBooleanFlag) f).getDefault(),
- false,
- !mSystemProperties.get(((SysPropBooleanFlag) f).getName()).isEmpty());
+ enabled = isEnabled((SysPropBooleanFlag) f);
+ teamfood = false;
+ overridden = !mSystemProperties.get(((SysPropBooleanFlag) f).getName()).isEmpty();
+ } else {
+ // TODO: add support for other flag types.
+ Log.w(TAG, "Unsupported Flag Type. Please file a bug.");
+ return null;
}
- // TODO: add support for other flag types.
- Log.w(TAG, "Unsupported Flag Type. Please file a bug.");
- return null;
+ if (enabled) {
+ return new ReleasedFlag(f.getId(), teamfood, overridden);
+ } else {
+ return new UnreleasedFlag(f.getId(), teamfood, overridden);
+ }
}
};
@@ -540,8 +559,10 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
}
private boolean isBooleanFlagEnabled(Flag<?> flag) {
- if (flag instanceof BooleanFlag) {
- return isEnabled((BooleanFlag) flag);
+ if (flag instanceof ReleasedFlag) {
+ return isEnabled((ReleasedFlag) flag);
+ } else if (flag instanceof UnreleasedFlag) {
+ return isEnabled((UnreleasedFlag) flag);
} else if (flag instanceof ResourceBooleanFlag) {
return isEnabled((ResourceBooleanFlag) flag);
} else if (flag instanceof SysPropFlag) {
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
index 1492a2bc0ceb..049b17d383a2 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
@@ -19,7 +19,6 @@ package com.android.systemui.flags;
import static java.util.Objects.requireNonNull;
import android.content.res.Resources;
-import android.provider.DeviceConfig;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -31,6 +30,8 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.util.DeviceConfigProxy;
+import org.jetbrains.annotations.NotNull;
+
import java.io.PrintWriter;
import java.util.Map;
@@ -47,18 +48,22 @@ public class FeatureFlagsRelease implements FeatureFlags, Dumpable {
private final Resources mResources;
private final SystemPropertiesHelper mSystemProperties;
private final DeviceConfigProxy mDeviceConfigProxy;
+ private final ServerFlagReader mServerFlagReader;
SparseBooleanArray mBooleanCache = new SparseBooleanArray();
SparseArray<String> mStringCache = new SparseArray<>();
+ private boolean mInited;
@Inject
public FeatureFlagsRelease(
@Main Resources resources,
SystemPropertiesHelper systemProperties,
DeviceConfigProxy deviceConfigProxy,
+ ServerFlagReader serverFlagReader,
DumpManager dumpManager) {
mResources = resources;
mSystemProperties = systemProperties;
mDeviceConfigProxy = deviceConfigProxy;
+ mServerFlagReader = serverFlagReader;
dumpManager.registerDumpable("SysUIFlags", this);
}
@@ -69,8 +74,13 @@ public class FeatureFlagsRelease implements FeatureFlags, Dumpable {
public void removeListener(@NonNull Listener listener) {}
@Override
- public boolean isEnabled(BooleanFlag flag) {
- return flag.getDefault();
+ public boolean isEnabled(@NotNull UnreleasedFlag flag) {
+ return false;
+ }
+
+ @Override
+ public boolean isEnabled(@NotNull ReleasedFlag flag) {
+ return mServerFlagReader.readServerOverride(flag.getId(), true);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 3d40e673a4b0..6eb77bd32a71 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -32,7 +32,7 @@ import java.util.Map;
*
* Flag Ids are integers.
* Ids must be unique. This is enforced in a unit test.
- * Ids need not be sequential. Flags can "claim" a chunk of ids for flags in related featurs with
+ * Ids need not be sequential. Flags can "claim" a chunk of ids for flags in related features with
* a comment. This is purely for organizational purposes.
*
* On public release builds, flags will always return their default value. There is no way to
@@ -41,30 +41,30 @@ import java.util.Map;
* See {@link FeatureFlagsDebug} for instructions on flipping the flags via adb.
*/
public class Flags {
- public static final BooleanFlag TEAMFOOD = new BooleanFlag(1, false);
+ public static final UnreleasedFlag TEAMFOOD = new UnreleasedFlag(1);
/***************************************/
// 100 - notification
- public static final BooleanFlag NOTIFICATION_PIPELINE_DEVELOPER_LOGGING =
- new BooleanFlag(103, false);
+ public static final UnreleasedFlag NOTIFICATION_PIPELINE_DEVELOPER_LOGGING =
+ new UnreleasedFlag(103);
- public static final BooleanFlag NSSL_DEBUG_LINES =
- new BooleanFlag(105, false);
+ public static final UnreleasedFlag NSSL_DEBUG_LINES =
+ new UnreleasedFlag(105);
- public static final BooleanFlag NSSL_DEBUG_REMOVE_ANIMATION =
- new BooleanFlag(106, false);
+ public static final UnreleasedFlag NSSL_DEBUG_REMOVE_ANIMATION =
+ new UnreleasedFlag(106);
- public static final BooleanFlag NEW_PIPELINE_CRASH_ON_CALL_TO_OLD_PIPELINE =
- new BooleanFlag(107, false);
+ public static final UnreleasedFlag NEW_PIPELINE_CRASH_ON_CALL_TO_OLD_PIPELINE =
+ new UnreleasedFlag(107);
public static final ResourceBooleanFlag NOTIFICATION_DRAG_TO_CONTENTS =
new ResourceBooleanFlag(108, R.bool.config_notificationToContents);
- public static final BooleanFlag REMOVE_UNRANKED_NOTIFICATIONS =
- new BooleanFlag(109, false, true);
+ public static final UnreleasedFlag REMOVE_UNRANKED_NOTIFICATIONS =
+ new UnreleasedFlag(109, true);
- public static final BooleanFlag FSI_REQUIRES_KEYGUARD =
- new BooleanFlag(110, false, true);
+ public static final UnreleasedFlag FSI_REQUIRES_KEYGUARD =
+ new UnreleasedFlag(110, true);
/***************************************/
// 200 - keyguard/lockscreen
@@ -73,11 +73,11 @@ public class Flags {
// public static final BooleanFlag KEYGUARD_LAYOUT =
// new BooleanFlag(200, true);
- public static final BooleanFlag LOCKSCREEN_ANIMATIONS =
- new BooleanFlag(201, true);
+ public static final ReleasedFlag LOCKSCREEN_ANIMATIONS =
+ new ReleasedFlag(201);
- public static final BooleanFlag NEW_UNLOCK_SWIPE_ANIMATION =
- new BooleanFlag(202, true);
+ public static final ReleasedFlag NEW_UNLOCK_SWIPE_ANIMATION =
+ new ReleasedFlag(202);
public static final ResourceBooleanFlag CHARGING_RIPPLE =
new ResourceBooleanFlag(203, R.bool.flag_charging_ripple);
@@ -92,25 +92,28 @@ public class Flags {
* Whether the KeyguardBottomArea(View|Controller) should use the modern architecture or the old
* one.
*/
- public static final BooleanFlag MODERN_BOTTOM_AREA = new BooleanFlag(
- 206,
- /* default= */ false,
- /* teamfood= */ true);
+ public static final UnreleasedFlag MODERN_BOTTOM_AREA = new UnreleasedFlag(206, true);
- public static final BooleanFlag LOCKSCREEN_CUSTOM_CLOCKS = new BooleanFlag(207, false);
+ public static final UnreleasedFlag LOCKSCREEN_CUSTOM_CLOCKS = new UnreleasedFlag(207);
+
+ /**
+ * Flag to enable the usage of the new bouncer data source. This is a refactor of and
+ * eventual replacement of KeyguardBouncer.java.
+ */
+ public static final ReleasedFlag MODERN_BOUNCER = new ReleasedFlag(208);
/***************************************/
// 300 - power menu
- public static final BooleanFlag POWER_MENU_LITE =
- new BooleanFlag(300, true);
+ public static final ReleasedFlag POWER_MENU_LITE =
+ new ReleasedFlag(300);
/***************************************/
// 400 - smartspace
- public static final BooleanFlag SMARTSPACE_DEDUPING =
- new BooleanFlag(400, true);
+ public static final ReleasedFlag SMARTSPACE_DEDUPING =
+ new ReleasedFlag(400);
- public static final BooleanFlag SMARTSPACE_SHARED_ELEMENT_TRANSITION_ENABLED =
- new BooleanFlag(401, true);
+ public static final ReleasedFlag SMARTSPACE_SHARED_ELEMENT_TRANSITION_ENABLED =
+ new ReleasedFlag(401);
public static final ResourceBooleanFlag SMARTSPACE =
new ResourceBooleanFlag(402, R.bool.flag_smartspace);
@@ -121,11 +124,11 @@ public class Flags {
* @deprecated Not needed anymore
*/
@Deprecated
- public static final BooleanFlag NEW_USER_SWITCHER =
- new BooleanFlag(500, true);
+ public static final ReleasedFlag NEW_USER_SWITCHER =
+ new ReleasedFlag(500);
- public static final BooleanFlag COMBINED_QS_HEADERS =
- new BooleanFlag(501, false, true);
+ public static final UnreleasedFlag COMBINED_QS_HEADERS =
+ new UnreleasedFlag(501, true);
public static final ResourceBooleanFlag PEOPLE_TILE =
new ResourceBooleanFlag(502, R.bool.flag_conversations);
@@ -137,35 +140,32 @@ public class Flags {
* @deprecated Not needed anymore
*/
@Deprecated
- public static final BooleanFlag NEW_FOOTER = new BooleanFlag(504, true);
+ public static final ReleasedFlag NEW_FOOTER = new ReleasedFlag(504);
- public static final BooleanFlag NEW_HEADER = new BooleanFlag(505, false, true);
+ public static final UnreleasedFlag NEW_HEADER = new UnreleasedFlag(505, true);
public static final ResourceBooleanFlag FULL_SCREEN_USER_SWITCHER =
new ResourceBooleanFlag(506, R.bool.config_enableFullscreenUserSwitcher);
/***************************************/
// 600- status bar
- public static final BooleanFlag COMBINED_STATUS_BAR_SIGNAL_ICONS =
- new BooleanFlag(601, false);
-
public static final ResourceBooleanFlag STATUS_BAR_USER_SWITCHER =
new ResourceBooleanFlag(602, R.bool.flag_user_switcher_chip);
- public static final BooleanFlag STATUS_BAR_LETTERBOX_APPEARANCE =
- new BooleanFlag(603, false);
+ public static final UnreleasedFlag STATUS_BAR_LETTERBOX_APPEARANCE =
+ new UnreleasedFlag(603, false);
- public static final BooleanFlag NEW_STATUS_BAR_PIPELINE = new BooleanFlag(604, false);
+ public static final UnreleasedFlag NEW_STATUS_BAR_PIPELINE = new UnreleasedFlag(604, true);
/***************************************/
// 700 - dialer/calls
- public static final BooleanFlag ONGOING_CALL_STATUS_BAR_CHIP =
- new BooleanFlag(700, true);
+ public static final ReleasedFlag ONGOING_CALL_STATUS_BAR_CHIP =
+ new ReleasedFlag(700);
- public static final BooleanFlag ONGOING_CALL_IN_IMMERSIVE =
- new BooleanFlag(701, true);
+ public static final ReleasedFlag ONGOING_CALL_IN_IMMERSIVE =
+ new ReleasedFlag(701);
- public static final BooleanFlag ONGOING_CALL_IN_IMMERSIVE_CHIP_TAP =
- new BooleanFlag(702, true);
+ public static final ReleasedFlag ONGOING_CALL_IN_IMMERSIVE_CHIP_TAP =
+ new ReleasedFlag(702);
/***************************************/
// 800 - general visual/theme
@@ -174,21 +174,21 @@ public class Flags {
/***************************************/
// 801 - region sampling
- public static final BooleanFlag REGION_SAMPLING =
- new BooleanFlag(801, false);
+ public static final UnreleasedFlag REGION_SAMPLING = new UnreleasedFlag(801);
/***************************************/
// 900 - media
- public static final BooleanFlag MEDIA_TAP_TO_TRANSFER = new BooleanFlag(900, true);
- public static final BooleanFlag MEDIA_SESSION_ACTIONS = new BooleanFlag(901, false);
- public static final BooleanFlag MEDIA_NEARBY_DEVICES = new BooleanFlag(903, true);
- public static final BooleanFlag MEDIA_MUTE_AWAIT = new BooleanFlag(904, true);
+ public static final ReleasedFlag MEDIA_TAP_TO_TRANSFER = new ReleasedFlag(900);
+ public static final UnreleasedFlag MEDIA_SESSION_ACTIONS = new UnreleasedFlag(901);
+ public static final ReleasedFlag MEDIA_NEARBY_DEVICES = new ReleasedFlag(903);
+ public static final ReleasedFlag MEDIA_MUTE_AWAIT = new ReleasedFlag(904);
// 1000 - dock
- public static final BooleanFlag SIMULATE_DOCK_THROUGH_CHARGING =
- new BooleanFlag(1000, true);
- public static final BooleanFlag DOCK_SETUP_ENABLED = new BooleanFlag(1001, true);
+ public static final ReleasedFlag SIMULATE_DOCK_THROUGH_CHARGING =
+ new ReleasedFlag(1000);
+ public static final ReleasedFlag DOCK_SETUP_ENABLED = new ReleasedFlag(1001);
+ public static final UnreleasedFlag ROUNDED_BOX_RIPPLE = new UnreleasedFlag(1002, false);
// 1100 - windowing
@Keep
@@ -211,6 +211,14 @@ public class Flags {
public static final SysPropBooleanFlag HIDE_NAVBAR_WINDOW =
new SysPropBooleanFlag(1103, "persist.wm.debug.hide_navbar_window", false);
+ @Keep
+ public static final SysPropBooleanFlag WM_DESKTOP_WINDOWING =
+ new SysPropBooleanFlag(1104, "persist.wm.debug.desktop_mode", false);
+
+ @Keep
+ public static final SysPropBooleanFlag WM_CAPTION_ON_SHELL =
+ new SysPropBooleanFlag(1105, "persist.wm.debug.caption_on_shell", false);
+
// 1200 - predictive back
@Keep
public static final SysPropBooleanFlag WM_ENABLE_PREDICTIVE_BACK = new SysPropBooleanFlag(
@@ -222,8 +230,12 @@ public class Flags {
public static final SysPropBooleanFlag WM_ALWAYS_ENFORCE_PREDICTIVE_BACK =
new SysPropBooleanFlag(1202, "persist.wm.debug.predictive_back_always_enforce", false);
- public static final BooleanFlag NEW_BACK_AFFORDANCE =
- new BooleanFlag(1203, false /* default */, true /* teamfood */);
+ public static final UnreleasedFlag NEW_BACK_AFFORDANCE =
+ new UnreleasedFlag(1203, false /* teamfood */);
+
+ // 1300 - screenshots
+
+ public static final UnreleasedFlag SCREENSHOT_REQUEST_PROCESSOR = new UnreleasedFlag(1300);
// Pay no attention to the reflection behind the curtain.
// ========================== Curtain ==========================
diff --git a/packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt b/packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt
new file mode 100644
index 000000000000..fc5b9f4eea05
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt
@@ -0,0 +1,80 @@
+/*
+ * 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.flags
+
+import com.android.systemui.util.DeviceConfigProxy
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
+
+interface ServerFlagReader {
+ /** Returns true if there is a server-side setting stored. */
+ fun hasOverride(flagId: Int): Boolean
+
+ /** Returns any stored server-side setting or the default if not set. */
+ fun readServerOverride(flagId: Int, default: Boolean): Boolean
+}
+
+class ServerFlagReaderImpl @Inject constructor(
+ private val deviceConfig: DeviceConfigProxy
+) : ServerFlagReader {
+ override fun hasOverride(flagId: Int): Boolean =
+ deviceConfig.getProperty(
+ SYSUI_NAMESPACE,
+ getServerOverrideName(flagId)
+ ) != null
+
+ override fun readServerOverride(flagId: Int, default: Boolean): Boolean {
+ return deviceConfig.getBoolean(
+ SYSUI_NAMESPACE,
+ getServerOverrideName(flagId),
+ default
+ )
+ }
+
+ private fun getServerOverrideName(flagId: Int): String {
+ return "flag_override_$flagId"
+ }
+}
+
+private val SYSUI_NAMESPACE = "systemui"
+
+@Module
+interface ServerFlagReaderModule {
+ @Binds
+ fun bindsReader(impl: ServerFlagReaderImpl): ServerFlagReader
+}
+
+class ServerFlagReaderFake : ServerFlagReader {
+ private val flagMap: MutableMap<Int, Boolean> = mutableMapOf()
+
+ override fun hasOverride(flagId: Int): Boolean {
+ return flagMap.containsKey(flagId)
+ }
+
+ override fun readServerOverride(flagId: Int, default: Boolean): Boolean {
+ return flagMap.getOrDefault(flagId, default)
+ }
+
+ fun setFlagValue(flagId: Int, value: Boolean) {
+ flagMap.put(flagId, value)
+ }
+
+ fun eraseFlag(flagId: Int) {
+ flagMap.remove(flagId)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt
index 044a57ced3fc..0a55294dfe8a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt
@@ -41,4 +41,12 @@ class LifecycleScreenStatusProvider @Inject constructor(screenLifecycle: ScreenL
override fun onScreenTurnedOn() {
listeners.forEach(ScreenListener::onScreenTurnedOn)
}
+
+ override fun onScreenTurningOff() {
+ listeners.forEach(ScreenListener::onScreenTurningOff)
+ }
+
+ override fun onScreenTurningOn(ignored: Runnable) {
+ listeners.forEach(ScreenListener::onScreenTurningOn)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
index 3202ecb9a287..df44957ec591 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
@@ -36,6 +36,7 @@ import com.android.systemui.util.kotlin.getOrNull
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
/** Home controls quick affordance data source. */
@@ -50,7 +51,13 @@ constructor(
private val appContext = context.applicationContext
override val state: Flow<KeyguardQuickAffordanceConfig.State> =
- stateInternal(component.getControlsListingController().getOrNull())
+ component.canShowWhileLockedSetting.flatMapLatest { canShowWhileLocked ->
+ if (canShowWhileLocked) {
+ stateInternal(component.getControlsListingController().getOrNull())
+ } else {
+ flowOf(KeyguardQuickAffordanceConfig.State.Hidden)
+ }
+ }
override fun onQuickAffordanceClicked(
animationController: ActivityLaunchAnimator.Controller?,
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
index dc23684dd517..6124e10144f2 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
@@ -22,17 +22,11 @@ import com.android.systemui.log.dagger.LogModule
import com.android.systemui.util.collection.RingBuffer
import com.google.errorprone.annotations.CompileTimeConstant
import java.io.PrintWriter
-import java.text.SimpleDateFormat
-import java.util.Arrays.stream
-import java.util.Locale
import java.util.concurrent.ArrayBlockingQueue
import java.util.concurrent.BlockingQueue
import kotlin.concurrent.thread
import kotlin.math.max
-const val UNBOUNDED_STACK_TRACE = -1
-const val NESTED_TRACE_DEPTH = 10
-
/**
* A simple ring buffer of recyclable log messages
*
@@ -74,18 +68,12 @@ const val NESTED_TRACE_DEPTH = 10
* @param maxSize The maximum number of messages to keep in memory at any one time. Buffers start
* out empty and grow up to [maxSize] as new messages are logged. Once the buffer's size reaches
* the maximum, it behaves like a ring buffer.
- * @param rootStackTraceDepth The number of stack trace elements to be logged for an exception when
- * the logBuffer is dumped. Defaulted to -1 [UNBOUNDED_STACK_TRACE] to print the entire stack trace.
- * @param nestedStackTraceDepth The number of stack trace elements to be logged for any nested
- * exceptions present in [Throwable.cause] or [Throwable.suppressedExceptions].
*/
class LogBuffer @JvmOverloads constructor(
private val name: String,
private val maxSize: Int,
private val logcatEchoTracker: LogcatEchoTracker,
private val systrace: Boolean = true,
- private val rootStackTraceDepth: Int = UNBOUNDED_STACK_TRACE,
- private val nestedStackTraceDepth: Int = NESTED_TRACE_DEPTH,
) {
private val buffer = RingBuffer(maxSize) { LogMessageImpl.create() }
@@ -236,7 +224,7 @@ class LogBuffer @JvmOverloads constructor(
val iterationStart = if (tailLength <= 0) { 0 } else { max(0, buffer.size - tailLength) }
for (i in iterationStart until buffer.size) {
- dumpMessage(buffer[i], pw)
+ buffer[i].dump(pw)
}
}
@@ -264,76 +252,6 @@ class LogBuffer @JvmOverloads constructor(
}
}
- private fun dumpMessage(
- message: LogMessage,
- pw: PrintWriter
- ) {
- val formattedTimestamp = DATE_FORMAT.format(message.timestamp)
- val shortLevel = message.level.shortString
- val messageToPrint = message.messagePrinter(message)
- val tag = message.tag
- printLikeLogcat(pw, formattedTimestamp, shortLevel, tag, messageToPrint)
- message.exception?.let { ex ->
- printException(
- pw,
- formattedTimestamp,
- shortLevel,
- ex,
- tag,
- stackTraceDepth = rootStackTraceDepth)
- }
- }
-
- private fun printException(
- pw: PrintWriter,
- timestamp: String,
- level: String,
- exception: Throwable,
- tag: String,
- exceptionMessagePrefix: String = "",
- stackTraceDepth: Int = UNBOUNDED_STACK_TRACE
- ) {
- val message = "$exceptionMessagePrefix$exception"
- printLikeLogcat(pw, timestamp, level, tag, message)
- var stacktraceStream = stream(exception.stackTrace)
- if (stackTraceDepth != UNBOUNDED_STACK_TRACE) {
- stacktraceStream = stacktraceStream.limit(stackTraceDepth.toLong())
- }
- stacktraceStream.forEach { line ->
- printLikeLogcat(pw, timestamp, level, tag, "\tat $line")
- }
- exception.cause?.let { cause ->
- printException(pw, timestamp, level, cause, tag, "Caused by: ", nestedStackTraceDepth)
- }
- exception.suppressedExceptions.forEach { suppressed ->
- printException(
- pw,
- timestamp,
- level,
- suppressed,
- tag,
- "Suppressed: ",
- nestedStackTraceDepth
- )
- }
- }
-
- private fun printLikeLogcat(
- pw: PrintWriter,
- formattedTimestamp: String,
- shortLogLevel: String,
- tag: String,
- message: String
- ) {
- pw.print(formattedTimestamp)
- pw.print(" ")
- pw.print(shortLogLevel)
- pw.print(" ")
- pw.print(tag)
- pw.print(": ")
- pw.println(message)
- }
-
private fun echo(message: LogMessage, toLogcat: Boolean, toSystrace: Boolean) {
if (toLogcat || toSystrace) {
val strMessage = message.messagePrinter(message)
@@ -370,5 +288,4 @@ class LogBuffer @JvmOverloads constructor(
typealias MessageInitializer = LogMessage.() -> Unit
private const val TAG = "LogBuffer"
-private val DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
private val FROZEN_MESSAGE = LogMessageImpl.create()
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogMessage.kt b/packages/SystemUI/src/com/android/systemui/log/LogMessage.kt
index 987aea8bff08..dae2592e116c 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogMessage.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogMessage.kt
@@ -16,6 +16,10 @@
package com.android.systemui.log
+import java.io.PrintWriter
+import java.text.SimpleDateFormat
+import java.util.Locale
+
/**
* Generic data class for storing messages logged to a [LogBuffer]
*
@@ -50,6 +54,17 @@ interface LogMessage {
var bool2: Boolean
var bool3: Boolean
var bool4: Boolean
+
+ /**
+ * Function that dumps the [LogMessage] to the provided [writer].
+ */
+ fun dump(writer: PrintWriter) {
+ val formattedTimestamp = DATE_FORMAT.format(timestamp)
+ val shortLevel = level.shortString
+ val messageToPrint = messagePrinter(this)
+ printLikeLogcat(writer, formattedTimestamp, shortLevel, tag, messageToPrint)
+ exception?.printStackTrace(writer)
+ }
}
/**
@@ -61,3 +76,21 @@ interface LogMessage {
* of the printer for each call, thwarting our attempts at avoiding any sort of allocation.
*/
typealias MessagePrinter = LogMessage.() -> String
+
+private fun printLikeLogcat(
+ pw: PrintWriter,
+ formattedTimestamp: String,
+ shortLogLevel: String,
+ tag: String,
+ message: String
+) {
+ pw.print(formattedTimestamp)
+ pw.print(" ")
+ pw.print(shortLogLevel)
+ pw.print(" ")
+ pw.print(tag)
+ pw.print(": ")
+ pw.println(message)
+}
+
+private val DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
index 81efdf591b41..e0c8d66cb6fd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
@@ -266,11 +266,11 @@ class MediaDataFilter @Inject constructor(
}
/**
- * Are there any media notifications active, including the recommendation?
+ * Are there any active media entries, including the recommendation?
*/
- fun hasActiveMediaOrRecommendation() =
- userEntries.any { it.value.active } ||
- (smartspaceMediaData.isActive && smartspaceMediaData.isValid())
+ fun hasActiveMediaOrRecommendation() = userEntries.any { it.value.active } ||
+ (smartspaceMediaData.isActive &&
+ (smartspaceMediaData.isValid() || reactivatedKey != null))
/**
* Are there any media entries we should display?
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index e9b6af44ddf3..e360d10d9362 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -263,6 +263,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
}
private void onGroupActionTriggered(boolean isChecked, MediaDevice device) {
+ disableSeekBar();
if (isChecked && isDeviceIncluded(mController.getSelectableMediaDevice(), device)) {
mController.addDeviceToPlayMedia(device);
} else if (!isChecked && isDeviceIncluded(mController.getDeselectableMediaDevice(),
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index e6116c16ec4a..43b0287c164e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -273,6 +273,8 @@ public abstract class MediaOutputBaseAdapter extends
void initSeekbar(MediaDevice device, boolean isCurrentSeekbarInvisible) {
if (!mController.isVolumeControlEnabled(device)) {
disableSeekBar();
+ } else {
+ enableSeekBar();
}
mSeekBar.setMaxVolume(device.getMaxVolume());
final int currentVolume = device.getCurrentVolume();
@@ -417,11 +419,16 @@ public abstract class MediaOutputBaseAdapter extends
return drawable;
}
- private void disableSeekBar() {
+ protected void disableSeekBar() {
mSeekBar.setEnabled(false);
mSeekBar.setOnTouchListener((v, event) -> true);
}
+ private void enableSeekBar() {
+ mSeekBar.setEnabled(true);
+ mSeekBar.setOnTouchListener((v, event) -> false);
+ }
+
protected void setUpDeviceIcon(MediaDevice device) {
ThreadUtils.postOnBackgroundThread(() -> {
Icon icon = mController.getDeviceIconCompat(device).toIcon(mContext);
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
index 5d7af522176a..6fe06e085556 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
@@ -194,6 +194,11 @@ public class MediaOutputMetricLogger {
}
private int getLoggingDeviceType(MediaDevice device, boolean isSourceDevice) {
+ if (device == null) {
+ return isSourceDevice
+ ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__UNKNOWN_TYPE
+ : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__UNKNOWN_TYPE;
+ }
switch (device.getDeviceType()) {
case MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE:
return isSourceDevice
@@ -229,6 +234,9 @@ public class MediaOutputMetricLogger {
}
private int getInteractionDeviceType(MediaDevice device) {
+ if (device == null) {
+ return SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__UNKNOWN_TYPE;
+ }
switch (device.getDeviceType()) {
case MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE:
return SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__BUILTIN_SPEAKER;
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
index 5f478ce32590..9ab83b84277e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
@@ -56,7 +56,7 @@ abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>(
internal val logger: MediaTttLogger,
internal val windowManager: WindowManager,
private val viewUtil: ViewUtil,
- @Main private val mainExecutor: DelayableExecutor,
+ @Main internal val mainExecutor: DelayableExecutor,
private val accessibilityManager: AccessibilityManager,
private val configurationController: ConfigurationController,
private val powerManager: PowerManager,
@@ -205,13 +205,15 @@ abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>(
*
* @param appPackageName the package name of the app playing the media. Will be used to fetch
* the app icon and app name if overrides aren't provided.
+ *
+ * @return the content description of the icon.
*/
internal fun setIcon(
currentChipView: ViewGroup,
appPackageName: String?,
appIconDrawableOverride: Drawable? = null,
appNameOverride: CharSequence? = null,
- ) {
+ ): CharSequence {
val appIconView = currentChipView.requireViewById<CachingIconView>(R.id.app_icon)
val iconInfo = getIconInfo(appPackageName)
@@ -224,6 +226,7 @@ abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>(
appIconView.contentDescription = appNameOverride ?: iconInfo.iconName
appIconView.setImageDrawable(appIconDrawableOverride ?: iconInfo.icon)
+ return appIconView.contentDescription.toString()
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 495f697f57e6..0f1ae00ae8fc 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -19,7 +19,6 @@ package com.android.systemui.media.taptotransfer.receiver
import android.annotation.SuppressLint
import android.app.StatusBarManager
import android.content.Context
-import android.graphics.PointF
import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
import android.media.MediaRoute2Info
@@ -202,10 +201,10 @@ class MediaTttChipControllerReceiver @Inject constructor(
val height = windowBounds.height()
val width = windowBounds.width()
- rippleView.radius = height / 5f
+ val maxDiameter = height / 2.5f
+ rippleView.setMaxSize(maxDiameter, maxDiameter)
// Center the ripple on the bottom of the screen in the middle.
- rippleView.origin = PointF(/* x= */ width / 2f, /* y= */ height.toFloat())
-
+ rippleView.setCenter(width * 0.5f, height.toFloat())
val color = Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColorAccent)
val colorWithAlpha = ColorUtils.setAlphaComponent(color, 70)
rippleView.setColor(colorWithAlpha)
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
index fed546bf52c2..6a505f06a495 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
@@ -23,10 +23,10 @@ import com.android.systemui.ripple.RippleView
/**
* An expanding ripple effect for the media tap-to-transfer receiver chip.
*/
-class ReceiverChipRippleView(
- context: Context?, attrs: AttributeSet?
-) : RippleView(context, attrs) {
+class ReceiverChipRippleView(context: Context?, attrs: AttributeSet?) : RippleView(context, attrs) {
init {
+ // TODO: use RippleShape#ELLIPSE when calling setupShader.
+ setupShader()
setRippleFill(true)
duration = 3000L
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
index 3ea11b8aa4dd..b94b8bfabfc1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
@@ -122,13 +122,12 @@ class MediaTttChipControllerSender @Inject constructor(
val chipState = newChipInfo.state
// App icon
- setIcon(currentChipView, newChipInfo.routeInfo.packageName)
+ val iconName = setIcon(currentChipView, newChipInfo.routeInfo.packageName)
// Text
val otherDeviceName = newChipInfo.routeInfo.name.toString()
- currentChipView.requireViewById<TextView>(R.id.text).apply {
- text = chipState.getChipTextString(context, otherDeviceName)
- }
+ val chipText = chipState.getChipTextString(context, otherDeviceName)
+ currentChipView.requireViewById<TextView>(R.id.text).text = chipText
// Loading
currentChipView.requireViewById<View>(R.id.loading).visibility =
@@ -145,17 +144,29 @@ class MediaTttChipControllerSender @Inject constructor(
// Failure
currentChipView.requireViewById<View>(R.id.failure_icon).visibility =
chipState.isTransferFailure.visibleIfTrue()
+
+ // For accessibility
+ currentChipView.requireViewById<ViewGroup>(
+ R.id.media_ttt_sender_chip_inner
+ ).contentDescription = "$iconName $chipText"
}
override fun animateChipIn(chipView: ViewGroup) {
+ val chipInnerView = chipView.requireViewById<ViewGroup>(R.id.media_ttt_sender_chip_inner)
ViewHierarchyAnimator.animateAddition(
- chipView.requireViewById<ViewGroup>(R.id.media_ttt_sender_chip_inner),
+ chipInnerView,
ViewHierarchyAnimator.Hotspot.TOP,
Interpolators.EMPHASIZED_DECELERATE,
- duration = 500L,
+ duration = ANIMATION_DURATION,
includeMargins = true,
includeFadeIn = true,
)
+
+ // We can only request focus once the animation finishes.
+ mainExecutor.executeDelayed(
+ { chipInnerView.requestAccessibilityFocus() },
+ ANIMATION_DURATION
+ )
}
override fun removeChip(removalReason: String) {
@@ -186,3 +197,4 @@ data class ChipSenderInfo(
}
const val SENDER_TAG = "MediaTapToTransferSender"
+private const val ANIMATION_DURATION = 500L
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index 6bc50a6b2406..da9fefab0b66 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -48,6 +48,7 @@ import android.view.accessibility.AccessibilityManager;
import androidx.annotation.NonNull;
+import com.android.keyguard.KeyguardViewController;
import com.android.systemui.Dumpable;
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
@@ -89,6 +90,7 @@ public final class NavBarHelper implements
private final AccessibilityManager mAccessibilityManager;
private final Lazy<AssistManager> mAssistManagerLazy;
private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
+ private final KeyguardViewController mKeyguardViewController;
private final UserTracker mUserTracker;
private final SystemActions mSystemActions;
private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
@@ -123,6 +125,7 @@ public final class NavBarHelper implements
OverviewProxyService overviewProxyService,
Lazy<AssistManager> assistManagerLazy,
Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
+ KeyguardViewController keyguardViewController,
NavigationModeController navigationModeController,
UserTracker userTracker,
DumpManager dumpManager) {
@@ -131,6 +134,7 @@ public final class NavBarHelper implements
mAccessibilityManager = accessibilityManager;
mAssistManagerLazy = assistManagerLazy;
mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
+ mKeyguardViewController = keyguardViewController;
mUserTracker = userTracker;
mSystemActions = systemActions;
accessibilityManager.addAccessibilityServicesStateChangeListener(this);
@@ -317,8 +321,12 @@ public final class NavBarHelper implements
* {@link InputMethodService} and the keyguard states.
*/
public boolean isImeShown(int vis) {
- View shadeWindowView = mCentralSurfacesOptionalLazy.get().get().getNotificationShadeWindowView();
- boolean isKeyguardShowing = mCentralSurfacesOptionalLazy.get().get().isKeyguardShowing();
+ View shadeWindowView = null;
+ if (mCentralSurfacesOptionalLazy.get().isPresent()) {
+ shadeWindowView =
+ mCentralSurfacesOptionalLazy.get().get().getNotificationShadeWindowView();
+ }
+ boolean isKeyguardShowing = mKeyguardViewController.isShowing();
boolean imeVisibleOnShade = shadeWindowView != null && shadeWindowView.isAttachedToWindow()
&& shadeWindowView.getRootWindowInsets().isVisible(WindowInsets.Type.ime());
return imeVisibleOnShade
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 2d7a809644c0..3789cbb1fb65 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -302,10 +302,6 @@ public class NavigationBarController implements
*/
@VisibleForTesting
void createNavigationBar(Display display, Bundle savedState, RegisterStatusBarResult result) {
- if (initializeTaskbarIfNecessary()) {
- return;
- }
-
if (display == null) {
return;
}
@@ -315,7 +311,7 @@ public class NavigationBarController implements
// We may show TaskBar on the default display for large screen device. Don't need to create
// navigation bar for this case.
- if (mIsTablet && isOnDefaultDisplay) {
+ if (isOnDefaultDisplay && initializeTaskbarIfNecessary()) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
index b05e75ea1c97..6e927b071727 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -42,7 +42,6 @@ import com.android.systemui.plugins.NavigationEdgeBackPlugin
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.ViewController
-import com.android.wm.shell.back.BackAnimation
import java.io.PrintWriter
import javax.inject.Inject
import kotlin.math.abs
@@ -119,7 +118,7 @@ class BackPanelController private constructor(
private val latencyTracker: LatencyTracker
) {
/** Construct a [BackPanelController]. */
- fun create(context: Context, backAnimation: BackAnimation?): BackPanelController {
+ fun create(context: Context): BackPanelController {
val backPanelController = BackPanelController(
context,
windowManager,
@@ -418,6 +417,7 @@ class BackPanelController private constructor(
stretchEntryBackIndicator(preThresholdStretchProgress(xTranslation))
GestureState.INACTIVE ->
mView.resetStretch()
+ else -> {}
}
// set y translation
@@ -510,7 +510,6 @@ class BackPanelController private constructor(
private fun playCommitBackAnimation() {
// Check if we should vibrate again
if (previousState != GestureState.FLUNG) {
- backCallback.triggerBack()
velocityTracker!!.computeCurrentVelocity(1000)
val isSlow = abs(velocityTracker!!.xVelocity) < 500
val hasNotVibratedRecently =
@@ -519,6 +518,10 @@ class BackPanelController private constructor(
vibratorHelper.vibrate(VibrationEffect.EFFECT_CLICK)
}
}
+ // Dispatch the actual back trigger
+ if (DEBUG) Log.d(TAG, "playCommitBackAnimation() invoked triggerBack() on backCallback")
+ backCallback.triggerBack()
+
playAnimation(setGoneEndListener)
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 057ed2439df3..6ac3eadb838d 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -60,6 +60,7 @@ import com.android.internal.policy.GestureNavigationSettingsObserver;
import com.android.internal.util.LatencyTracker;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
@@ -82,6 +83,7 @@ import com.android.systemui.shared.tracing.ProtoTraceable;
import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.tracing.nano.EdgeBackGestureHandlerProto;
import com.android.systemui.tracing.nano.SystemUiTraceProto;
+import com.android.systemui.util.Assert;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.pip.Pip;
@@ -191,6 +193,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
private final int mDisplayId;
private final Executor mMainExecutor;
+ private final Executor mBackgroundExecutor;
private final Rect mPipExcludedBounds = new Rect();
private final Rect mNavBarOverlayExcludedBounds = new Rect();
@@ -251,6 +254,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
private BackGestureTfClassifierProvider mBackGestureTfClassifierProvider;
private Map<String, Integer> mVocab;
private boolean mUseMLModel;
+ private boolean mMLModelIsLoading;
// minimum width below which we do not run the model
private int mMLEnableWidth;
private float mMLModelThreshold;
@@ -318,6 +322,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
SysUiState sysUiState,
PluginManager pluginManager,
@Main Executor executor,
+ @Background Executor backgroundExecutor,
BroadcastDispatcher broadcastDispatcher,
ProtoTracer protoTracer,
NavigationModeController navigationModeController,
@@ -334,6 +339,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
mContext = context;
mDisplayId = context.getDisplayId();
mMainExecutor = executor;
+ mBackgroundExecutor = backgroundExecutor;
mOverviewProxyService = overviewProxyService;
mSysUiState = sysUiState;
mPluginManager = pluginManager;
@@ -568,10 +574,10 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
private void resetEdgeBackPlugin() {
if (mIsNewBackAffordanceEnabled) {
setEdgeBackPlugin(
- mBackPanelControllerFactory.create(mContext, mBackAnimation));
+ mBackPanelControllerFactory.create(mContext));
} else {
setEdgeBackPlugin(
- new NavigationBarEdgePanel(mContext, mBackAnimation, mLatencyTracker));
+ new NavigationBarEdgePanel(mContext, mLatencyTracker));
}
}
@@ -631,28 +637,63 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
return;
}
- if (newState) {
- mBackGestureTfClassifierProvider = mBackGestureTfClassifierProviderProvider.get();
- mMLModelThreshold = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.BACK_GESTURE_ML_MODEL_THRESHOLD, 0.9f);
- if (mBackGestureTfClassifierProvider.isActive()) {
- Trace.beginSection("EdgeBackGestureHandler#loadVocab");
- mVocab = mBackGestureTfClassifierProvider.loadVocab(mContext.getAssets());
- Trace.endSection();
- mUseMLModel = true;
+ mUseMLModel = newState;
+
+ if (mUseMLModel) {
+ Assert.isMainThread();
+ if (mMLModelIsLoading) {
+ Log.d(TAG, "Model tried to load while already loading.");
return;
}
- }
-
- mUseMLModel = false;
- if (mBackGestureTfClassifierProvider != null) {
+ mMLModelIsLoading = true;
+ mBackgroundExecutor.execute(() -> loadMLModel());
+ } else if (mBackGestureTfClassifierProvider != null) {
mBackGestureTfClassifierProvider.release();
mBackGestureTfClassifierProvider = null;
+ mVocab = null;
+ }
+ }
+
+ private void loadMLModel() {
+ BackGestureTfClassifierProvider provider = mBackGestureTfClassifierProviderProvider.get();
+ float threshold = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.BACK_GESTURE_ML_MODEL_THRESHOLD, 0.9f);
+ Map<String, Integer> vocab = null;
+ if (provider != null && !provider.isActive()) {
+ provider.release();
+ provider = null;
+ Log.w(TAG, "Cannot load model because it isn't active");
+ }
+ if (provider != null) {
+ Trace.beginSection("EdgeBackGestureHandler#loadVocab");
+ vocab = provider.loadVocab(mContext.getAssets());
+ Trace.endSection();
+ }
+ BackGestureTfClassifierProvider finalProvider = provider;
+ Map<String, Integer> finalVocab = vocab;
+ mMainExecutor.execute(() -> onMLModelLoadFinished(finalProvider, finalVocab, threshold));
+ }
+
+ private void onMLModelLoadFinished(BackGestureTfClassifierProvider provider,
+ Map<String, Integer> vocab, float threshold) {
+ Assert.isMainThread();
+ mMLModelIsLoading = false;
+ if (!mUseMLModel) {
+ // This can happen if the user disables Gesture Nav while the model is loading.
+ if (provider != null) {
+ provider.release();
+ }
+ Log.d(TAG, "Model finished loading but isn't needed.");
+ return;
}
+ mBackGestureTfClassifierProvider = provider;
+ mVocab = vocab;
+ mMLModelThreshold = threshold;
}
private int getBackGesturePredictionsCategory(int x, int y, int app) {
- if (app == -1) {
+ BackGestureTfClassifierProvider provider = mBackGestureTfClassifierProvider;
+ if (provider == null || app == -1) {
return -1;
}
int distanceFromEdge;
@@ -673,7 +714,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
new long[]{(long) y},
};
- mMLResults = mBackGestureTfClassifierProvider.predict(featuresVector);
+ mMLResults = provider.predict(featuresVector);
if (mMLResults == -1) {
return -1;
}
@@ -1031,6 +1072,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
private final SysUiState mSysUiState;
private final PluginManager mPluginManager;
private final Executor mExecutor;
+ private final Executor mBackgroundExecutor;
private final BroadcastDispatcher mBroadcastDispatcher;
private final ProtoTracer mProtoTracer;
private final NavigationModeController mNavigationModeController;
@@ -1050,6 +1092,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
SysUiState sysUiState,
PluginManager pluginManager,
@Main Executor executor,
+ @Background Executor backgroundExecutor,
BroadcastDispatcher broadcastDispatcher,
ProtoTracer protoTracer,
NavigationModeController navigationModeController,
@@ -1067,6 +1110,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
mSysUiState = sysUiState;
mPluginManager = pluginManager;
mExecutor = executor;
+ mBackgroundExecutor = backgroundExecutor;
mBroadcastDispatcher = broadcastDispatcher;
mProtoTracer = protoTracer;
mNavigationModeController = navigationModeController;
@@ -1089,6 +1133,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
mSysUiState,
mPluginManager,
mExecutor,
+ mBackgroundExecutor,
mBroadcastDispatcher,
mProtoTracer,
mNavigationModeController,
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
index eba9d3fdcab8..122852f7d07a 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
@@ -43,7 +43,6 @@ import android.view.View;
import android.view.WindowManager;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
-import android.window.BackEvent;
import androidx.core.graphics.ColorUtils;
import androidx.dynamicanimation.animation.DynamicAnimation;
@@ -59,7 +58,6 @@ import com.android.systemui.animation.Interpolators;
import com.android.systemui.plugins.NavigationEdgeBackPlugin;
import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
import com.android.systemui.statusbar.VibratorHelper;
-import com.android.wm.shell.back.BackAnimation;
import java.io.PrintWriter;
import java.util.concurrent.Executor;
@@ -283,14 +281,11 @@ public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPl
}
};
private BackCallback mBackCallback;
- private BackAnimation mBackAnimation;
- public NavigationBarEdgePanel(Context context,
- BackAnimation backAnimation, LatencyTracker latencyTracker) {
+ public NavigationBarEdgePanel(Context context, LatencyTracker latencyTracker) {
super(context);
mWindowManager = context.getSystemService(WindowManager.class);
- mBackAnimation = backAnimation;
mVibratorHelper = Dependency.get(VibratorHelper.class);
mDensity = context.getResources().getDisplayMetrics().density;
@@ -360,7 +355,6 @@ public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPl
.getDimension(R.dimen.navigation_edge_action_drag_threshold);
mSwipeProgressThreshold = context.getResources()
.getDimension(R.dimen.navigation_edge_action_progress_threshold);
- initializeBackAnimation();
setVisibility(GONE);
@@ -388,17 +382,6 @@ public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPl
mLatencyTracker = latencyTracker;
}
- public void setBackAnimation(BackAnimation backAnimation) {
- mBackAnimation = backAnimation;
- initializeBackAnimation();
- }
-
- private void initializeBackAnimation() {
- if (mBackAnimation != null) {
- mBackAnimation.setSwipeThresholds(mSwipeTriggerThreshold, mSwipeProgressThreshold);
- }
- }
-
@Override
public void onDestroy() {
cancelFailsafe();
@@ -484,12 +467,6 @@ public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPl
@Override
public void onMotionEvent(MotionEvent event) {
- if (mBackAnimation != null) {
- mBackAnimation.onBackMotion(
- event.getX(), event.getY(),
- event.getActionMasked(),
- mIsLeftPanel ? BackEvent.EDGE_LEFT : BackEvent.EDGE_RIGHT);
- }
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
@@ -903,9 +880,6 @@ public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPl
// Whenever the trigger back state changes the existing translation animation should be
// cancelled
mTranslationAnimation.cancel();
- if (mBackAnimation != null) {
- mBackAnimation.setTriggerBack(triggerBack);
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 4552abd402b0..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
@@ -110,6 +117,11 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
private Context mUserContext;
private UserTracker mUserTracker;
private SecureSettings mSecureSettings;
+ // Keep track of whether mTilesList contains the same information as the Settings value.
+ // This is a performance optimization to reduce the number of blocking calls to Settings from
+ // main thread.
+ // This is enforced by only cleaning the flag at the end of a successful run of #onTuningChanged
+ private boolean mTilesListDirty = true;
private final TileServiceRequestController mTileServiceRequestController;
private TileLifecycleManager.Factory mTileLifeCycleManagerFactory;
@@ -130,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;
@@ -143,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;
@@ -374,6 +388,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
// the ones that are in the setting, update the Setting.
saveTilesToSettings(mTileSpecs);
}
+ mTilesListDirty = false;
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).onTilesChanged();
}
@@ -386,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)));
}
@@ -436,6 +456,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
);
}
+ // When calling this, you may want to modify mTilesListDirty accordingly.
@MainThread
private void saveTilesToSettings(List<String> tileSpecs) {
mSecureSettings.putStringForUser(TILES_SETTING, TextUtils.join(",", tileSpecs),
@@ -445,9 +466,15 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
@MainThread
private void changeTileSpecs(Predicate<List<String>> changeFunction) {
- final String setting = mSecureSettings.getStringForUser(TILES_SETTING, mCurrentUser);
- final List<String> tileSpecs = loadTileSpecs(mContext, setting);
+ final List<String> tileSpecs;
+ if (!mTilesListDirty) {
+ tileSpecs = new ArrayList<>(mTileSpecs);
+ } else {
+ tileSpecs = loadTileSpecs(mContext,
+ mSecureSettings.getStringForUser(TILES_SETTING, mCurrentUser));
+ }
if (changeFunction.test(tileSpecs)) {
+ mTilesListDirty = true;
saveTilesToSettings(tileSpecs);
}
}
@@ -502,11 +529,12 @@ 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();
}
}
if (DEBUG) Log.d(TAG, "saveCurrentTiles " + newTiles);
+ mTilesListDirty = true;
saveTilesToSettings(newTiles);
}
@@ -538,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/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index ec0d0811ee76..eeb1010693fc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -134,18 +134,9 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
mQSCarrierGroupController
.setOnSingleCarrierChangedListener(mView::setIsSingleCarrier);
- List<String> rssiIgnoredSlots;
-
- if (mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)) {
- rssiIgnoredSlots = List.of(
- getResources().getString(com.android.internal.R.string.status_bar_no_calling),
- getResources().getString(com.android.internal.R.string.status_bar_call_strength)
- );
- } else {
- rssiIgnoredSlots = List.of(
- getResources().getString(com.android.internal.R.string.status_bar_mobile)
- );
- }
+ List<String> rssiIgnoredSlots = List.of(
+ getResources().getString(com.android.internal.R.string.status_bar_mobile)
+ );
mView.onAttach(mIconManager, mQSExpansionPathInterpolator, rssiIgnoredSlots,
mInsetsProvider, mFeatureFlags.isEnabled(Flags.COMBINED_QS_HEADERS));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt b/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt
index 2dac63905524..e925b5472c27 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt
@@ -27,7 +27,6 @@ data class CellSignalState(
@JvmField val contentDescription: String? = null,
@JvmField val typeContentDescription: String? = null,
@JvmField val roaming: Boolean = false,
- @JvmField val providerModelBehavior: Boolean = false
) {
/**
* Changes the visibility of this state by returning a copy with the visibility changed.
@@ -41,4 +40,4 @@ data class CellSignalState(
if (this.visible == visible) return this
else return copy(visible = visible)
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
index 592da6554b90..703b95a082dc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
@@ -45,7 +45,7 @@ public class QSCarrier extends LinearLayout {
private View mSpacer;
@Nullable
private CellSignalState mLastSignalState;
- private boolean mProviderModelInitialized = false;
+ private boolean mMobileSignalInitialized = false;
private boolean mIsSingleCarrier;
public QSCarrier(Context context) {
@@ -96,35 +96,25 @@ public class QSCarrier extends LinearLayout {
mMobileRoaming.setImageTintList(colorStateList);
mMobileSignal.setImageTintList(colorStateList);
- if (state.providerModelBehavior) {
- if (!mProviderModelInitialized) {
- mProviderModelInitialized = true;
- mMobileSignal.setImageDrawable(
- mContext.getDrawable(R.drawable.ic_qs_no_calling_sms));
- }
- mMobileSignal.setImageDrawable(mContext.getDrawable(state.mobileSignalIconId));
- mMobileSignal.setContentDescription(state.contentDescription);
- } else {
- if (!mProviderModelInitialized) {
- mProviderModelInitialized = true;
- mMobileSignal.setImageDrawable(new SignalDrawable(mContext));
- }
- mMobileSignal.setImageLevel(state.mobileSignalIconId);
- StringBuilder contentDescription = new StringBuilder();
- if (state.contentDescription != null) {
- contentDescription.append(state.contentDescription).append(", ");
- }
- if (state.roaming) {
- contentDescription
- .append(mContext.getString(R.string.data_connection_roaming))
- .append(", ");
- }
- // TODO: show mobile data off/no internet text for 5 seconds before carrier text
- if (hasValidTypeContentDescription(state.typeContentDescription)) {
- contentDescription.append(state.typeContentDescription);
- }
- mMobileSignal.setContentDescription(contentDescription);
+ if (!mMobileSignalInitialized) {
+ mMobileSignalInitialized = true;
+ mMobileSignal.setImageDrawable(new SignalDrawable(mContext));
}
+ mMobileSignal.setImageLevel(state.mobileSignalIconId);
+ StringBuilder contentDescription = new StringBuilder();
+ if (state.contentDescription != null) {
+ contentDescription.append(state.contentDescription).append(", ");
+ }
+ if (state.roaming) {
+ contentDescription
+ .append(mContext.getString(R.string.data_connection_roaming))
+ .append(", ");
+ }
+ // TODO: show mobile data off/no internet text for 5 seconds before carrier text
+ if (hasValidTypeContentDescription(state.typeContentDescription)) {
+ contentDescription.append(state.typeContentDescription);
+ }
+ mMobileSignal.setContentDescription(contentDescription);
}
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
index 209d09d0f907..6a8bf759a849 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
@@ -42,10 +42,7 @@ import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.statusbar.connectivity.IconState;
import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.connectivity.SignalCallback;
@@ -78,7 +75,6 @@ public class QSCarrierGroupController {
private QSCarrier[] mCarrierGroups = new QSCarrier[SIM_SLOTS];
private int[] mLastSignalLevel = new int[SIM_SLOTS];
private String[] mLastSignalLevelDescription = new String[SIM_SLOTS];
- private final boolean mProviderModel;
private final CarrierConfigTracker mCarrierConfigTracker;
private boolean mIsSingleCarrier;
@@ -90,9 +86,6 @@ public class QSCarrierGroupController {
private final SignalCallback mSignalCallback = new SignalCallback() {
@Override
public void setMobileDataIndicators(@NonNull MobileDataIndicators indicators) {
- if (mProviderModel) {
- return;
- }
int slotIndex = getSlotIndex(indicators.subId);
if (slotIndex >= SIM_SLOTS) {
Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex);
@@ -107,91 +100,12 @@ public class QSCarrierGroupController {
indicators.statusIcon.icon,
indicators.statusIcon.contentDescription,
indicators.typeContentDescription.toString(),
- indicators.roaming,
- mProviderModel
+ indicators.roaming
);
mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
}
@Override
- public void setCallIndicator(@NonNull IconState statusIcon, int subId) {
- if (!mProviderModel) {
- return;
- }
- int slotIndex = getSlotIndex(subId);
- if (slotIndex >= SIM_SLOTS) {
- Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex);
- return;
- }
- if (slotIndex == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
- Log.e(TAG, "Invalid SIM slot index for subscription: " + subId);
- return;
- }
-
- boolean displayCallStrengthIcon =
- mCarrierConfigTracker.getCallStrengthConfig(subId);
-
- if (statusIcon.icon == R.drawable.ic_qs_no_calling_sms) {
- if (statusIcon.visible) {
- mInfos[slotIndex] = new CellSignalState(
- true,
- statusIcon.icon,
- statusIcon.contentDescription,
- "",
- false,
- mProviderModel);
- } else {
- // Whenever the no Calling & SMS state is cleared, switched to the last
- // known call strength icon.
- if (displayCallStrengthIcon) {
- mInfos[slotIndex] = new CellSignalState(
- true,
- mLastSignalLevel[slotIndex],
- mLastSignalLevelDescription[slotIndex],
- "",
- false,
- mProviderModel);
- } else {
- mInfos[slotIndex] = new CellSignalState(
- true,
- R.drawable.ic_qs_sim_card,
- "",
- "",
- false,
- mProviderModel);
- }
- }
- mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
- } else {
- mLastSignalLevel[slotIndex] = statusIcon.icon;
- mLastSignalLevelDescription[slotIndex] = statusIcon.contentDescription;
- // Only Shows the call strength icon when the no Calling & SMS icon is not
- // shown.
- if (mInfos[slotIndex].mobileSignalIconId
- != R.drawable.ic_qs_no_calling_sms) {
- if (displayCallStrengthIcon) {
- mInfos[slotIndex] = new CellSignalState(
- true,
- statusIcon.icon,
- statusIcon.contentDescription,
- "",
- false,
- mProviderModel);
- } else {
- mInfos[slotIndex] = new CellSignalState(
- true,
- R.drawable.ic_qs_sim_card,
- "",
- "",
- false,
- mProviderModel);
- }
- mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
- }
- }
- }
-
- @Override
public void setNoSims(boolean hasNoSims, boolean simDetected) {
if (hasNoSims) {
for (int i = 0; i < SIM_SLOTS; i++) {
@@ -219,14 +133,8 @@ public class QSCarrierGroupController {
@Background Handler bgHandler, @Main Looper mainLooper,
NetworkController networkController,
CarrierTextManager.Builder carrierTextManagerBuilder, Context context,
- CarrierConfigTracker carrierConfigTracker, FeatureFlags featureFlags,
- SlotIndexResolver slotIndexResolver) {
+ CarrierConfigTracker carrierConfigTracker, SlotIndexResolver slotIndexResolver) {
- if (featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)) {
- mProviderModel = true;
- } else {
- mProviderModel = false;
- }
mActivityStarter = activityStarter;
mBgHandler = bgHandler;
mNetworkController = networkController;
@@ -262,8 +170,7 @@ public class QSCarrierGroupController {
R.drawable.ic_qs_no_calling_sms,
context.getText(AccessibilityContentDescriptions.NO_CALLING).toString(),
"",
- false,
- mProviderModel);
+ false);
mLastSignalLevel[i] = TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[0];
mLastSignalLevelDescription[i] =
context.getText(AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0])
@@ -351,8 +258,7 @@ public class QSCarrierGroupController {
for (int i = 0; i < SIM_SLOTS; i++) {
if (mInfos[i].visible
&& mInfos[i].mobileSignalIconId == R.drawable.ic_qs_sim_card) {
- mInfos[i] = new CellSignalState(true, R.drawable.ic_blank, "", "", false,
- mProviderModel);
+ mInfos[i] = new CellSignalState(true, R.drawable.ic_blank, "", "", false);
}
}
}
@@ -470,15 +376,13 @@ public class QSCarrierGroupController {
private final CarrierTextManager.Builder mCarrierTextControllerBuilder;
private final Context mContext;
private final CarrierConfigTracker mCarrierConfigTracker;
- private final FeatureFlags mFeatureFlags;
private final SlotIndexResolver mSlotIndexResolver;
@Inject
public Builder(ActivityStarter activityStarter, @Background Handler handler,
@Main Looper looper, NetworkController networkController,
CarrierTextManager.Builder carrierTextControllerBuilder, Context context,
- CarrierConfigTracker carrierConfigTracker, FeatureFlags featureFlags,
- SlotIndexResolver slotIndexResolver) {
+ CarrierConfigTracker carrierConfigTracker, SlotIndexResolver slotIndexResolver) {
mActivityStarter = activityStarter;
mHandler = handler;
mLooper = looper;
@@ -486,7 +390,6 @@ public class QSCarrierGroupController {
mCarrierTextControllerBuilder = carrierTextControllerBuilder;
mContext = context;
mCarrierConfigTracker = carrierConfigTracker;
- mFeatureFlags = featureFlags;
mSlotIndexResolver = slotIndexResolver;
}
@@ -498,7 +401,7 @@ public class QSCarrierGroupController {
public QSCarrierGroupController build() {
return new QSCarrierGroupController(mView, mActivityStarter, mHandler, mLooper,
mNetworkController, mCarrierTextControllerBuilder, mContext,
- mCarrierConfigTracker, mFeatureFlags, mSlotIndexResolver);
+ mCarrierConfigTracker, mSlotIndexResolver);
}
}
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 86ef85824eb0..ab795faf57e6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
@@ -69,36 +69,63 @@ class QSLogger @Inject constructor(
})
}
- fun logTileClick(tileSpec: String, statusBarState: Int, state: Int) {
+ fun logTileClick(tileSpec: String, statusBarState: Int, state: Int, eventId: Int) {
log(DEBUG, {
str1 = tileSpec
- int1 = statusBarState
+ int1 = eventId
str2 = StatusBarState.toString(statusBarState)
str3 = toStateString(state)
}, {
- "[$str1] Tile clicked. StatusBarState=$str2. TileState=$str3"
+ "[$str1][$int1] Tile clicked. StatusBarState=$str2. TileState=$str3"
})
}
- fun logTileSecondaryClick(tileSpec: String, statusBarState: Int, state: Int) {
+ fun logHandleClick(tileSpec: String, eventId: Int) {
log(DEBUG, {
str1 = tileSpec
- int1 = statusBarState
+ int1 = eventId
+ }, {
+ "[$str1][$int1] Tile handling click."
+ })
+ }
+
+ fun logTileSecondaryClick(tileSpec: String, statusBarState: Int, state: Int, eventId: Int) {
+ log(DEBUG, {
+ str1 = tileSpec
+ int1 = eventId
str2 = StatusBarState.toString(statusBarState)
str3 = toStateString(state)
}, {
- "[$str1] Tile long clicked. StatusBarState=$str2. TileState=$str3"
+ "[$str1][$int1] Tile secondary clicked. StatusBarState=$str2. TileState=$str3"
+ })
+ }
+
+ fun logHandleSecondaryClick(tileSpec: String, eventId: Int) {
+ log(DEBUG, {
+ str1 = tileSpec
+ int1 = eventId
+ }, {
+ "[$str1][$int1] Tile handling secondary click."
})
}
- fun logTileLongClick(tileSpec: String, statusBarState: Int, state: Int) {
+ fun logTileLongClick(tileSpec: String, statusBarState: Int, state: Int, eventId: Int) {
log(DEBUG, {
str1 = tileSpec
- int1 = statusBarState
+ int1 = eventId
str2 = StatusBarState.toString(statusBarState)
str3 = toStateString(state)
}, {
- "[$str1] Tile long clicked. StatusBarState=$str2. TileState=$str3"
+ "[$str1][$int1] Tile long clicked. StatusBarState=$str2. TileState=$str3"
+ })
+ }
+
+ fun logHandleLongClick(tileSpec: String, eventId: Int) {
+ log(DEBUG, {
+ str1 = tileSpec
+ int1 = eventId
+ }, {
+ "[$str1][$int1] Tile handling long click."
})
}
@@ -144,4 +171,4 @@ class QSLogger @Inject constructor(
) {
buffer.log(TAG, logLevel, initializer, printer)
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 740e12ab5839..2cffe8951b56 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -105,6 +105,9 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
private final FalsingManager mFalsingManager;
protected final QSLogger mQSLogger;
private volatile int mReadyState;
+ // Keeps track of the click event, to match it with the handling in the background thread
+ // Only read and modified in main thread (where click events come through).
+ private int mClickEventId = 0;
private final ArrayList<Callback> mCallbacks = new ArrayList<>();
private final Object mStaleListener = new Object();
@@ -295,9 +298,11 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
mStatusBarStateController.getState())));
mUiEventLogger.logWithInstanceId(QSEvent.QS_ACTION_CLICK, 0, getMetricsSpec(),
getInstanceId());
- mQSLogger.logTileClick(mTileSpec, mStatusBarStateController.getState(), mState.state);
+ final int eventId = mClickEventId++;
+ mQSLogger.logTileClick(mTileSpec, mStatusBarStateController.getState(), mState.state,
+ eventId);
if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- mHandler.obtainMessage(H.CLICK, view).sendToTarget();
+ mHandler.obtainMessage(H.CLICK, eventId, 0, view).sendToTarget();
}
}
@@ -307,9 +312,10 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
mStatusBarStateController.getState())));
mUiEventLogger.logWithInstanceId(QSEvent.QS_ACTION_SECONDARY_CLICK, 0, getMetricsSpec(),
getInstanceId());
+ final int eventId = mClickEventId++;
mQSLogger.logTileSecondaryClick(mTileSpec, mStatusBarStateController.getState(),
- mState.state);
- mHandler.obtainMessage(H.SECONDARY_CLICK, view).sendToTarget();
+ mState.state, eventId);
+ mHandler.obtainMessage(H.SECONDARY_CLICK, eventId, 0, view).sendToTarget();
}
@Override
@@ -319,8 +325,10 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
mStatusBarStateController.getState())));
mUiEventLogger.logWithInstanceId(QSEvent.QS_ACTION_LONG_PRESS, 0, getMetricsSpec(),
getInstanceId());
- mQSLogger.logTileLongClick(mTileSpec, mStatusBarStateController.getState(), mState.state);
- mHandler.obtainMessage(H.LONG_CLICK, view).sendToTarget();
+ final int eventId = mClickEventId++;
+ mQSLogger.logTileLongClick(mTileSpec, mStatusBarStateController.getState(), mState.state,
+ eventId);
+ mHandler.obtainMessage(H.LONG_CLICK, eventId, 0, view).sendToTarget();
}
public LogMaker populate(LogMaker logMaker) {
@@ -590,13 +598,16 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
mContext, mEnforcedAdmin);
mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
} else {
+ mQSLogger.logHandleClick(mTileSpec, msg.arg1);
handleClick((View) msg.obj);
}
} else if (msg.what == SECONDARY_CLICK) {
name = "handleSecondaryClick";
+ mQSLogger.logHandleSecondaryClick(mTileSpec, msg.arg1);
handleSecondaryClick((View) msg.obj);
} else if (msg.what == LONG_CLICK) {
name = "handleLongClick";
+ mQSLogger.logHandleLongClick(mTileSpec, msg.arg1);
handleLongClick((View) msg.obj);
} else if (msg.what == REFRESH_STATE) {
name = "handleRefreshState";
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index f629a0361ddb..d5efe367ae2b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -806,7 +806,8 @@ public class InternetDialogController implements AccessPointController.AccessPoi
return;
}
- mTelephonyManager.setDataEnabled(enabled);
+ mTelephonyManager.setDataEnabledForReason(
+ TelephonyManager.DATA_ENABLED_REASON_USER, enabled);
if (disableOtherSubscriptions) {
final List<SubscriptionInfo> subInfoList =
mSubscriptionManager.getActiveSubscriptionInfoList();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 438236d6a63d..30862b78c93f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -638,12 +638,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
// Listen for user setup
startTracking();
- screenLifecycle.addObserver(new ScreenLifecycle.Observer() {
- @Override
- public void onScreenTurnedOn() {
- notifyScreenTurnedOn();
- }
- });
+ screenLifecycle.addObserver(mLifecycleObserver);
// Connect to the service
updateEnabledState();
@@ -951,20 +946,55 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
}
- /**
- * Notifies the Launcher that screen turned on and ready to use
- */
- public void notifyScreenTurnedOn() {
- try {
- if (mOverviewProxy != null) {
- mOverviewProxy.onScreenTurnedOn();
- } else {
- Log.e(TAG_OPS, "Failed to get overview proxy for screen turned on event.");
+ private final ScreenLifecycle.Observer mLifecycleObserver = new ScreenLifecycle.Observer() {
+ /**
+ * Notifies the Launcher that screen turned on and ready to use
+ */
+ @Override
+ public void onScreenTurnedOn() {
+ try {
+ if (mOverviewProxy != null) {
+ mOverviewProxy.onScreenTurnedOn();
+ } else {
+ Log.e(TAG_OPS, "Failed to get overview proxy for screen turned on event.");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG_OPS, "Failed to call onScreenTurnedOn()", e);
}
- } catch (RemoteException e) {
- Log.e(TAG_OPS, "Failed to call notifyScreenTurnedOn()", e);
}
- }
+
+ /**
+ * Notifies the Launcher that screen is starting to turn on.
+ */
+ @Override
+ public void onScreenTurningOff() {
+ try {
+ if (mOverviewProxy != null) {
+ mOverviewProxy.onScreenTurningOff();
+ } else {
+ Log.e(TAG_OPS, "Failed to get overview proxy for screen turning off event.");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG_OPS, "Failed to call onScreenTurningOff()", e);
+ }
+ }
+
+ /**
+ * Notifies the Launcher that screen is starting to turn on.
+ */
+ @Override
+ public void onScreenTurningOn(@NonNull Runnable ignored) {
+ try {
+ if (mOverviewProxy != null) {
+ mOverviewProxy.onScreenTurningOn();
+ } else {
+ Log.e(TAG_OPS, "Failed to get overview proxy for screen turning on event.");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG_OPS, "Failed to call onScreenTurningOn()", e);
+ }
+ }
+ };
void notifyToggleRecentApps() {
for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt b/packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt
index 93a2efc4e96d..56a187429af6 100644
--- a/packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt
+++ b/packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt
@@ -20,93 +20,108 @@ import android.graphics.RuntimeShader
import android.util.MathUtils
/**
- * Shader class that renders an expanding charging ripple effect. A charging ripple contains
- * three elements:
- * 1. an expanding filled circle that appears in the beginning and quickly fades away
+ * Shader class that renders an expanding ripple effect. The ripple contains three elements:
+ *
+ * 1. an expanding filled [RippleShape] that appears in the beginning and quickly fades away
* 2. an expanding ring that appears throughout the effect
* 3. an expanding ring-shaped area that reveals noise over #2.
*
+ * The ripple shader will be default to the circle shape if not specified.
+ *
* Modeled after frameworks/base/graphics/java/android/graphics/drawable/RippleShader.java.
*/
-class RippleShader internal constructor() : RuntimeShader(SHADER) {
+class RippleShader internal constructor(rippleShape: RippleShape = RippleShape.CIRCLE) :
+ RuntimeShader(buildShader(rippleShape)) {
+
+ /** Shapes that the [RippleShader] supports. */
+ enum class RippleShape {
+ CIRCLE,
+ ROUNDED_BOX,
+ ELLIPSE
+ }
+ //language=AGSL
companion object {
- private const val SHADER_UNIFORMS = """uniform vec2 in_origin;
+ private const val SHADER_UNIFORMS = """uniform vec2 in_center;
+ uniform vec2 in_size;
uniform float in_progress;
- uniform float in_maxRadius;
+ uniform float in_cornerRadius;
+ uniform float in_thickness;
uniform float in_time;
uniform float in_distort_radial;
uniform float in_distort_xy;
- uniform float in_radius;
uniform float in_fadeSparkle;
- uniform float in_fadeCircle;
+ uniform float in_fadeFill;
uniform float in_fadeRing;
uniform float in_blur;
uniform float in_pixelDensity;
layout(color) uniform vec4 in_color;
uniform float in_sparkle_strength;"""
- private const val SHADER_LIB = """float triangleNoise(vec2 n) {
- n = fract(n * vec2(5.3987, 5.4421));
- n += dot(n.yx, n.xy + vec2(21.5351, 14.3137));
- float xy = n.x * n.y;
- return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;
- }
- const float PI = 3.1415926535897932384626;
- float threshold(float v, float l, float h) {
- return step(l, v) * (1.0 - step(h, v));
- }
+ private const val SHADER_CIRCLE_MAIN = """vec4 main(vec2 p) {
+ vec2 p_distorted = distort(p, in_time, in_distort_radial, in_distort_xy);
+ float radius = in_size.x * 0.5;
+ float sparkleRing = soften(circleRing(p_distorted-in_center, radius), in_blur);
+ float inside = soften(sdCircle(p_distorted-in_center, radius * 1.2), in_blur);
+ float sparkle = sparkles(p - mod(p, in_pixelDensity * 0.8), in_time * 0.00175)
+ * (1.-sparkleRing) * in_fadeSparkle;
+
+ float rippleInsideAlpha = (1.-inside) * in_fadeFill;
+ float rippleRingAlpha = (1.-sparkleRing) * in_fadeRing;
+ float rippleAlpha = max(rippleInsideAlpha, rippleRingAlpha) * 0.45;
+ vec4 ripple = in_color * rippleAlpha;
+ return mix(ripple, vec4(sparkle), sparkle * in_sparkle_strength);
+ }
+ """
+
+ private const val SHADER_ROUNDED_BOX_MAIN = """vec4 main(vec2 p) {
+ float sparkleRing = soften(roundedBoxRing(p-in_center, in_size, in_cornerRadius,
+ in_thickness), in_blur);
+ float inside = soften(sdRoundedBox(p-in_center, in_size * 1.2, in_cornerRadius),
+ in_blur);
+ float sparkle = sparkles(p - mod(p, in_pixelDensity * 0.8), in_time * 0.00175)
+ * (1.-sparkleRing) * in_fadeSparkle;
+
+ float rippleInsideAlpha = (1.-inside) * in_fadeFill;
+ float rippleRingAlpha = (1.-sparkleRing) * in_fadeRing;
+ float rippleAlpha = max(rippleInsideAlpha, rippleRingAlpha) * 0.45;
+ vec4 ripple = in_color * rippleAlpha;
+ return mix(ripple, vec4(sparkle), sparkle * in_sparkle_strength);
+ }
+ """
- float sparkles(vec2 uv, float t) {
- float n = triangleNoise(uv);
- float s = 0.0;
- for (float i = 0; i < 4; i += 1) {
- float l = i * 0.01;
- float h = l + 0.1;
- float o = smoothstep(n - l, h, n);
- o *= abs(sin(PI * o * (t + 0.55 * i)));
- s += o;
- }
- return s;
- }
+ private const val SHADER_ELLIPSE_MAIN = """vec4 main(vec2 p) {
+ vec2 p_distorted = distort(p, in_time, in_distort_radial, in_distort_xy);
- float softCircle(vec2 uv, vec2 xy, float radius, float blur) {
- float blurHalf = blur * 0.5;
- float d = distance(uv, xy);
- return 1. - smoothstep(1. - blurHalf, 1. + blurHalf, d / radius);
- }
+ float sparkleRing = soften(ellipseRing(p_distorted-in_center, in_size), in_blur);
+ float inside = soften(sdEllipse(p_distorted-in_center, in_size * 1.2), in_blur);
+ float sparkle = sparkles(p - mod(p, in_pixelDensity * 0.8), in_time * 0.00175)
+ * (1.-sparkleRing) * in_fadeSparkle;
- float softRing(vec2 uv, vec2 xy, float radius, float blur) {
- float thickness_half = radius * 0.25;
- float circle_outer = softCircle(uv, xy, radius + thickness_half, blur);
- float circle_inner = softCircle(uv, xy, radius - thickness_half, blur);
- return circle_outer - circle_inner;
+ float rippleInsideAlpha = (1.-inside) * in_fadeFill;
+ float rippleRingAlpha = (1.-sparkleRing) * in_fadeRing;
+ float rippleAlpha = max(rippleInsideAlpha, rippleRingAlpha) * 0.45;
+ vec4 ripple = in_color * rippleAlpha;
+ return mix(ripple, vec4(sparkle), sparkle * in_sparkle_strength);
+ }
+ """
+
+ private const val CIRCLE_SHADER = SHADER_UNIFORMS + RippleShaderUtilLibrary.SHADER_LIB +
+ SdfShaderLibrary.SHADER_SDF_OPERATION_LIB + SdfShaderLibrary.CIRCLE_SDF +
+ SHADER_CIRCLE_MAIN
+ private const val ROUNDED_BOX_SHADER = SHADER_UNIFORMS +
+ RippleShaderUtilLibrary.SHADER_LIB + SdfShaderLibrary.SHADER_SDF_OPERATION_LIB +
+ SdfShaderLibrary.ROUNDED_BOX_SDF + SHADER_ROUNDED_BOX_MAIN
+ private const val ELLIPSE_SHADER = SHADER_UNIFORMS + RippleShaderUtilLibrary.SHADER_LIB +
+ SdfShaderLibrary.SHADER_SDF_OPERATION_LIB + SdfShaderLibrary.ELLIPSE_SDF +
+ SHADER_ELLIPSE_MAIN
+
+ private fun buildShader(rippleShape: RippleShape): String =
+ when (rippleShape) {
+ RippleShape.CIRCLE -> CIRCLE_SHADER
+ RippleShape.ROUNDED_BOX -> ROUNDED_BOX_SHADER
+ RippleShape.ELLIPSE -> ELLIPSE_SHADER
}
- vec2 distort(vec2 p, vec2 origin, float time,
- float distort_amount_radial, float distort_amount_xy) {
- float2 distance = origin - p;
- float angle = atan(distance.y, distance.x);
- return p + vec2(sin(angle * 8 + time * 0.003 + 1.641),
- cos(angle * 5 + 2.14 + time * 0.00412)) * distort_amount_radial
- + vec2(sin(p.x * 0.01 + time * 0.00215 + 0.8123),
- cos(p.y * 0.01 + time * 0.005931)) * distort_amount_xy;
- }"""
- private const val SHADER_MAIN = """vec4 main(vec2 p) {
- vec2 p_distorted = distort(p, in_origin, in_time, in_distort_radial,
- in_distort_xy);
-
- // Draw shapes
- float sparkleRing = softRing(p_distorted, in_origin, in_radius, in_blur);
- float sparkle = sparkles(p - mod(p, in_pixelDensity * 0.8), in_time * 0.00175)
- * sparkleRing * in_fadeSparkle;
- float circle = softCircle(p_distorted, in_origin, in_radius * 1.2, in_blur);
- float rippleAlpha = max(circle * in_fadeCircle,
- softRing(p_distorted, in_origin, in_radius, in_blur) * in_fadeRing) * 0.45;
- vec4 ripple = in_color * rippleAlpha;
- return mix(ripple, vec4(sparkle), sparkle * in_sparkle_strength);
- }"""
- private const val SHADER = SHADER_UNIFORMS + SHADER_LIB + SHADER_MAIN
-
private fun subProgress(start: Float, end: Float, progress: Float): Float {
val min = Math.min(start, end)
val max = Math.max(start, end)
@@ -116,22 +131,18 @@ class RippleShader internal constructor() : RuntimeShader(SHADER) {
}
/**
- * Maximum radius of the ripple.
+ * Sets the center position of the ripple.
*/
- var radius: Float = 0.0f
- set(value) {
- field = value
- setFloatUniform("in_maxRadius", value)
- }
+ fun setCenter(x: Float, y: Float) {
+ setFloatUniform("in_center", x, y)
+ }
- /**
- * Origin coordinate of the ripple.
- */
- var origin: PointF = PointF()
- set(value) {
- field = value
- setFloatUniform("in_origin", value.x, value.y)
- }
+ /** Max width of the ripple. */
+ private var maxSize: PointF = PointF()
+ fun setMaxSize(width: Float, height: Float) {
+ maxSize.x = width
+ maxSize.y = height
+ }
/**
* Progress of the ripple. Float value between [0, 1].
@@ -140,20 +151,27 @@ class RippleShader internal constructor() : RuntimeShader(SHADER) {
set(value) {
field = value
setFloatUniform("in_progress", value)
- setFloatUniform("in_radius",
- (1 - (1 - value) * (1 - value) * (1 - value))* radius)
+ val curvedProg = 1 - (1 - value) * (1 - value) * (1 - value)
+
+ setFloatUniform("in_size", /* width= */ maxSize.x * curvedProg,
+ /* height= */ maxSize.y * curvedProg)
+ setFloatUniform("in_thickness", maxSize.y * curvedProg * 0.5f)
+ // radius should not exceed width and height values.
+ setFloatUniform("in_cornerRadius",
+ Math.min(maxSize.x, maxSize.y) * curvedProg)
+
setFloatUniform("in_blur", MathUtils.lerp(1.25f, 0.5f, value))
val fadeIn = subProgress(0f, 0.1f, value)
val fadeOutNoise = subProgress(0.4f, 1f, value)
var fadeOutRipple = 0f
- var fadeCircle = 0f
+ var fadeFill = 0f
if (!rippleFill) {
- fadeCircle = subProgress(0f, 0.2f, value)
+ fadeFill = subProgress(0f, 0.6f, value)
fadeOutRipple = subProgress(0.3f, 1f, value)
}
setFloatUniform("in_fadeSparkle", Math.min(fadeIn, 1 - fadeOutNoise))
- setFloatUniform("in_fadeCircle", 1 - fadeCircle)
+ setFloatUniform("in_fadeFill", 1 - fadeFill)
setFloatUniform("in_fadeRing", Math.min(fadeIn, 1 - fadeOutRipple))
}
@@ -169,7 +187,7 @@ class RippleShader internal constructor() : RuntimeShader(SHADER) {
/**
* A hex value representing the ripple color, in the format of ARGB
*/
- var color: Int = 0xffffff.toInt()
+ var color: Int = 0xffffff
set(value) {
field = value
setColorUniform("in_color", value)
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/RippleShaderUtilLibrary.kt b/packages/SystemUI/src/com/android/systemui/ripple/RippleShaderUtilLibrary.kt
new file mode 100644
index 000000000000..6de46483892b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ripple/RippleShaderUtilLibrary.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.systemui.ripple
+
+/** A common utility functions that are used for computing [RippleShader]. */
+class RippleShaderUtilLibrary {
+ //language=AGSL
+ companion object {
+ const val SHADER_LIB = """
+ float triangleNoise(vec2 n) {
+ n = fract(n * vec2(5.3987, 5.4421));
+ n += dot(n.yx, n.xy + vec2(21.5351, 14.3137));
+ float xy = n.x * n.y;
+ return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;
+ }
+ const float PI = 3.1415926535897932384626;
+
+ float sparkles(vec2 uv, float t) {
+ float n = triangleNoise(uv);
+ float s = 0.0;
+ for (float i = 0; i < 4; i += 1) {
+ float l = i * 0.01;
+ float h = l + 0.1;
+ float o = smoothstep(n - l, h, n);
+ o *= abs(sin(PI * o * (t + 0.55 * i)));
+ s += o;
+ }
+ return s;
+ }
+
+ vec2 distort(vec2 p, float time, float distort_amount_radial,
+ float distort_amount_xy) {
+ float angle = atan(p.y, p.x);
+ return p + vec2(sin(angle * 8 + time * 0.003 + 1.641),
+ cos(angle * 5 + 2.14 + time * 0.00412)) * distort_amount_radial
+ + vec2(sin(p.x * 0.01 + time * 0.00215 + 0.8123),
+ cos(p.y * 0.01 + time * 0.005931)) * distort_amount_xy;
+ }"""
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt b/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt
index fc52464ecf85..8b0120177268 100644
--- a/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt
@@ -23,39 +23,44 @@ import android.content.Context
import android.content.res.Configuration
import android.graphics.Canvas
import android.graphics.Paint
-import android.graphics.PointF
import android.util.AttributeSet
import android.view.View
+import com.android.systemui.ripple.RippleShader.RippleShape
private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.3f
+private const val RIPPLE_DEFAULT_COLOR: Int = 0xffffffff.toInt()
/**
- * A generic expanding ripple effect. To trigger the ripple expansion, set [radius] and [origin],
- * then call [startRipple].
+ * A generic expanding ripple effect.
+ *
+ * Set up the shader with a desired [RippleShape] using [setupShader], [setMaxSize] and [setCenter],
+ * then call [startRipple] to trigger the ripple expansion.
*/
open class RippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
- private val rippleShader = RippleShader()
- private val defaultColor: Int = 0xffffffff.toInt()
+
+ private lateinit var rippleShader: RippleShader
+ lateinit var rippleShape: RippleShape
+ private set
+
private val ripplePaint = Paint()
var rippleInProgress: Boolean = false
- var radius: Float = 0.0f
- set(value) {
- rippleShader.radius = value
- field = value
- }
- var origin: PointF = PointF()
- set(value) {
- rippleShader.origin = value
- field = value
- }
var duration: Long = 1750
- init {
- rippleShader.color = defaultColor
- rippleShader.progress = 0f
- rippleShader.sparkleStrength = RIPPLE_SPARKLE_STRENGTH
- ripplePaint.shader = rippleShader
+ private var maxWidth: Float = 0.0f
+ private var maxHeight: Float = 0.0f
+ fun setMaxSize(maxWidth: Float, maxHeight: Float) {
+ this.maxWidth = maxWidth
+ this.maxHeight = maxHeight
+ rippleShader.setMaxSize(maxWidth, maxHeight)
+ }
+
+ private var centerX: Float = 0.0f
+ private var centerY: Float = 0.0f
+ fun setCenter(x: Float, y: Float) {
+ this.centerX = x
+ this.centerY = y
+ rippleShader.setCenter(x, y)
}
override fun onConfigurationChanged(newConfig: Configuration?) {
@@ -68,6 +73,18 @@ open class RippleView(context: Context?, attrs: AttributeSet?) : View(context, a
super.onAttachedToWindow()
}
+ /** Initializes the shader. Must be called before [startRipple]. */
+ fun setupShader(rippleShape: RippleShape = RippleShape.CIRCLE) {
+ this.rippleShape = rippleShape
+ rippleShader = RippleShader(rippleShape)
+
+ rippleShader.color = RIPPLE_DEFAULT_COLOR
+ rippleShader.progress = 0f
+ rippleShader.sparkleStrength = RIPPLE_SPARKLE_STRENGTH
+
+ ripplePaint.shader = rippleShader
+ }
+
@JvmOverloads
fun startRipple(onAnimationEnd: Runnable? = null) {
if (rippleInProgress) {
@@ -113,11 +130,24 @@ open class RippleView(context: Context?, attrs: AttributeSet?) : View(context, a
// if it's unsupported.
return
}
- // To reduce overdraw, we mask the effect to a circle whose radius is big enough to cover
- // the active effect area. Values here should be kept in sync with the
- // animation implementation in the ripple shader.
- val maskRadius = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) *
- (1 - rippleShader.progress)) * radius * 2
- canvas.drawCircle(origin.x, origin.y, maskRadius, ripplePaint)
+ // To reduce overdraw, we mask the effect to a circle or a rectangle that's bigger than the
+ // active effect area. Values here should be kept in sync with the animation implementation
+ // in the ripple shader.
+ if (rippleShape == RippleShape.CIRCLE) {
+ val maskRadius = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) *
+ (1 - rippleShader.progress)) * maxWidth
+ canvas.drawCircle(centerX, centerY, maskRadius, ripplePaint)
+ } else {
+ val maskWidth = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) *
+ (1 - rippleShader.progress)) * maxWidth * 2
+ val maskHeight = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) *
+ (1 - rippleShader.progress)) * maxHeight * 2
+ canvas.drawRect(
+ /* left= */ centerX - maskWidth,
+ /* top= */ centerY - maskHeight,
+ /* right= */ centerX + maskWidth,
+ /* bottom= */ centerY + maskHeight,
+ ripplePaint)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/SdfShaderLibrary.kt b/packages/SystemUI/src/com/android/systemui/ripple/SdfShaderLibrary.kt
new file mode 100644
index 000000000000..5e256c653992
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ripple/SdfShaderLibrary.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.ripple
+
+/** Library class that contains 2D signed distance functions. */
+class SdfShaderLibrary {
+ //language=AGSL
+ companion object {
+ const val CIRCLE_SDF = """
+ float sdCircle(vec2 p, float r) {
+ return (length(p)-r) / r;
+ }
+
+ float circleRing(vec2 p, float radius) {
+ float thicknessHalf = radius * 0.25;
+
+ float outerCircle = sdCircle(p, radius + thicknessHalf);
+ float innerCircle = sdCircle(p, radius);
+
+ return subtract(outerCircle, innerCircle);
+ }
+ """
+
+ const val ROUNDED_BOX_SDF = """
+ float sdRoundedBox(vec2 p, vec2 size, float cornerRadius) {
+ size *= 0.5;
+ cornerRadius *= 0.5;
+ vec2 d = abs(p)-size+cornerRadius;
+
+ float outside = length(max(d, 0.0));
+ float inside = min(max(d.x, d.y), 0.0);
+
+ return (outside+inside-cornerRadius)/size.y;
+ }
+
+ float roundedBoxRing(vec2 p, vec2 size, float cornerRadius,
+ float borderThickness) {
+ float outerRoundBox = sdRoundedBox(p, size, cornerRadius);
+ float innerRoundBox = sdRoundedBox(p, size - vec2(borderThickness),
+ cornerRadius - borderThickness);
+ return subtract(outerRoundBox, innerRoundBox);
+ }
+ """
+
+ // Used non-trigonometry parametrization and Halley's method (iterative) for root finding.
+ // This is more expensive than the regular circle SDF, recommend to use the circle SDF if
+ // possible.
+ const val ELLIPSE_SDF = """float sdEllipse(vec2 p, vec2 wh) {
+ wh *= 0.5;
+
+ // symmetry
+ (wh.x > wh.y) ? wh = wh.yx, p = abs(p.yx) : p = abs(p);
+
+ vec2 u = wh*p, v = wh*wh;
+
+ float U1 = u.y/2.0; float U5 = 4.0*U1;
+ float U2 = v.y-v.x; float U6 = 6.0*U1;
+ float U3 = u.x-U2; float U7 = 3.0*U3;
+ float U4 = u.x+U2;
+
+ float t = 0.5;
+ for (int i = 0; i < 3; i ++) {
+ float F1 = t*(t*t*(U1*t+U3)+U4)-U1;
+ float F2 = t*t*(U5*t+U7)+U4;
+ float F3 = t*(U6*t+U7);
+
+ t += (F1*F2)/(F1*F3-F2*F2);
+ }
+
+ t = clamp(t, 0.0, 1.0);
+
+ float d = distance(p, wh*vec2(1.0-t*t,2.0*t)/(t*t+1.0));
+ d /= wh.y;
+
+ return (dot(p/wh,p/wh)>1.0) ? d : -d;
+ }
+
+ float ellipseRing(vec2 p, vec2 wh) {
+ vec2 thicknessHalf = wh * 0.25;
+
+ float outerEllipse = sdEllipse(p, wh + thicknessHalf);
+ float innerEllipse = sdEllipse(p, wh);
+
+ return subtract(outerEllipse, innerEllipse);
+ }
+ """
+
+ const val SHADER_SDF_OPERATION_LIB = """
+ float soften(float d, float blur) {
+ float blurHalf = blur * 0.5;
+ return smoothstep(-blurHalf, blurHalf, d);
+ }
+
+ float subtract(float outer, float inner) {
+ return max(outer, -inner);
+ }
+ """
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageCapture.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCapture.kt
new file mode 100644
index 000000000000..39f35a59ff42
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCapture.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.screenshot
+
+import android.graphics.Bitmap
+import android.graphics.Rect
+
+interface ImageCapture {
+
+ fun captureDisplay(displayId: Int, crop: Rect? = null): Bitmap?
+
+ fun captureTask(taskId: Int): Bitmap?
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt
new file mode 100644
index 000000000000..258c4360922d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt
@@ -0,0 +1,78 @@
+/*
+ * 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.screenshot
+
+import android.app.IActivityTaskManager
+import android.graphics.Bitmap
+import android.graphics.Rect
+import android.hardware.display.DisplayManager
+import android.os.IBinder
+import android.util.Log
+import android.view.DisplayAddress
+import android.view.SurfaceControl
+import android.view.SurfaceControl.DisplayCaptureArgs
+import android.view.SurfaceControl.ScreenshotHardwareBuffer
+import androidx.annotation.VisibleForTesting
+import javax.inject.Inject
+
+private const val TAG = "ImageCaptureImpl"
+
+open class ImageCaptureImpl @Inject constructor(
+ private val displayManager: DisplayManager,
+ private val atmService: IActivityTaskManager
+) : ImageCapture {
+
+ override fun captureDisplay(displayId: Int, crop: Rect?): Bitmap? {
+ val width = crop?.width() ?: 0
+ val height = crop?.height() ?: 0
+ val sourceCrop = crop ?: Rect()
+ val displayToken = physicalDisplayToken(displayId) ?: return null
+ val buffer = captureDisplay(displayToken, width, height, sourceCrop)
+
+ return buffer?.asBitmap()
+ }
+
+ override fun captureTask(taskId: Int): Bitmap? {
+ val snapshot = atmService.takeTaskSnapshot(taskId)
+ return Bitmap.wrapHardwareBuffer(snapshot.hardwareBuffer, snapshot.colorSpace)
+ }
+
+ @VisibleForTesting
+ open fun physicalDisplayToken(displayId: Int): IBinder? {
+ val display = displayManager.getDisplay(displayId)
+ if (display == null) {
+ Log.e(TAG, "No display with id: $displayId")
+ return null
+ }
+ val address = display.address
+ if (address !is DisplayAddress.Physical) {
+ Log.e(TAG, "Display does not have a physical address: $display")
+ return null
+ }
+ return SurfaceControl.getPhysicalDisplayToken(address.physicalDisplayId)
+ }
+
+ @VisibleForTesting
+ open fun captureDisplay(displayToken: IBinder, width: Int, height: Int, crop: Rect): ScreenshotHardwareBuffer? {
+ val captureArgs = DisplayCaptureArgs.Builder(displayToken)
+ .setSize(width, height)
+ .setSourceCrop(crop)
+ .build()
+ return SurfaceControl.captureDisplay(captureArgs)
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
new file mode 100644
index 000000000000..beb54c85e021
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
@@ -0,0 +1,68 @@
+/*
+ * 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.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
+import com.android.internal.util.ScreenshotHelper.HardwareBitmapBundler
+import com.android.internal.util.ScreenshotHelper.ScreenshotRequest
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback
+import java.util.function.Consumer
+import javax.inject.Inject
+
+/**
+ * Processes a screenshot request sent from {@link ScreenshotHelper}.
+ */
+@SysUISingleton
+internal class RequestProcessor @Inject constructor(
+ private val controller: ScreenshotController,
+) {
+ fun processRequest(
+ @ScreenshotType type: Int,
+ onSavedListener: Consumer<Uri>,
+ request: ScreenshotRequest,
+ callback: RequestCallback
+ ) {
+
+ if (type == TAKE_SCREENSHOT_PROVIDED_IMAGE) {
+ val image = HardwareBitmapBundler.bundleToHardwareBitmap(request.bitmapBundle)
+
+ controller.handleImageAsScreenshot(
+ image, request.boundsInScreen, request.insets,
+ request.taskId, request.userId, request.topComponent, onSavedListener, callback
+ )
+ return
+ }
+
+ when (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")
+ }
+ }
+
+ companion object {
+ const val TAG: String = "RequestProcessor"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 82de389060ae..69ee8e8fb8dc 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -57,14 +57,12 @@ import android.media.AudioSystem;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
-import android.os.IBinder;
import android.os.RemoteException;
import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Pair;
import android.view.Display;
-import android.view.DisplayAddress;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner;
import android.view.KeyEvent;
@@ -72,7 +70,6 @@ import android.view.LayoutInflater;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.ScrollCaptureResponse;
-import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewRootImpl;
import android.view.ViewTreeObserver;
@@ -249,6 +246,7 @@ public class ScreenshotController {
private final ScreenshotSmartActions mScreenshotSmartActions;
private final UiEventLogger mUiEventLogger;
private final ImageExporter mImageExporter;
+ private final ImageCapture mImageCapture;
private final Executor mMainExecutor;
private final ExecutorService mBgExecutor;
private final BroadcastSender mBroadcastSender;
@@ -295,6 +293,7 @@ public class ScreenshotController {
ScrollCaptureClient scrollCaptureClient,
UiEventLogger uiEventLogger,
ImageExporter imageExporter,
+ ImageCapture imageCapture,
@Main Executor mainExecutor,
ScrollCaptureController scrollCaptureController,
LongScreenshotData longScreenshotHolder,
@@ -308,6 +307,7 @@ public class ScreenshotController {
mScrollCaptureClient = scrollCaptureClient;
mUiEventLogger = uiEventLogger;
mImageExporter = imageExporter;
+ mImageCapture = imageCapture;
mMainExecutor = mainExecutor;
mScrollCaptureController = scrollCaptureController;
mLongScreenshotHolder = longScreenshotHolder;
@@ -531,7 +531,7 @@ public class ScreenshotController {
// copy the input Rect, since SurfaceControl.screenshot can mutate it
Rect screenRect = new Rect(crop);
- Bitmap screenshot = captureScreenshot(crop);
+ Bitmap screenshot = mImageCapture.captureDisplay(DEFAULT_DISPLAY, crop);
if (screenshot == null) {
Log.e(TAG, "takeScreenshotInternal: Screenshot bitmap was null");
@@ -549,32 +549,6 @@ public class ScreenshotController {
ClipboardOverlayController.SELF_PERMISSION);
}
- private Bitmap captureScreenshot(Rect crop) {
- int width = crop.width();
- int height = crop.height();
- Bitmap screenshot = null;
- final Display display = getDefaultDisplay();
- final DisplayAddress address = display.getAddress();
- if (!(address instanceof DisplayAddress.Physical)) {
- Log.e(TAG, "Skipping Screenshot - Default display does not have a physical address: "
- + display);
- } else {
- final DisplayAddress.Physical physicalAddress = (DisplayAddress.Physical) address;
-
- final IBinder displayToken = SurfaceControl.getPhysicalDisplayToken(
- physicalAddress.getPhysicalDisplayId());
- final SurfaceControl.DisplayCaptureArgs captureArgs =
- new SurfaceControl.DisplayCaptureArgs.Builder(displayToken)
- .setSourceCrop(crop)
- .setSize(width, height)
- .build();
- final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
- SurfaceControl.captureDisplay(captureArgs);
- screenshot = screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
- }
- return screenshot;
- }
-
private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
Insets screenInsets, ComponentName topComponent, boolean showFlash) {
withWindowAttached(() ->
@@ -720,7 +694,7 @@ public class ScreenshotController {
mScreenshotView.showScrollChip(response.getPackageName(), /* onClick */ () -> {
DisplayMetrics displayMetrics = new DisplayMetrics();
getDefaultDisplay().getRealMetrics(displayMetrics);
- Bitmap newScreenshot = captureScreenshot(
+ Bitmap newScreenshot = mImageCapture.captureDisplay(DEFAULT_DISPLAY,
new Rect(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels));
mScreenshotView.prepareScrollingTransition(response, mScreenBitmap, newScreenshot,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 32d82037efb9..f1f0223632b7 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -21,6 +21,7 @@ import static android.content.Intent.ACTION_CLOSE_SYSTEM_DIALOGS;
import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_PROCESS_COMPLETE;
import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_URI;
+import static com.android.systemui.flags.Flags.SCREENSHOT_REQUEST_PROCESSOR;
import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK;
import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS;
import static com.android.systemui.screenshot.LogConfig.DEBUG_SERVICE;
@@ -57,6 +58,8 @@ import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.ScreenshotHelper;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.FlagListenable.FlagEvent;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -75,6 +78,8 @@ public class TakeScreenshotService extends Service {
private final Handler mHandler;
private final Context mContext;
private final @Background Executor mBgExecutor;
+ private final RequestProcessor mProcessor;
+ private final FeatureFlags mFeatureFlags;
private final BroadcastReceiver mCloseSystemDialogs = new BroadcastReceiver() {
@Override
@@ -104,7 +109,8 @@ public class TakeScreenshotService extends Service {
public TakeScreenshotService(ScreenshotController screenshotController, UserManager userManager,
DevicePolicyManager devicePolicyManager, UiEventLogger uiEventLogger,
ScreenshotNotificationsController notificationsController, Context context,
- @Background Executor bgExecutor) {
+ @Background Executor bgExecutor, FeatureFlags featureFlags,
+ RequestProcessor processor) {
if (DEBUG_SERVICE) {
Log.d(TAG, "new " + this);
}
@@ -116,6 +122,9 @@ public class TakeScreenshotService extends Service {
mNotificationsController = notificationsController;
mContext = context;
mBgExecutor = bgExecutor;
+ mFeatureFlags = featureFlags;
+ mFeatureFlags.addListener(SCREENSHOT_REQUEST_PROCESSOR, FlagEvent::requestNoRestart);
+ mProcessor = processor;
}
@Override
@@ -218,6 +227,12 @@ public class TakeScreenshotService extends Service {
mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(screenshotRequest.getSource()), 0,
topComponent == null ? "" : topComponent.getPackageName());
+ if (mFeatureFlags.isEnabled(SCREENSHOT_REQUEST_PROCESSOR)) {
+ Log.d(TAG, "handleMessage: Using request processor");
+ mProcessor.processRequest(msg.what, uriConsumer, screenshotRequest, requestCallback);
+ return true;
+ }
+
switch (msg.what) {
case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
if (DEBUG_SERVICE) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
index 91ef3c360186..3e44258744f2 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
@@ -18,6 +18,8 @@ package com.android.systemui.screenshot.dagger;
import android.app.Service;
+import com.android.systemui.screenshot.ImageCapture;
+import com.android.systemui.screenshot.ImageCaptureImpl;
import com.android.systemui.screenshot.TakeScreenshotService;
import dagger.Binds;
@@ -37,4 +39,6 @@ public abstract class ScreenshotModule {
@ClassKey(TakeScreenshotService.class)
public abstract Service bindTakeScreenshotService(TakeScreenshotService service);
+ @Binds
+ public abstract ImageCapture bindImageCapture(ImageCaptureImpl capture);
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
index 5793105e481e..0f9ac360cbe1 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
@@ -264,14 +264,8 @@ class LargeScreenShadeHeaderController @Inject constructor(
Utils.getColorAttrDefaultColor(header.context, android.R.attr.textColorPrimary)
)
- carrierIconSlots = if (featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)) {
- listOf(
- header.context.getString(com.android.internal.R.string.status_bar_no_calling),
- header.context.getString(com.android.internal.R.string.status_bar_call_strength)
- )
- } else {
+ carrierIconSlots =
listOf(header.context.getString(com.android.internal.R.string.status_bar_mobile))
- }
qsCarrierGroupController = qsCarrierGroupControllerBuilder
.setQSCarrierGroup(qsCarrierGroup)
.build()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 58e9bb85503a..7f0e76b9d336 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -44,7 +44,6 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
-import android.app.ActivityManager;
import android.app.Fragment;
import android.app.StatusBarManager;
import android.content.ContentResolver;
@@ -288,7 +287,6 @@ public final class NotificationPanelViewController extends PanelViewController {
private final NotificationPanelView mView;
private final VibratorHelper mVibratorHelper;
private final MetricsLogger mMetricsLogger;
- private final ActivityManager mActivityManager;
private final ConfigurationController mConfigurationController;
private final Provider<FlingAnimationUtils.Builder> mFlingAnimationUtilsBuilder;
private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
@@ -341,14 +339,14 @@ public final class NotificationPanelViewController extends PanelViewController {
private final RecordingController mRecordingController;
private final PanelEventsEmitter mPanelEventsEmitter;
private boolean mSplitShadeEnabled;
- // The bottom padding reserved for elements of the keyguard measuring notifications
+ /** The bottom padding reserved for elements of the keyguard measuring notifications. */
private float mKeyguardNotificationBottomPadding;
/**
* The top padding from where notification should start in lockscreen.
* Should be static also during animations and should match the Y of the first notification.
*/
private float mKeyguardNotificationTopPadding;
- // Current max allowed keyguard notifications determined by measuring the panel
+ /** Current max allowed keyguard notifications determined by measuring the panel. */
private int mMaxAllowedKeyguardNotifications;
private KeyguardQsUserSwitchController mKeyguardQsUserSwitchController;
@@ -728,7 +726,6 @@ public final class NotificationPanelViewController extends PanelViewController {
AccessibilityManager accessibilityManager, @DisplayId int displayId,
KeyguardUpdateMonitor keyguardUpdateMonitor,
MetricsLogger metricsLogger,
- ActivityManager activityManager,
ConfigurationController configurationController,
Provider<FlingAnimationUtils.Builder> flingAnimationUtilsBuilder,
StatusBarTouchableRegionManager statusBarTouchableRegionManager,
@@ -798,7 +795,6 @@ public final class NotificationPanelViewController extends PanelViewController {
panelExpansionStateManager,
ambientState,
interactionJankMonitor,
- keyguardUnlockAnimationController,
systemClock);
mView = view;
mVibratorHelper = vibratorHelper;
@@ -808,7 +804,6 @@ public final class NotificationPanelViewController extends PanelViewController {
mQRCodeScannerController = qrCodeScannerController;
mControlsComponent = controlsComponent;
mMetricsLogger = metricsLogger;
- mActivityManager = activityManager;
mConfigurationController = configurationController;
mFlingAnimationUtilsBuilder = flingAnimationUtilsBuilder;
mMediaHierarchyManager = mediaHierarchyManager;
@@ -1504,10 +1499,7 @@ public final class NotificationPanelViewController extends PanelViewController {
}
private void updateKeyguardStatusViewAlignment(boolean animate) {
- boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
- .getVisibleNotificationCount() != 0
- || mMediaDataManager.hasActiveMediaOrRecommendation();
- boolean shouldBeCentered = !mSplitShadeEnabled || !hasVisibleNotifications || mDozing;
+ boolean shouldBeCentered = shouldKeyguardStatusViewBeCentered();
if (mStatusViewCentered != shouldBeCentered) {
mStatusViewCentered = shouldBeCentered;
ConstraintSet constraintSet = new ConstraintSet();
@@ -1531,6 +1523,15 @@ public final class NotificationPanelViewController extends PanelViewController {
mKeyguardUnfoldTransition.ifPresent(t -> t.setStatusViewCentered(mStatusViewCentered));
}
+ private boolean shouldKeyguardStatusViewBeCentered() {
+ boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
+ .getVisibleNotificationCount() != 0
+ || mMediaDataManager.hasActiveMediaOrRecommendation();
+ boolean isOnAod = mDozing && mDozeParameters.getAlwaysOn();
+ return !mSplitShadeEnabled || !hasVisibleNotifications || isOnAod
+ || hasPulsingNotifications();
+ }
+
/**
* @return the padding of the stackscroller when unlocked
*/
@@ -3969,7 +3970,7 @@ public final class NotificationPanelViewController extends PanelViewController {
* notification data being displayed. In the new notification pipeline, this is handled in
* {@link ShadeViewManager}.
*/
- public void updateNotificationViews(String reason) {
+ public void updateNotificationViews() {
mNotificationStackScrollLayoutController.updateFooter();
mNotificationIconAreaController.updateNotificationIcons(createVisibleEntriesList());
@@ -4426,7 +4427,7 @@ public final class NotificationPanelViewController extends PanelViewController {
NotificationStackScrollLayout.OnEmptySpaceClickListener {
@Override
public void onEmptySpaceClicked(float x, float y) {
- onEmptySpaceClick(x);
+ onEmptySpaceClick();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 15e1129e43b5..1d9210592b78 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -89,7 +89,6 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
Dumpable, ConfigurationListener {
private static final String TAG = "NotificationShadeWindowController";
- private static final boolean DEBUG = false;
private final Context mContext;
private final WindowManager mWindowManager;
@@ -190,7 +189,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
return;
}
}
- mCallbacks.add(new WeakReference<StatusBarWindowCallback>(callback));
+ mCallbacks.add(new WeakReference<>(callback));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
index 121d69d34678..e52170e13292 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
@@ -18,6 +18,8 @@ package com.android.systemui.shade;
import static android.view.WindowInsets.Type.systemBars;
+import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG;
+
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
import android.annotation.LayoutRes;
@@ -52,14 +54,12 @@ import android.widget.FrameLayout;
import com.android.internal.view.FloatingActionMode;
import com.android.internal.widget.floatingtoolbar.FloatingToolbar;
import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
/**
* Combined keyguard and notification panel view. Also holding backdrop and scrims.
*/
public class NotificationShadeWindowView extends FrameLayout {
public static final String TAG = "NotificationShadeWindowView";
- public static final boolean DEBUG = CentralSurfaces.DEBUG;
private int mRightInset = 0;
private int mLeftInset = 0;
@@ -221,7 +221,7 @@ public class NotificationShadeWindowView extends FrameLayout {
}
}
- class LayoutParams extends FrameLayout.LayoutParams {
+ private static class LayoutParams extends FrameLayout.LayoutParams {
public boolean ignoreRightInset;
@@ -243,7 +243,7 @@ public class NotificationShadeWindowView extends FrameLayout {
public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback,
int type) {
if (type == ActionMode.TYPE_FLOATING) {
- return startActionMode(originalView, callback, type);
+ return startActionMode(originalView, callback);
}
return super.startActionModeForChild(originalView, callback, type);
}
@@ -258,14 +258,10 @@ public class NotificationShadeWindowView extends FrameLayout {
final FloatingActionMode mode =
new FloatingActionMode(mContext, callback, originatingView, mFloatingToolbar);
mFloatingActionModeOriginatingView = originatingView;
- mFloatingToolbarPreDrawListener =
- new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- mode.updateViewLocationInWindow();
- return true;
- }
- };
+ mFloatingToolbarPreDrawListener = () -> {
+ mode.updateViewLocationInWindow();
+ return true;
+ };
return mode;
}
@@ -292,10 +288,10 @@ public class NotificationShadeWindowView extends FrameLayout {
}
private ActionMode startActionMode(
- View originatingView, ActionMode.Callback callback, int type) {
+ View originatingView, ActionMode.Callback callback) {
ActionMode.Callback2 wrappedCallback = new ActionModeCallback2Wrapper(callback);
ActionMode mode = createFloatingActionMode(originatingView, wrappedCallback);
- if (mode != null && wrappedCallback.onCreateActionMode(mode, mode.getMenu())) {
+ if (wrappedCallback.onCreateActionMode(mode, mode.getMenu())) {
setHandledFloatingActionMode(mode);
} else {
mode = null;
@@ -382,7 +378,7 @@ public class NotificationShadeWindowView extends FrameLayout {
/**
* Minimal window to satisfy FloatingToolbar.
*/
- private Window mFakeWindow = new Window(mContext) {
+ private final Window mFakeWindow = new Window(mContext) {
@Override
public void takeSurface(SurfaceHolder.Callback2 callback) {
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
index 587e0e6dd834..02316b7965ca 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
@@ -48,13 +48,12 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
private View mStackScroller;
private View mKeyguardStatusBar;
- private ArrayList<View> mDrawingOrderedChildren = new ArrayList<>();
- private ArrayList<View> mLayoutDrawingOrder = new ArrayList<>();
+ private final ArrayList<View> mDrawingOrderedChildren = new ArrayList<>();
+ private final ArrayList<View> mLayoutDrawingOrder = new ArrayList<>();
private final Comparator<View> mIndexComparator = Comparator.comparingInt(this::indexOfChild);
private Consumer<WindowInsets> mInsetsChangedListener = insets -> {};
private Consumer<QS> mQSFragmentAttachedListener = qs -> {};
private QS mQs;
- private View mQSScrollView;
private View mQSContainer;
@Nullable
@@ -76,7 +75,6 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
public void onFragmentViewCreated(String tag, Fragment fragment) {
mQs = (QS) fragment;
mQSFragmentAttachedListener.accept(mQs);
- mQSScrollView = mQs.getView().findViewById(R.id.expanded_qs_scroll_view);
mQSContainer = mQs.getView().findViewById(R.id.quick_settings_container);
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/PanelView.java b/packages/SystemUI/src/com/android/systemui/shade/PanelView.java
index 1082967d3b5f..efff0db742d7 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/PanelView.java
@@ -49,10 +49,6 @@ public abstract class PanelView extends FrameLayout {
super(context, attrs, defStyleAttr);
}
- public PanelView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
public void setOnTouchListener(PanelViewController.TouchHandler touchHandler) {
super.setOnTouchListener(touchHandler);
mTouchHandler = touchHandler;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java
index 229acf4ed061..4aad245f96fd 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java
@@ -19,11 +19,11 @@ package com.android.systemui.shade;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
import static com.android.systemui.classifier.Classifier.GENERIC;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import static com.android.systemui.classifier.Classifier.UNLOCK;
+import static com.android.systemui.shade.PanelView.DEBUG;
import static java.lang.Float.isNaN;
@@ -53,7 +53,6 @@ import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.doze.DozeLog;
-import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.StatusBarState;
@@ -77,7 +76,6 @@ import java.io.PrintWriter;
import java.util.List;
public abstract class PanelViewController {
- public static final boolean DEBUG = PanelView.DEBUG;
public static final String TAG = PanelView.class.getSimpleName();
public static final float FLING_MAX_LENGTH_SECONDS = 0.6f;
public static final float FLING_SPEED_UP_FACTOR = 0.6f;
@@ -97,7 +95,7 @@ public abstract class PanelViewController {
protected boolean mTouchSlopExceededBeforeDown;
private float mMinExpandHeight;
private boolean mPanelUpdateWhenAnimatorEnds;
- private boolean mVibrateOnOpening;
+ private final boolean mVibrateOnOpening;
protected boolean mIsLaunchAnimationRunning;
private int mFixedDuration = NO_FIXED_DURATION;
protected float mOverExpansion;
@@ -144,7 +142,6 @@ public abstract class PanelViewController {
private int mTouchSlop;
private float mSlopMultiplier;
protected boolean mHintAnimationRunning;
- private boolean mOverExpandedBeforeFling;
private boolean mTouchAboveFalsingThreshold;
private int mUnlockFalsingThreshold;
private boolean mTouchStartedInEmptyArea;
@@ -155,9 +152,9 @@ public abstract class PanelViewController {
private ValueAnimator mHeightAnimator;
private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
- private FlingAnimationUtils mFlingAnimationUtils;
- private FlingAnimationUtils mFlingAnimationUtilsClosing;
- private FlingAnimationUtils mFlingAnimationUtilsDismissing;
+ private final FlingAnimationUtils mFlingAnimationUtils;
+ private final FlingAnimationUtils mFlingAnimationUtilsClosing;
+ private final FlingAnimationUtils mFlingAnimationUtilsDismissing;
private final LatencyTracker mLatencyTracker;
private final FalsingManager mFalsingManager;
private final DozeLog mDozeLog;
@@ -174,13 +171,14 @@ public abstract class PanelViewController {
private float mInitialTouchY;
private float mInitialTouchX;
private boolean mTouchDisabled;
+ private boolean mInitialTouchFromKeyguard;
/**
* Whether or not the PanelView can be expanded or collapsed with a drag.
*/
- private boolean mNotificationsDragEnabled;
+ private final boolean mNotificationsDragEnabled;
- private Interpolator mBounceInterpolator;
+ private final Interpolator mBounceInterpolator;
protected KeyguardBottomAreaView mKeyguardBottomArea;
/**
@@ -201,7 +199,6 @@ public abstract class PanelViewController {
protected final AmbientState mAmbientState;
protected final LockscreenGestureLogger mLockscreenGestureLogger;
private final PanelExpansionStateManager mPanelExpansionStateManager;
- private final TouchHandler mTouchHandler;
private final InteractionJankMonitor mInteractionJankMonitor;
protected final SystemClock mSystemClock;
@@ -229,8 +226,6 @@ public abstract class PanelViewController {
return mAmbientState;
}
- private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
-
public PanelViewController(
PanelView view,
FalsingManager falsingManager,
@@ -247,9 +242,7 @@ public abstract class PanelViewController {
PanelExpansionStateManager panelExpansionStateManager,
AmbientState ambientState,
InteractionJankMonitor interactionJankMonitor,
- KeyguardUnlockAnimationController keyguardUnlockAnimationController,
SystemClock systemClock) {
- mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
keyguardStateController.addCallback(new KeyguardStateController.Callback() {
@Override
public void onKeyguardFadingAwayChanged() {
@@ -261,7 +254,7 @@ public abstract class PanelViewController {
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mLockscreenGestureLogger = lockscreenGestureLogger;
mPanelExpansionStateManager = panelExpansionStateManager;
- mTouchHandler = createTouchHandler();
+ TouchHandler touchHandler = createTouchHandler();
mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
@@ -274,7 +267,7 @@ public abstract class PanelViewController {
});
mView.addOnLayoutChangeListener(createLayoutChangeListener());
- mView.setOnTouchListener(mTouchHandler);
+ mView.setOnTouchListener(touchHandler);
mView.setOnConfigurationChangedListener(createOnConfigurationChangedListener());
mResources = mView.getResources();
@@ -398,11 +391,12 @@ public abstract class PanelViewController {
public void startExpandMotion(float newX, float newY, boolean startTracking,
float expandedHeight) {
if (!mHandlingPointerUp && !mStatusBarStateController.isDozing()) {
- beginJankMonitoring(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ beginJankMonitoring();
}
mInitialOffsetOnTouch = expandedHeight;
mInitialTouchY = newY;
mInitialTouchX = newX;
+ mInitialTouchFromKeyguard = mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
if (startTracking) {
mTouchSlopExceeded = true;
setExpandedHeight(mInitialOffsetOnTouch);
@@ -425,20 +419,14 @@ public abstract class PanelViewController {
mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
final boolean expand;
- if (event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel) {
- // If the keyguard is fading away, don't expand it again. This can happen if you're
- // swiping to unlock, the app below the keyguard is in landscape, and the screen
- // rotates while your finger is still down after the swipe to unlock.
- if (mKeyguardStateController.isKeyguardFadingAway()) {
- expand = false;
- } else if (onKeyguard) {
+ if (mKeyguardStateController.isKeyguardFadingAway()
+ || (mInitialTouchFromKeyguard && !onKeyguard)) {
+ // Don't expand for any touches that started from the keyguard and ended after the
+ // keyguard is gone.
+ expand = false;
+ } else if (event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel) {
+ if (onKeyguard) {
expand = true;
- } else if (mKeyguardStateController.isKeyguardFadingAway()) {
- // If we're in the middle of dismissing the keyguard, don't expand due to the
- // cancelled gesture. Gesture cancellation during an unlock is expected in some
- // situations, such keeping your finger down while swiping to unlock to an app
- // that is locked in landscape (the rotation will cancel the touch event).
- expand = false;
} else if (mCentralSurfaces.isBouncerShowingOverDream()) {
expand = false;
} else {
@@ -475,7 +463,7 @@ public abstract class PanelViewController {
} else if (!mCentralSurfaces.isBouncerShowing()
&& !mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()
&& !mKeyguardStateController.isKeyguardGoingAway()) {
- boolean expands = onEmptySpaceClick(mInitialTouchX);
+ boolean expands = onEmptySpaceClick();
onTrackingStopped(expands);
}
mVelocityTracker.clear();
@@ -670,7 +658,7 @@ public abstract class PanelViewController {
@Override
public void onAnimationStart(Animator animation) {
if (!mStatusBarStateController.isDozing()) {
- beginJankMonitoring(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ beginJankMonitoring();
}
}
@@ -702,10 +690,8 @@ public abstract class PanelViewController {
mIsSpringBackAnimation = true;
ValueAnimator animator = ValueAnimator.ofFloat(mOverExpansion, 0);
animator.addUpdateListener(
- animation -> {
- setOverExpansionInternal((float) animation.getAnimatedValue(),
- false /* isFromGesture */);
- });
+ animation -> setOverExpansionInternal((float) animation.getAnimatedValue(),
+ false /* isFromGesture */));
animator.setDuration(SHADE_OPEN_SPRING_BACK_DURATION);
animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
animator.addListener(new AnimatorListenerAdapter() {
@@ -731,10 +717,10 @@ public abstract class PanelViewController {
setAnimator(null);
mKeyguardStateController.notifyPanelFlingEnd();
if (!cancelled) {
- endJankMonitoring(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ endJankMonitoring();
notifyExpandingFinished();
} else {
- cancelJankMonitoring(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ cancelJankMonitoring();
}
updatePanelExpansionAndVisibility();
}
@@ -773,13 +759,6 @@ public abstract class PanelViewController {
setExpandedHeight(currentMaxPanelHeight);
}
- private float getStackHeightFraction(float height) {
- final float gestureFraction = height / getMaxPanelHeight();
- final float stackHeightFraction = Interpolators.ACCELERATE_DECELERATE
- .getInterpolation(gestureFraction);
- return stackHeightFraction;
- }
-
public void setExpandedHeightInternal(float h) {
if (isNaN(h)) {
Log.wtf(TAG, "ExpandedHeight set to NaN");
@@ -911,13 +890,8 @@ public abstract class PanelViewController {
return !isFullyCollapsed() && !mTracking && !mClosing;
}
- private final Runnable mFlingCollapseRunnable = new Runnable() {
- @Override
- public void run() {
- fling(0, false /* expand */, mNextCollapseSpeedUpFactor,
- false /* expandBecauseOfFalsing */);
- }
- };
+ private final Runnable mFlingCollapseRunnable = () -> fling(0, false /* expand */,
+ mNextCollapseSpeedUpFactor, false /* expandBecauseOfFalsing */);
public void expand(final boolean animate) {
if (!isFullyCollapsed() && !isCollapsing()) {
@@ -950,7 +924,7 @@ public abstract class PanelViewController {
mView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
if (mAnimateAfterExpanding) {
notifyExpandingStarted();
- beginJankMonitoring(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ beginJankMonitoring();
fling(0, true /* expand */);
} else {
setExpandedFraction(1f);
@@ -1150,7 +1124,7 @@ public abstract class PanelViewController {
*
* @return whether the panel will be expanded after the action performed by this method
*/
- protected boolean onEmptySpaceClick(float x) {
+ protected boolean onEmptySpaceClick() {
if (mHintAnimationRunning) {
return true;
}
@@ -1432,9 +1406,9 @@ public abstract class PanelViewController {
// mHeightAnimator is null, there is no remaining frame, ends instrumenting.
if (mHeightAnimator == null) {
if (event.getActionMasked() == MotionEvent.ACTION_UP) {
- endJankMonitoring(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ endJankMonitoring();
} else {
- cancelJankMonitoring(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ cancelJankMonitoring();
}
}
break;
@@ -1465,28 +1439,32 @@ public abstract class PanelViewController {
}
}
- private void beginJankMonitoring(int cuj) {
+ private void beginJankMonitoring() {
if (mInteractionJankMonitor == null) {
return;
}
InteractionJankMonitor.Configuration.Builder builder =
- InteractionJankMonitor.Configuration.Builder.withView(cuj, mView)
+ InteractionJankMonitor.Configuration.Builder.withView(
+ InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE,
+ mView)
.setTag(isFullyCollapsed() ? "Expand" : "Collapse");
mInteractionJankMonitor.begin(builder);
}
- private void endJankMonitoring(int cuj) {
+ private void endJankMonitoring() {
if (mInteractionJankMonitor == null) {
return;
}
- InteractionJankMonitor.getInstance().end(cuj);
+ InteractionJankMonitor.getInstance().end(
+ InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
}
- private void cancelJankMonitoring(int cuj) {
+ private void cancelJankMonitoring() {
if (mInteractionJankMonitor == null) {
return;
}
- InteractionJankMonitor.getInstance().cancel(cuj);
+ InteractionJankMonitor.getInstance().cancel(
+ InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
}
protected float getExpansionFraction() {
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
index 1b74ac36ebf0..b02a45a913db 100644
--- a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
@@ -18,7 +18,7 @@ package com.android.systemui.smartspace.dagger
import com.android.systemui.plugins.BcSmartspaceDataPlugin
import com.android.systemui.smartspace.SmartspacePrecondition
import com.android.systemui.smartspace.SmartspaceTargetFilter
-import com.android.systemui.smartspace.filters.LockscreenTargetFilter
+import com.android.systemui.smartspace.filters.LockscreenAndDreamTargetFilter
import com.android.systemui.smartspace.preconditions.LockscreenPrecondition
import dagger.Binds
import dagger.BindsOptionalOf
@@ -61,7 +61,7 @@ abstract class SmartspaceModule {
@Binds
@Named(LOCKSCREEN_SMARTSPACE_TARGET_FILTER)
abstract fun provideLockscreenSmartspaceTargetFilter(
- filter: LockscreenTargetFilter?
+ filter: LockscreenAndDreamTargetFilter?
): SmartspaceTargetFilter?
@Binds
@@ -69,4 +69,4 @@ abstract class SmartspaceModule {
abstract fun bindSmartspacePrecondition(
lockscreenPrecondition: LockscreenPrecondition?
): SmartspacePrecondition?
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenTargetFilter.kt b/packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenAndDreamTargetFilter.kt
index 6ad490169c17..aa4cf75a7a6e 100644
--- a/packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenTargetFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenAndDreamTargetFilter.kt
@@ -32,9 +32,9 @@ import java.util.concurrent.Executor
import javax.inject.Inject
/**
- * {@link SmartspaceTargetFilter} for smartspace targets that show above the lockscreen.
+ * {@link SmartspaceTargetFilter} for smartspace targets that show above the lockscreen and dreams.
*/
-class LockscreenTargetFilter @Inject constructor(
+class LockscreenAndDreamTargetFilter @Inject constructor(
private val secureSettings: SecureSettings,
private val userTracker: UserTracker,
private val execution: Execution,
@@ -149,4 +149,4 @@ class LockscreenTargetFilter @Inject constructor(
listeners.forEach { it.onCriteriaChanged() }
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
index 3013ad0070a0..a57d849b24fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
@@ -61,21 +61,19 @@ public class StatusBarMobileView extends FrameLayout implements DarkReceiver,
private int mVisibleState = -1;
private DualToneHandler mDualToneHandler;
private boolean mForceHidden;
- private boolean mProviderModel;
/**
* Designated constructor
*/
public static StatusBarMobileView fromContext(
Context context,
- String slot,
- boolean providerModel
+ String slot
) {
LayoutInflater inflater = LayoutInflater.from(context);
StatusBarMobileView v = (StatusBarMobileView)
inflater.inflate(R.layout.status_bar_mobile_signal_group, null);
v.setSlot(slot);
- v.init(providerModel);
+ v.init();
v.setVisibleState(STATE_ICON);
return v;
}
@@ -108,17 +106,12 @@ public class StatusBarMobileView extends FrameLayout implements DarkReceiver,
outRect.bottom += translationY;
}
- private void init(boolean providerModel) {
- mProviderModel = providerModel;
+ private void init() {
mDualToneHandler = new DualToneHandler(getContext());
mMobileGroup = findViewById(R.id.mobile_group);
mMobile = findViewById(R.id.mobile_signal);
mMobileType = findViewById(R.id.mobile_type);
- if (mProviderModel) {
- mMobileRoaming = findViewById(R.id.mobile_roaming_large);
- } else {
- mMobileRoaming = findViewById(R.id.mobile_roaming);
- }
+ mMobileRoaming = findViewById(R.id.mobile_roaming);
mMobileRoamingSpace = findViewById(R.id.mobile_roaming_space);
mIn = findViewById(R.id.mobile_in);
mOut = findViewById(R.id.mobile_out);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java
index 6914ae67f4ba..163878004177 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java
@@ -21,6 +21,7 @@ import android.os.Message;
import android.telephony.SubscriptionInfo;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.connectivity.NetworkController.EmergencyListener;
@@ -36,6 +37,7 @@ import javax.inject.Inject;
* Implements network listeners and forwards the calls along onto other listeners but on
* the current or specified Looper.
*/
+@SysUISingleton
public class CallbackHandler extends Handler implements EmergencyListener, SignalCallback {
private static final String TAG = "CallbackHandler";
private static final int MSG_EMERGENCE_CHANGED = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
index f2014e9deb72..ec221b7eccc0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
@@ -26,25 +26,17 @@ import android.net.NetworkCapabilities;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings.Global;
-import android.telephony.AccessNetworkConstants;
import android.telephony.CellSignalStrength;
import android.telephony.CellSignalStrengthCdma;
-import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
-import android.telephony.ims.ImsException;
-import android.telephony.ims.ImsMmTelManager;
-import android.telephony.ims.ImsReasonInfo;
-import android.telephony.ims.ImsRegistrationAttributes;
-import android.telephony.ims.RegistrationManager.RegistrationCallback;
import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.settingslib.AccessibilityContentDescriptions;
import com.android.settingslib.SignalIcon.MobileIconGroup;
import com.android.settingslib.graph.SignalDrawable;
import com.android.settingslib.mobile.MobileMappings.Config;
@@ -54,8 +46,6 @@ import com.android.settingslib.mobile.MobileStatusTracker.SubscriptionDefaults;
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.net.SignalStrengthUtil;
import com.android.systemui.R;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.util.CarrierConfigTracker;
import java.io.PrintWriter;
@@ -70,33 +60,22 @@ import java.util.Map;
public class MobileSignalController extends SignalController<MobileState, MobileIconGroup> {
private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
private static final int STATUS_HISTORY_SIZE = 64;
- private static final int IMS_TYPE_WWAN = 1;
- private static final int IMS_TYPE_WLAN = 2;
- private static final int IMS_TYPE_WLAN_CROSS_SIM = 3;
private final TelephonyManager mPhone;
private final CarrierConfigTracker mCarrierConfigTracker;
- private final ImsMmTelManager mImsMmTelManager;
private final SubscriptionDefaults mDefaults;
private final String mNetworkNameDefault;
private final String mNetworkNameSeparator;
private final ContentObserver mObserver;
- private final boolean mProviderModelBehavior;
- private final Handler mReceiverHandler;
- private int mImsType = IMS_TYPE_WWAN;
// Save entire info for logging, we only use the id.
final SubscriptionInfo mSubscriptionInfo;
private Map<String, MobileIconGroup> mNetworkToIconLookup;
- private int mLastLevel;
private MobileIconGroup mDefaultIcons;
private Config mConfig;
@VisibleForTesting
boolean mInflateSignalStrengths = false;
- private int mLastWwanLevel;
- private int mLastWlanLevel;
- private int mLastWlanCrossSimLevel;
@VisibleForTesting
- MobileStatusTracker mMobileStatusTracker;
+ final MobileStatusTracker mMobileStatusTracker;
// Save the previous STATUS_HISTORY_SIZE states for logging.
private final String[] mMobileStatusHistory = new String[STATUS_HISTORY_SIZE];
@@ -133,52 +112,6 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
}
};
- private final RegistrationCallback mRegistrationCallback = new RegistrationCallback() {
- @Override
- public void onRegistered(ImsRegistrationAttributes attributes) {
- Log.d(mTag, "onRegistered: " + "attributes=" + attributes);
- int imsTransportType = attributes.getTransportType();
- int registrationAttributes = attributes.getAttributeFlags();
- if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
- mImsType = IMS_TYPE_WWAN;
- IconState statusIcon = new IconState(
- true,
- getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
- getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- } else if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
- if (registrationAttributes == 0) {
- mImsType = IMS_TYPE_WLAN;
- IconState statusIcon = new IconState(
- true,
- getCallStrengthIcon(mLastWlanLevel, /* isWifi= */true),
- getCallStrengthDescription(mLastWlanLevel, /* isWifi= */true));
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- } else if (registrationAttributes
- == ImsRegistrationAttributes.ATTR_EPDG_OVER_CELL_INTERNET) {
- mImsType = IMS_TYPE_WLAN_CROSS_SIM;
- IconState statusIcon = new IconState(
- true,
- getCallStrengthIcon(mLastWlanCrossSimLevel, /* isWifi= */false),
- getCallStrengthDescription(
- mLastWlanCrossSimLevel, /* isWifi= */false));
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- }
- }
- }
-
- @Override
- public void onUnregistered(ImsReasonInfo info) {
- Log.d(mTag, "onDeregistered: " + "info=" + info);
- mImsType = IMS_TYPE_WWAN;
- IconState statusIcon = new IconState(
- true,
- getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
- getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- }
- };
-
// TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't
// need listener lists anymore.
public MobileSignalController(
@@ -192,7 +125,7 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
SubscriptionDefaults defaults,
Looper receiverLooper,
CarrierConfigTracker carrierConfigTracker,
- FeatureFlags featureFlags
+ MobileStatusTrackerFactory mobileStatusTrackerFactory
) {
super("MobileSignalController(" + info.getSubscriptionId() + ")", context,
NetworkCapabilities.TRANSPORT_CELLULAR, callbackHandler,
@@ -206,7 +139,6 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
R.string.status_bar_network_name_separator).toString();
mNetworkNameDefault = getTextIfExists(
com.android.internal.R.string.lockscreen_carrier_default).toString();
- mReceiverHandler = new Handler(receiverLooper);
mNetworkToIconLookup = mapIconSets(mConfig);
mDefaultIcons = getDefaultIcons(mConfig);
@@ -223,10 +155,7 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
updateTelephony();
}
};
- mImsMmTelManager = ImsMmTelManager.createForSubscriptionId(info.getSubscriptionId());
- mMobileStatusTracker = new MobileStatusTracker(mPhone, receiverLooper,
- info, mDefaults, mMobileCallback);
- mProviderModelBehavior = featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS);
+ mMobileStatusTracker = mobileStatusTrackerFactory.createTracker(mMobileCallback);
}
void setConfiguration(Config config) {
@@ -271,41 +200,14 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
mContext.getContentResolver().registerContentObserver(Global.getUriFor(
Global.MOBILE_DATA + mSubscriptionInfo.getSubscriptionId()),
true, mObserver);
- if (mProviderModelBehavior) {
- mReceiverHandler.post(mTryRegisterIms);
- }
}
- // There is no listener to monitor whether the IMS service is ready, so we have to retry the
- // IMS registration.
- private final Runnable mTryRegisterIms = new Runnable() {
- private static final int MAX_RETRY = 12;
- private int mRetryCount;
-
- @Override
- public void run() {
- try {
- mRetryCount++;
- mImsMmTelManager.registerImsRegistrationCallback(
- mReceiverHandler::post, mRegistrationCallback);
- Log.d(mTag, "registerImsRegistrationCallback succeeded");
- } catch (RuntimeException | ImsException e) {
- if (mRetryCount < MAX_RETRY) {
- Log.e(mTag, mRetryCount + " registerImsRegistrationCallback failed", e);
- // Wait for 5 seconds to retry
- mReceiverHandler.postDelayed(mTryRegisterIms, 5000);
- }
- }
- }
- };
-
/**
* Stop listening for phone state changes.
*/
public void unregisterListener() {
mMobileStatusTracker.setListening(false);
mContext.getContentResolver().unregisterContentObserver(mObserver);
- mImsMmTelManager.unregisterImsRegistrationCallback(mRegistrationCallback);
}
private void updateInflateSignalStrength() {
@@ -394,7 +296,7 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
CharSequence qsDescription = null;
if (mCurrentState.dataSim) {
- // If using provider model behavior, only show QS icons if the state is also default
+ // only show QS icons if the state is also default
if (!mCurrentState.isDefault) {
return new QsInfo(qsTypeIcon, qsIcon, qsDescription);
}
@@ -416,32 +318,15 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
private SbInfo getSbInfo(String contentDescription, int dataTypeIcon) {
final boolean dataDisabled = mCurrentState.isDataDisabledOrNotDefault();
- boolean showTriangle = false;
- int typeIcon = 0;
- IconState statusIcon = null;
-
- if (mProviderModelBehavior) {
- boolean showDataIconStatusBar = (mCurrentState.dataConnected || dataDisabled)
- && (mCurrentState.dataSim && mCurrentState.isDefault);
- typeIcon =
- (showDataIconStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0;
- showDataIconStatusBar |= mCurrentState.roaming;
- statusIcon = new IconState(
- showDataIconStatusBar && !mCurrentState.airplaneMode,
- getCurrentIconId(), contentDescription);
-
- showTriangle = showDataIconStatusBar && !mCurrentState.airplaneMode;
- } else {
- statusIcon = new IconState(
- mCurrentState.enabled && !mCurrentState.airplaneMode,
- getCurrentIconId(), contentDescription);
+ IconState statusIcon = new IconState(
+ mCurrentState.enabled && !mCurrentState.airplaneMode,
+ getCurrentIconId(), contentDescription);
- boolean showDataIconInStatusBar =
- (mCurrentState.dataConnected && mCurrentState.isDefault) || dataDisabled;
- typeIcon =
- (showDataIconInStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0;
- showTriangle = mCurrentState.enabled && !mCurrentState.airplaneMode;
- }
+ boolean showDataIconInStatusBar =
+ (mCurrentState.dataConnected && mCurrentState.isDefault) || dataDisabled;
+ int typeIcon =
+ (showDataIconInStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0;
+ boolean showTriangle = mCurrentState.enabled && !mCurrentState.airplaneMode;
return new SbInfo(showTriangle, typeIcon, statusIcon);
}
@@ -560,144 +445,7 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
}
private void updateMobileStatus(MobileStatus mobileStatus) {
- int lastVoiceState = mCurrentState.getVoiceServiceState();
mCurrentState.setFromMobileStatus(mobileStatus);
-
- notifyMobileLevelChangeIfNecessary(mobileStatus.signalStrength);
- if (mProviderModelBehavior) {
- maybeNotifyCallStateChanged(lastVoiceState);
- }
- }
-
- /** Call state changed is only applicable when provider model behavior is true */
- private void maybeNotifyCallStateChanged(int lastVoiceState) {
- int currentVoiceState = mCurrentState.getVoiceServiceState();
- if (lastVoiceState == currentVoiceState) {
- return;
- }
- // Only update the no calling Status in the below scenarios
- // 1. The first valid voice state has been received
- // 2. The voice state has been changed and either the last or current state is
- // ServiceState.STATE_IN_SERVICE
- if (lastVoiceState == -1
- || (lastVoiceState == ServiceState.STATE_IN_SERVICE
- || currentVoiceState == ServiceState.STATE_IN_SERVICE)) {
- boolean isNoCalling = mCurrentState.isNoCalling();
- isNoCalling &= !hideNoCalling();
- IconState statusIcon = new IconState(isNoCalling,
- R.drawable.ic_qs_no_calling_sms,
- getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString());
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- }
- }
-
- void updateNoCallingState() {
- int currentVoiceState = mCurrentState.getVoiceServiceState();
- boolean isNoCalling = currentVoiceState != ServiceState.STATE_IN_SERVICE;
- isNoCalling &= !hideNoCalling();
- IconState statusIcon = new IconState(isNoCalling,
- R.drawable.ic_qs_no_calling_sms,
- getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString());
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- }
-
- private boolean hideNoCalling() {
- return mNetworkController.hasDefaultNetwork()
- && mCarrierConfigTracker.getNoCallingConfig(mSubscriptionInfo.getSubscriptionId());
- }
-
- private int getCallStrengthIcon(int level, boolean isWifi) {
- return isWifi ? TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[level]
- : TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[level];
- }
-
- private String getCallStrengthDescription(int level, boolean isWifi) {
- return isWifi
- ? getTextIfExists(AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH[level])
- .toString()
- : getTextIfExists(AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[level])
- .toString();
- }
-
- void refreshCallIndicator(SignalCallback callback) {
- boolean isNoCalling = mCurrentState.isNoCalling();
- isNoCalling &= !hideNoCalling();
- IconState statusIcon = new IconState(isNoCalling,
- R.drawable.ic_qs_no_calling_sms,
- getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString());
- callback.setCallIndicator(statusIcon, mSubscriptionInfo.getSubscriptionId());
-
- switch (mImsType) {
- case IMS_TYPE_WWAN:
- statusIcon = new IconState(
- true,
- getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
- getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
- break;
- case IMS_TYPE_WLAN:
- statusIcon = new IconState(
- true,
- getCallStrengthIcon(mLastWlanLevel, /* isWifi= */true),
- getCallStrengthDescription(mLastWlanLevel, /* isWifi= */true));
- break;
- case IMS_TYPE_WLAN_CROSS_SIM:
- statusIcon = new IconState(
- true,
- getCallStrengthIcon(mLastWlanCrossSimLevel, /* isWifi= */false),
- getCallStrengthDescription(mLastWlanCrossSimLevel, /* isWifi= */false));
- }
- callback.setCallIndicator(statusIcon, mSubscriptionInfo.getSubscriptionId());
- }
-
- void notifyWifiLevelChange(int level) {
- if (!mProviderModelBehavior) {
- return;
- }
- mLastWlanLevel = level;
- if (mImsType != IMS_TYPE_WLAN) {
- return;
- }
- IconState statusIcon = new IconState(
- true,
- getCallStrengthIcon(level, /* isWifi= */true),
- getCallStrengthDescription(level, /* isWifi= */true));
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- }
-
- void notifyDefaultMobileLevelChange(int level) {
- if (!mProviderModelBehavior) {
- return;
- }
- mLastWlanCrossSimLevel = level;
- if (mImsType != IMS_TYPE_WLAN_CROSS_SIM) {
- return;
- }
- IconState statusIcon = new IconState(
- true,
- getCallStrengthIcon(level, /* isWifi= */false),
- getCallStrengthDescription(level, /* isWifi= */false));
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- }
-
- void notifyMobileLevelChangeIfNecessary(SignalStrength signalStrength) {
- if (!mProviderModelBehavior) {
- return;
- }
- int newLevel = getSignalLevel(signalStrength);
- if (newLevel != mLastLevel) {
- mLastLevel = newLevel;
- mLastWwanLevel = newLevel;
- if (mImsType == IMS_TYPE_WWAN) {
- IconState statusIcon = new IconState(
- true,
- getCallStrengthIcon(newLevel, /* isWifi= */false),
- getCallStrengthDescription(newLevel, /* isWifi= */false));
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- }
- if (mCurrentState.dataSim) {
- mNetworkController.notifyDefaultMobileLevelChange(newLevel);
- }
- }
}
int getSignalLevel(SignalStrength signalStrength) {
@@ -801,19 +549,14 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
mMobileStatusHistoryIndex = (mMobileStatusHistoryIndex + 1) % STATUS_HISTORY_SIZE;
}
- @VisibleForTesting
- void setImsType(int imsType) {
- mImsType = imsType;
- }
-
@Override
public void dump(PrintWriter pw) {
super.dump(pw);
pw.println(" mSubscription=" + mSubscriptionInfo + ",");
- pw.println(" mProviderModelBehavior=" + mProviderModelBehavior + ",");
pw.println(" mInflateSignalStrengths=" + mInflateSignalStrengths + ",");
pw.println(" isDataDisabled=" + isDataDisabled() + ",");
pw.println(" mNetworkToIconLookup=" + mNetworkToIconLookup + ",");
+ pw.println(" mMobileStatusTracker.isListening=" + mMobileStatusTracker.isListening());
pw.println(" MobileStatusHistory");
int size = 0;
for (int i = 0; i < STATUS_HISTORY_SIZE; i++) {
@@ -843,6 +586,11 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
icon = iconState;
description = desc;
}
+
+ @Override
+ public String toString() {
+ return "QsInfo: ratTypeIcon=" + ratTypeIcon + " icon=" + icon;
+ }
}
/** Box for status bar icon info */
@@ -856,5 +604,11 @@ public class MobileSignalController extends SignalController<MobileState, Mobile
ratTypeIcon = typeIcon;
icon = iconState;
}
+
+ @Override
+ public String toString() {
+ return "SbInfo: showTriangle=" + showTriangle + " ratTypeIcon=" + ratTypeIcon
+ + " icon=" + icon;
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt
new file mode 100644
index 000000000000..793817948803
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.connectivity
+
+import android.content.Context
+import android.os.Looper
+import android.telephony.SubscriptionInfo
+import android.telephony.TelephonyManager
+import com.android.settingslib.mobile.MobileMappings
+import com.android.settingslib.mobile.MobileStatusTracker
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.util.CarrierConfigTracker
+import javax.inject.Inject
+
+/**
+ * Factory to make MobileSignalController injectable
+ */
+@SysUISingleton
+internal class MobileSignalControllerFactory @Inject constructor(
+ val context: Context,
+ val callbackHandler: CallbackHandler,
+ val carrierConfigTracker: CarrierConfigTracker,
+) {
+ fun createMobileSignalController(
+ config: MobileMappings.Config,
+ hasMobileData: Boolean,
+ phone: TelephonyManager,
+ networkController: NetworkControllerImpl,
+ subscriptionInfo: SubscriptionInfo,
+ subscriptionDefaults: MobileStatusTracker.SubscriptionDefaults,
+ receiverLooper: Looper,
+ ): MobileSignalController {
+ val mobileTrackerFactory = MobileStatusTrackerFactory(
+ phone,
+ receiverLooper,
+ subscriptionInfo,
+ subscriptionDefaults)
+
+ return MobileSignalController(
+ context,
+ config,
+ hasMobileData,
+ phone,
+ callbackHandler,
+ networkController,
+ subscriptionInfo,
+ subscriptionDefaults,
+ receiverLooper,
+ carrierConfigTracker,
+ mobileTrackerFactory,
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileStatusTrackerFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileStatusTrackerFactory.kt
new file mode 100644
index 000000000000..a4c1a1989933
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileStatusTrackerFactory.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.connectivity
+
+import android.os.Looper
+import android.telephony.SubscriptionInfo
+import android.telephony.TelephonyManager
+import com.android.settingslib.mobile.MobileStatusTracker
+
+/**
+ * Factory for [MobileStatusTracker], which lives in SettingsLib
+ */
+class MobileStatusTrackerFactory (
+ val phone: TelephonyManager,
+ val receiverLooper: Looper,
+ val info: SubscriptionInfo,
+ val defaults: MobileStatusTracker.SubscriptionDefaults,
+) {
+ fun createTracker(
+ callback: MobileStatusTracker.Callback
+ ): MobileStatusTracker {
+ return MobileStatusTracker(
+ phone,
+ receiverLooper,
+ info,
+ defaults,
+ callback)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
index 268531d48f9d..ea7ec4f7fc39 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
@@ -71,8 +71,6 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.LogLevel;
import com.android.systemui.log.dagger.StatusBarNetworkControllerLog;
@@ -133,12 +131,11 @@ public class NetworkControllerImpl extends BroadcastReceiver
private final BroadcastDispatcher mBroadcastDispatcher;
private final DemoModeController mDemoModeController;
private final Object mLock = new Object();
- private final boolean mProviderModelBehavior;
private Config mConfig;
private final CarrierConfigTracker mCarrierConfigTracker;
- private final FeatureFlags mFeatureFlags;
private final DumpManager mDumpManager;
private final LogBuffer mLogBuffer;
+ private final MobileSignalControllerFactory mMobileFactory;
private TelephonyCallback.ActiveDataSubscriptionIdListener mPhoneStateListener;
private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -234,9 +231,9 @@ public class NetworkControllerImpl extends BroadcastReceiver
DemoModeController demoModeController,
CarrierConfigTracker carrierConfigTracker,
WifiStatusTrackerFactory trackerFactory,
+ MobileSignalControllerFactory mobileFactory,
@Main Handler handler,
InternetDialogFactory internetDialogFactory,
- FeatureFlags featureFlags,
DumpManager dumpManager,
@StatusBarNetworkControllerLog LogBuffer logBuffer) {
this(context, connectivityManager,
@@ -256,8 +253,8 @@ public class NetworkControllerImpl extends BroadcastReceiver
demoModeController,
carrierConfigTracker,
trackerFactory,
+ mobileFactory,
handler,
- featureFlags,
dumpManager,
logBuffer);
mReceiverHandler.post(mRegisterListeners);
@@ -282,8 +279,8 @@ public class NetworkControllerImpl extends BroadcastReceiver
DemoModeController demoModeController,
CarrierConfigTracker carrierConfigTracker,
WifiStatusTrackerFactory trackerFactory,
+ MobileSignalControllerFactory mobileFactory,
@Main Handler handler,
- FeatureFlags featureFlags,
DumpManager dumpManager,
LogBuffer logBuffer
) {
@@ -297,6 +294,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
mCallbackHandler = callbackHandler;
mDataSaverController = new DataSaverControllerImpl(context);
mBroadcastDispatcher = broadcastDispatcher;
+ mMobileFactory = mobileFactory;
mSubscriptionManager = subManager;
mSubDefaults = defaultsHandler;
@@ -304,7 +302,6 @@ public class NetworkControllerImpl extends BroadcastReceiver
mHasMobileDataFeature = telephonyManager.isDataCapable();
mDemoModeController = demoModeController;
mCarrierConfigTracker = carrierConfigTracker;
- mFeatureFlags = featureFlags;
mDumpManager = dumpManager;
mLogBuffer = logBuffer;
@@ -456,7 +453,6 @@ public class NetworkControllerImpl extends BroadcastReceiver
};
mDemoModeController.addCallback(this);
- mProviderModelBehavior = mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS);
mDumpManager.registerDumpable(TAG, this);
}
@@ -497,16 +493,16 @@ public class NetworkControllerImpl extends BroadcastReceiver
// broadcasts
IntentFilter filter = new IntentFilter();
- filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+ filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+ filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ filter.addAction(Intent.ACTION_SERVICE_STATE);
filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
+ filter.addAction(Settings.Panel.ACTION_INTERNET_CONNECTIVITY);
filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
filter.addAction(TelephonyManager.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
- filter.addAction(Intent.ACTION_SERVICE_STATE);
filter.addAction(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED);
- filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
- filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- filter.addAction(Settings.Panel.ACTION_INTERNET_CONNECTIVITY);
+ filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mReceiverHandler);
mListening = true;
@@ -659,20 +655,6 @@ public class NetworkControllerImpl extends BroadcastReceiver
return controller != null ? controller.getNetworkNameForCarrierWiFi() : "";
}
- void notifyWifiLevelChange(int level) {
- for (int i = 0; i < mMobileSignalControllers.size(); i++) {
- MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
- mobileSignalController.notifyWifiLevelChange(level);
- }
- }
-
- void notifyDefaultMobileLevelChange(int level) {
- for (int i = 0; i < mMobileSignalControllers.size(); i++) {
- MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
- mobileSignalController.notifyDefaultMobileLevelChange(level);
- }
- }
-
private void notifyControllersMobileDataChanged() {
for (int i = 0; i < mMobileSignalControllers.size(); i++) {
MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
@@ -745,9 +727,6 @@ public class NetworkControllerImpl extends BroadcastReceiver
for (int i = 0; i < mMobileSignalControllers.size(); i++) {
MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
mobileSignalController.notifyListeners(cb);
- if (mProviderModelBehavior) {
- mobileSignalController.refreshCallIndicator(cb);
- }
}
mCallbackHandler.setListening(cb, true);
}
@@ -862,9 +841,6 @@ public class NetworkControllerImpl extends BroadcastReceiver
for (int i = 0; i < mMobileSignalControllers.size(); i++) {
MobileSignalController controller = mMobileSignalControllers.valueAt(i);
controller.setConfiguration(mConfig);
- if (mProviderModelBehavior) {
- controller.refreshCallIndicator(mCallbackHandler);
- }
}
refreshLocale();
}
@@ -981,11 +957,15 @@ public class NetworkControllerImpl extends BroadcastReceiver
mMobileSignalControllers.put(subId, cachedControllers.get(subId));
cachedControllers.remove(subId);
} else {
- MobileSignalController controller = new MobileSignalController(mContext, mConfig,
- mHasMobileDataFeature, mPhone.createForSubscriptionId(subId),
- mCallbackHandler, this, subscriptions.get(i),
- mSubDefaults, mReceiverHandler.getLooper(), mCarrierConfigTracker,
- mFeatureFlags);
+ MobileSignalController controller = mMobileFactory.createMobileSignalController(
+ mConfig,
+ mHasMobileDataFeature,
+ mPhone.createForSubscriptionId(subId),
+ this,
+ subscriptions.get(i),
+ mSubDefaults,
+ mReceiverHandler.getLooper()
+ );
controller.setUserSetupComplete(mUserSetup);
mMobileSignalControllers.put(subId, controller);
if (subscriptions.get(i).getSimSlotIndex() == 0) {
@@ -1139,24 +1119,11 @@ public class NetworkControllerImpl extends BroadcastReceiver
|| mValidatedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
pushConnectivityToSignals();
- if (mProviderModelBehavior) {
- mNoDefaultNetwork = !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_CELLULAR)
- && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_WIFI)
- && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
- mCallbackHandler.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition,
- mNoNetworksAvailable);
- for (int i = 0; i < mMobileSignalControllers.size(); i++) {
- MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
- mobileSignalController.updateNoCallingState();
- }
- notifyAllListeners();
- } else {
- mNoDefaultNetwork = !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_CELLULAR)
- && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_WIFI)
- && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
- mCallbackHandler.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition,
- mNoNetworksAvailable);
- }
+ mNoDefaultNetwork = !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_CELLULAR)
+ && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_WIFI)
+ && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
+ mCallbackHandler.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition,
+ mNoNetworksAvailable);
}
/**
@@ -1346,7 +1313,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
mMobileSignalControllers.clear();
int start = mSubscriptionManager.getActiveSubscriptionInfoCountMax();
for (int i = start /* get out of normal index range */; i < start + num; i++) {
- subs.add(addSignalController(i, i));
+ subs.add(addDemoModeSignalController(i, i));
}
mCallbackHandler.setSubs(subs);
for (int i = 0; i < mMobileSignalControllers.size(); i++) {
@@ -1372,7 +1339,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
List<SubscriptionInfo> subs = new ArrayList<>();
while (mMobileSignalControllers.size() <= slot) {
int nextSlot = mMobileSignalControllers.size();
- subs.add(addSignalController(nextSlot, nextSlot));
+ subs.add(addDemoModeSignalController(nextSlot, nextSlot));
}
if (!subs.isEmpty()) {
mCallbackHandler.setSubs(subs);
@@ -1462,14 +1429,20 @@ public class NetworkControllerImpl extends BroadcastReceiver
mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE;
}
- private SubscriptionInfo addSignalController(int id, int simSlotIndex) {
+ private SubscriptionInfo addDemoModeSignalController(int id, int simSlotIndex) {
SubscriptionInfo info = new SubscriptionInfo(id, "", simSlotIndex, "", "", 0, 0, "", 0,
null, null, null, "", false, null, null);
- MobileSignalController controller = new MobileSignalController(mContext,
- mConfig, mHasMobileDataFeature,
- mPhone.createForSubscriptionId(info.getSubscriptionId()), mCallbackHandler, this,
- info, mSubDefaults, mReceiverHandler.getLooper(), mCarrierConfigTracker,
- mFeatureFlags);
+
+ MobileSignalController controller = mMobileFactory.createMobileSignalController(
+ mConfig,
+ mHasMobileDataFeature,
+ mPhone.createForSubscriptionId(info.getSubscriptionId()),
+ this,
+ info,
+ mSubDefaults,
+ mReceiverHandler.getLooper()
+ );
+
mMobileSignalControllers.put(id, controller);
controller.getState().userSetup = true;
return info;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
index 87cdb17245f5..12f2c22ab86a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
@@ -222,7 +222,6 @@ public class WifiSignalController extends SignalController<WifiState, IconGroup>
mCurrentState.connected = mWifiTracker.connected;
mCurrentState.ssid = mWifiTracker.ssid;
mCurrentState.rssi = mWifiTracker.rssi;
- boolean levelChanged = mCurrentState.level != mWifiTracker.level;
mCurrentState.level = mWifiTracker.level;
mCurrentState.statusLabel = mWifiTracker.statusLabel;
mCurrentState.isCarrierMerged = mWifiTracker.isCarrierMerged;
@@ -230,10 +229,6 @@ public class WifiSignalController extends SignalController<WifiState, IconGroup>
mCurrentState.iconGroup =
mCurrentState.isCarrierMerged ? mCarrierMergedWifiIconGroup
: mUnmergedWifiIconGroup;
-
- if (levelChanged) {
- mNetworkController.notifyWifiLevelChange(mCurrentState.level);
- }
}
boolean isCarrierMergedWifi(int subId) {
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 48e34501ef59..0951e821cdc2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -19,7 +19,9 @@ package com.android.systemui.statusbar.dagger;
import android.app.IActivityManager;
import android.content.Context;
import android.os.Handler;
+import android.os.RemoteException;
import android.service.dreams.IDreamManager;
+import android.util.Log;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.statusbar.IStatusBarService;
@@ -60,10 +62,12 @@ import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
import com.android.systemui.statusbar.phone.StatusBarIconList;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallFlags;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.statusbar.window.StatusBarWindowController;
import com.android.systemui.tracing.ProtoTracer;
@@ -274,7 +278,30 @@ public interface CentralSurfacesDependenciesModule {
@Provides
@SysUISingleton
static DialogLaunchAnimator provideDialogLaunchAnimator(IDreamManager dreamManager,
+ KeyguardStateController keyguardStateController,
+ Lazy<StatusBarKeyguardViewManager> statusBarKeyguardViewManager,
InteractionJankMonitor interactionJankMonitor) {
- return new DialogLaunchAnimator(dreamManager, interactionJankMonitor);
+ DialogLaunchAnimator.Callback callback = new DialogLaunchAnimator.Callback() {
+ @Override
+ public boolean isDreaming() {
+ try {
+ return dreamManager.isDreaming();
+ } catch (RemoteException e) {
+ Log.e("DialogLaunchAnimator.Callback", "dreamManager.isDreaming failed", e);
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isUnlocked() {
+ return keyguardStateController.isUnlocked();
+ }
+
+ @Override
+ public boolean isShowingAlternateAuthOnUnlock() {
+ return statusBarKeyguardViewManager.get().shouldShowAltAuth();
+ }
+ };
+ return new DialogLaunchAnimator(callback, interactionJankMonitor);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 126a986ee5f4..dbf4810b4fd7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -18,8 +18,10 @@ package com.android.systemui.statusbar.notification
import android.animation.ObjectAnimator
import android.util.FloatProperty
+import com.android.systemui.Dumpable
import com.android.systemui.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -32,17 +34,20 @@ import com.android.systemui.statusbar.phone.panelstate.PanelExpansionChangeEvent
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionListener
import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
+import java.io.PrintWriter
import javax.inject.Inject
import kotlin.math.min
@SysUISingleton
class NotificationWakeUpCoordinator @Inject constructor(
+ dumpManager: DumpManager,
private val mHeadsUpManager: HeadsUpManager,
private val statusBarStateController: StatusBarStateController,
private val bypassController: KeyguardBypassController,
private val dozeParameters: DozeParameters,
private val screenOffAnimationController: ScreenOffAnimationController
-) : OnHeadsUpChangedListener, StatusBarStateController.StateListener, PanelExpansionListener {
+) : OnHeadsUpChangedListener, StatusBarStateController.StateListener, PanelExpansionListener,
+ Dumpable {
private val mNotificationVisibility = object : FloatProperty<NotificationWakeUpCoordinator>(
"notificationVisibility") {
@@ -60,6 +65,7 @@ class NotificationWakeUpCoordinator @Inject constructor(
private var mLinearDozeAmount: Float = 0.0f
private var mDozeAmount: Float = 0.0f
+ private var mDozeAmountSource: String = "init"
private var mNotificationVisibleAmount = 0.0f
private var mNotificationsVisible = false
private var mNotificationsVisibleForExpansion = false
@@ -142,6 +148,7 @@ class NotificationWakeUpCoordinator @Inject constructor(
}
init {
+ dumpManager.registerDumpable(this)
mHeadsUpManager.addListener(this)
statusBarStateController.addCallback(this)
addListener(object : WakeUpListener {
@@ -248,13 +255,14 @@ class NotificationWakeUpCoordinator @Inject constructor(
// Let's notify the scroller that an animation started
notifyAnimationStart(mLinearDozeAmount == 1.0f)
}
- setDozeAmount(linear, eased)
+ setDozeAmount(linear, eased, source = "StatusBar")
}
- fun setDozeAmount(linear: Float, eased: Float) {
+ fun setDozeAmount(linear: Float, eased: Float, source: String) {
val changed = linear != mLinearDozeAmount
mLinearDozeAmount = linear
mDozeAmount = eased
+ mDozeAmountSource = source
mStackScrollerController.setDozeAmount(mDozeAmount)
updateHideAmount()
if (changed && linear == 0.0f) {
@@ -271,7 +279,7 @@ class NotificationWakeUpCoordinator @Inject constructor(
// undefined state, so it's an indication that we should do state cleanup. We override
// the doze amount to 0f (not dozing) so that the notifications are no longer hidden.
// See: UnlockedScreenOffAnimationController.onFinishedWakingUp()
- setDozeAmount(0f, 0f)
+ setDozeAmount(0f, 0f, source = "Override: Shade->Shade (lock cancelled by unlock)")
}
if (overrideDozeAmountIfAnimatingScreenOff(mLinearDozeAmount)) {
@@ -311,12 +319,11 @@ class NotificationWakeUpCoordinator @Inject constructor(
*/
private fun overrideDozeAmountIfBypass(): Boolean {
if (bypassController.bypassEnabled) {
- var amount = 1.0f
- if (statusBarStateController.state == StatusBarState.SHADE ||
- statusBarStateController.state == StatusBarState.SHADE_LOCKED) {
- amount = 0.0f
+ if (statusBarStateController.state == StatusBarState.KEYGUARD) {
+ setDozeAmount(1f, 1f, source = "Override: bypass (keyguard)")
+ } else {
+ setDozeAmount(0f, 0f, source = "Override: bypass (shade)")
}
- setDozeAmount(amount, amount)
return true
}
return false
@@ -332,7 +339,7 @@ class NotificationWakeUpCoordinator @Inject constructor(
*/
private fun overrideDozeAmountIfAnimatingScreenOff(linearDozeAmount: Float): Boolean {
if (screenOffAnimationController.overrideNotificationsFullyDozingOnKeyguard()) {
- setDozeAmount(1f, 1f)
+ setDozeAmount(1f, 1f, source = "Override: animating screen off")
return true
}
@@ -426,4 +433,24 @@ class NotificationWakeUpCoordinator @Inject constructor(
*/
@JvmDefault fun onPulseExpansionChanged(expandingChanged: Boolean) {}
}
-} \ No newline at end of file
+
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ pw.println("mLinearDozeAmount: $mLinearDozeAmount")
+ pw.println("mDozeAmount: $mDozeAmount")
+ pw.println("mDozeAmountSource: $mDozeAmountSource")
+ pw.println("mNotificationVisibleAmount: $mNotificationVisibleAmount")
+ pw.println("mNotificationsVisible: $mNotificationsVisible")
+ pw.println("mNotificationsVisibleForExpansion: $mNotificationsVisibleForExpansion")
+ pw.println("mVisibilityAmount: $mVisibilityAmount")
+ pw.println("mLinearVisibilityAmount: $mLinearVisibilityAmount")
+ pw.println("pulseExpanding: $pulseExpanding")
+ pw.println("state: ${StatusBarState.toString(state)}")
+ pw.println("fullyAwake: $fullyAwake")
+ pw.println("wakingUp: $wakingUp")
+ pw.println("willWakeUp: $willWakeUp")
+ pw.println("collapsedEnoughToHide: $collapsedEnoughToHide")
+ pw.println("pulsing: $pulsing")
+ pw.println("notificationsFullyHidden: $notificationsFullyHidden")
+ pw.println("canShowPulsingHuns: $canShowPulsingHuns")
+ }
+}
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 a2ea0ba95c8f..a5bf88e1770b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -31,7 +31,7 @@ import static androidx.core.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_
import static androidx.lifecycle.Lifecycle.State.RESUMED;
import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
-import static com.android.systemui.charging.WirelessChargingLayout.UNKNOWN_BATTERY_LEVEL;
+import static com.android.systemui.charging.WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
@@ -171,6 +171,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSPanelController;
import com.android.systemui.recents.ScreenPinningRequest;
+import com.android.systemui.ripple.RippleShader.RippleShape;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.settings.brightness.BrightnessSliderController;
import com.android.systemui.shade.NotificationPanelViewController;
@@ -1089,7 +1090,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
isFolded, willGoToSleep, isShadeOpen, leaveOpen));
}
if (leaveOpen) {
- mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
if (mKeyguardStateController.isShowing()) {
// When device state changes on keyguard we don't want to keep the state of
// the shade and instead we open clean state of keyguard with shade closed.
@@ -1098,6 +1098,9 @@ public class CentralSurfacesImpl extends CoreStartable implements
// expanded. To prevent that we can close QS which resets QS and some parts of
// the shade to its default state. Read more in b/201537421
mCloseQsBeforeScreenOff = true;
+ } else {
+ // below makes shade stay open when going from folded to unfolded
+ mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
}
}
}
@@ -2192,7 +2195,8 @@ public class CentralSurfacesImpl extends CoreStartable implements
public void onAnimationEnded() {
mNotificationShadeWindowController.setRequestTopUi(false, TAG);
}
- }, false, sUiEventLogger).show(animationDelay);
+ }, /* isDozing= */ false, RippleShape.CIRCLE,
+ sUiEventLogger).show(animationDelay);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
index 6dbbf0d53246..fc8e7d5f6aa2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
@@ -30,7 +30,6 @@ import com.android.internal.statusbar.StatusBarIcon;
import com.android.systemui.R;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.StatusBarIconView;
@@ -255,9 +254,7 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da
public void addMobileView(MobileIconState state) {
Log.d(TAG, "addMobileView: ");
- StatusBarMobileView view = StatusBarMobileView.fromContext(
- mContext, state.slot,
- mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS));
+ StatusBarMobileView view = StatusBarMobileView.fromContext(mContext, state.slot);
view.applyMobileState(state);
view.setStaticDrawableColor(mColor);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index a2140c6ab6b7..7b8c5fc998fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -423,6 +423,21 @@ public class NotificationIconContainer extends ViewGroup {
+ getActualPaddingEnd();
}
+ @VisibleForTesting
+ boolean shouldForceOverflow(int i, int speedBumpIndex, float iconAppearAmount,
+ int maxVisibleIcons) {
+ return speedBumpIndex != -1 && i >= speedBumpIndex
+ && iconAppearAmount > 0.0f || i >= maxVisibleIcons;
+ }
+
+ @VisibleForTesting
+ boolean isOverflowing(boolean isLastChild, float translationX, float layoutEnd,
+ float iconSize) {
+ // Layout end, as used here, does not include padding end.
+ final float overflowX = isLastChild ? layoutEnd : layoutEnd - iconSize;
+ return translationX >= overflowX;
+ }
+
/**
* Calculate the horizontal translations for each notification based on how much the icons
* are inserted into the notification container.
@@ -448,26 +463,26 @@ public class NotificationIconContainer extends ViewGroup {
if (mFirstVisibleIconState == null) {
mFirstVisibleIconState = iconState;
}
- boolean forceOverflow = mSpeedBumpIndex != -1 && i >= mSpeedBumpIndex
- && iconState.iconAppearAmount > 0.0f || i >= maxVisibleIcons;
- boolean isLastChild = i == childCount - 1;
- float drawingScale = mOnLockScreen && view instanceof StatusBarIconView
- ? ((StatusBarIconView) view).getIconScaleIncreased()
- : 1f;
iconState.visibleState = iconState.hidden
? StatusBarIconView.STATE_HIDDEN
: StatusBarIconView.STATE_ICON;
- final float overflowDotX = layoutEnd - mIconSize;
- boolean isOverflowing = translationX > overflowDotX;
+ final boolean forceOverflow = shouldForceOverflow(i, mSpeedBumpIndex,
+ iconState.iconAppearAmount, maxVisibleIcons);
+ final boolean isOverflowing = forceOverflow || isOverflowing(
+ /* isLastChild= */ i == childCount - 1, translationX, layoutEnd, mIconSize);
- if (firstOverflowIndex == -1 && (forceOverflow || isOverflowing)) {
- firstOverflowIndex = isLastChild && !forceOverflow ? i - 1 : i;
+ // First icon to overflow.
+ if (firstOverflowIndex == -1 && isOverflowing) {
+ firstOverflowIndex = i;
mVisualOverflowStart = layoutEnd - mIconSize;
if (forceOverflow || mIsStaticLayout) {
mVisualOverflowStart = Math.min(translationX, mVisualOverflowStart);
}
}
+ final float drawingScale = mOnLockScreen && view instanceof StatusBarIconView
+ ? ((StatusBarIconView) view).getIconScaleIncreased()
+ : 1f;
translationX += iconState.iconAppearAmount * view.getWidth() * drawingScale;
}
mNumDots = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 31d9266057da..30b640b583e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -38,7 +38,6 @@ import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.demomode.DemoModeCommandReceiver;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.StatusBarIconView;
@@ -361,9 +360,7 @@ public interface StatusBarIconController {
}
private StatusBarMobileView onCreateStatusBarMobileView(String slot) {
- StatusBarMobileView view = StatusBarMobileView.fromContext(
- mContext, slot,
- mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS));
+ StatusBarMobileView view = StatusBarMobileView.fromContext(mContext, slot);
return view;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 4d9c81160040..e9771d2be4e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.phone;
import static android.view.WindowInsets.Type.navigationBars;
import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
+import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_DISMISS_BOUNCER;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_UNLOCK_COLLAPSING;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
@@ -387,6 +388,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
} else if (mShowing && !hideBouncerOverDream) {
if (!isWakeAndUnlocking()
+ && !(mBiometricUnlockController.getMode() == MODE_DISMISS_BOUNCER)
&& !mCentralSurfaces.isInLaunchTransition()
&& !isUnlockCollapsing()) {
mBouncer.setExpansion(fraction);
@@ -398,9 +400,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
} else if (!mShowing && mBouncer.inTransit()) {
// Keyguard is not visible anymore, but expansion animation was still running.
- // We need to keep propagating the expansion state to the bouncer, otherwise it will be
- // stuck in transit.
- mBouncer.setExpansion(fraction);
+ // We need to hide the bouncer, otherwise it will be stuck in transit.
+ mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
} else if (mPulsing && fraction == KeyguardBouncer.EXPANSION_VISIBLE) {
// Panel expanded while pulsing but didn't translate the bouncer (because we are
// unlocked.) Let's simply wake-up to dismiss the lock screen.
@@ -470,7 +471,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
showBouncer(scrimmed);
}
- private boolean shouldShowAltAuth() {
+ /** Whether we should show the alternate authentication instead of the traditional bouncer. */
+ public boolean shouldShowAltAuth() {
return mAlternateAuthInterceptor != null
&& mKeyguardUpdateManager.isUnlockingWithBiometricAllowed(true);
}
@@ -739,8 +741,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
mNotificationShadeWindowController.setKeyguardOccluded(mOccluded);
- // setDozing(false) will call reset once we stop dozing.
- if (!mDozing) {
+ // setDozing(false) will call reset once we stop dozing. Also, if we're going away, there's
+ // no need to reset the keyguard views as we'll be gone shortly. Resetting now could cause
+ // unexpected visible behavior if the keyguard is still visible as we're animating unlocked.
+ if (!mDozing && !mKeyguardStateController.isKeyguardGoingAway()) {
// If Keyguard is reshown, don't hide the bouncer as it might just have been requested
// by a FLAG_DISMISS_KEYGUARD_ACTIVITY.
reset(isOccluding /* hideBouncerWhenShowing*/);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index ee242a4b1b75..492734e93dca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -26,8 +26,6 @@ import android.util.Log;
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.statusbar.connectivity.IconState;
import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
import com.android.systemui.statusbar.connectivity.NetworkController;
@@ -66,7 +64,6 @@ public class StatusBarSignalPolicy implements SignalCallback,
private final Handler mHandler = Handler.getMain();
private final CarrierConfigTracker mCarrierConfigTracker;
private final TunerService mTunerService;
- private final FeatureFlags mFeatureFlags;
private boolean mHideAirplane;
private boolean mHideMobile;
@@ -90,8 +87,7 @@ public class StatusBarSignalPolicy implements SignalCallback,
CarrierConfigTracker carrierConfigTracker,
NetworkController networkController,
SecurityController securityController,
- TunerService tunerService,
- FeatureFlags featureFlags
+ TunerService tunerService
) {
mContext = context;
@@ -100,7 +96,6 @@ public class StatusBarSignalPolicy implements SignalCallback,
mNetworkController = networkController;
mSecurityController = securityController;
mTunerService = tunerService;
- mFeatureFlags = featureFlags;
mSlotAirplane = mContext.getString(com.android.internal.R.string.status_bar_airplane);
mSlotMobile = mContext.getString(com.android.internal.R.string.status_bar_mobile);
@@ -378,40 +373,6 @@ public class StatusBarSignalPolicy implements SignalCallback,
}
@Override
- public void setConnectivityStatus(boolean noDefaultNetwork, boolean noValidatedNetwork,
- boolean noNetworksAvailable) {
- if (!mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)) {
- return;
- }
- if (DEBUG) {
- Log.d(TAG, "setConnectivityStatus: "
- + "noDefaultNetwork = " + noDefaultNetwork + ","
- + "noValidatedNetwork = " + noValidatedNetwork + ","
- + "noNetworksAvailable = " + noNetworksAvailable);
- }
- WifiIconState newState = mWifiIconState.copy();
- newState.noDefaultNetwork = noDefaultNetwork;
- newState.noValidatedNetwork = noValidatedNetwork;
- newState.noNetworksAvailable = noNetworksAvailable;
- newState.slot = mSlotWifi;
- newState.airplaneSpacerVisible = mIsAirplaneMode;
- if (noDefaultNetwork && noNetworksAvailable && !mIsAirplaneMode) {
- newState.visible = true;
- newState.resId = R.drawable.ic_qs_no_internet_unavailable;
- } else if (noDefaultNetwork && !noNetworksAvailable
- && (!mIsAirplaneMode || (mIsAirplaneMode && mIsWifiEnabled))) {
- newState.visible = true;
- newState.resId = R.drawable.ic_qs_no_internet_available;
- } else {
- newState.visible = false;
- newState.resId = 0;
- }
- updateWifiIconWithState(newState);
- mWifiIconState = newState;
- }
-
-
- @Override
public void setEthernetIndicators(IconState state) {
boolean visible = state.visible && !mHideEthernet;
int resId = state.icon;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index f4068258af72..f61b488a4fbb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -473,6 +473,10 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
int state = mAnimationScheduler.getAnimationState();
if (state == IDLE || state == SHOWING_PERSISTENT_DOT) {
animateShow(mEndSideContent, animate);
+ } else {
+ // We are in the middle of a system status event animation, which will animate the
+ // alpha (but not the visibility). Allow the view to become visible again
+ mEndSideContent.setVisibility(View.VISIBLE);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollector.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollector.kt
new file mode 100644
index 000000000000..6c02b0d44db3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollector.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline
+
+import com.android.systemui.statusbar.pipeline.repository.NetworkCapabilityInfo
+import kotlinx.coroutines.flow.StateFlow
+
+/**
+ * Interface exposing a flow for raw connectivity information. Clients should collect on
+ * [rawConnectivityInfoFlow] to get updates on connectivity information.
+ *
+ * Note: [rawConnectivityInfoFlow] should be a *hot* flow, so that we only create one instance of it
+ * and all clients get references to the same flow.
+ *
+ * This will be used for the new status bar pipeline to compile information we need to display some
+ * of the icons in the RHS of the status bar.
+ */
+interface ConnectivityInfoCollector {
+ val rawConnectivityInfoFlow: StateFlow<RawConnectivityInfo>
+}
+
+/**
+ * An object containing all of the raw connectivity information.
+ *
+ * Importantly, all the information in this object should not be processed at all (i.e., the data
+ * that we receive from callbacks should be piped straight into this object and not be filtered,
+ * manipulated, or processed in any way). Instead, any listeners on
+ * [ConnectivityInfoCollector.rawConnectivityInfoFlow] can do the processing.
+ *
+ * This allows us to keep all the processing in one place which is beneficial for logging and
+ * debugging purposes.
+ */
+data class RawConnectivityInfo(
+ val networkCapabilityInfo: Map<Int, NetworkCapabilityInfo> = emptyMap(),
+)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollectorImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollectorImpl.kt
new file mode 100644
index 000000000000..8d69422c7427
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollectorImpl.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.statusbar.pipeline
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.statusbar.pipeline.repository.NetworkCapabilitiesRepo
+import kotlinx.coroutines.CoroutineScope
+import javax.inject.Inject
+import kotlinx.coroutines.flow.SharingStarted.Companion.Lazily
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * The real implementation of [ConnectivityInfoCollector] that will collect information from all the
+ * relevant connectivity callbacks and compile it into [rawConnectivityInfoFlow].
+ */
+@SysUISingleton
+class ConnectivityInfoCollectorImpl @Inject constructor(
+ networkCapabilitiesRepo: NetworkCapabilitiesRepo,
+ @Application scope: CoroutineScope,
+) : ConnectivityInfoCollector {
+ override val rawConnectivityInfoFlow: StateFlow<RawConnectivityInfo> =
+ // TODO(b/238425913): Collect all the separate flows for individual raw information into
+ // this final flow.
+ networkCapabilitiesRepo.dataStream
+ .map {
+ RawConnectivityInfo(networkCapabilityInfo = it)
+ }
+ .stateIn(scope, started = Lazily, initialValue = RawConnectivityInfo())
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt
index a8419145d6ed..1aae25058ba8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt
@@ -19,7 +19,15 @@ package com.android.systemui.statusbar.pipeline
import android.content.Context
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.statusbar.pipeline.repository.NetworkCapabilityInfo
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted.Companion.Lazily
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
/**
* A processor that transforms raw connectivity information that we get from callbacks and turns it
@@ -27,20 +35,43 @@ import javax.inject.Inject
*
* This will be used for the new status bar pipeline to calculate the list of icons that should be
* displayed in the RHS of the status bar.
+ *
+ * Anyone can listen to [processedInfoFlow] to get updates to the processed data.
*/
@SysUISingleton
class ConnectivityInfoProcessor @Inject constructor(
+ connectivityInfoCollector: ConnectivityInfoCollector,
context: Context,
- private val statusBarPipelineFlags: StatusBarPipelineFlags,
+ @Application private val scope: CoroutineScope,
+ statusBarPipelineFlags: StatusBarPipelineFlags,
) : CoreStartable(context) {
+ // Note: This flow will not start running until a client calls `collect` on it, which means that
+ // [connectivityInfoCollector]'s flow will also not start anything until that `collect` call
+ // happens.
+ val processedInfoFlow: Flow<ProcessedConnectivityInfo> =
+ if (!statusBarPipelineFlags.isNewPipelineEnabled())
+ emptyFlow()
+ else connectivityInfoCollector.rawConnectivityInfoFlow
+ .map { it.process() }
+ .stateIn(
+ scope,
+ started = Lazily,
+ initialValue = ProcessedConnectivityInfo()
+ )
+
override fun start() {
- if (statusBarPipelineFlags.isNewPipelineEnabled()) {
- init()
- }
}
- /** Initializes this processor and everything it depends on. */
- private fun init() {
- // TODO(b/238425913): Register all the connectivity callbacks here.
+ private fun RawConnectivityInfo.process(): ProcessedConnectivityInfo {
+ // TODO(b/238425913): Actually process the raw info into meaningful data.
+ return ProcessedConnectivityInfo(this.networkCapabilityInfo)
}
}
+
+/**
+ * An object containing connectivity info that has been processed into data that can be directly
+ * used by the status bar (and potentially other SysUI areas) to display icons.
+ */
+data class ProcessedConnectivityInfo(
+ val networkCapabilityInfo: Map<Int, NetworkCapabilityInfo> = emptyMap(),
+)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index 771bb0c11fb6..c4e2b732f388 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -17,6 +17,8 @@
package com.android.systemui.statusbar.pipeline.dagger
import com.android.systemui.CoreStartable
+import com.android.systemui.statusbar.pipeline.ConnectivityInfoCollector
+import com.android.systemui.statusbar.pipeline.ConnectivityInfoCollectorImpl
import com.android.systemui.statusbar.pipeline.ConnectivityInfoProcessor
import dagger.Binds
import dagger.Module
@@ -30,4 +32,9 @@ abstract class StatusBarPipelineModule {
@IntoMap
@ClassKey(ConnectivityInfoProcessor::class)
abstract fun bindConnectivityInfoProcessor(cip: ConnectivityInfoProcessor): CoreStartable
+
+ @Binds
+ abstract fun provideConnectivityInfoCollector(
+ impl: ConnectivityInfoCollectorImpl
+ ): ConnectivityInfoCollector
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index e22a896227ef..4c762702892a 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -405,8 +405,8 @@ public class BubblesManager implements Dumpable {
}
@Override
- public void onEntryUpdated(NotificationEntry entry) {
- BubblesManager.this.onEntryUpdated(entry);
+ public void onEntryUpdated(NotificationEntry entry, boolean fromSystem) {
+ BubblesManager.this.onEntryUpdated(entry, fromSystem);
}
@Override
@@ -444,9 +444,10 @@ public class BubblesManager implements Dumpable {
}
}
- void onEntryUpdated(NotificationEntry entry) {
+ void onEntryUpdated(NotificationEntry entry, boolean fromSystem) {
+ boolean shouldBubble = mNotificationInterruptStateProvider.shouldBubbleUp(entry);
mBubbles.onEntryUpdated(notifToBubbleEntry(entry),
- mNotificationInterruptStateProvider.shouldBubbleUp(entry));
+ shouldBubble, fromSystem);
}
void onEntryRemoved(NotificationEntry entry) {
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 12597e0896b1..eba279587629 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -118,6 +118,33 @@ public final class WMShell extends CoreStartable
private final UserInfoController mUserInfoController;
private final Executor mSysUiMainExecutor;
+ // Listeners and callbacks. Note that we prefer member variable over anonymous class here to
+ // avoid the situation that some implementations, like KeyguardUpdateMonitor, use WeakReference
+ // internally and anonymous class could be released after registration.
+ private final ConfigurationController.ConfigurationListener mConfigurationListener =
+ new ConfigurationController.ConfigurationListener() {
+ @Override
+ public void onConfigChanged(Configuration newConfig) {
+ mShell.onConfigurationChanged(newConfig);
+ }
+ };
+ private final KeyguardStateController.Callback mKeyguardStateCallback =
+ new KeyguardStateController.Callback() {
+ @Override
+ public void onKeyguardShowingChanged() {
+ mShell.onKeyguardVisibilityChanged(mKeyguardStateController.isShowing(),
+ mKeyguardStateController.isOccluded(),
+ mKeyguardStateController.isAnimatingBetweenKeyguardAndSurfaceBehind());
+ }
+ };
+ private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onKeyguardDismissAnimationFinished() {
+ mShell.onKeyguardDismissAnimationFinished();
+ }
+ };
+
private boolean mIsSysUiStateValid;
private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback;
private WakefulnessLifecycle.Observer mWakefulnessObserver;
@@ -159,28 +186,11 @@ public final class WMShell extends CoreStartable
public void start() {
// Notify with the initial configuration and subscribe for new config changes
mShell.onConfigurationChanged(mContext.getResources().getConfiguration());
- mConfigurationController.addCallback(new ConfigurationController.ConfigurationListener() {
- @Override
- public void onConfigChanged(Configuration newConfig) {
- mShell.onConfigurationChanged(newConfig);
- }
- });
+ mConfigurationController.addCallback(mConfigurationListener);
// Subscribe to keyguard changes
- mKeyguardStateController.addCallback(new KeyguardStateController.Callback() {
- @Override
- public void onKeyguardShowingChanged() {
- mShell.onKeyguardVisibilityChanged(mKeyguardStateController.isShowing(),
- mKeyguardStateController.isOccluded(),
- mKeyguardStateController.isAnimatingBetweenKeyguardAndSurfaceBehind());
- }
- });
- mKeyguardUpdateMonitor.registerCallback(new KeyguardUpdateMonitorCallback() {
- @Override
- public void onKeyguardDismissAnimationFinished() {
- mShell.onKeyguardDismissAnimationFinished();
- }
- });
+ mKeyguardStateController.addCallback(mKeyguardStateCallback);
+ mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
// TODO: Consider piping config change and other common calls to a shell component to
// delegate internally
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
index dffad6ccbea5..80385e69cfa4 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
@@ -44,6 +44,7 @@ import android.view.SurfaceView;
import androidx.test.filters.SmallTest;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.SysuiTestCase;
import org.junit.After;
@@ -190,7 +191,7 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase {
private void verifyViewDismissed(SurfaceView v) throws Exception {
verify(mKeyguardSecurityContainer).removeView(v);
- verify(mKeyguardCallback).dismiss(true, TARGET_USER_ID, true);
+ verify(mKeyguardCallback).dismiss(true, TARGET_USER_ID, true, SecurityMode.Invalid);
assertThat(mContext.isBound(mComponentName)).isFalse();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
index ad6d146fb5f3..bb455da92ba2 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
@@ -86,10 +86,11 @@ private fun faceModel(user: Int) = KeyguardFaceListenModel(
becauseCannotSkipBouncer = false,
biometricSettingEnabledForUser = false,
bouncerFullyShown = false,
+ onlyFaceEnrolled = false,
faceAuthenticated = false,
faceDisabled = false,
goingToSleep = false,
- keyguardAwake = false,
+ keyguardAwakeExcludingBouncerShowing = false,
keyguardGoingAway = false,
listeningForFaceAssistant = false,
occludingAppRequestingFaceAuth = false,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index dc87a6a45131..aecec9d100cc 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -21,7 +21,10 @@ import static android.view.WindowInsets.Type.ime;
import static com.android.keyguard.KeyguardSecurityContainer.MODE_DEFAULT;
import static com.android.keyguard.KeyguardSecurityContainer.MODE_ONE_HANDED;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
@@ -76,6 +79,8 @@ import java.util.Optional;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper()
public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
+ private static final int TARGET_USER_ID = 100;
+
@Rule
public MockitoRule mRule = MockitoJUnit.rule();
@@ -381,6 +386,44 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
verify(mSidefpsController, never()).show();
}
+ @Test
+ public void showNextSecurityScreenOrFinish_setsSecurityScreenToPinAfterSimPinUnlock() {
+ // GIVEN the current security method is SimPin
+ when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(false);
+ when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(TARGET_USER_ID)).thenReturn(false);
+ mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.SimPin);
+
+ // WHEN a request is made from the SimPin screens to show the next security method
+ when(mKeyguardSecurityModel.getSecurityMode(TARGET_USER_ID)).thenReturn(SecurityMode.PIN);
+ mKeyguardSecurityContainerController.showNextSecurityScreenOrFinish(
+ /* authenticated= */true,
+ TARGET_USER_ID,
+ /* bypassSecondaryLockScreen= */true,
+ SecurityMode.SimPin);
+
+ // THEN the next security method of PIN is set, and the keyguard is not marked as done
+ verify(mSecurityCallback, never()).finish(anyBoolean(), anyInt());
+ assertThat(mKeyguardSecurityContainerController.getCurrentSecurityMode())
+ .isEqualTo(SecurityMode.PIN);
+ }
+
+ @Test
+ public void showNextSecurityScreenOrFinish_ignoresCallWhenSecurityMethodHasChanged() {
+ //GIVEN current security mode has been set to PIN
+ mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.PIN);
+
+ //WHEN a request comes from SimPin to dismiss the security screens
+ boolean keyguardDone = mKeyguardSecurityContainerController.showNextSecurityScreenOrFinish(
+ /* authenticated= */true,
+ TARGET_USER_ID,
+ /* bypassSecondaryLockScreen= */true,
+ SecurityMode.SimPin);
+
+ //THEN no action has happened, which will not dismiss the security screens
+ assertThat(keyguardDone).isEqualTo(false);
+ verify(mKeyguardUpdateMonitor, never()).getUserHasTrust(anyInt());
+ }
+
private void setupConditionsToEnableSideFpsHint() {
attachView();
setSideFpsHintEnabledFromResources(true);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 85ecfd3056ff..c67737136b3b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -16,11 +16,13 @@
package com.android.keyguard;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
+import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT;
import static com.google.common.truth.Truth.assertThat;
@@ -31,7 +33,6 @@ import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -41,6 +42,7 @@ import static org.mockito.Mockito.when;
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.IActivityManager;
import android.app.admin.DevicePolicyManager;
import android.app.trust.IStrongAuthTracker;
import android.app.trust.TrustManager;
@@ -52,6 +54,7 @@ import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricSourceType;
@@ -68,6 +71,7 @@ import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IRemoteCallback;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.telephony.ServiceState;
@@ -106,6 +110,7 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
+import org.mockito.internal.util.reflection.FieldSetter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -182,26 +187,39 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
private ActiveUnlockConfig mActiveUnlockConfig;
@Mock
private KeyguardUpdateMonitorLogger mKeyguardUpdateMonitorLogger;
+ @Mock
+ private IActivityManager mActivityService;
+
+ private final int mCurrentUserId = 100;
+ private final UserInfo mCurrentUserInfo = new UserInfo(mCurrentUserId, "Test user", 0);
+
+ @Captor
+ private ArgumentCaptor<IBiometricEnabledOnKeyguardCallback>
+ mBiometricEnabledCallbackArgCaptor;
+ @Captor
+ private ArgumentCaptor<FaceManager.AuthenticationCallback> mAuthenticationCallbackCaptor;
+ @Captor
+ private ArgumentCaptor<CancellationSignal> mCancellationSignalCaptor;
+
// Direct executor
- private Executor mBackgroundExecutor = Runnable::run;
- private Executor mMainExecutor = Runnable::run;
+ private final Executor mBackgroundExecutor = Runnable::run;
+ private final Executor mMainExecutor = Runnable::run;
private TestableLooper mTestableLooper;
+ private Handler mHandler;
private TestableKeyguardUpdateMonitor mKeyguardUpdateMonitor;
private TestableContext mSpiedContext;
private MockitoSession mMockitoSession;
private StatusBarStateController.StateListener mStatusBarStateListener;
+ private IBiometricEnabledOnKeyguardCallback mBiometricEnabledOnKeyguardCallback;
@Before
- public void setup() {
+ public void setup() throws RemoteException {
MockitoAnnotations.initMocks(this);
mSpiedContext = spy(mContext);
when(mPackageManager.hasSystemFeature(anyString())).thenReturn(true);
when(mSpiedContext.getPackageManager()).thenReturn(mPackageManager);
- doAnswer(invocation -> {
- IBiometricEnabledOnKeyguardCallback callback = invocation.getArgument(0);
- callback.onChanged(true /* enabled */, KeyguardUpdateMonitor.getCurrentUser());
- return null;
- }).when(mBiometricManager).registerEnabledOnKeyguardCallback(any());
+ when(mActivityService.getCurrentUser()).thenReturn(mCurrentUserInfo);
+ when(mActivityService.getCurrentUserId()).thenReturn(mCurrentUserId);
when(mFaceManager.isHardwareDetected()).thenReturn(true);
when(mFaceManager.hasEnrolledTemplates()).thenReturn(true);
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
@@ -262,13 +280,27 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
.startMocking();
ExtendedMockito.doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
.when(SubscriptionManager::getDefaultSubscriptionId);
+ KeyguardUpdateMonitor.setCurrentUser(mCurrentUserId);
ExtendedMockito.doReturn(KeyguardUpdateMonitor.getCurrentUser())
.when(ActivityManager::getCurrentUser);
+ ExtendedMockito.doReturn(mActivityService).when(ActivityManager::getService);
mTestableLooper = TestableLooper.get(this);
allowTestableLooperAsMainThread();
mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mSpiedContext);
+ verify(mBiometricManager)
+ .registerEnabledOnKeyguardCallback(mBiometricEnabledCallbackArgCaptor.capture());
+ mBiometricEnabledOnKeyguardCallback = mBiometricEnabledCallbackArgCaptor.getValue();
+ biometricsEnabledForCurrentUser();
+
+ mHandler = spy(mKeyguardUpdateMonitor.getHandler());
+ try {
+ FieldSetter.setField(mKeyguardUpdateMonitor,
+ KeyguardUpdateMonitor.class.getDeclaredField("mHandler"), mHandler);
+ } catch (NoSuchFieldException e) {
+
+ }
verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture());
mStatusBarStateListener = mStatusBarStateListenerCaptor.getValue();
mKeyguardUpdateMonitor.registerCallback(mTestCallback);
@@ -308,7 +340,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
when(mTelephonyManager.getActiveModemCount()).thenReturn(1);
when(mTelephonyManager.getSimState(anyInt())).thenReturn(state);
- when(mSubscriptionManager.getSubscriptionIds(anyInt())).thenReturn(new int[] { subId });
+ when(mSubscriptionManager.getSubscriptionIds(anyInt())).thenReturn(new int[]{subId});
KeyguardUpdateMonitor testKUM = new TestableKeyguardUpdateMonitor(mSpiedContext);
@@ -483,7 +515,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
// Even SimState Loaded, still need ACTION_SERVICE_STATE turn on mTelephonyCapable
assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isFalse();
- Intent intentServiceState = new Intent(Intent.ACTION_SERVICE_STATE);
+ Intent intentServiceState = new Intent(Intent.ACTION_SERVICE_STATE);
intentSimState.putExtra(Intent.EXTRA_SIM_STATE
, Intent.SIM_STATE_LOADED);
mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
@@ -498,7 +530,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mTestableLooper.processAllMessages();
verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(),
- anyInt());
+ anyInt());
verify(mFingerprintManager, never()).detectFingerprint(any(), any(), anyInt());
}
@@ -548,11 +580,12 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testTriesToAuthenticate_whenBouncer() {
+ fingerprintIsNotEnrolled();
setKeyguardBouncerVisibility(true);
verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
- verify(mFaceManager).isHardwareDetected();
- verify(mFaceManager).hasEnrolledTemplates(anyInt());
+ verify(mFaceManager, atLeastOnce()).isHardwareDetected();
+ verify(mFaceManager, atLeastOnce()).hasEnrolledTemplates(anyInt());
}
@Test
@@ -718,7 +751,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
verify(mLockPatternUtils).requireStrongAuth(anyInt(), anyInt());
}
-
@Test
public void testFaceAndFingerprintLockout() {
mKeyguardUpdateMonitor.dispatchStartedWakingUp();
@@ -769,7 +801,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
public void testBiometricsCleared_whenUserSwitches() throws Exception {
final IRemoteCallback reply = new IRemoteCallback.Stub() {
@Override
- public void sendResult(Bundle data) {} // do nothing
+ public void sendResult(Bundle data) {
+ } // do nothing
};
final BiometricAuthenticated dummyAuthentication =
new BiometricAuthenticated(true /* authenticated */, true /* strong */);
@@ -787,7 +820,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
public void testMultiUserJankMonitor_whenUserSwitches() throws Exception {
final IRemoteCallback reply = new IRemoteCallback.Stub() {
@Override
- public void sendResult(Bundle data) {} // do nothing
+ public void sendResult(Bundle data) {
+ } // do nothing
};
mKeyguardUpdateMonitor.handleUserSwitchComplete(10 /* user */);
verify(mInteractionJankMonitor).end(InteractionJankMonitor.CUJ_USER_SWITCH);
@@ -924,6 +958,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testSecondaryLockscreenRequirement() {
+ KeyguardUpdateMonitor.setCurrentUser(UserHandle.myUserId());
int user = KeyguardUpdateMonitor.getCurrentUser();
String packageName = "fake.test.package";
String cls = "FakeService";
@@ -1097,8 +1132,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testShouldNotUpdateBiometricListeningStateOnStatusBarStateChange() {
// GIVEN state for face auth should run aside from StatusBarState
- when(mDevicePolicyManager.getKeyguardDisabledFeatures(null,
- KeyguardUpdateMonitor.getCurrentUser())).thenReturn(0);
+ biometricsNotDisabledThroughDevicePolicyManager();
mStatusBarStateListener.onStateChanged(StatusBarState.SHADE_LOCKED);
setKeyguardBouncerVisibility(false /* isVisible */);
mKeyguardUpdateMonitor.dispatchStartedWakingUp();
@@ -1153,6 +1187,471 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
verify(mTestCallback).showTrustGrantedMessage("Unlocked by wearable");
}
+ @Test
+ public void testShouldListenForFace_whenFaceManagerNotAvailable_returnsFalse() {
+ mFaceManager = null;
+ mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mSpiedContext);
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenFpIsLockedOut_returnsFalse() throws RemoteException {
+ // Face auth should run when the following is true.
+ keyguardNotGoingAway();
+ bouncerFullyVisibleAndNotGoingToSleep();
+ currentUserIsPrimary();
+ strongAuthNotRequired();
+ biometricsEnabledForCurrentUser();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ userNotCurrentlySwitching();
+ mTestableLooper.processAllMessages();
+
+ // Fingerprint is locked out.
+ fingerprintErrorLockedOut();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenFaceIsAlreadyAuthenticated_returnsFalse()
+ throws RemoteException {
+ // Face auth should run when the following is true.
+ bouncerFullyVisibleAndNotGoingToSleep();
+ fingerprintIsNotEnrolled();
+ keyguardNotGoingAway();
+ currentUserIsPrimary();
+ strongAuthNotRequired();
+ biometricsEnabledForCurrentUser();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ userNotCurrentlySwitching();
+
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+
+ triggerSuccessfulFaceAuth();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenUserIsNotPrimary_returnsFalse() throws RemoteException {
+ // This disables face auth
+ when(mUserManager.isPrimaryUser()).thenReturn(false);
+ mKeyguardUpdateMonitor =
+ new TestableKeyguardUpdateMonitor(mSpiedContext);
+
+ // Preconditions for face auth to run
+ keyguardNotGoingAway();
+ bouncerFullyVisibleAndNotGoingToSleep();
+ strongAuthNotRequired();
+ biometricsEnabledForCurrentUser();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ userNotCurrentlySwitching();
+ mTestableLooper.processAllMessages();
+
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenStrongAuthDoesNotAllowScanning_returnsFalse()
+ throws RemoteException {
+ // Preconditions for face auth to run
+ keyguardNotGoingAway();
+ bouncerFullyVisibleAndNotGoingToSleep();
+ currentUserIsPrimary();
+ biometricsEnabledForCurrentUser();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ userNotCurrentlySwitching();
+
+ // This disables face auth
+ when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
+ .thenReturn(STRONG_AUTH_REQUIRED_AFTER_BOOT);
+ mTestableLooper.processAllMessages();
+
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenBiometricsDisabledForUser_returnsFalse()
+ throws RemoteException {
+ // Preconditions for face auth to run
+ keyguardNotGoingAway();
+ bouncerFullyVisibleAndNotGoingToSleep();
+ fingerprintIsNotEnrolled();
+ currentUserIsPrimary();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ biometricsEnabledForCurrentUser();
+ userNotCurrentlySwitching();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+
+ // This disables face auth
+ biometricsDisabledForCurrentUser();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenUserCurrentlySwitching_returnsFalse()
+ throws RemoteException {
+ // Preconditions for face auth to run
+ keyguardNotGoingAway();
+ bouncerFullyVisibleAndNotGoingToSleep();
+ fingerprintIsNotEnrolled();
+ currentUserIsPrimary();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ biometricsEnabledForCurrentUser();
+ userNotCurrentlySwitching();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+
+ userCurrentlySwitching();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenSecureCameraLaunched_returnsFalse()
+ throws RemoteException {
+ // Preconditions for face auth to run
+ keyguardNotGoingAway();
+ bouncerFullyVisibleAndNotGoingToSleep();
+ fingerprintIsNotEnrolled();
+ currentUserIsPrimary();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ biometricsEnabledForCurrentUser();
+ userNotCurrentlySwitching();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+
+ secureCameraLaunched();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenOccludingAppRequestsFaceAuth_returnsTrue()
+ throws RemoteException {
+ // Preconditions for face auth to run
+ keyguardNotGoingAway();
+ bouncerFullyVisibleAndNotGoingToSleep();
+ currentUserIsPrimary();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ biometricsEnabledForCurrentUser();
+ userNotCurrentlySwitching();
+ mTestableLooper.processAllMessages();
+
+ secureCameraLaunched();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+
+ occludingAppRequestsFaceAuth();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenBouncerShowingAndDeviceIsAwake_returnsTrue()
+ throws RemoteException {
+ // Preconditions for face auth to run
+ keyguardNotGoingAway();
+ currentUserIsPrimary();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ biometricsEnabledForCurrentUser();
+ userNotCurrentlySwitching();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+
+ bouncerFullyVisibleAndNotGoingToSleep();
+ fingerprintIsNotEnrolled();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenAuthInterruptIsActive_returnsTrue()
+ throws RemoteException {
+ // Preconditions for face auth to run
+ keyguardNotGoingAway();
+ currentUserIsPrimary();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ biometricsEnabledForCurrentUser();
+ userNotCurrentlySwitching();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+
+ triggerAuthInterrupt();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenKeyguardIsAwake_returnsTrue() throws RemoteException {
+ // Preconditions for face auth to run
+ keyguardNotGoingAway();
+ currentUserIsPrimary();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ biometricsEnabledForCurrentUser();
+ userNotCurrentlySwitching();
+ bouncerFullyVisible();
+
+ statusBarShadeIsLocked();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+
+ deviceNotGoingToSleep();
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ deviceIsInteractive();
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ keyguardIsVisible();
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ statusBarShadeIsNotLocked();
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ bouncerNotFullyVisible();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenUdfpsFingerDown_returnsTrue() throws RemoteException {
+ // Preconditions for face auth to run
+ keyguardNotGoingAway();
+ currentUserIsPrimary();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ biometricsEnabledForCurrentUser();
+ userNotCurrentlySwitching();
+ when(mAuthController.isUdfpsFingerDown()).thenReturn(false);
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+
+ when(mAuthController.isUdfpsFingerDown()).thenReturn(true);
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenUdfpsBouncerIsShowing_returnsTrue()
+ throws RemoteException {
+ // Preconditions for face auth to run
+ keyguardNotGoingAway();
+ currentUserIsPrimary();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ biometricsEnabledForCurrentUser();
+ userNotCurrentlySwitching();
+ mTestableLooper.processAllMessages();
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+
+ mKeyguardUpdateMonitor.setUdfpsBouncerShowing(true);
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+ }
+
+ @Test
+ public void testBouncerVisibility_whenBothFingerprintAndFaceIsEnrolled_stopsFaceAuth()
+ throws RemoteException {
+ // Both fingerprint and face are enrolled by default
+ // Preconditions for face auth to run
+ keyguardNotGoingAway();
+ currentUserIsPrimary();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ biometricsEnabledForCurrentUser();
+ userNotCurrentlySwitching();
+ deviceNotGoingToSleep();
+ deviceIsInteractive();
+ statusBarShadeIsNotLocked();
+ keyguardIsVisible();
+
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+
+ mKeyguardUpdateMonitor.requestFaceAuth(true);
+ verify(mFaceManager).authenticate(any(),
+ mCancellationSignalCaptor.capture(),
+ mAuthenticationCallbackCaptor.capture(),
+ any(),
+ anyInt(),
+ anyBoolean());
+ CancellationSignal cancelSignal = mCancellationSignalCaptor.getValue();
+
+ bouncerFullyVisible();
+ mTestableLooper.processAllMessages();
+
+ assertThat(cancelSignal.isCanceled()).isTrue();
+ }
+
+ @Test
+ public void testFingerprintCanAuth_whenCancellationNotReceivedAndAuthFailed() {
+ mKeyguardUpdateMonitor.dispatchStartedWakingUp();
+ mTestableLooper.processAllMessages();
+ mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
+
+ verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
+ verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(),
+ anyInt());
+
+ mKeyguardUpdateMonitor.onFaceAuthenticated(0, false);
+ // Make sure keyguard is going away after face auth attempt, and that it calls
+ // updateBiometricStateListeningState.
+ mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(false);
+ mTestableLooper.processAllMessages();
+
+ verify(mHandler).postDelayed(mKeyguardUpdateMonitor.mFpCancelNotReceived,
+ DEFAULT_CANCEL_SIGNAL_TIMEOUT);
+
+ mKeyguardUpdateMonitor.onFingerprintAuthenticated(0, true);
+ mTestableLooper.processAllMessages();
+
+ verify(mHandler, times(1)).removeCallbacks(mKeyguardUpdateMonitor.mFpCancelNotReceived);
+ mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
+ mTestableLooper.processAllMessages();
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(anyBoolean())).isEqualTo(true);
+ }
+
+ private void fingerprintIsNotEnrolled() {
+ when(mFingerprintManager.hasEnrolledTemplates(mCurrentUserId)).thenReturn(false);
+ }
+
+ private void statusBarShadeIsNotLocked() {
+ mStatusBarStateListener.onStateChanged(StatusBarState.KEYGUARD);
+ }
+
+ private void statusBarShadeIsLocked() {
+ mStatusBarStateListener.onStateChanged(StatusBarState.SHADE_LOCKED);
+ }
+
+ private void keyguardIsVisible() {
+ mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
+ }
+
+ private void triggerAuthInterrupt() {
+ mKeyguardUpdateMonitor.onAuthInterruptDetected(true);
+ }
+
+ private void occludingAppRequestsFaceAuth() {
+ mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true);
+ }
+
+ private void secureCameraLaunched() {
+ mKeyguardUpdateMonitor.onCameraLaunched();
+ }
+
+ private void userCurrentlySwitching() {
+ mKeyguardUpdateMonitor.setSwitchingUser(true);
+ }
+
+ private void fingerprintErrorLockedOut() {
+ mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
+ .onAuthenticationError(FINGERPRINT_ERROR_LOCKOUT, "Fingerprint locked out");
+ }
+
+ private void triggerSuccessfulFaceAuth() {
+ mKeyguardUpdateMonitor.requestFaceAuth(true);
+ verify(mFaceManager).authenticate(any(),
+ any(),
+ mAuthenticationCallbackCaptor.capture(),
+ any(),
+ anyInt(),
+ anyBoolean());
+ mAuthenticationCallbackCaptor.getValue()
+ .onAuthenticationSucceeded(
+ new FaceManager.AuthenticationResult(null, null, mCurrentUserId, false));
+ }
+
+ private void currentUserIsPrimary() {
+ when(mUserManager.isPrimaryUser()).thenReturn(true);
+ }
+
+ private void biometricsNotDisabledThroughDevicePolicyManager() {
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(null,
+ KeyguardUpdateMonitor.getCurrentUser())).thenReturn(0);
+ }
+
+ private void biometricsEnabledForCurrentUser() throws RemoteException {
+ mBiometricEnabledOnKeyguardCallback.onChanged(true, KeyguardUpdateMonitor.getCurrentUser());
+ }
+
+ private void biometricsDisabledForCurrentUser() throws RemoteException {
+ mBiometricEnabledOnKeyguardCallback.onChanged(
+ false,
+ KeyguardUpdateMonitor.getCurrentUser()
+ );
+ }
+
+ private void strongAuthNotRequired() {
+ when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
+ .thenReturn(0);
+ }
+
+ private void currentUserDoesNotHaveTrust() {
+ mKeyguardUpdateMonitor.onTrustChanged(
+ false,
+ KeyguardUpdateMonitor.getCurrentUser(),
+ -1,
+ new ArrayList<>()
+ );
+ }
+
+ private void userNotCurrentlySwitching() {
+ mKeyguardUpdateMonitor.setSwitchingUser(false);
+ }
+
+ private void keyguardNotGoingAway() {
+ mKeyguardUpdateMonitor.setKeyguardGoingAway(false);
+ }
+
+ private void bouncerFullyVisibleAndNotGoingToSleep() {
+ bouncerFullyVisible();
+ deviceNotGoingToSleep();
+ }
+
+ private void deviceNotGoingToSleep() {
+ mKeyguardUpdateMonitor.dispatchFinishedGoingToSleep(/* value doesn't matter */1);
+ }
+
+ private void deviceIsInteractive() {
+ mKeyguardUpdateMonitor.dispatchStartedWakingUp();
+ }
+
+ private void bouncerNotFullyVisible() {
+ setKeyguardBouncerVisibility(false);
+ }
+
+ private void bouncerFullyVisible() {
+ setKeyguardBouncerVisibility(true);
+ }
+
private void setKeyguardBouncerVisibility(boolean isVisible) {
mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(isVisible, isVisible);
mTestableLooper.processAllMessages();
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/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
index c48cbb19b40a..0f112415df0d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
@@ -26,6 +26,7 @@ import junit.framework.Assert.assertNotNull
import junit.framework.Assert.assertNull
import junit.framework.Assert.assertTrue
import junit.framework.AssertionFailedError
+import kotlin.concurrent.thread
import org.junit.After
import org.junit.Before
import org.junit.Rule
@@ -34,19 +35,18 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.Spy
import org.mockito.junit.MockitoJUnit
-import kotlin.concurrent.thread
@SmallTest
@RunWith(AndroidTestingRunner::class)
@RunWithLooper
class ActivityLaunchAnimatorTest : SysuiTestCase() {
private val launchContainer = LinearLayout(mContext)
- private val testLaunchAnimator = LaunchAnimator(TEST_TIMINGS, TEST_INTERPOLATORS)
+ private val testLaunchAnimator = fakeLaunchAnimator()
@Mock lateinit var callback: ActivityLaunchAnimator.Callback
@Mock lateinit var listener: ActivityLaunchAnimator.Listener
@Spy private val controller = TestLaunchAnimatorController(launchContainer)
@@ -77,12 +77,13 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() {
// We start in a new thread so that we can ensure that the callbacks are called in the main
// thread.
thread {
- animator.startIntentWithAnimation(
+ animator.startIntentWithAnimation(
controller = controller,
animate = animate,
intentStarter = intentStarter
- )
- }.join()
+ )
+ }
+ .join()
}
@Test
@@ -197,14 +198,25 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() {
val bounds = Rect(10 /* left */, 20 /* top */, 30 /* right */, 40 /* bottom */)
val taskInfo = ActivityManager.RunningTaskInfo()
taskInfo.topActivity = ComponentName("com.android.systemui", "FakeActivity")
- taskInfo.topActivityInfo = ActivityInfo().apply {
- applicationInfo = ApplicationInfo()
- }
+ taskInfo.topActivityInfo = ActivityInfo().apply { applicationInfo = ApplicationInfo() }
return RemoteAnimationTarget(
- 0, RemoteAnimationTarget.MODE_OPENING, SurfaceControl(), false, Rect(), Rect(), 0,
- Point(), Rect(), bounds, WindowConfiguration(), false, SurfaceControl(), Rect(),
- taskInfo, false
+ 0,
+ RemoteAnimationTarget.MODE_OPENING,
+ SurfaceControl(),
+ false,
+ Rect(),
+ Rect(),
+ 0,
+ Point(),
+ Rect(),
+ bounds,
+ WindowConfiguration(),
+ false,
+ SurfaceControl(),
+ Rect(),
+ taskInfo,
+ false
)
}
}
@@ -213,17 +225,17 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() {
* A simple implementation of [ActivityLaunchAnimator.Controller] which throws if it is called
* outside of the main thread.
*/
-private class TestLaunchAnimatorController(
- override var launchContainer: ViewGroup
-) : ActivityLaunchAnimator.Controller {
- override fun createAnimatorState() = LaunchAnimator.State(
+private class TestLaunchAnimatorController(override var launchContainer: ViewGroup) :
+ ActivityLaunchAnimator.Controller {
+ override fun createAnimatorState() =
+ LaunchAnimator.State(
top = 100,
bottom = 200,
left = 300,
right = 400,
topCornerRadius = 10f,
bottomCornerRadius = 20f
- )
+ )
private fun assertOnMainThread() {
if (Looper.myLooper() != Looper.getMainLooper()) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
index 4218e0904c43..7c1e384f8c30 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
@@ -5,7 +5,6 @@ import android.content.Context
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
-import android.service.dreams.IDreamManager
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.ViewUtils
@@ -38,19 +37,16 @@ import org.mockito.junit.MockitoJUnit
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
class DialogLaunchAnimatorTest : SysuiTestCase() {
- private val launchAnimator = LaunchAnimator(TEST_TIMINGS, TEST_INTERPOLATORS)
private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
private val attachedViews = mutableSetOf<View>()
- @Mock lateinit var dreamManager: IDreamManager
@Mock lateinit var interactionJankMonitor: InteractionJankMonitor
@get:Rule val rule = MockitoJUnit.rule()
@Before
fun setUp() {
- dialogLaunchAnimator = DialogLaunchAnimator(
- dreamManager, interactionJankMonitor, launchAnimator, isForTesting = true
- )
+ dialogLaunchAnimator =
+ fakeDialogLaunchAnimator(interactionJankMonitor = interactionJankMonitor)
}
@After
@@ -153,6 +149,22 @@ class DialogLaunchAnimatorTest : SysuiTestCase() {
}
@Test
+ fun testActivityLaunchWhenLockedWithoutAlternateAuth() {
+ val dialogLaunchAnimator =
+ fakeDialogLaunchAnimator(isUnlocked = false, isShowingAlternateAuthOnUnlock = false)
+ val dialog = createAndShowDialog(dialogLaunchAnimator)
+ assertNull(dialogLaunchAnimator.createActivityLaunchController(dialog.contentView))
+ }
+
+ @Test
+ fun testActivityLaunchWhenLockedWithAlternateAuth() {
+ val dialogLaunchAnimator =
+ fakeDialogLaunchAnimator(isUnlocked = false, isShowingAlternateAuthOnUnlock = true)
+ val dialog = createAndShowDialog(dialogLaunchAnimator)
+ assertNotNull(dialogLaunchAnimator.createActivityLaunchController(dialog.contentView))
+ }
+
+ @Test
fun testDialogAnimationIsChangedByAnimator() {
// Important: the power menu animation relies on this behavior to know when to animate (see
// http://ag/16774605).
@@ -193,11 +205,13 @@ class DialogLaunchAnimatorTest : SysuiTestCase() {
verify(interactionJankMonitor).end(InteractionJankMonitor.CUJ_USER_DIALOG_OPEN)
}
- private fun createAndShowDialog(): TestDialog {
+ private fun createAndShowDialog(
+ animator: DialogLaunchAnimator = dialogLaunchAnimator,
+ ): TestDialog {
val touchSurface = createTouchSurface()
return runOnMainThreadAndWaitForIdleSync {
val dialog = TestDialog(context)
- dialogLaunchAnimator.showFromView(dialog, touchSurface)
+ animator.showFromView(dialog, touchSurface)
dialog
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TestValues.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TestValues.kt
deleted file mode 100644
index dadf94e2a9dd..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/TestValues.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.android.systemui.animation
-
-/**
- * A [LaunchAnimator.Timings] to be used in tests.
- *
- * Note that all timings except the total duration are non-zero to avoid divide-by-zero exceptions
- * when computing the progress of a sub-animation (the contents fade in/out).
- */
-val TEST_TIMINGS = LaunchAnimator.Timings(
- totalDuration = 0L,
- contentBeforeFadeOutDelay = 1L,
- contentBeforeFadeOutDuration = 1L,
- contentAfterFadeInDelay = 1L,
- contentAfterFadeInDuration = 1L
-)
-
-/** A [LaunchAnimator.Interpolators] to be used in tests. */
-val TEST_INTERPOLATORS = LaunchAnimator.Interpolators(
- positionInterpolator = Interpolators.STANDARD,
- positionXInterpolator = Interpolators.STANDARD,
- contentBeforeFadeOutInterpolator = Interpolators.STANDARD,
- contentAfterFadeInInterpolator = Interpolators.STANDARD
-) \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
index 6978490a1252..3ac28c8d0d37 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
@@ -35,12 +35,12 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.any
import org.mockito.Mockito.eq
-import org.mockito.Mockito.reset
import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@@ -63,6 +63,7 @@ class WiredChargingRippleControllerTest : SysuiTestCase() {
controller = WiredChargingRippleController(
commandRegistry, batteryController, configurationController,
featureFlags, context, windowManager, systemClock, uiEventLogger)
+ rippleView.setupShader()
controller.rippleView = rippleView // Replace the real ripple view with a mock instance
controller.registerCallbacks()
}
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/dreams/complication/ComplicationUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java
index 2915f5a504d7..e099c9269d3f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java
@@ -20,6 +20,7 @@ import static com.android.systemui.dreams.complication.Complication.COMPLICATION
import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_CAST_INFO;
import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_DATE;
import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_HOME_CONTROLS;
+import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_SMARTSPACE;
import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_TIME;
import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_WEATHER;
import static com.android.systemui.dreams.complication.ComplicationUtils.convertComplicationType;
@@ -60,6 +61,8 @@ public class ComplicationUtilsTest extends SysuiTestCase {
.isEqualTo(COMPLICATION_TYPE_CAST_INFO);
assertThat(convertComplicationType(DreamBackend.COMPLICATION_TYPE_HOME_CONTROLS))
.isEqualTo(COMPLICATION_TYPE_HOME_CONTROLS);
+ assertThat(convertComplicationType(DreamBackend.COMPLICATION_TYPE_SMARTSPACE))
+ .isEqualTo(COMPLICATION_TYPE_SMARTSPACE);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlags.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlags.kt
index b2a4e33978a1..7b1455cb2e46 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlags.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlags.kt
@@ -19,7 +19,6 @@ package com.android.systemui.flags
import android.util.SparseArray
import android.util.SparseBooleanArray
import androidx.core.util.containsKey
-import java.lang.IllegalStateException
class FakeFeatureFlags : FeatureFlags {
private val booleanFlags = SparseBooleanArray()
@@ -57,7 +56,10 @@ class FakeFeatureFlags : FeatureFlags {
stringFlags.put(flag.id, value)
}
- override fun isEnabled(flag: BooleanFlag): Boolean = requireBooleanValue(flag.id)
+
+ override fun isEnabled(flag: UnreleasedFlag): Boolean = requireBooleanValue(flag.id)
+
+ override fun isEnabled(flag: ReleasedFlag): Boolean = requireBooleanValue(flag.id)
override fun isEnabled(flag: ResourceBooleanFlag): Boolean = requireBooleanValue(flag.id)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
index 7d4b4f53e380..ff579a1cc4aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
@@ -29,11 +29,12 @@ import org.junit.runner.RunWith
@RunWith(AndroidTestingRunner::class)
class FakeFeatureFlagsTest : SysuiTestCase() {
- private val booleanFlag = BooleanFlag(-1000)
- private val stringFlag = StringFlag(-1001)
- private val resourceBooleanFlag = ResourceBooleanFlag(-1002, resourceId = -1)
- private val resourceStringFlag = ResourceStringFlag(-1003, resourceId = -1)
- private val sysPropBooleanFlag = SysPropBooleanFlag(-1004, name = "test")
+ private val unreleasedFlag = UnreleasedFlag(-1000)
+ private val releasedFlag = ReleasedFlag(-1001)
+ private val stringFlag = StringFlag(-1002)
+ private val resourceBooleanFlag = ResourceBooleanFlag(-1003, resourceId = -1)
+ private val resourceStringFlag = ResourceStringFlag(-1004, resourceId = -1)
+ private val sysPropBooleanFlag = SysPropBooleanFlag(-1005, name = "test")
/**
* FakeFeatureFlags does not honor any default values. All flags which are accessed must be
@@ -49,56 +50,66 @@ class FakeFeatureFlagsTest : SysuiTestCase() {
assertThat(ex.message).contains("TEAMFOOD")
}
try {
- assertThat(flags.isEnabled(booleanFlag)).isFalse()
+ assertThat(flags.isEnabled(unreleasedFlag)).isFalse()
fail("Expected an exception when accessing an unspecified flag.")
} catch (ex: IllegalStateException) {
assertThat(ex.message).contains("UNKNOWN(id=-1000)")
}
try {
+ assertThat(flags.isEnabled(releasedFlag)).isFalse()
+ fail("Expected an exception when accessing an unspecified flag.")
+ } catch (ex: IllegalStateException) {
+ assertThat(ex.message).contains("UNKNOWN(id=-1001)")
+ }
+ try {
assertThat(flags.isEnabled(resourceBooleanFlag)).isFalse()
fail("Expected an exception when accessing an unspecified flag.")
} catch (ex: IllegalStateException) {
- assertThat(ex.message).contains("UNKNOWN(id=-1002)")
+ assertThat(ex.message).contains("UNKNOWN(id=-1003)")
}
try {
assertThat(flags.isEnabled(sysPropBooleanFlag)).isFalse()
fail("Expected an exception when accessing an unspecified flag.")
} catch (ex: IllegalStateException) {
- assertThat(ex.message).contains("UNKNOWN(id=-1004)")
+ assertThat(ex.message).contains("UNKNOWN(id=-1005)")
}
try {
assertThat(flags.getString(stringFlag)).isEmpty()
fail("Expected an exception when accessing an unspecified flag.")
} catch (ex: IllegalStateException) {
- assertThat(ex.message).contains("UNKNOWN(id=-1001)")
+ assertThat(ex.message).contains("UNKNOWN(id=-1002)")
}
try {
assertThat(flags.getString(resourceStringFlag)).isEmpty()
fail("Expected an exception when accessing an unspecified flag.")
} catch (ex: IllegalStateException) {
- assertThat(ex.message).contains("UNKNOWN(id=-1003)")
+ assertThat(ex.message).contains("UNKNOWN(id=-1004)")
}
}
@Test
fun specifiedFlagsReturnCorrectValues() {
val flags = FakeFeatureFlags()
- flags.set(booleanFlag, false)
+ flags.set(unreleasedFlag, false)
+ flags.set(releasedFlag, false)
flags.set(resourceBooleanFlag, false)
flags.set(sysPropBooleanFlag, false)
flags.set(resourceStringFlag, "")
- assertThat(flags.isEnabled(booleanFlag)).isFalse()
+ assertThat(flags.isEnabled(unreleasedFlag)).isFalse()
+ assertThat(flags.isEnabled(releasedFlag)).isFalse()
assertThat(flags.isEnabled(resourceBooleanFlag)).isFalse()
assertThat(flags.isEnabled(sysPropBooleanFlag)).isFalse()
assertThat(flags.getString(resourceStringFlag)).isEmpty()
- flags.set(booleanFlag, true)
+ flags.set(unreleasedFlag, true)
+ flags.set(releasedFlag, true)
flags.set(resourceBooleanFlag, true)
flags.set(sysPropBooleanFlag, true)
flags.set(resourceStringFlag, "Android")
- assertThat(flags.isEnabled(booleanFlag)).isTrue()
+ assertThat(flags.isEnabled(unreleasedFlag)).isTrue()
+ assertThat(flags.isEnabled(releasedFlag)).isTrue()
assertThat(flags.isEnabled(resourceBooleanFlag)).isTrue()
assertThat(flags.isEnabled(sysPropBooleanFlag)).isTrue()
assertThat(flags.getString(resourceStringFlag)).isEqualTo("Android")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
index 51f3404a9bca..4511193d41d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
@@ -35,6 +35,10 @@ import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.mockito.withArgCaptor
import com.android.systemui.util.settings.SecureSettings
import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import java.io.Serializable
+import java.io.StringWriter
+import java.util.function.Consumer
import org.junit.Assert
import org.junit.Before
import org.junit.Test
@@ -48,12 +52,8 @@ import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.MockitoAnnotations
-import java.io.PrintWriter
-import java.io.Serializable
-import java.io.StringWriter
-import java.util.function.Consumer
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
/**
* NOTE: This test is for the version of FeatureFlagManager in src-release, which should not allow
@@ -75,10 +75,11 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
private val flagMap = mutableMapOf<Int, Flag<*>>()
private lateinit var broadcastReceiver: BroadcastReceiver
private lateinit var clearCacheAction: Consumer<Int>
+ private val serverFlagReader = ServerFlagReaderFake()
private val deviceConfig = DeviceConfigProxyFake()
- private val teamfoodableFlagA = BooleanFlag(500, false, true)
- private val teamfoodableFlagB = BooleanFlag(501, true, true)
+ private val teamfoodableFlagA = UnreleasedFlag(500, true)
+ private val teamfoodableFlagB = ReleasedFlag(501, true)
@Before
fun setup() {
@@ -93,6 +94,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
resources,
dumpManager,
deviceConfig,
+ serverFlagReader,
flagMap,
commandRegistry,
barService
@@ -109,40 +111,41 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
}
@Test
- fun testReadBooleanFlag() {
+ fun readBooleanFlag() {
// Remember that the TEAMFOOD flag is id#1 and has special behavior.
whenever(flagManager.readFlagValue<Boolean>(eq(3), any())).thenReturn(true)
whenever(flagManager.readFlagValue<Boolean>(eq(4), any())).thenReturn(false)
- assertThat(mFeatureFlagsDebug.isEnabled(BooleanFlag(2, true))).isTrue()
- assertThat(mFeatureFlagsDebug.isEnabled(BooleanFlag(3, false))).isTrue()
- assertThat(mFeatureFlagsDebug.isEnabled(BooleanFlag(4, true))).isFalse()
- assertThat(mFeatureFlagsDebug.isEnabled(BooleanFlag(5, false))).isFalse()
+
+ assertThat(mFeatureFlagsDebug.isEnabled(ReleasedFlag(2))).isTrue()
+ assertThat(mFeatureFlagsDebug.isEnabled(UnreleasedFlag(3))).isTrue()
+ assertThat(mFeatureFlagsDebug.isEnabled(ReleasedFlag(4))).isFalse()
+ assertThat(mFeatureFlagsDebug.isEnabled(UnreleasedFlag(5))).isFalse()
}
@Test
- fun testTeamFoodFlag_False() {
+ fun teamFoodFlag_False() {
whenever(flagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(false)
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagA)).isFalse()
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagB)).isTrue()
// Regular boolean flags should still test the same.
// Only our teamfoodableFlag should change.
- testReadBooleanFlag()
+ readBooleanFlag()
}
@Test
- fun testTeamFoodFlag_True() {
+ fun teamFoodFlag_True() {
whenever(flagManager.readFlagValue<Boolean>(eq(1), any())).thenReturn(true)
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagA)).isTrue()
assertThat(mFeatureFlagsDebug.isEnabled(teamfoodableFlagB)).isTrue()
// Regular boolean flags should still test the same.
// Only our teamfoodableFlag should change.
- testReadBooleanFlag()
+ readBooleanFlag()
}
@Test
- fun testTeamFoodFlag_Overridden() {
+ fun teamFoodFlag_Overridden() {
whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagA.id), any()))
.thenReturn(true)
whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagB.id), any()))
@@ -153,11 +156,11 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
// Regular boolean flags should still test the same.
// Only our teamfoodableFlag should change.
- testReadBooleanFlag()
+ readBooleanFlag()
}
@Test
- fun testReadResourceBooleanFlag() {
+ fun readResourceBooleanFlag() {
whenever(resources.getBoolean(1001)).thenReturn(false)
whenever(resources.getBoolean(1002)).thenReturn(true)
whenever(resources.getBoolean(1003)).thenReturn(false)
@@ -182,7 +185,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
}
@Test
- fun testReadSysPropBooleanFlag() {
+ fun readSysPropBooleanFlag() {
whenever(systemProperties.getBoolean(anyString(), anyBoolean())).thenAnswer {
if ("b".equals(it.getArgument<String?>(0))) {
return@thenAnswer true
@@ -198,7 +201,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
}
@Test
- fun testReadDeviceConfigBooleanFlag() {
+ fun readDeviceConfigBooleanFlag() {
val namespace = "test_namespace"
deviceConfig.setProperty(namespace, "a", "true", false)
deviceConfig.setProperty(namespace, "b", "false", false)
@@ -213,7 +216,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
}
@Test
- fun testReadStringFlag() {
+ fun readStringFlag() {
whenever(flagManager.readFlagValue<String>(eq(3), any())).thenReturn("foo")
whenever(flagManager.readFlagValue<String>(eq(4), any())).thenReturn("bar")
assertThat(mFeatureFlagsDebug.getString(StringFlag(1, "biz"))).isEqualTo("biz")
@@ -223,7 +226,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
}
@Test
- fun testReadResourceStringFlag() {
+ fun readResourceStringFlag() {
whenever(resources.getString(1001)).thenReturn("")
whenever(resources.getString(1002)).thenReturn("resource2")
whenever(resources.getString(1003)).thenReturn("resource3")
@@ -253,8 +256,8 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
}
@Test
- fun testBroadcastReceiverIgnoresInvalidData() {
- addFlag(BooleanFlag(1, false))
+ fun broadcastReceiver_IgnoresInvalidData() {
+ addFlag(UnreleasedFlag(1))
addFlag(ResourceBooleanFlag(2, 1002))
addFlag(StringFlag(3, "flag3"))
addFlag(ResourceStringFlag(4, 1004))
@@ -272,10 +275,10 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
}
@Test
- fun testIntentWithIdButNoValueKeyClears() {
- addFlag(BooleanFlag(1, false))
+ fun intentWithId_NoValueKeyClears() {
+ addFlag(UnreleasedFlag(1))
- // trying to erase an id not in the map does noting
+ // trying to erase an id not in the map does nothing
broadcastReceiver.onReceive(
mockContext,
Intent(FlagManager.ACTION_SET_FLAG).putExtra(FlagManager.EXTRA_ID, 0)
@@ -291,9 +294,9 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
}
@Test
- fun testSetBooleanFlag() {
- addFlag(BooleanFlag(1, false))
- addFlag(BooleanFlag(2, false))
+ fun setBooleanFlag() {
+ addFlag(UnreleasedFlag(1))
+ addFlag(UnreleasedFlag(2))
addFlag(ResourceBooleanFlag(3, 1003))
addFlag(ResourceBooleanFlag(4, 1004))
@@ -311,7 +314,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
}
@Test
- fun testSetStringFlag() {
+ fun setStringFlag() {
addFlag(StringFlag(1, "flag1"))
addFlag(ResourceStringFlag(2, 1002))
@@ -323,7 +326,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
}
@Test
- fun testSetFlagClearsCache() {
+ fun setFlag_ClearsCache() {
val flag1 = addFlag(StringFlag(1, "flag1"))
whenever(flagManager.readFlagValue<String>(eq(1), any())).thenReturn("original")
@@ -345,12 +348,30 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
}
@Test
- fun testRegisterCommand() {
+ fun serverSide_Overrides_MakesFalse() {
+ val flag = ReleasedFlag(100)
+
+ serverFlagReader.setFlagValue(flag.id, false)
+
+ assertThat(mFeatureFlagsDebug.isEnabled(flag)).isFalse()
+ }
+
+ @Test
+ fun serverSide_Overrides_MakesTrue() {
+ val flag = UnreleasedFlag(100)
+
+ serverFlagReader.setFlagValue(flag.id, true)
+
+ assertThat(mFeatureFlagsDebug.isEnabled(flag)).isTrue()
+ }
+
+ @Test
+ fun statusBarCommand_IsRegistered() {
verify(commandRegistry).registerCommand(anyString(), any())
}
@Test
- fun testNoOpCommand() {
+ fun noOpCommand() {
val cmd = captureCommand()
cmd.execute(pw, ArrayList())
@@ -360,72 +381,42 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
}
@Test
- fun testReadFlagCommand() {
- addFlag(BooleanFlag(1, false))
+ fun readFlagCommand() {
+ addFlag(UnreleasedFlag(1))
val cmd = captureCommand()
cmd.execute(pw, listOf("1"))
verify(flagManager).readFlagValue<Boolean>(eq(1), any())
}
@Test
- fun testSetFlagCommand() {
- addFlag(BooleanFlag(1, false))
+ fun setFlagCommand() {
+ addFlag(UnreleasedFlag(1))
val cmd = captureCommand()
cmd.execute(pw, listOf("1", "on"))
verifyPutData(1, "{\"type\":\"boolean\",\"value\":true}")
}
@Test
- fun testToggleFlagCommand() {
- addFlag(BooleanFlag(1, true))
+ fun toggleFlagCommand() {
+ addFlag(ReleasedFlag(1))
val cmd = captureCommand()
cmd.execute(pw, listOf("1", "toggle"))
verifyPutData(1, "{\"type\":\"boolean\",\"value\":false}", 2)
}
@Test
- fun testEraseFlagCommand() {
- addFlag(BooleanFlag(1, true))
+ fun eraseFlagCommand() {
+ addFlag(ReleasedFlag(1))
val cmd = captureCommand()
cmd.execute(pw, listOf("1", "erase"))
verify(secureSettings).putStringForUser(eq("key-1"), eq(""), anyInt())
}
- private fun verifyPutData(id: Int, data: String, numReads: Int = 1) {
- inOrder(flagManager, secureSettings).apply {
- verify(flagManager, times(numReads)).readFlagValue(eq(id), any<FlagSerializer<*>>())
- verify(flagManager).idToSettingsKey(eq(id))
- verify(secureSettings).putStringForUser(eq("key-$id"), eq(data), anyInt())
- verify(flagManager).dispatchListenersAndMaybeRestart(eq(id), any())
- }.verifyNoMoreInteractions()
- verifyNoMoreInteractions(flagManager, secureSettings)
- }
-
- private fun setByBroadcast(id: Int, value: Serializable?) {
- val intent = Intent(FlagManager.ACTION_SET_FLAG)
- intent.putExtra(FlagManager.EXTRA_ID, id)
- intent.putExtra(FlagManager.EXTRA_VALUE, value)
- broadcastReceiver.onReceive(mockContext, intent)
- }
-
- private fun <F : Flag<*>> addFlag(flag: F): F {
- val old = flagMap.put(flag.id, flag)
- check(old == null) { "Flag ${flag.id} already registered" }
- return flag
- }
-
- private fun captureCommand(): Command {
- val captor = argumentCaptor<Function0<Command>>()
- verify(commandRegistry).registerCommand(anyString(), capture(captor))
-
- return captor.value.invoke()
- }
-
@Test
- fun testDump() {
- val flag1 = BooleanFlag(1, true)
+ fun dumpFormat() {
+ val flag1 = ReleasedFlag(1)
val flag2 = ResourceBooleanFlag(2, 1002)
- val flag3 = BooleanFlag(3, false)
+ val flag3 = UnreleasedFlag(3)
val flag4 = StringFlag(4, "")
val flag5 = StringFlag(5, "flag5default")
val flag6 = ResourceStringFlag(6, 1006)
@@ -457,6 +448,36 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
assertThat(dump).contains(" sysui_flag_7: [length=9] \"override7\"\n")
}
+ private fun verifyPutData(id: Int, data: String, numReads: Int = 1) {
+ inOrder(flagManager, secureSettings).apply {
+ verify(flagManager, times(numReads)).readFlagValue(eq(id), any<FlagSerializer<*>>())
+ verify(flagManager).idToSettingsKey(eq(id))
+ verify(secureSettings).putStringForUser(eq("key-$id"), eq(data), anyInt())
+ verify(flagManager).dispatchListenersAndMaybeRestart(eq(id), any())
+ }.verifyNoMoreInteractions()
+ verifyNoMoreInteractions(flagManager, secureSettings)
+ }
+
+ private fun setByBroadcast(id: Int, value: Serializable?) {
+ val intent = Intent(FlagManager.ACTION_SET_FLAG)
+ intent.putExtra(FlagManager.EXTRA_ID, id)
+ intent.putExtra(FlagManager.EXTRA_VALUE, value)
+ broadcastReceiver.onReceive(mockContext, intent)
+ }
+
+ private fun <F : Flag<*>> addFlag(flag: F): F {
+ val old = flagMap.put(flag.id, flag)
+ check(old == null) { "Flag ${flag.id} already registered" }
+ return flag
+ }
+
+ private fun captureCommand(): Command {
+ val captor = argumentCaptor<Function0<Command>>()
+ verify(commandRegistry).registerCommand(anyString(), capture(captor))
+
+ return captor.value.invoke()
+ }
+
private fun dumpToString(): String {
val sw = StringWriter()
val pw = PrintWriter(sw)
@@ -464,4 +485,4 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
pw.flush()
return sw.toString()
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
index 6b683f456ea7..e94b5202956d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
@@ -30,8 +30,8 @@ import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
-import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
/**
* NOTE: This test is for the version of FeatureFlagManager in src-release, which should not allow
@@ -44,13 +44,18 @@ class FeatureFlagsReleaseTest : SysuiTestCase() {
@Mock private lateinit var mResources: Resources
@Mock private lateinit var mSystemProperties: SystemPropertiesHelper
@Mock private lateinit var mDumpManager: DumpManager
+ private val serverFlagReader = ServerFlagReaderFake()
private val deviceConfig = DeviceConfigProxyFake()
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- mFeatureFlagsRelease = FeatureFlagsRelease(mResources, mSystemProperties, deviceConfig,
+ mFeatureFlagsRelease = FeatureFlagsRelease(
+ mResources,
+ mSystemProperties,
+ deviceConfig,
+ serverFlagReader,
mDumpManager)
}
@@ -113,4 +118,22 @@ class FeatureFlagsReleaseTest : SysuiTestCase() {
whenever(mSystemProperties.getBoolean(flagName, flagDefault)).thenReturn(flagDefault)
assertThat(mFeatureFlagsRelease.isEnabled(flag)).isEqualTo(flagDefault)
}
-} \ No newline at end of file
+
+ @Test
+ fun serverSide_OverridesReleased_MakesFalse() {
+ val flag = ReleasedFlag(100)
+
+ serverFlagReader.setFlagValue(flag.id, false)
+
+ assertThat(mFeatureFlagsRelease.isEnabled(flag)).isFalse()
+ }
+
+ @Test
+ fun serverSide_OverridesUnreleased_Ignored() {
+ val flag = UnreleasedFlag(100)
+
+ serverFlagReader.setFlagValue(flag.id, true)
+
+ assertThat(mFeatureFlagsRelease.isEnabled(flag)).isFalse()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
index a2eca81b04ed..17324a01502b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
@@ -26,6 +26,7 @@ import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
+import java.util.function.Consumer
import org.junit.Assert.assertThrows
import org.junit.Before
import org.junit.Test
@@ -33,9 +34,8 @@ import org.mockito.Mock
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
-import org.mockito.MockitoAnnotations
-import java.util.function.Consumer
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
/**
* NOTE: This test is for the version of FeatureFlagManager in src-release, which should not allow
@@ -64,14 +64,14 @@ class FlagManagerTest : SysuiTestCase() {
verifyNoMoreInteractions(mFlagSettingsHelper)
// adding the first listener registers the observer
- mFlagManager.addListener(BooleanFlag(1, true), listener1)
+ mFlagManager.addListener(ReleasedFlag(1), listener1)
val observer = withArgCaptor<ContentObserver> {
verify(mFlagSettingsHelper).registerContentObserver(any(), any(), capture())
}
verifyNoMoreInteractions(mFlagSettingsHelper)
// adding another listener does nothing
- mFlagManager.addListener(BooleanFlag(2, true), listener2)
+ mFlagManager.addListener(ReleasedFlag(2), listener2)
verifyNoMoreInteractions(mFlagSettingsHelper)
// removing the original listener does nothing with second one still present
@@ -89,7 +89,7 @@ class FlagManagerTest : SysuiTestCase() {
val listener = mock<FlagListenable.Listener>()
val clearCacheAction = mock<Consumer<Int>>()
mFlagManager.clearCacheAction = clearCacheAction
- mFlagManager.addListener(BooleanFlag(1, true), listener)
+ mFlagManager.addListener(ReleasedFlag(1), listener)
val observer = withArgCaptor<ContentObserver> {
verify(mFlagSettingsHelper).registerContentObserver(any(), any(), capture())
}
@@ -101,8 +101,8 @@ class FlagManagerTest : SysuiTestCase() {
fun testObserverInvokesListeners() {
val listener1 = mock<FlagListenable.Listener>()
val listener10 = mock<FlagListenable.Listener>()
- mFlagManager.addListener(BooleanFlag(1, true), listener1)
- mFlagManager.addListener(BooleanFlag(10, true), listener10)
+ mFlagManager.addListener(ReleasedFlag(1), listener1)
+ mFlagManager.addListener(ReleasedFlag(10), listener10)
val observer = withArgCaptor<ContentObserver> {
verify(mFlagSettingsHelper).registerContentObserver(any(), any(), capture())
}
@@ -127,8 +127,8 @@ class FlagManagerTest : SysuiTestCase() {
fun testOnlySpecificFlagListenerIsInvoked() {
val listener1 = mock<FlagListenable.Listener>()
val listener10 = mock<FlagListenable.Listener>()
- mFlagManager.addListener(BooleanFlag(1, true), listener1)
- mFlagManager.addListener(BooleanFlag(10, true), listener10)
+ mFlagManager.addListener(ReleasedFlag(1), listener1)
+ mFlagManager.addListener(ReleasedFlag(10), listener10)
mFlagManager.dispatchListenersAndMaybeRestart(1, null)
val flagEvent1 = withArgCaptor<FlagListenable.FlagEvent> {
@@ -148,8 +148,8 @@ class FlagManagerTest : SysuiTestCase() {
@Test
fun testSameListenerCanBeUsedForMultipleFlags() {
val listener = mock<FlagListenable.Listener>()
- mFlagManager.addListener(BooleanFlag(1, true), listener)
- mFlagManager.addListener(BooleanFlag(10, true), listener)
+ mFlagManager.addListener(ReleasedFlag(1), listener)
+ mFlagManager.addListener(ReleasedFlag(10), listener)
mFlagManager.dispatchListenersAndMaybeRestart(1, null)
val flagEvent1 = withArgCaptor<FlagListenable.FlagEvent> {
@@ -177,7 +177,7 @@ class FlagManagerTest : SysuiTestCase() {
@Test
fun testListenerCanSuppressRestart() {
val restartAction = mock<Consumer<Boolean>>()
- mFlagManager.addListener(BooleanFlag(1, true)) { event ->
+ mFlagManager.addListener(ReleasedFlag(1)) { event ->
event.requestNoRestart()
}
mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)
@@ -188,7 +188,7 @@ class FlagManagerTest : SysuiTestCase() {
@Test
fun testListenerOnlySuppressesRestartForOwnFlag() {
val restartAction = mock<Consumer<Boolean>>()
- mFlagManager.addListener(BooleanFlag(10, true)) { event ->
+ mFlagManager.addListener(ReleasedFlag(10)) { event ->
event.requestNoRestart()
}
mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)
@@ -199,10 +199,10 @@ class FlagManagerTest : SysuiTestCase() {
@Test
fun testRestartWhenNotAllListenersRequestSuppress() {
val restartAction = mock<Consumer<Boolean>>()
- mFlagManager.addListener(BooleanFlag(10, true)) { event ->
+ mFlagManager.addListener(ReleasedFlag(10)) { event ->
event.requestNoRestart()
}
- mFlagManager.addListener(BooleanFlag(10, true)) {
+ mFlagManager.addListener(ReleasedFlag(10)) {
// do not request
}
mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction)
@@ -295,4 +295,4 @@ class FlagManagerTest : SysuiTestCase() {
assertThat(StringFlagSerializer.toSettingsData("foo"))
.isEqualTo("{\"type\":\"string\",\"value\":\"foo\"}")
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java
index 25c302885e07..250cc48fece3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java
@@ -107,11 +107,11 @@ public class FlagsTest extends SysuiTestCase {
}
private static class DuplicateFlagContainer {
- public static final BooleanFlag A_FLAG = new BooleanFlag(0);
- public static final BooleanFlag B_FLAG = new BooleanFlag(0);
+ public static final BooleanFlag A_FLAG = new UnreleasedFlag(0);
+ public static final BooleanFlag B_FLAG = new UnreleasedFlag(0);
public static final StringFlag C_FLAG = new StringFlag(0);
- public static final BooleanFlag D_FLAG = new BooleanFlag(1);
+ public static final BooleanFlag D_FLAG = new UnreleasedFlag(1);
public static final DoubleFlag E_FLAG = new DoubleFlag(3);
public static final DoubleFlag F_FLAG = new DoubleFlag(3);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
index bcc76abc89ba..810c6dc4776d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.keyguard.data.repository
+package com.android.systemui.keyguard.data.quickaffordance
import androidx.test.filters.SmallTest
import com.android.systemui.R
@@ -22,11 +22,10 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.controls.dagger.ControlsComponent
import com.android.systemui.controls.management.ControlsListingController
-import com.android.systemui.keyguard.data.quickaffordance.HomeControlsKeyguardQuickAffordanceConfig
-import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import java.util.Optional
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.runBlockingTest
@@ -50,18 +49,19 @@ class HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest : SysuiTes
companion object {
@Parameters(
name =
- "feature enabled = {0}, has favorites = {1}, has service infos = {2} - expected" +
- " visible = {3}"
+ "feature enabled = {0}, has favorites = {1}, has service infos = {2}, can show" +
+ " while locked = {3} - expected visible = {4}"
)
@JvmStatic
fun data() =
- (0 until 8)
+ (0 until 16)
.map { combination ->
arrayOf(
- /* isFeatureEnabled= */ combination and 0b100 != 0,
- /* hasFavorites= */ combination and 0b010 != 0,
- /* hasServiceInfos= */ combination and 0b001 != 0,
- /* isVisible= */ combination == 0b111,
+ /* isFeatureEnabled= */ combination and 0b1000 != 0,
+ /* hasFavorites= */ combination and 0b0100 != 0,
+ /* hasServiceInfos= */ combination and 0b0010 != 0,
+ /* canShowWhileLocked= */ combination and 0b0001 != 0,
+ /* isVisible= */ combination == 0b1111,
)
}
.toList()
@@ -79,7 +79,8 @@ class HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest : SysuiTes
@JvmField @Parameter(0) var isFeatureEnabled: Boolean = false
@JvmField @Parameter(1) var hasFavorites: Boolean = false
@JvmField @Parameter(2) var hasServiceInfos: Boolean = false
- @JvmField @Parameter(3) var isVisible: Boolean = false
+ @JvmField @Parameter(3) var canShowWhileLocked: Boolean = false
+ @JvmField @Parameter(4) var isVisible: Boolean = false
@Before
fun setUp() {
@@ -89,6 +90,8 @@ class HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest : SysuiTes
whenever(component.getControlsController()).thenReturn(Optional.of(controlsController))
whenever(component.getControlsListingController())
.thenReturn(Optional.of(controlsListingController))
+ whenever(component.canShowWhileLockedSetting)
+ .thenReturn(MutableStateFlow(canShowWhileLocked))
underTest =
HomeControlsKeyguardQuickAffordanceConfig(
@@ -111,14 +114,16 @@ class HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest : SysuiTes
val values = mutableListOf<KeyguardQuickAffordanceConfig.State>()
val job = underTest.state.onEach(values::add).launchIn(this)
- verify(controlsListingController).addCallback(callbackCaptor.capture())
- callbackCaptor.value.onServicesUpdated(
- if (hasServiceInfos) {
- listOf(mock())
- } else {
- emptyList()
- }
- )
+ if (canShowWhileLocked) {
+ verify(controlsListingController).addCallback(callbackCaptor.capture())
+ callbackCaptor.value.onServicesUpdated(
+ if (hasServiceInfos) {
+ listOf(mock())
+ } else {
+ emptyList()
+ }
+ )
+ }
assertThat(values.last())
.isInstanceOf(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
index 592e80b9e7d9..ef588f5ce255 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
@@ -51,6 +51,7 @@ class HomeControlsKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ whenever(component.canShowWhileLockedSetting).thenReturn(MutableStateFlow(true))
underTest =
HomeControlsKeyguardQuickAffordanceConfig(
@@ -60,7 +61,26 @@ class HomeControlsKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
}
@Test
- fun `state - when listing controller is missing - returns None`() = runBlockingTest {
+ fun `state - when cannot show while locked - returns Hidden`() = runBlockingTest {
+ whenever(component.canShowWhileLockedSetting).thenReturn(MutableStateFlow(false))
+ whenever(component.isEnabled()).thenReturn(true)
+ whenever(component.getTileImageId()).thenReturn(R.drawable.controls_icon)
+ whenever(component.getTileTitleId()).thenReturn(R.string.quick_controls_title)
+ val controlsController = mock<ControlsController>()
+ whenever(component.getControlsController()).thenReturn(Optional.of(controlsController))
+ whenever(component.getControlsListingController()).thenReturn(Optional.empty())
+ whenever(controlsController.getFavorites()).thenReturn(listOf(mock()))
+
+ val values = mutableListOf<KeyguardQuickAffordanceConfig.State>()
+ val job = underTest.state.onEach(values::add).launchIn(this)
+
+ assertThat(values.last())
+ .isInstanceOf(KeyguardQuickAffordanceConfig.State.Hidden::class.java)
+ job.cancel()
+ }
+
+ @Test
+ fun `state - when listing controller is missing - returns Hidden`() = runBlockingTest {
whenever(component.isEnabled()).thenReturn(true)
whenever(component.getTileImageId()).thenReturn(R.drawable.controls_icon)
whenever(component.getTileTitleId()).thenReturn(R.string.quick_controls_title)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt
index 4abb973817b1..56aff3c2fc8b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt
@@ -24,16 +24,11 @@ class LogBufferTest : SysuiTestCase() {
@Before
fun setup() {
outputWriter = StringWriter()
- buffer = createBuffer(UNBOUNDED_STACK_TRACE, NESTED_TRACE_DEPTH)
+ buffer = createBuffer()
}
- private fun createBuffer(rootTraceDepth: Int, nestedTraceDepth: Int): LogBuffer {
- return LogBuffer("TestBuffer",
- 1,
- logcatEchoTracker,
- false,
- rootStackTraceDepth = rootTraceDepth,
- nestedStackTraceDepth = nestedTraceDepth)
+ private fun createBuffer(): LogBuffer {
+ return LogBuffer("TestBuffer", 1, logcatEchoTracker, false)
}
@Test
@@ -56,95 +51,83 @@ class LogBufferTest : SysuiTestCase() {
}
@Test
- fun dump_writesExceptionAndStacktraceLimitedToGivenDepth() {
- buffer = createBuffer(rootTraceDepth = 2, nestedTraceDepth = -1)
- // stack trace depth of 5
- val exception = createTestException("Exception message", "TestClass", 5)
+ fun dump_writesExceptionAndStacktrace() {
+ buffer = createBuffer()
+ val exception = createTestException("Exception message", "TestClass")
buffer.log("Tag", LogLevel.ERROR, { str1 = "Extra message" }, { str1!! }, exception)
val dumpedString = dumpBuffer()
- // logs are limited to depth 2
- assertThat(dumpedString).contains("E Tag: Extra message")
- assertThat(dumpedString).contains("E Tag: java.lang.RuntimeException: Exception message")
- assertThat(dumpedString).contains("E Tag: \tat TestClass.TestMethod(TestClass.java:1)")
- assertThat(dumpedString).contains("E Tag: \tat TestClass.TestMethod(TestClass.java:2)")
- assertThat(dumpedString)
- .doesNotContain("E Tag: \tat TestClass.TestMethod(TestClass.java:3)")
+ assertThat(dumpedString).contains("Extra message")
+ assertThat(dumpedString).contains("java.lang.RuntimeException: Exception message")
+ assertThat(dumpedString).contains("at TestClass.TestMethod(TestClass.java:1)")
+ assertThat(dumpedString).contains("at TestClass.TestMethod(TestClass.java:2)")
}
@Test
- fun dump_writesCauseAndStacktraceLimitedToGivenDepth() {
- buffer = createBuffer(rootTraceDepth = 0, nestedTraceDepth = 2)
+ fun dump_writesCauseAndStacktrace() {
+ buffer = createBuffer()
val exception = createTestException("Exception message",
"TestClass",
- 1,
- cause = createTestException("The real cause!", "TestClass", 5))
+ cause = createTestException("The real cause!", "TestClass"))
buffer.log("Tag", LogLevel.ERROR, { str1 = "Extra message" }, { str1!! }, exception)
val dumpedString = dumpBuffer()
- // logs are limited to depth 2
- assertThat(dumpedString)
- .contains("E Tag: Caused by: java.lang.RuntimeException: The real cause!")
- assertThat(dumpedString).contains("E Tag: \tat TestClass.TestMethod(TestClass.java:1)")
- assertThat(dumpedString).contains("E Tag: \tat TestClass.TestMethod(TestClass.java:2)")
assertThat(dumpedString)
- .doesNotContain("E Tag: \tat TestClass.TestMethod(TestClass.java:3)")
+ .contains("Caused by: java.lang.RuntimeException: The real cause!")
+ assertThat(dumpedString).contains("at TestClass.TestMethod(TestClass.java:1)")
+ assertThat(dumpedString).contains("at TestClass.TestMethod(TestClass.java:2)")
}
@Test
- fun dump_writesSuppressedExceptionAndStacktraceLimitedToGivenDepth() {
- buffer = createBuffer(rootTraceDepth = 0, nestedTraceDepth = 2)
+ fun dump_writesSuppressedExceptionAndStacktrace() {
+ buffer = createBuffer()
val exception = RuntimeException("Root exception message")
exception.addSuppressed(
createTestException(
"First suppressed exception",
"FirstClass",
- 5,
- createTestException("Cause of suppressed exp", "ThirdClass", 5)
+ createTestException("Cause of suppressed exp", "ThirdClass")
))
exception.addSuppressed(
- createTestException("Second suppressed exception", "SecondClass", 5))
+ createTestException("Second suppressed exception", "SecondClass"))
buffer.log("Tag", LogLevel.ERROR, { str1 = "Extra message" }, { str1!! }, exception)
val dumpedStr = dumpBuffer()
- // logs are limited to depth 2
// first suppressed exception
assertThat(dumpedStr)
- .contains("E Tag: Suppressed: " +
+ .contains("Suppressed: " +
"java.lang.RuntimeException: First suppressed exception")
- assertThat(dumpedStr).contains("E Tag: \tat FirstClass.TestMethod(FirstClass.java:1)")
- assertThat(dumpedStr).contains("E Tag: \tat FirstClass.TestMethod(FirstClass.java:2)")
- assertThat(dumpedStr)
- .doesNotContain("E Tag: \tat FirstClass.TestMethod(FirstClass.java:3)")
+ assertThat(dumpedStr).contains("at FirstClass.TestMethod(FirstClass.java:1)")
+ assertThat(dumpedStr).contains("at FirstClass.TestMethod(FirstClass.java:2)")
assertThat(dumpedStr)
- .contains("E Tag: Caused by: java.lang.RuntimeException: Cause of suppressed exp")
- assertThat(dumpedStr).contains("E Tag: \tat ThirdClass.TestMethod(ThirdClass.java:1)")
- assertThat(dumpedStr).contains("E Tag: \tat ThirdClass.TestMethod(ThirdClass.java:2)")
- assertThat(dumpedStr)
- .doesNotContain("E Tag: \tat ThirdClass.TestMethod(ThirdClass.java:3)")
+ .contains("Caused by: java.lang.RuntimeException: Cause of suppressed exp")
+ assertThat(dumpedStr).contains("at ThirdClass.TestMethod(ThirdClass.java:1)")
+ assertThat(dumpedStr).contains("at ThirdClass.TestMethod(ThirdClass.java:2)")
// second suppressed exception
assertThat(dumpedStr)
- .contains("E Tag: Suppressed: " +
+ .contains("Suppressed: " +
"java.lang.RuntimeException: Second suppressed exception")
- assertThat(dumpedStr).contains("E Tag: \tat SecondClass.TestMethod(SecondClass.java:1)")
- assertThat(dumpedStr).contains("E Tag: \tat SecondClass.TestMethod(SecondClass.java:2)")
- assertThat(dumpedStr)
- .doesNotContain("E Tag: \tat SecondClass.TestMethod(SecondClass.java:3)")
+ assertThat(dumpedStr).contains("at SecondClass.TestMethod(SecondClass.java:1)")
+ assertThat(dumpedStr).contains("at SecondClass.TestMethod(SecondClass.java:2)")
}
private fun createTestException(
- message: String,
- errorClass: String,
- stackTraceLength: Int,
- cause: Throwable? = null
+ message: String,
+ errorClass: String,
+ cause: Throwable? = null,
): Exception {
val exception = RuntimeException(message, cause)
- exception.stackTrace = createStackTraceElements(errorClass, stackTraceLength)
+ exception.stackTrace = (1..5).map { lineNumber ->
+ StackTraceElement(errorClass,
+ "TestMethod",
+ "$errorClass.java",
+ lineNumber)
+ }.toTypedArray()
return exception
}
@@ -152,16 +135,4 @@ class LogBufferTest : SysuiTestCase() {
buffer.dump(PrintWriter(outputWriter), tailLength = 100)
return outputWriter.toString()
}
-
- private fun createStackTraceElements(
- errorClass: String,
- stackTraceLength: Int
- ): Array<StackTraceElement> {
- return (1..stackTraceLength).map { lineNumber ->
- StackTraceElement(errorClass,
- "TestMethod",
- "$errorClass.java",
- lineNumber)
- }.toTypedArray()
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
index 6a532d74967f..6468fe1a81d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
@@ -17,9 +17,9 @@
package com.android.systemui.media
import android.app.smartspace.SmartspaceAction
-import androidx.test.filters.SmallTest
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
@@ -29,18 +29,18 @@ import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.Executor
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
-import java.util.concurrent.Executor
private const val KEY = "TEST_KEY"
private const val KEY_ALT = "TEST_KEY_2"
@@ -433,7 +433,7 @@ class MediaDataFilterTest : SysuiTestCase() {
val dataCurrentAndActive = dataCurrent.copy(active = true)
verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true),
eq(100), eq(true))
- assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
+ assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isTrue()
// Smartspace update shouldn't be propagated for the empty rec list.
verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
verify(logger, never()).logRecommendationAdded(any(), any())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
index 3d3ac836d264..83168cb87dfe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
@@ -305,7 +305,7 @@ class MediaResumeListenerTest : SysuiTestCase() {
// Then we save an update with the current time
verify(sharedPrefsEditor).putString(any(), (capture(componentCaptor)))
componentCaptor.value.split(ResumeMediaBrowser.DELIMITER.toRegex())
- ?.dropLastWhile { it.isEmpty() }.forEach {
+ .dropLastWhile { it.isEmpty() }.forEach {
val result = it.split("/")
assertThat(result.size).isEqualTo(3)
assertThat(result[2].toLong()).isEqualTo(currentTime)
@@ -392,7 +392,7 @@ class MediaResumeListenerTest : SysuiTestCase() {
// Then we store the new lastPlayed time
verify(sharedPrefsEditor).putString(any(), (capture(componentCaptor)))
componentCaptor.value.split(ResumeMediaBrowser.DELIMITER.toRegex())
- ?.dropLastWhile { it.isEmpty() }.forEach {
+ .dropLastWhile { it.isEmpty() }.forEach {
val result = it.split("/")
assertThat(result.size).isEqualTo(3)
assertThat(result[2].toLong()).isEqualTo(currentTime)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 568e0cb22f18..260bb8760f1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -254,4 +254,30 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
verify(mMediaOutputController).connectDevice(mMediaDevice2);
}
+
+ @Test
+ public void onItemClick_onGroupActionTriggered_verifySeekbarDisabled() {
+ when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(mMediaDevices);
+ List<MediaDevice> selectableDevices = new ArrayList<>();
+ selectableDevices.add(mMediaDevice1);
+ when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(selectableDevices);
+ when(mMediaOutputController.hasAdjustVolumeUserRestriction()).thenReturn(true);
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
+ mViewHolder.mContainerLayout.performClick();
+
+ assertThat(mViewHolder.mSeekBar.isEnabled()).isFalse();
+ }
+
+ @Test
+ public void onBindViewHolder_volumeControlChangeToEnabled_enableSeekbarAgain() {
+ when(mMediaOutputController.isVolumeControlEnabled(mMediaDevice1)).thenReturn(false);
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+ assertThat(mViewHolder.mSeekBar.isEnabled()).isFalse();
+
+ when(mMediaOutputController.isVolumeControlEnabled(mMediaDevice1)).thenReturn(true);
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
+ assertThat(mViewHolder.mSeekBar.isEnabled()).isTrue();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
index edcf4791e6b1..80731037481a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
@@ -39,6 +39,7 @@ import android.view.accessibility.AccessibilityManager;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.keyguard.KeyguardViewController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
@@ -112,7 +113,7 @@ public class NavBarHelperTest extends SysuiTestCase {
mNavBarHelper = new NavBarHelper(mContext, mAccessibilityManager,
mAccessibilityButtonModeObserver, mAccessibilityButtonTargetObserver,
mSystemActions, mOverviewProxyService, mAssistManagerLazy,
- () -> Optional.of(mock(CentralSurfaces.class)),
+ () -> Optional.of(mock(CentralSurfaces.class)), mock(KeyguardViewController.class),
mNavigationModeController, mUserTracker, mDumpManager);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index fe5f433ace47..51f0953771cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -72,6 +72,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
+import com.android.keyguard.KeyguardViewController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.SysuiTestableContext;
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
@@ -193,6 +194,8 @@ public class NavigationBarTest extends SysuiTestCase {
@Mock
private CentralSurfaces mCentralSurfaces;
@Mock
+ private KeyguardViewController mKeyguardViewController;
+ @Mock
private UserContextProvider mUserContextProvider;
@Mock
private Resources mResources;
@@ -237,8 +240,8 @@ public class NavigationBarTest extends SysuiTestCase {
mock(AccessibilityButtonTargetsObserver.class),
mSystemActions, mOverviewProxyService,
() -> mock(AssistManager.class), () -> Optional.of(mCentralSurfaces),
- mock(NavigationModeController.class), mock(UserTracker.class),
- mock(DumpManager.class)));
+ mKeyguardViewController, mock(NavigationModeController.class),
+ mock(UserTracker.class), mock(DumpManager.class)));
mNavigationBar = createNavBar(mContext);
mExternalDisplayNavigationBar = createNavBar(mSysuiTestableContextExternal);
});
@@ -377,7 +380,7 @@ public class NavigationBarTest extends SysuiTestCase {
// Verify navbar didn't alter and showing back icon when the keyguard is showing without
// requesting IME insets visible.
- doReturn(true).when(mCentralSurfaces).isKeyguardShowing();
+ doReturn(true).when(mKeyguardViewController).isShowing();
mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
BACK_DISPOSITION_DEFAULT, true);
assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
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/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
index 07c8af953d1e..be14cc51ef96 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
@@ -26,7 +26,6 @@ import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.colorextraction.SysuiColorExtractor
import com.android.systemui.demomode.DemoModeController
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.qs.carrier.QSCarrierGroup
import com.android.systemui.qs.carrier.QSCarrierGroupController
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
@@ -46,10 +45,10 @@ import org.junit.runner.RunWith
import org.mockito.Answers
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@@ -162,7 +161,6 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
@Test
fun testRSSISlot_notCombined() {
- `when`(featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)).thenReturn(false)
controller.init()
val captor = argumentCaptor<List<String>>()
@@ -174,20 +172,6 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
}
@Test
- fun testRSSISlot_combined() {
- `when`(featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)).thenReturn(true)
- controller.init()
-
- val captor = argumentCaptor<List<String>>()
- verify(view).onAttach(any(), any(), capture(captor), any(), anyBoolean())
-
- assertThat(captor.value).containsExactly(
- mContext.getString(com.android.internal.R.string.status_bar_no_calling),
- mContext.getString(com.android.internal.R.string.status_bar_call_strength)
- )
- }
-
- @Test
fun testSingleCarrierCallback() {
controller.init()
reset(view)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
index 09ce37b4ac62..1e7722ae8395 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
@@ -44,7 +44,6 @@ import android.widget.TextView;
import androidx.test.filters.SmallTest;
import com.android.keyguard.CarrierTextManager;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.connectivity.IconState;
import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
@@ -88,7 +87,6 @@ public class QSCarrierGroupControllerTest extends LeakCheckedTest {
@Mock
private QSCarrier mQSCarrier3;
private TestableLooper mTestableLooper;
- @Mock private FeatureFlags mFeatureFlags;
@Mock
private QSCarrierGroupController.OnSingleCarrierChangedListener mOnSingleCarrierChangedListener;
@@ -130,7 +128,7 @@ public class QSCarrierGroupControllerTest extends LeakCheckedTest {
mQSCarrierGroupController = new QSCarrierGroupController.Builder(
mActivityStarter, handler, TestableLooper.get(this).getLooper(),
mNetworkController, mCarrierTextControllerBuilder, mContext, mCarrierConfigTracker,
- mFeatureFlags, mSlotIndexResolver)
+ mSlotIndexResolver)
.setQSCarrierGroup(mQSCarrierGroup)
.build();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
index 5212255078fc..99a17a613041 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
@@ -22,13 +22,11 @@ import static org.junit.Assert.assertTrue;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.util.FeatureFlagUtils;
import android.view.LayoutInflater;
import android.view.View;
import androidx.test.filters.SmallTest;
-import com.android.settingslib.graph.SignalDrawable;
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
@@ -59,14 +57,14 @@ public class QSCarrierTest extends SysuiTestCase {
@Test
public void testUpdateState_first() {
- CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+ CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
assertTrue(mQSCarrier.updateState(c, false));
}
@Test
public void testUpdateState_same() {
- CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+ CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
assertTrue(mQSCarrier.updateState(c, false));
assertFalse(mQSCarrier.updateState(c, false));
@@ -74,7 +72,7 @@ public class QSCarrierTest extends SysuiTestCase {
@Test
public void testUpdateState_changed() {
- CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+ CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
assertTrue(mQSCarrier.updateState(c, false));
@@ -85,14 +83,14 @@ public class QSCarrierTest extends SysuiTestCase {
@Test
public void testUpdateState_singleCarrier_first() {
- CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+ CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
assertTrue(mQSCarrier.updateState(c, true));
}
@Test
public void testUpdateState_singleCarrier_noShowIcon() {
- CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+ CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
mQSCarrier.updateState(c, true);
@@ -101,7 +99,7 @@ public class QSCarrierTest extends SysuiTestCase {
@Test
public void testUpdateState_multiCarrier_showIcon() {
- CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+ CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
mQSCarrier.updateState(c, false);
@@ -110,7 +108,7 @@ public class QSCarrierTest extends SysuiTestCase {
@Test
public void testUpdateState_changeSingleMultiSingle() {
- CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+ CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
mQSCarrier.updateState(c, true);
assertEquals(View.GONE, mQSCarrier.getRSSIView().getVisibility());
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/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index 5336ef09f368..ba49f3fa66ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -37,6 +37,7 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -78,6 +79,7 @@ import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
import org.mockito.Captor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -144,7 +146,25 @@ public class QSTileImplTest extends SysuiTestCase {
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
mTile.click(null /* view */);
- verify(mQsLogger).logTileClick(SPEC, StatusBarState.SHADE, Tile.STATE_ACTIVE);
+ verify(mQsLogger).logTileClick(eq(SPEC), eq(StatusBarState.SHADE), eq(Tile.STATE_ACTIVE),
+ anyInt());
+ }
+
+ @Test
+ public void testHandleClick_log() {
+ mTile.click(null);
+ mTile.click(null);
+ mTestableLooper.processAllMessages();
+ mTile.click(null);
+ mTestableLooper.processAllMessages();
+
+ InOrder inOrder = inOrder(mQsLogger);
+ inOrder.verify(mQsLogger).logTileClick(eq(SPEC), anyInt(), anyInt(), eq(0));
+ inOrder.verify(mQsLogger).logTileClick(eq(SPEC), anyInt(), anyInt(), eq(1));
+ inOrder.verify(mQsLogger).logHandleClick(SPEC, 0);
+ inOrder.verify(mQsLogger).logHandleClick(SPEC, 1);
+ inOrder.verify(mQsLogger).logTileClick(eq(SPEC), anyInt(), anyInt(), eq(2));
+ inOrder.verify(mQsLogger).logHandleClick(SPEC, 2);
}
@Test
@@ -183,7 +203,25 @@ public class QSTileImplTest extends SysuiTestCase {
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
mTile.secondaryClick(null /* view */);
- verify(mQsLogger).logTileSecondaryClick(SPEC, StatusBarState.SHADE, Tile.STATE_ACTIVE);
+ verify(mQsLogger).logTileSecondaryClick(eq(SPEC), eq(StatusBarState.SHADE),
+ eq(Tile.STATE_ACTIVE), anyInt());
+ }
+
+ @Test
+ public void testHandleSecondaryClick_log() {
+ mTile.secondaryClick(null);
+ mTile.secondaryClick(null);
+ mTestableLooper.processAllMessages();
+ mTile.secondaryClick(null);
+ mTestableLooper.processAllMessages();
+
+ InOrder inOrder = inOrder(mQsLogger);
+ inOrder.verify(mQsLogger).logTileSecondaryClick(eq(SPEC), anyInt(), anyInt(), eq(0));
+ inOrder.verify(mQsLogger).logTileSecondaryClick(eq(SPEC), anyInt(), anyInt(), eq(1));
+ inOrder.verify(mQsLogger).logHandleSecondaryClick(SPEC, 0);
+ inOrder.verify(mQsLogger).logHandleSecondaryClick(SPEC, 1);
+ inOrder.verify(mQsLogger).logTileSecondaryClick(eq(SPEC), anyInt(), anyInt(), eq(2));
+ inOrder.verify(mQsLogger).logHandleSecondaryClick(SPEC, 2);
}
@Test
@@ -210,7 +248,25 @@ public class QSTileImplTest extends SysuiTestCase {
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
mTile.longClick(null /* view */);
- verify(mQsLogger).logTileLongClick(SPEC, StatusBarState.SHADE, Tile.STATE_ACTIVE);
+ verify(mQsLogger).logTileLongClick(eq(SPEC), eq(StatusBarState.SHADE),
+ eq(Tile.STATE_ACTIVE), anyInt());
+ }
+
+ @Test
+ public void testHandleLongClick_log() {
+ mTile.longClick(null);
+ mTile.longClick(null);
+ mTestableLooper.processAllMessages();
+ mTile.longClick(null);
+ mTestableLooper.processAllMessages();
+
+ InOrder inOrder = inOrder(mQsLogger);
+ inOrder.verify(mQsLogger).logTileLongClick(eq(SPEC), anyInt(), anyInt(), eq(0));
+ inOrder.verify(mQsLogger).logTileLongClick(eq(SPEC), anyInt(), anyInt(), eq(1));
+ inOrder.verify(mQsLogger).logHandleLongClick(SPEC, 0);
+ inOrder.verify(mQsLogger).logHandleLongClick(SPEC, 1);
+ inOrder.verify(mQsLogger).logTileLongClick(eq(SPEC), anyInt(), anyInt(), eq(2));
+ inOrder.verify(mQsLogger).logHandleLongClick(SPEC, 2);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ripple/RippleViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/ripple/RippleViewTest.kt
new file mode 100644
index 000000000000..2d2f4cc9edd2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/ripple/RippleViewTest.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.ripple
+
+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
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class RippleViewTest : SysuiTestCase() {
+ @Mock
+ private lateinit var rippleView: RippleView
+
+ @Before
+ fun setup() {
+ rippleView = RippleView(context, null)
+ }
+
+ @Test
+ fun testSetupShader_compilesCircle() {
+ rippleView.setupShader(RippleShader.RippleShape.CIRCLE)
+ }
+
+ @Test
+ fun testSetupShader_compilesRoundedBox() {
+ rippleView.setupShader(RippleShader.RippleShape.ROUNDED_BOX)
+ }
+
+ @Test
+ fun testSetupShader_compilesEllipse() {
+ rippleView.setupShader(RippleShader.RippleShape.ELLIPSE)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt
new file mode 100644
index 000000000000..ce3f20d4d39d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt
@@ -0,0 +1,84 @@
+/*
+ * 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.screenshot
+
+import android.app.IActivityTaskManager
+import android.graphics.Rect
+import android.hardware.display.DisplayManager
+import android.os.Binder
+import android.os.IBinder
+import android.testing.AndroidTestingRunner
+import android.view.Display
+import android.view.SurfaceControl.ScreenshotHardwareBuffer
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Test the logic within ImageCaptureImpl
+ */
+@RunWith(AndroidTestingRunner::class)
+class ImageCaptureImplTest : SysuiTestCase() {
+ private val displayManager = mock<DisplayManager>()
+ private val atmService = mock<IActivityTaskManager>()
+ private val capture = TestableImageCaptureImpl(displayManager, atmService)
+
+ @Test
+ fun captureDisplayWithCrop() {
+ capture.captureDisplay(Display.DEFAULT_DISPLAY, Rect(1, 2, 3, 4))
+ assertThat(capture.token).isNotNull()
+ assertThat(capture.width!!).isEqualTo(2)
+ assertThat(capture.height!!).isEqualTo(2)
+ assertThat(capture.crop!!).isEqualTo(Rect(1, 2, 3, 4))
+ }
+
+ @Test
+ fun captureDisplayWithNullCrop() {
+ capture.captureDisplay(Display.DEFAULT_DISPLAY, null)
+ assertThat(capture.token).isNotNull()
+ assertThat(capture.width!!).isEqualTo(0)
+ assertThat(capture.height!!).isEqualTo(0)
+ assertThat(capture.crop!!).isEqualTo(Rect())
+ }
+
+ class TestableImageCaptureImpl(
+ displayManager: DisplayManager,
+ atmService: IActivityTaskManager
+ ) :
+ ImageCaptureImpl(displayManager, atmService) {
+
+ var token: IBinder? = null
+ var width: Int? = null
+ var height: Int? = null
+ var crop: Rect? = null
+
+ override fun physicalDisplayToken(displayId: Int): IBinder {
+ return Binder()
+ }
+
+ override fun captureDisplay(displayToken: IBinder, width: Int, height: Int, crop: Rect):
+ ScreenshotHardwareBuffer {
+ this.token = displayToken
+ this.width = width
+ this.height = height
+ this.crop = crop
+ return ScreenshotHardwareBuffer(null, null, false, false)
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
new file mode 100644
index 000000000000..002f23a9ed6d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
@@ -0,0 +1,106 @@
+/*
+ * 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.screenshot
+
+import android.content.ComponentName
+import android.graphics.Bitmap
+import android.graphics.ColorSpace
+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 com.android.internal.util.ScreenshotHelper.HardwareBitmapBundler
+import com.android.internal.util.ScreenshotHelper.ScreenshotRequest
+import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import java.util.function.Consumer
+import org.junit.Test
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.isNull
+
+class RequestProcessorTest {
+ private val controller = mock<ScreenshotController>()
+ private val bitmapCaptor = argumentCaptor<Bitmap>()
+
+ @Test
+ fun testFullScreenshot() {
+ val request = ScreenshotRequest(ScreenshotSource.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)
+
+ verify(controller).takeScreenshotFullscreen(/* topComponent */ isNull(),
+ eq(onSavedListener), eq(callback))
+ }
+
+ @Test
+ fun testSelectedRegionScreenshot() {
+ val request = ScreenshotRequest(ScreenshotSource.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)
+
+ verify(controller).takeScreenshotPartial(/* topComponent */ isNull(),
+ eq(onSavedListener), eq(callback))
+ }
+
+ @Test
+ fun testProvidedImageScreenshot() {
+ val taskId = 1111
+ val userId = 2222
+ val bounds = Rect(50, 50, 150, 150)
+ val topComponent = ComponentName("test", "test")
+ val processor = RequestProcessor(controller)
+
+ val buffer = HardwareBuffer.create(100, 100, HardwareBuffer.RGBA_8888, 1,
+ HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE)
+ 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 onSavedListener = mock<Consumer<Uri>>()
+ val callback = mock<RequestCallback>()
+
+ processor.processRequest(WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE, onSavedListener,
+ request, callback)
+
+ verify(controller).handleImageAsScreenshot(
+ bitmapCaptor.capture(), eq(bounds), eq(Insets.NONE), eq(taskId), eq(userId),
+ eq(topComponent), eq(onSavedListener), eq(callback)
+ )
+
+ assertThat(bitmapCaptor.value.equalsHardwareBitmap(bitmap)).isTrue()
+ }
+
+ private fun Bitmap.equalsHardwareBitmap(bitmap: Bitmap): Boolean {
+ return bitmap.hardwareBuffer == this.hardwareBuffer &&
+ bitmap.colorSpace == this.colorSpace
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index c9405c890278..fc28349e1e57 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -47,6 +47,7 @@ import android.content.ContentResolver;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
+import android.graphics.PointF;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
@@ -461,6 +462,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
NotificationWakeUpCoordinator coordinator =
new NotificationWakeUpCoordinator(
+ mDumpManager,
mock(HeadsUpManagerPhone.class),
new StatusBarStateControllerImpl(new UiEventLoggerFake(), mDumpManager,
mInteractionJankMonitor),
@@ -527,7 +529,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mNotificationShadeWindowController,
mDozeLog, mDozeParameters, mCommandQueue, mVibratorHelper,
mLatencyTracker, mPowerManager, mAccessibilityManager, 0, mUpdateMonitor,
- mMetricsLogger, mActivityManager, mConfigurationController,
+ mMetricsLogger, mConfigurationController,
() -> flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager,
mConversationNotificationManager, mMediaHiearchyManager,
mStatusBarKeyguardViewManager,
@@ -902,6 +904,76 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
}
@Test
+ public void keyguardStatusView_splitShade_dozing_alwaysDozingOn_isCentered() {
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ true);
+
+ setDozing(/* dozing= */ true, /* dozingAlwaysOn= */ true);
+
+ assertThat(isKeyguardStatusViewCentered()).isTrue();
+ }
+
+ @Test
+ public void keyguardStatusView_splitShade_dozing_alwaysDozingOff_isNotCentered() {
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ true);
+
+ setDozing(/* dozing= */ true, /* dozingAlwaysOn= */ false);
+
+ assertThat(isKeyguardStatusViewCentered()).isFalse();
+ }
+
+ @Test
+ public void keyguardStatusView_splitShade_notDozing_alwaysDozingOn_isNotCentered() {
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ true);
+
+ setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ true);
+
+ assertThat(isKeyguardStatusViewCentered()).isFalse();
+ }
+
+ @Test
+ public void keyguardStatusView_splitShade_pulsing_isCentered() {
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+ when(mNotificationListContainer.hasPulsingNotifications()).thenReturn(true);
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ true);
+
+ setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ true);
+
+ assertThat(isKeyguardStatusViewCentered()).isFalse();
+ }
+
+ @Test
+ public void keyguardStatusView_splitShade_notPulsing_isNotCentered() {
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+ when(mNotificationListContainer.hasPulsingNotifications()).thenReturn(false);
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ true);
+
+ setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ true);
+
+ assertThat(isKeyguardStatusViewCentered()).isFalse();
+ }
+
+ @Test
+ public void keyguardStatusView_singleShade_isCentered() {
+ enableSplitShade(/* enabled= */ false);
+ // The conditions below would make the clock NOT be centered on split shade.
+ // On single shade it should always be centered though.
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+ when(mNotificationListContainer.hasPulsingNotifications()).thenReturn(false);
+ mStatusBarStateController.setState(KEYGUARD);
+ setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ false);
+
+ assertThat(isKeyguardStatusViewCentered()).isFalse();
+ }
+
+ @Test
public void testDisableUserSwitcherAfterEnabling_returnsViewStubToTheViewHierarchy() {
givenViewAttached();
when(mResources.getBoolean(
@@ -1473,4 +1545,19 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
.thenReturn(splitShadeFullTransitionDistance);
mNotificationPanelViewController.updateResources();
}
+
+ private void setDozing(boolean dozing, boolean dozingAlwaysOn) {
+ when(mDozeParameters.getAlwaysOn()).thenReturn(dozingAlwaysOn);
+ mNotificationPanelViewController.setDozing(
+ /* dozing= */ dozing,
+ /* animate= */ false,
+ /* wakeUpTouchLocation= */ new PointF()
+ );
+ }
+
+ private boolean isKeyguardStatusViewCentered() {
+ mNotificationPanelViewController.updateResources();
+ return getConstraintSetLayout(R.id.keyguard_status_view).endToEnd
+ == ConstraintSet.PARENT_ID;
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenTargetFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt
index 185a8384dd75..6b34ca7284eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenTargetFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt
@@ -27,13 +27,14 @@ import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.settings.UserTracker
-import com.android.systemui.smartspace.filters.LockscreenTargetFilter
+import com.android.systemui.smartspace.filters.LockscreenAndDreamTargetFilter
import com.android.systemui.util.concurrency.Execution
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.withArgCaptor
import com.android.systemui.util.settings.SecureSettings
import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.Executor
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -41,18 +42,17 @@ import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.atLeast
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
-import java.util.concurrent.Executor
@SmallTest
@TestableLooper.RunWithLooper
@RunWith(AndroidTestingRunner::class)
-class LockscreenTargetFilterTest : SysuiTestCase() {
+class LockscreenAndDreamTargetFilterTest : SysuiTestCase() {
@Mock
private lateinit var secureSettings: SecureSettings
@@ -99,7 +99,7 @@ class LockscreenTargetFilterTest : SysuiTestCase() {
`when`(secureSettings
.getIntForUser(eq(setting) ?: setting, anyInt(), anyInt()))
.thenReturn(0)
- var filter = LockscreenTargetFilter(secureSettings, userTracker, execution, handler,
+ var filter = LockscreenAndDreamTargetFilter(secureSettings, userTracker, execution, handler,
contentResolver, uiExecution)
filter.addListener(listener)
@@ -132,7 +132,7 @@ class LockscreenTargetFilterTest : SysuiTestCase() {
`when`(secureSettings
.getIntForUser(eq(setting) ?: setting, anyInt(), anyInt()))
.thenReturn(0)
- var filter = LockscreenTargetFilter(secureSettings, userTracker, execution, handler,
+ var filter = LockscreenAndDreamTargetFilter(secureSettings, userTracker, execution, handler,
contentResolver, uiExecution)
filter.addListener(listener)
@@ -146,4 +146,4 @@ class LockscreenTargetFilterTest : SysuiTestCase() {
verify(listener).onCriteriaChanged()
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
index 0d1879cb2593..f8a0d2fc415c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
@@ -70,8 +70,6 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
@@ -127,8 +125,8 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
protected CarrierConfigTracker mCarrierConfigTracker;
protected FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
protected Handler mMainHandler;
- protected FeatureFlags mFeatureFlags;
protected WifiStatusTrackerFactory mWifiStatusTrackerFactory;
+ protected MobileSignalControllerFactory mMobileFactory;
protected int mSubId;
@@ -158,9 +156,6 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
- mFeatureFlags = mock(FeatureFlags.class);
- when(mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)).thenReturn(false);
-
mInstrumentation = InstrumentationRegistry.getInstrumentation();
Settings.Global.putInt(mContext.getContentResolver(), Global.AIRPLANE_MODE_ON, 0);
TestableResources res = mContext.getOrCreateTestableResources();
@@ -224,6 +219,11 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
mWifiStatusTrackerFactory = new WifiStatusTrackerFactory(
mContext, mMockWm, mMockNsm, mMockCm, mMainHandler);
+ mMobileFactory = new MobileSignalControllerFactory(
+ mContext,
+ mCallbackHandler,
+ mCarrierConfigTracker
+ );
mNetworkController = new NetworkControllerImpl(mContext,
mMockCm,
@@ -243,8 +243,8 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
mDemoModeController,
mCarrierConfigTracker,
mWifiStatusTrackerFactory,
+ mMobileFactory,
mMainHandler,
- mFeatureFlags,
mock(DumpManager.class),
mock(LogBuffer.class)
);
@@ -438,10 +438,6 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
updateSignalStrength();
}
- public void setImsType(int imsType) {
- mMobileSignalController.setImsType(imsType);
- }
-
public void setIsGsm(boolean gsm) {
when(mSignalStrength.isGsm()).thenReturn(gsm);
updateSignalStrength();
@@ -637,5 +633,4 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
protected void assertDataNetworkNameEquals(String expected) {
assertEquals("Data network name", expected, mNetworkController.getMobileDataNetworkName());
}
-
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
index e3dd6f4e6e40..ed8a3e16cdd1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
@@ -145,8 +145,8 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest {
mDemoModeController,
mock(CarrierConfigTracker.class),
mWifiStatusTrackerFactory,
+ mMobileFactory,
new Handler(TestableLooper.get(this).getLooper()),
- mFeatureFlags,
mock(DumpManager.class),
mock(LogBuffer.class));
setupNetworkController();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
index 698899a8fc36..a76676e01c15 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
@@ -85,8 +85,8 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
mDemoModeController,
mCarrierConfigTracker,
mWifiStatusTrackerFactory,
+ mMobileFactory,
mMainHandler,
- mFeatureFlags,
mock(DumpManager.class),
mock(LogBuffer.class)
);
@@ -121,8 +121,8 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
mDemoModeController,
mCarrierConfigTracker,
mWifiStatusTrackerFactory,
+ mMobileFactory,
mMainHandler,
- mFeatureFlags,
mock(DumpManager.class),
mock(LogBuffer.class));
TestableLooper.get(this).processAllMessages();
@@ -155,8 +155,8 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
mDemoModeController,
mock(CarrierConfigTracker.class),
mWifiStatusTrackerFactory,
+ mMobileFactory,
mMainHandler,
- mFeatureFlags,
mock(DumpManager.class),
mock(LogBuffer.class));
setupNetworkController();
@@ -192,8 +192,8 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
mDemoModeController,
mock(CarrierConfigTracker.class),
mWifiStatusTrackerFactory,
+ mMobileFactory,
mMainHandler,
- mFeatureFlags,
mock(DumpManager.class),
mock(LogBuffer.class));
mNetworkController.registerListeners();
@@ -277,8 +277,8 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
mDemoModeController,
mock(CarrierConfigTracker.class),
mWifiStatusTrackerFactory,
+ mMobileFactory,
mMainHandler,
- mFeatureFlags,
mock(DumpManager.class),
mock(LogBuffer.class));
setupNetworkController();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
index 3f7149159247..68170ea4b518 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
@@ -30,7 +30,6 @@ import android.net.NetworkInfo;
import android.net.vcn.VcnTransportInfo;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import android.telephony.CellSignalStrength;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
@@ -285,44 +284,6 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest {
verifyLastMobileDataIndicatorsForVcn(false, 1, 0, false);
}
- @Test
- public void testCallStrengh() {
- if (true) return;
- String testSsid = "Test SSID";
- setWifiEnabled(true);
- setWifiState(true, testSsid);
- // Set the ImsType to be IMS_TYPE_WLAN
- setImsType(2);
- setWifiLevel(1);
- for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
- setWifiLevel(testLevel);
- verifyLastCallStrength(TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[testLevel]);
- }
- // Set the ImsType to be IMS_TYPE_WWAN
- setImsType(1);
- setupDefaultSignal();
- for (int testStrength = 0;
- testStrength < CellSignalStrength.getNumSignalStrengthLevels(); testStrength++) {
- setLevel(testStrength);
- verifyLastCallStrength(TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[testStrength]);
- }
- }
-
- @Test
- public void testNonPrimaryWiFi() {
- if (true) return;
- String testSsid = "Test SSID";
- setWifiEnabled(true);
- setWifiState(true, testSsid);
- // Set the ImsType to be IMS_TYPE_WLAN
- setImsType(2);
- setWifiLevel(1);
- verifyLastCallStrength(TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[1]);
- when(mWifiInfo.isPrimary()).thenReturn(false);
- setWifiLevel(3);
- verifyLastCallStrength(TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[1]);
- }
-
protected void setWifiActivity(int activity) {
// TODO: Not this, because this variable probably isn't sticking around.
mNetworkController.mWifiSignalController.setActivity(activity);
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 2a3509ce3a2c..7b7f45a44615 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
@@ -965,6 +965,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Test
public void deviceStateChange_unfolded_shadeOpen_setsLeaveOpenOnKeyguardHide() {
+ when(mKeyguardStateController.isShowing()).thenReturn(false);
setFoldedStates(FOLD_STATE_FOLDED);
setGoToSleepStates(FOLD_STATE_FOLDED);
when(mNotificationPanelViewController.isFullyExpanded()).thenReturn(true);
@@ -975,6 +976,19 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
}
@Test
+ public void deviceStateChange_unfolded_shadeOpen_onKeyguard_doesNotSetLeaveOpenOnKeyguardHide() {
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ setFoldedStates(FOLD_STATE_FOLDED);
+ setGoToSleepStates(FOLD_STATE_FOLDED);
+ when(mNotificationPanelViewController.isFullyExpanded()).thenReturn(true);
+
+ setDeviceState(FOLD_STATE_UNFOLDED);
+
+ verify(mStatusBarStateController, never()).setLeaveOpenOnKeyguardHide(true);
+ }
+
+
+ @Test
public void deviceStateChange_unfolded_shadeClose_doesNotSetLeaveOpenOnKeyguardHide() {
setFoldedStates(FOLD_STATE_FOLDED);
setGoToSleepStates(FOLD_STATE_FOLDED);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
index 2ff6dd43e84e..086e5df50f3f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
@@ -153,6 +153,106 @@ class NotificationIconContainerTest : SysuiTestCase() {
assertTrue(iconContainer.hasOverflow())
}
+ @Test
+ fun shouldForceOverflow_appearingAboveSpeedBump_true() {
+ val forceOverflow = iconContainer.shouldForceOverflow(
+ /* i= */ 1,
+ /* speedBumpIndex= */ 0,
+ /* iconAppearAmount= */ 1f,
+ /* maxVisibleIcons= */ 5
+ )
+ assertTrue(forceOverflow);
+ }
+
+ @Test
+ fun shouldForceOverflow_moreThanMaxVisible_true() {
+ val forceOverflow = iconContainer.shouldForceOverflow(
+ /* i= */ 10,
+ /* speedBumpIndex= */ 11,
+ /* iconAppearAmount= */ 0f,
+ /* maxVisibleIcons= */ 5
+ )
+ assertTrue(forceOverflow);
+ }
+
+ @Test
+ fun shouldForceOverflow_belowSpeedBumpAndLessThanMaxVisible_false() {
+ val forceOverflow = iconContainer.shouldForceOverflow(
+ /* i= */ 0,
+ /* speedBumpIndex= */ 11,
+ /* iconAppearAmount= */ 0f,
+ /* maxVisibleIcons= */ 5
+ )
+ assertFalse(forceOverflow);
+ }
+
+ @Test
+ fun isOverflowing_lastChildXLessThanLayoutEnd_false() {
+ val isOverflowing = iconContainer.isOverflowing(
+ /* isLastChild= */ true,
+ /* translationX= */ 0f,
+ /* layoutEnd= */ 10f,
+ /* iconSize= */ 2f,
+ )
+ assertFalse(isOverflowing)
+ }
+
+
+ @Test
+ fun isOverflowing_lastChildXEqualToLayoutEnd_true() {
+ val isOverflowing = iconContainer.isOverflowing(
+ /* isLastChild= */ true,
+ /* translationX= */ 10f,
+ /* layoutEnd= */ 10f,
+ /* iconSize= */ 2f,
+ )
+ assertTrue(isOverflowing)
+ }
+
+ @Test
+ fun isOverflowing_lastChildXGreaterThanLayoutEnd_true() {
+ val isOverflowing = iconContainer.isOverflowing(
+ /* isLastChild= */ true,
+ /* translationX= */ 20f,
+ /* layoutEnd= */ 10f,
+ /* iconSize= */ 2f,
+ )
+ assertTrue(isOverflowing)
+ }
+
+ @Test
+ fun isOverflowing_notLastChildXLessThanDotX_false() {
+ val isOverflowing = iconContainer.isOverflowing(
+ /* isLastChild= */ false,
+ /* translationX= */ 0f,
+ /* layoutEnd= */ 10f,
+ /* iconSize= */ 2f,
+ )
+ assertFalse(isOverflowing)
+ }
+
+ @Test
+ fun isOverflowing_notLastChildXGreaterThanDotX_true() {
+ val isOverflowing = iconContainer.isOverflowing(
+ /* isLastChild= */ false,
+ /* translationX= */ 20f,
+ /* layoutEnd= */ 10f,
+ /* iconSize= */ 2f,
+ )
+ assertTrue(isOverflowing)
+ }
+
+ @Test
+ fun isOverflowing_notLastChildXEqualToDotX_true() {
+ val isOverflowing = iconContainer.isOverflowing(
+ /* isLastChild= */ false,
+ /* translationX= */ 8f,
+ /* layoutEnd= */ 10f,
+ /* iconSize= */ 2f,
+ )
+ assertTrue(isOverflowing)
+ }
+
private fun mockStatusBarIcon() : StatusBarIconView {
val iconView = mock(StatusBarIconView::class.java)
whenever(iconView.width).thenReturn(10)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 1046dbc914c3..2dcdcfce56eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -202,12 +202,12 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
}
@Test
- public void onPanelExpansionChanged_propagatesToBouncer_evenAfterHidden() {
+ public void onPanelExpansionChanged_hideBouncer_afterKeyguardHidden() {
mStatusBarKeyguardViewManager.hide(0, 0);
when(mBouncer.inTransit()).thenReturn(true);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
- verify(mBouncer).setExpansion(eq(EXPANSION_EVENT.getFraction()));
+ verify(mBouncer).setExpansion(eq(KeyguardBouncer.EXPANSION_HIDDEN));
}
@Test
@@ -249,6 +249,23 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
}
@Test
+ public void onPanelExpansionChanged_neverTranslatesBouncerWhenDismissBouncer() {
+ // Since KeyguardBouncer.EXPANSION_VISIBLE = 0 panel expansion, if the unlock is dismissing
+ // the bouncer, there may be an onPanelExpansionChanged(0) call to collapse the panel
+ // which would mistakenly cause the bouncer to show briefly before its visibility
+ // is set to hide. Therefore, we don't want to propagate panelExpansionChanged to the
+ // bouncer if the bouncer is dismissing as a result of a biometric unlock.
+ when(mBiometricUnlockController.getMode())
+ .thenReturn(BiometricUnlockController.MODE_DISMISS_BOUNCER);
+ mStatusBarKeyguardViewManager.onPanelExpansionChanged(
+ expansionEvent(
+ /* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
+ /* expanded= */ true,
+ /* tracking= */ false));
+ verify(mBouncer, never()).setExpansion(anyFloat());
+ }
+
+ @Test
public void onPanelExpansionChanged_neverTranslatesBouncerWhenLaunchingApp() {
when(mCentralSurfaces.isInLaunchTransition()).thenReturn(true);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 1a8ffc1f78cb..4c8599d99ddd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -16,6 +16,11 @@ package com.android.systemui.statusbar.phone.fragment;
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_IN;
+import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_OUT;
+import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.IDLE;
+import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.RUNNING_CHIP_ANIM;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -25,6 +30,7 @@ import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.animation.Animator;
import android.app.Fragment;
import android.app.StatusBarManager;
import android.content.Context;
@@ -130,7 +136,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- public void testDisableSystemInfo() {
+ public void testDisableSystemInfo_systemAnimationIdle_doesHide() {
+ when(mAnimationScheduler.getAnimationState()).thenReturn(IDLE);
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false);
@@ -143,6 +150,98 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
+ public void testSystemStatusAnimation_startedDisabled_finishedWithAnimator_showsSystemInfo() {
+ // GIVEN the status bar hides the system info via disable flags, while there is no event
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+ when(mAnimationScheduler.getAnimationState()).thenReturn(IDLE);
+ fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false);
+ assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
+
+ // WHEN the disable flags are cleared during a system event animation
+ when(mAnimationScheduler.getAnimationState()).thenReturn(RUNNING_CHIP_ANIM);
+ fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+ // THEN the view is made visible again, but still low alpha
+ assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
+ assertEquals(0, getEndSideContentView().getAlpha(), 0.01);
+
+ // WHEN the system event animation finishes
+ when(mAnimationScheduler.getAnimationState()).thenReturn(ANIMATING_OUT);
+ Animator anim = fragment.onSystemEventAnimationFinish(false);
+ anim.start();
+ processAllMessages();
+ anim.end();
+
+ // THEN the system info is full alpha
+ assertEquals(1, getEndSideContentView().getAlpha(), 0.01);
+ }
+
+ @Test
+ public void testSystemStatusAnimation_systemInfoDisabled_staysInvisible() {
+ // GIVEN the status bar hides the system info via disable flags, while there is no event
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+ when(mAnimationScheduler.getAnimationState()).thenReturn(IDLE);
+ fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false);
+ assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
+
+ // WHEN the system event animation finishes
+ when(mAnimationScheduler.getAnimationState()).thenReturn(ANIMATING_OUT);
+ Animator anim = fragment.onSystemEventAnimationFinish(false);
+ anim.start();
+ processAllMessages();
+ anim.end();
+
+ // THEN the system info is at full alpha, but still INVISIBLE (since the disable flag is
+ // still set)
+ assertEquals(1, getEndSideContentView().getAlpha(), 0.01);
+ assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
+ }
+
+
+ @Test
+ public void testSystemStatusAnimation_notDisabled_animatesAlphaZero() {
+ // GIVEN the status bar is not disabled
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+ when(mAnimationScheduler.getAnimationState()).thenReturn(ANIMATING_IN);
+ // WHEN the system event animation begins
+ Animator anim = fragment.onSystemEventAnimationBegin();
+ anim.start();
+ processAllMessages();
+ anim.end();
+
+ // THEN the system info is visible but alpha 0
+ assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
+ assertEquals(0, getEndSideContentView().getAlpha(), 0.01);
+ }
+
+ @Test
+ public void testSystemStatusAnimation_notDisabled_animatesBackToAlphaOne() {
+ // GIVEN the status bar is not disabled
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+ when(mAnimationScheduler.getAnimationState()).thenReturn(ANIMATING_IN);
+ // WHEN the system event animation begins
+ Animator anim = fragment.onSystemEventAnimationBegin();
+ anim.start();
+ processAllMessages();
+ anim.end();
+
+ // THEN the system info is visible but alpha 0
+ assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
+ assertEquals(0, getEndSideContentView().getAlpha(), 0.01);
+
+ // WHEN the system event animation finishes
+ when(mAnimationScheduler.getAnimationState()).thenReturn(ANIMATING_OUT);
+ anim = fragment.onSystemEventAnimationFinish(false);
+ anim.start();
+ processAllMessages();
+ anim.end();
+
+ // THEN the syste info is full alpha and VISIBLE
+ assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
+ assertEquals(1, getEndSideContentView().getAlpha(), 0.01);
+ }
+
+ @Test
public void testDisableNotifications() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessorTest.kt
new file mode 100644
index 000000000000..515a7c936f4d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessorTest.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline
+
+import android.net.NetworkCapabilities
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.pipeline.repository.NetworkCapabilityInfo
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.InternalCoroutinesApi
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.yield
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.`when` as whenever
+
+@OptIn(InternalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class ConnectivityInfoProcessorTest : SysuiTestCase() {
+
+ private val statusBarPipelineFlags = mock<StatusBarPipelineFlags>()
+
+ @Before
+ fun setUp() {
+ whenever(statusBarPipelineFlags.isNewPipelineEnabled()).thenReturn(true)
+ }
+
+ @Test
+ fun collectorInfoUpdated_processedInfoAlsoUpdated() = runBlocking {
+ // GIVEN a processor hooked up to a collector
+ val scope = CoroutineScope(Dispatchers.Unconfined)
+ val collector = FakeConnectivityInfoCollector()
+ val processor = ConnectivityInfoProcessor(
+ collector,
+ context,
+ scope,
+ statusBarPipelineFlags,
+ )
+
+ var mostRecentValue: ProcessedConnectivityInfo? = null
+ val job = launch(start = CoroutineStart.UNDISPATCHED) {
+ processor.processedInfoFlow.collect {
+ mostRecentValue = it
+ }
+ }
+
+ // WHEN the collector emits a value
+ val networkCapabilityInfo = mapOf(
+ 10 to NetworkCapabilityInfo(mock(), NetworkCapabilities.Builder().build())
+ )
+ collector.emitValue(RawConnectivityInfo(networkCapabilityInfo))
+ // Because our job uses [CoroutineStart.UNDISPATCHED], it executes in the same thread as
+ // this test. So, our test needs to yield to let the job run.
+ // Note: Once we upgrade our Kotlin coroutines testing library, we won't need this.
+ yield()
+
+ // THEN the processor receives it
+ assertThat(mostRecentValue?.networkCapabilityInfo).isEqualTo(networkCapabilityInfo)
+
+ job.cancel()
+ scope.cancel()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/FakeConnectivityInfoCollector.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/FakeConnectivityInfoCollector.kt
new file mode 100644
index 000000000000..710e5f6eacd3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/FakeConnectivityInfoCollector.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline
+
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/**
+ * A test-friendly implementation of [ConnectivityInfoCollector] that just emits whatever value it
+ * receives in [emitValue].
+ */
+class FakeConnectivityInfoCollector : ConnectivityInfoCollector {
+ private val _rawConnectivityInfoFlow = MutableStateFlow(RawConnectivityInfo())
+ override val rawConnectivityInfoFlow = _rawConnectivityInfoFlow.asStateFlow()
+
+ suspend fun emitValue(value: RawConnectivityInfo) {
+ _rawConnectivityInfoFlow.emit(value)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
index 87fca1f23f1a..7e0704007700 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
@@ -30,13 +30,13 @@ import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener
import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.Executor
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.Executor
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@SmallTest
@@ -331,6 +331,47 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING)
}
+ @Test
+ fun screenOff_whileFolded_hingeAngleProviderRemainsOff() {
+ setFoldState(folded = true)
+ assertThat(testHingeAngleProvider.isStarted).isFalse()
+
+ screenOnStatusProvider.notifyScreenTurningOff()
+
+ assertThat(testHingeAngleProvider.isStarted).isFalse()
+ }
+
+ @Test
+ fun screenOff_whileUnfolded_hingeAngleProviderStops() {
+ setFoldState(folded = false)
+ assertThat(testHingeAngleProvider.isStarted).isTrue()
+
+ screenOnStatusProvider.notifyScreenTurningOff()
+
+ assertThat(testHingeAngleProvider.isStarted).isFalse()
+ }
+
+ @Test
+ fun screenOn_whileUnfoldedAndScreenOff_hingeAngleProviderStarted() {
+ setFoldState(folded = false)
+ screenOnStatusProvider.notifyScreenTurningOff()
+ assertThat(testHingeAngleProvider.isStarted).isFalse()
+
+ screenOnStatusProvider.notifyScreenTurningOn()
+
+ assertThat(testHingeAngleProvider.isStarted).isTrue()
+ }
+
+ @Test
+ fun screenOn_whileFolded_hingeAngleRemainsOff() {
+ setFoldState(folded = true)
+ assertThat(testHingeAngleProvider.isStarted).isFalse()
+
+ screenOnStatusProvider.notifyScreenTurningOn()
+
+ assertThat(testHingeAngleProvider.isStarted).isFalse()
+ }
+
private fun setupForegroundActivityType(isHomeActivity: Boolean?) {
whenever(activityTypeProvider.isHomeActivity).thenReturn(isHomeActivity)
}
@@ -391,6 +432,14 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
fun notifyScreenTurnedOn() {
callbacks.forEach { it.onScreenTurnedOn() }
}
+
+ fun notifyScreenTurningOn() {
+ callbacks.forEach { it.onScreenTurningOn() }
+ }
+
+ fun notifyScreenTurningOff() {
+ callbacks.forEach { it.onScreenTurningOff() }
+ }
}
private class TestHingeAngleProvider : HingeAngleProvider {
@@ -398,11 +447,11 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
var isStarted: Boolean = false
override fun start() {
- isStarted = true;
+ isStarted = true
}
override fun stop() {
- isStarted = false;
+ isStarted = false
}
override fun addCallback(listener: Consumer<Float>) {
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/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index fee17c785ed2..18acf3f6ce53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -33,6 +33,7 @@ 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.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -621,7 +622,7 @@ public class BubblesTest extends SysuiTestCase {
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
// Send update
- mEntryListener.onEntryUpdated(mRow);
+ mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
// Nothing should have changed
// Notif is suppressed after expansion
@@ -789,7 +790,7 @@ public class BubblesTest extends SysuiTestCase {
@Test
public void testAddNotif_notBubble() {
mEntryListener.onEntryAdded(mNonBubbleNotifRow.getEntry());
- mEntryListener.onEntryUpdated(mNonBubbleNotifRow.getEntry());
+ mEntryListener.onEntryUpdated(mNonBubbleNotifRow.getEntry(), /* fromSystem= */ true);
assertThat(mBubbleController.hasBubbles()).isFalse();
}
@@ -827,7 +828,7 @@ public class BubblesTest extends SysuiTestCase {
NotificationListenerService.Ranking ranking = new RankingBuilder(
mRow.getRanking()).setCanBubble(false).build();
mRow.setRanking(ranking);
- mEntryListener.onEntryUpdated(mRow);
+ mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
assertFalse(mBubbleController.hasBubbles());
verify(mDeleteIntent, never()).send();
@@ -1432,6 +1433,100 @@ public class BubblesTest extends SysuiTestCase {
assertThat(mBubbleData.hasBubbleInStackWithKey(mBubbleEntry.getKey())).isFalse();
}
+ /**
+ * Verifies that if a bubble is in the overflow and a non-interruptive notification update
+ * comes in for it, it stays in the overflow but the entry is updated.
+ */
+ @Test
+ public void testNonInterruptiveUpdate_doesntBubbleFromOverflow() {
+ mEntryListener.onEntryAdded(mRow);
+ mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
+
+ // Dismiss the bubble so it's in the overflow
+ mBubbleController.removeBubble(
+ mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
+ assertThat(mBubbleData.hasOverflowBubbleWithKey(mRow.getKey())).isTrue();
+
+ // Update the entry to not show in shade
+ setMetadataFlags(mRow,
+ Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, /* enableFlag= */ true);
+ mBubbleController.updateBubble(mBubbleEntry,
+ /* suppressFlyout= */ false, /* showInShade= */ true);
+
+ // Check that the update was applied - shouldn't be show in shade
+ assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
+ // Check that it wasn't inflated (1 because it would've been inflated via onEntryAdded)
+ verify(mBubbleController, times(1)).inflateAndAdd(
+ any(Bubble.class), anyBoolean(), anyBoolean());
+ }
+
+ /**
+ * Verifies that if a bubble is active, and a non-interruptive notification update comes in for
+ * it, it doesn't trigger a new inflate and add for that bubble.
+ */
+ @Test
+ public void testNonInterruptiveUpdate_doesntTriggerInflate() {
+ mEntryListener.onEntryAdded(mRow);
+ mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
+
+ // Update the entry to not show in shade
+ setMetadataFlags(mRow,
+ Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, /* enableFlag= */ true);
+ mBubbleController.updateBubble(mBubbleEntry,
+ /* suppressFlyout= */ false, /* showInShade= */ true);
+
+ // Check that the update was applied - shouldn't be show in shade
+ assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
+ // Check that it wasn't inflated (1 because it would've been inflated via onEntryAdded)
+ verify(mBubbleController, times(1)).inflateAndAdd(
+ any(Bubble.class), anyBoolean(), anyBoolean());
+ }
+
+ /**
+ * Verifies that if a bubble is in the overflow and a non-interruptive notification update
+ * comes in for it with FLAG_BUBBLE that the flag is removed.
+ */
+ @Test
+ public void testNonInterruptiveUpdate_doesntOverrideOverflowFlagBubble() {
+ mEntryListener.onEntryAdded(mRow);
+ mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
+
+ // Dismiss the bubble so it's in the overflow
+ mBubbleController.removeBubble(
+ mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
+ assertThat(mBubbleData.hasOverflowBubbleWithKey(mRow.getKey())).isTrue();
+ // Once it's in the overflow it's not actively a bubble (doesn't have FLAG_BUBBLE)
+ Bubble b = mBubbleData.getOverflowBubbleWithKey(mBubbleEntry.getKey());
+ assertThat(b.isBubble()).isFalse();
+
+ // Send a non-notifying update that has FLAG_BUBBLE
+ mRow.getSbn().getNotification().flags = FLAG_BUBBLE;
+ assertThat(mRow.getSbn().getNotification().isBubbleNotification()).isTrue();
+ mBubbleController.updateBubble(mBubbleEntry,
+ /* suppressFlyout= */ false, /* showInShade= */ true);
+
+ // Verify that it still doesn't have FLAG_BUBBLE because it's in the overflow.
+ b = mBubbleData.getOverflowBubbleWithKey(mBubbleEntry.getKey());
+ assertThat(b.isBubble()).isFalse();
+ }
+
+ @Test
+ public void testNonSystemUpdatesIgnored() {
+ mEntryListener.onEntryAdded(mRow);
+ assertThat(mBubbleController.hasBubbles()).isTrue();
+
+ mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ false);
+ mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ false);
+ mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ false);
+
+ // Check that it wasn't inflated (1 because it would've been inflated via onEntryAdded)
+ verify(mBubbleController, times(1)).inflateAndAdd(
+ any(Bubble.class), anyBoolean(), anyBoolean());
+ }
+
/** Creates a bubble using the userId and package. */
private Bubble createBubble(int userId, String pkg) {
final UserHandle userHandle = new UserHandle(userId);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
index c52ea60f0bfc..c83189dbc616 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
@@ -15,6 +15,8 @@
*/
package com.android.systemui;
+import static com.android.systemui.animation.FakeDialogLaunchAnimatorKt.fakeDialogLaunchAnimator;
+
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -34,6 +36,7 @@ import androidx.test.uiautomator.UiDevice;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.broadcast.FakeBroadcastDispatcher;
import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger;
@@ -119,6 +122,7 @@ public abstract class SysuiTestCase {
// is missing (constructing the actual one would throw).
// TODO(b/219008720): Remove this.
mDependency.injectMockDependency(SystemUIDialogManager.class);
+ mDependency.injectTestDependency(DialogLaunchAnimator.class, fakeDialogLaunchAnimator());
}
@After
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogLaunchAnimator.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogLaunchAnimator.kt
new file mode 100644
index 000000000000..990db77463f6
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogLaunchAnimator.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.animation
+
+import com.android.internal.jank.InteractionJankMonitor
+import org.mockito.Mockito.mock
+
+/** A [DialogLaunchAnimator] to be used in tests. */
+@JvmOverloads
+fun fakeDialogLaunchAnimator(
+ isUnlocked: Boolean = true,
+ isShowingAlternateAuthOnUnlock: Boolean = false,
+ interactionJankMonitor: InteractionJankMonitor = mock(InteractionJankMonitor::class.java),
+): DialogLaunchAnimator {
+ return DialogLaunchAnimator(
+ FakeCallback(
+ isUnlocked = isUnlocked,
+ isShowingAlternateAuthOnUnlock = isShowingAlternateAuthOnUnlock,
+ ),
+ interactionJankMonitor,
+ fakeLaunchAnimator(),
+ isForTesting = true,
+ )
+}
+
+private class FakeCallback(
+ private val isDreaming: Boolean = false,
+ private val isUnlocked: Boolean = true,
+ private val isShowingAlternateAuthOnUnlock: Boolean = false,
+) : DialogLaunchAnimator.Callback {
+ override fun isDreaming(): Boolean = isDreaming
+ override fun isUnlocked(): Boolean = isUnlocked
+ override fun isShowingAlternateAuthOnUnlock() = isShowingAlternateAuthOnUnlock
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeLaunchAnimator.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeLaunchAnimator.kt
new file mode 100644
index 000000000000..5b431e72e2ac
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeLaunchAnimator.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.animation
+
+/** A [LaunchAnimator] to be used in tests. */
+fun fakeLaunchAnimator(): LaunchAnimator {
+ return LaunchAnimator(TEST_TIMINGS, TEST_INTERPOLATORS)
+}
+
+/**
+ * A [LaunchAnimator.Timings] to be used in tests.
+ *
+ * Note that all timings except the total duration are non-zero to avoid divide-by-zero exceptions
+ * when computing the progress of a sub-animation (the contents fade in/out).
+ */
+private val TEST_TIMINGS =
+ LaunchAnimator.Timings(
+ totalDuration = 0L,
+ contentBeforeFadeOutDelay = 1L,
+ contentBeforeFadeOutDuration = 1L,
+ contentAfterFadeInDelay = 1L,
+ contentAfterFadeInDuration = 1L
+ )
+
+/** A [LaunchAnimator.Interpolators] to be used in tests. */
+private val TEST_INTERPOLATORS =
+ LaunchAnimator.Interpolators(
+ positionInterpolator = Interpolators.STANDARD,
+ positionXInterpolator = Interpolators.STANDARD,
+ contentBeforeFadeOutInterpolator = Interpolators.STANDARD,
+ contentAfterFadeInInterpolator = Interpolators.STANDARD
+ )
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/SystemUI/tests/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
index 309acdf13a5d..309acdf13a5d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
index e8038fd7dfa6..19cfc805d17b 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
@@ -65,6 +65,7 @@ constructor(
private val halfOpenedTimeoutMillis: Int = config.halfFoldedTimeoutMillis
private var isFolded = false
+ private var isScreenOn = false
private var isUnfoldHandled = true
override fun start() {
@@ -198,6 +199,25 @@ constructor(
isUnfoldHandled = true
}
}
+
+ override fun onScreenTurningOn() {
+ isScreenOn = true
+ updateHingeAngleProviderState()
+ }
+
+ override fun onScreenTurningOff() {
+ isScreenOn = false
+ updateHingeAngleProviderState()
+ }
+ }
+
+ /** While the screen is off or the device is folded, hinge angle updates are not needed. */
+ private fun updateHingeAngleProviderState() {
+ if (isScreenOn && !isFolded) {
+ hingeAngleProvider.start()
+ } else {
+ hingeAngleProvider.stop()
+ }
}
private inner class HingeAngleListener : Consumer<Float> {
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt
index 3fc5d610dc2d..577137ca12f3 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt
@@ -30,8 +30,10 @@ internal class HingeSensorAngleProvider(
private val sensorListener = HingeAngleSensorListener()
private val listeners: MutableList<Consumer<Float>> = arrayListOf()
+ var started = false
override fun start() = executor.execute {
+ if (started) return@execute
Trace.beginSection("HingeSensorAngleProvider#start")
val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_HINGE_ANGLE)
sensorManager.registerListener(
@@ -40,10 +42,13 @@ internal class HingeSensorAngleProvider(
SensorManager.SENSOR_DELAY_FASTEST
)
Trace.endSection()
+ started = true
}
override fun stop() = executor.execute {
+ if (!started) return@execute
sensorManager.unregisterListener(sensorListener)
+ started = false
}
override fun removeCallback(listener: Consumer<Float>) {
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt
index d95e050474de..f09b53dc8436 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt
@@ -25,5 +25,15 @@ interface ScreenStatusProvider : CallbackController<ScreenListener> {
* Called when the screen is on and ready (windows are drawn and screen blocker is removed)
*/
fun onScreenTurnedOn()
+
+ /**
+ * Called when the screen is starting to be turned off.
+ */
+ fun onScreenTurningOff()
+
+ /**
+ * Called when the screen is starting to be turned on.
+ */
+ fun onScreenTurningOn()
}
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 9b11a68e3860..a185b585aeda 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -1115,10 +1115,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
registerForBroadcastsLocked(provider, getWidgetIds(provider.widgets));
saveGroupStateAsync(userId);
-
- if (DEBUG) {
- Slog.i(TAG, "Bound widget " + appWidgetId + " to provider " + provider.id);
- }
+ Slog.i(TAG, "Bound widget " + appWidgetId + " to provider " + provider.id);
}
return true;
@@ -4251,6 +4248,10 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
IAppWidgetHost callbacks;
boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
+ private static final boolean DEBUG = true;
+
+ private static final String TAG = "AppWidgetServiceHost";
+
int tag = TAG_UNDEFINED; // for use while saving state (the index)
// Sequence no for the last update successfully sent. This is updated whenever a
// widget update is successfully sent to the host callbacks. As all new/undelivered updates
@@ -4321,6 +4322,11 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
final SparseArray<String> uids = new SparseArray<>();
for (int i = widgets.size() - 1; i >= 0; i--) {
final Widget widget = widgets.get(i);
+ if (widget.provider == null) {
+ if (DEBUG) {
+ Slog.e(TAG, "Widget with no provider " + widget.toString());
+ }
+ }
final ProviderId providerId = widget.provider.id;
uids.put(providerId.uid, providerId.componentName.getPackageName());
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 35e9060b58d4..b25518875cf0 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -56,6 +56,7 @@ import com.android.server.wm.ActivityTaskManagerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
@@ -208,6 +209,10 @@ public class VirtualDeviceManagerService extends SystemService {
}
@VisibleForTesting
+ VirtualDeviceManagerInternal getLocalServiceInstance() {
+ return mLocalService;
+ }
+
class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub implements
VirtualDeviceImpl.PendingTrampolineCallback {
@@ -308,10 +313,11 @@ public class VirtualDeviceManagerService extends SystemService {
final long tokenTwo = Binder.clearCallingIdentity();
try {
virtualDeviceImpl.onVirtualDisplayCreatedLocked(gwpc, displayId);
- return displayId;
} finally {
Binder.restoreCallingIdentity(tokenTwo);
}
+ mLocalService.onVirtualDisplayCreated(displayId);
+ return displayId;
}
@Nullable
@@ -378,6 +384,10 @@ public class VirtualDeviceManagerService extends SystemService {
}
private final class LocalService extends VirtualDeviceManagerInternal {
+ @GuardedBy("mVirtualDeviceManagerLock")
+ private final ArrayList<VirtualDeviceManagerInternal.VirtualDisplayListener>
+ mVirtualDisplayListeners = new ArrayList<>();
+
@Override
public boolean isValidVirtualDevice(IVirtualDevice virtualDevice) {
synchronized (mVirtualDeviceManagerLock) {
@@ -386,10 +396,30 @@ public class VirtualDeviceManagerService extends SystemService {
}
@Override
+ public void onVirtualDisplayCreated(int displayId) {
+ final VirtualDisplayListener[] listeners;
+ synchronized (mVirtualDeviceManagerLock) {
+ listeners = mVirtualDisplayListeners.toArray(new VirtualDisplayListener[0]);
+ }
+ mHandler.post(() -> {
+ for (VirtualDisplayListener listener : listeners) {
+ listener.onVirtualDisplayCreated(displayId);
+ }
+ });
+ }
+
+ @Override
public void onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId) {
+ final VirtualDisplayListener[] listeners;
synchronized (mVirtualDeviceManagerLock) {
((VirtualDeviceImpl) virtualDevice).onVirtualDisplayRemovedLocked(displayId);
+ listeners = mVirtualDisplayListeners.toArray(new VirtualDisplayListener[0]);
}
+ mHandler.post(() -> {
+ for (VirtualDisplayListener listener : listeners) {
+ listener.onVirtualDisplayRemoved(displayId);
+ }
+ });
}
@Override
@@ -435,6 +465,22 @@ public class VirtualDeviceManagerService extends SystemService {
}
return false;
}
+
+ @Override
+ public void registerVirtualDisplayListener(
+ @NonNull VirtualDisplayListener listener) {
+ synchronized (mVirtualDeviceManagerLock) {
+ mVirtualDisplayListeners.add(listener);
+ }
+ }
+
+ @Override
+ public void unregisterVirtualDisplayListener(
+ @NonNull VirtualDisplayListener listener) {
+ synchronized (mVirtualDeviceManagerLock) {
+ mVirtualDisplayListeners.remove(listener);
+ }
+ }
}
private static final class PendingTrampolineMap {
diff --git a/services/core/java/com/android/server/VpnManagerService.java b/services/core/java/com/android/server/VpnManagerService.java
index c9a420eabbd8..32dc47019fd5 100644
--- a/services/core/java/com/android/server/VpnManagerService.java
+++ b/services/core/java/com/android/server/VpnManagerService.java
@@ -138,6 +138,12 @@ public class VpnManagerService extends IVpnManager.Stub {
INetd netd, int userId) {
return new Vpn(looper, context, nms, netd, userId, new VpnProfileStore());
}
+
+ /** Create a LockDownVpnTracker. */
+ public LockdownVpnTracker createLockDownVpnTracker(Context context, Handler handler,
+ Vpn vpn, VpnProfile profile) {
+ return new LockdownVpnTracker(context, handler, vpn, profile);
+ }
}
public VpnManagerService(Context context, Dependencies deps) {
@@ -502,8 +508,7 @@ public class VpnManagerService extends IVpnManager.Stub {
logw("VPN for user " + user + " not ready yet. Skipping lockdown");
return false;
}
- setLockdownTracker(
- new LockdownVpnTracker(mContext, mHandler, vpn, profile));
+ setLockdownTracker(mDeps.createLockDownVpnTracker(mContext, mHandler, vpn, profile));
}
return true;
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index ef784bb278c0..5393b2a93125 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -160,6 +160,7 @@ public class Watchdog implements Dumpable {
public static final String[] AIDL_INTERFACE_PREFIXES_OF_INTEREST = new String[] {
"android.hardware.biometrics.face.IFace/",
"android.hardware.biometrics.fingerprint.IFingerprint/",
+ "android.hardware.graphics.composer3.IComposer/",
"android.hardware.input.processor.IInputProcessor/",
"android.hardware.light.ILights/",
"android.hardware.power.IPower/",
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 002aa773d85a..e7ea903bf485 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -3595,11 +3595,12 @@ public final class ActiveServices {
/**
* Bump the given service record into executing state.
- * @param oomAdjReason The caller requests it to perform the oomAdjUpdate if it's not null.
+ * @param oomAdjReason The caller requests it to perform the oomAdjUpdate not {@link
+ * OomAdjuster#OOM_ADJ_REASON_NONE}.
* @return {@code true} if it performed oomAdjUpdate.
*/
- private boolean bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why,
- @Nullable String oomAdjReason) {
+ private boolean bumpServiceExecutingLocked(
+ ServiceRecord r, boolean fg, String why, @OomAdjuster.OomAdjReason int oomAdjReason) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, ">>> EXECUTING "
+ why + " of " + r + " in app " + r.app);
else if (DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING, ">>> EXECUTING "
@@ -3651,7 +3652,7 @@ public final class ActiveServices {
}
}
boolean oomAdjusted = false;
- if (oomAdjReason != null && r.app != null
+ if (oomAdjReason != OomAdjuster.OOM_ADJ_REASON_NONE && r.app != null
&& r.app.mState.getCurProcState() > ActivityManager.PROCESS_STATE_SERVICE) {
// Force an immediate oomAdjUpdate, so the client app could be in the correct process
// state before doing any service related transactions
@@ -4385,7 +4386,7 @@ public final class ActiveServices {
final ProcessServiceRecord psr = app.mServices;
final boolean newService = psr.startService(r);
- bumpServiceExecutingLocked(r, execInFg, "create", null /* oomAdjReason */);
+ bumpServiceExecutingLocked(r, execInFg, "create", OomAdjuster.OOM_ADJ_REASON_NONE);
mAm.updateLruProcessLocked(app, false, null);
updateServiceForegroundLocked(psr, /* oomAdj= */ false);
// Force an immediate oomAdjUpdate, so the client app could be in the correct process state
@@ -4511,7 +4512,7 @@ public final class ActiveServices {
mAm.grantImplicitAccess(r.userId, si.intent, si.callingId,
UserHandle.getAppId(r.appInfo.uid)
);
- bumpServiceExecutingLocked(r, execInFg, "start", null /* oomAdjReason */);
+ bumpServiceExecutingLocked(r, execInFg, "start", OomAdjuster.OOM_ADJ_REASON_NONE);
if (r.fgRequired && !r.fgWaiting) {
if (!r.isForeground) {
if (DEBUG_BACKGROUND_CHECK) {
@@ -4780,7 +4781,7 @@ public final class ActiveServices {
updateServiceForegroundLocked(r.app.mServices, false);
try {
oomAdjusted |= bumpServiceExecutingLocked(r, false, "destroy",
- oomAdjusted ? null : OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
+ oomAdjusted ? 0 : OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
mDestroyingServices.add(r);
r.destroying = true;
r.app.getThread().scheduleStopService(r);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5729a06830cf..7f1cf0339460 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8894,9 +8894,9 @@ public class ActivityManagerService extends IActivityManager.Stub
InputStreamReader input = null;
try {
java.lang.Process logcat = new ProcessBuilder(
- // Time out after 10s, but kill logcat with SEGV
+ // Time out after 10s of inactivity, but kill logcat with SEGV
// so we can investigate why it didn't finish.
- "/system/bin/timeout", "-s", "SEGV", "10s",
+ "/system/bin/timeout", "-i", "-s", "SEGV", "10s",
// Merge several logcat streams, and take the last N lines.
"/system/bin/logcat", "-v", "threadtime", "-b", "events", "-b", "system",
"-b", "main", "-b", "crash", "-t", String.valueOf(lines))
@@ -14692,6 +14692,18 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
+ if (!Build.IS_DEBUGGABLE && callingUid != ROOT_UID && callingUid != SHELL_UID
+ && callingUid != SYSTEM_UID) {
+ // If it's not debug build and not called from root/shell/system uid, reject it.
+ final String msg = "Permission Denial: instrumentation test "
+ + className + " from pid=" + callingPid + ", uid=" + callingUid
+ + ", pkgName=" + mInternal.getPackageNameByPid(callingPid)
+ + " not allowed because it's not started from SHELL";
+ Slog.wtfQuiet(TAG, msg);
+ reportStartInstrumentationFailureLocked(watcher, className, msg);
+ throw new SecurityException(msg);
+ }
+
boolean disableHiddenApiChecks = ai.usesNonSdkApi()
|| (flags & INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS) != 0;
boolean disableTestApiChecks = disableHiddenApiChecks
@@ -15598,7 +15610,7 @@ public class ActivityManagerService extends IActivityManager.Stub
* {@link #enqueueOomAdjTargetLocked}.
*/
@GuardedBy("this")
- void updateOomAdjPendingTargetsLocked(String oomAdjReason) {
+ void updateOomAdjPendingTargetsLocked(@OomAdjuster.OomAdjReason int oomAdjReason) {
mOomAdjuster.updateOomAdjPendingTargetsLocked(oomAdjReason);
}
@@ -15617,7 +15629,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@GuardedBy("this")
- final void updateOomAdjLocked(String oomAdjReason) {
+ final void updateOomAdjLocked(@OomAdjuster.OomAdjReason int oomAdjReason) {
mOomAdjuster.updateOomAdjLocked(oomAdjReason);
}
@@ -15629,7 +15641,8 @@ public class ActivityManagerService extends IActivityManager.Stub
* @return whether updateOomAdjLocked(app) was successful.
*/
@GuardedBy("this")
- final boolean updateOomAdjLocked(ProcessRecord app, String oomAdjReason) {
+ final boolean updateOomAdjLocked(
+ ProcessRecord app, @OomAdjuster.OomAdjReason int oomAdjReason) {
return mOomAdjuster.updateOomAdjLocked(app, oomAdjReason);
}
@@ -15862,14 +15875,16 @@ public class ActivityManagerService extends IActivityManager.Stub
mOomAdjuster.setUidTempAllowlistStateLSP(uid, onAllowlist);
}
- private void trimApplications(boolean forceFullOomAdj, String oomAdjReason) {
+ private void trimApplications(
+ boolean forceFullOomAdj, @OomAdjuster.OomAdjReason int oomAdjReason) {
synchronized (this) {
trimApplicationsLocked(forceFullOomAdj, oomAdjReason);
}
}
@GuardedBy("this")
- private void trimApplicationsLocked(boolean forceFullOomAdj, String oomAdjReason) {
+ private void trimApplicationsLocked(
+ boolean forceFullOomAdj, @OomAdjuster.OomAdjReason int oomAdjReason) {
// First remove any unused application processes whose package
// has been removed.
boolean didSomething = false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 6609d4a91f21..dbabe99c79d5 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -989,26 +989,40 @@ final class ActivityManagerShellCommand extends ShellCommand {
@NeverCompile
int runCompact(PrintWriter pw) {
- String processName = getNextArgRequired();
- String uid = getNextArgRequired();
- String op = getNextArgRequired();
ProcessRecord app;
- synchronized (mInternal.mProcLock) {
- app = mInternal.getProcessRecordLocked(processName, Integer.parseInt(uid));
- }
- pw.println("Process record found pid: " + app.mPid);
- if (op.equals("full")) {
- pw.println("Executing full compaction for " + app.mPid);
+ String op = getNextArgRequired();
+ boolean isFullCompact = op.equals("full");
+ boolean isSomeCompact = op.equals("some");
+ if (isFullCompact || isSomeCompact) {
+ String processName = getNextArgRequired();
+ String uid = getNextArgRequired();
synchronized (mInternal.mProcLock) {
- mInternal.mOomAdjuster.mCachedAppOptimizer.compactAppFull(app, true);
+ app = mInternal.getProcessRecordLocked(processName, Integer.parseInt(uid));
+ }
+ pw.println("Process record found pid: " + app.mPid);
+ if (isFullCompact) {
+ pw.println("Executing full compaction for " + app.mPid);
+ synchronized (mInternal.mProcLock) {
+ mInternal.mOomAdjuster.mCachedAppOptimizer.compactApp(app,
+ CachedAppOptimizer.CompactProfile.FULL,
+ CachedAppOptimizer.CompactSource.APP, true);
+ }
+ pw.println("Finished full compaction for " + app.mPid);
+ } else if (isSomeCompact) {
+ pw.println("Executing some compaction for " + app.mPid);
+ synchronized (mInternal.mProcLock) {
+ mInternal.mOomAdjuster.mCachedAppOptimizer.compactApp(app,
+ CachedAppOptimizer.CompactProfile.SOME,
+ CachedAppOptimizer.CompactSource.APP, true);
+ }
+ pw.println("Finished some compaction for " + app.mPid);
}
- pw.println("Finished full compaction for " + app.mPid);
- } else if (op.equals("some")) {
- pw.println("Executing some compaction for " + app.mPid);
+ } else if (op.equals("system")) {
+ pw.println("Executing system compaction");
synchronized (mInternal.mProcLock) {
- mInternal.mOomAdjuster.mCachedAppOptimizer.compactAppSome(app, true);
+ mInternal.mOomAdjuster.mCachedAppOptimizer.compactAllSystem();
}
- pw.println("Finished some compaction for " + app.mPid);
+ pw.println("Finished system compaction");
} else {
getErrPrintWriter().println("Error: unknown compact command '" + op + "'");
return -1;
@@ -3570,10 +3584,11 @@ final class ActivityManagerShellCommand extends ShellCommand {
pw.println(" --allow-background-activity-starts: The receiver may start activities");
pw.println(" even if in the background.");
pw.println(" --async: Send without waiting for the completion of the receiver.");
- pw.println(" compact <process_name> <Package UID> [some|full]");
+ pw.println(" compact [some|full|system] <process_name> <Package UID>");
pw.println(" Force process compaction.");
pw.println(" some: execute file compaction.");
pw.println(" full: execute anon + file compaction.");
+ pw.println(" system: system compaction.");
pw.println(" instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]");
pw.println(" [--user <USER_ID> | current]");
pw.println(" [--no-hidden-api-checks [--no-test-api-access]]");
diff --git a/services/core/java/com/android/server/am/AssistDataRequester.java b/services/core/java/com/android/server/am/AssistDataRequester.java
index 98ad81d7d9de..98129ed12afe 100644
--- a/services/core/java/com/android/server/am/AssistDataRequester.java
+++ b/services/core/java/com/android/server/am/AssistDataRequester.java
@@ -23,6 +23,8 @@ import static android.app.AppOpsManager.OP_NONE;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityTaskManager;
import android.app.AppOpsManager;
import android.app.IActivityTaskManager;
@@ -41,6 +43,7 @@ import com.android.internal.logging.MetricsLogger;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/**
* Helper class to asynchronously fetch the assist data and screenshot from the current running
@@ -141,50 +144,35 @@ public class AssistDataRequester extends IAssistDataReceiver.Stub {
}
/**
- * Request that autofill data be loaded asynchronously. The resulting data will be provided
- * through the {@link AssistDataRequesterCallbacks}.
- *
- * See {@link #requestData(List, boolean, boolean, boolean, boolean, boolean, boolean, int,
- * String, boolean)}.
- */
- public void requestAutofillData(List<IBinder> activityTokens, int callingUid,
- String callingPackage) {
- requestData(activityTokens, true /* requestAutofillData */,
- true /* fetchData */, false /* fetchScreenshot */,
- true /* fetchStructure */, true /* allowFetchData */,
- false /* allowFetchScreenshot */, false /* ignoreTopActivityCheck */, callingUid,
- callingPackage);
- }
-
- /**
* Request that assist data be loaded asynchronously. The resulting data will be provided
* through the {@link AssistDataRequesterCallbacks}.
*
- * See {@link #requestData(List, boolean, boolean, boolean, boolean, boolean, boolean, int,
- * String, boolean)}.
+ * See {@link #requestData(List, boolean, boolean, boolean, boolean, boolean, boolean, boolean,
+ * int, String, String)}.
*/
- public void requestAssistData(List<IBinder> activityTokens, final boolean fetchData,
+ public void requestAssistData(@NonNull List<IBinder> activityTokens, final boolean fetchData,
final boolean fetchScreenshot, boolean allowFetchData, boolean allowFetchScreenshot,
- int callingUid, String callingPackage) {
+ int callingUid, @NonNull String callingPackage,
+ @Nullable String callingAttributionTag) {
requestAssistData(activityTokens, fetchData, fetchScreenshot, true /* fetchStructure */,
allowFetchData, allowFetchScreenshot, false /* ignoreTopActivityCheck */,
- callingUid, callingPackage);
+ callingUid, callingPackage, callingAttributionTag);
}
/**
* Request that assist data be loaded asynchronously. The resulting data will be provided
* through the {@link AssistDataRequesterCallbacks}.
*
- * See {@link #requestData(List, boolean, boolean, boolean, boolean, boolean, boolean, int,
- * String, boolean)}.
+ * See {@link #requestData(List, boolean, boolean, boolean, boolean, boolean, boolean, boolean,
+ * int, String, String)}.
*/
- public void requestAssistData(List<IBinder> activityTokens, final boolean fetchData,
+ public void requestAssistData(@NonNull List<IBinder> activityTokens, final boolean fetchData,
final boolean fetchScreenshot, final boolean fetchStructure, boolean allowFetchData,
boolean allowFetchScreenshot, boolean ignoreTopActivityCheck, int callingUid,
- String callingPackage) {
+ @NonNull String callingPackage, @Nullable String callingAttributionTag) {
requestData(activityTokens, false /* requestAutofillData */, fetchData, fetchScreenshot,
fetchStructure, allowFetchData, allowFetchScreenshot, ignoreTopActivityCheck,
- callingUid, callingPackage);
+ callingUid, callingPackage, callingAttributionTag);
}
/**
@@ -208,14 +196,22 @@ public class AssistDataRequester extends IAssistDataReceiver.Stub {
* requester is allowed to fetch the assist screenshot
* @param ignoreTopActivityCheck overrides the check for whether the activity is in focus when
* making the request. Used when passing an activity from Recents.
+ * @param callingUid the uid of the real caller
+ * @param callingPackage the package name of the real caller
+ * @param callingAttributionTag The {@link Context#createAttributionContext attribution tag}
+ * of the calling context or {@code null} for default attribution
*/
- private void requestData(List<IBinder> activityTokens, final boolean requestAutofillData,
- final boolean fetchData, final boolean fetchScreenshot, final boolean fetchStructure,
- boolean allowFetchData, boolean allowFetchScreenshot, boolean ignoreTopActivityCheck,
- int callingUid, String callingPackage) {
+ private void requestData(@NonNull List<IBinder> activityTokens,
+ final boolean requestAutofillData, final boolean fetchData,
+ final boolean fetchScreenshot, final boolean fetchStructure, boolean allowFetchData,
+ boolean allowFetchScreenshot, boolean ignoreTopActivityCheck, int callingUid,
+ @NonNull String callingPackage, @Nullable String callingAttributionTag) {
// TODO(b/34090158): Known issue, if the assist data is not allowed on the current activity,
// then no assist data is requested for any of the other activities
+ Objects.requireNonNull(activityTokens);
+ Objects.requireNonNull(callingPackage);
+
// Early exit if there are no activity to fetch for
if (activityTokens.isEmpty()) {
// No activities, just dispatch request-complete
@@ -241,8 +237,9 @@ public class AssistDataRequester extends IAssistDataReceiver.Stub {
mAssistScreenshot.clear();
if (fetchData) {
- if (mAppOpsManager.checkOpNoThrow(mRequestStructureAppOps, callingUid, callingPackage)
- == MODE_ALLOWED && allowFetchData) {
+ if (mAppOpsManager.noteOpNoThrow(mRequestStructureAppOps, callingUid,
+ callingPackage, callingAttributionTag, /* message */ null) == MODE_ALLOWED
+ && allowFetchData) {
final int numActivities = activityTokens.size();
for (int i = 0; i < numActivities; i++) {
IBinder topActivity = activityTokens.get(i);
@@ -291,8 +288,9 @@ public class AssistDataRequester extends IAssistDataReceiver.Stub {
}
if (fetchScreenshot) {
- if (mAppOpsManager.checkOpNoThrow(mRequestScreenshotAppOps, callingUid, callingPackage)
- == MODE_ALLOWED && allowFetchScreenshot) {
+ if (mAppOpsManager.noteOpNoThrow(mRequestScreenshotAppOps, callingUid,
+ callingPackage, callingAttributionTag, /* message */ null) == MODE_ALLOWED
+ && allowFetchScreenshot) {
try {
MetricsLogger.count(mContext, "assist_with_screen", 1);
mPendingScreenshotCount++;
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index c88d82cf0cf2..45265ac0c8f3 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -25,7 +25,6 @@ import android.app.ActivityThread;
import android.app.ApplicationExitInfo;
import android.database.ContentObserver;
import android.net.Uri;
-import android.os.Debug;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManagerInternal;
@@ -40,20 +39,21 @@ import android.text.TextUtils;
import android.util.EventLog;
import android.util.Slog;
import android.util.SparseArray;
-
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.ProcLocksReader;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.ServiceThread;
-
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.EnumMap;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
+import java.util.LinkedList;
import java.util.Map;
import java.util.Random;
import java.util.Set;
@@ -99,7 +99,7 @@ public final class CachedAppOptimizer {
private static final int COMPACT_ACTION_NONE = 0;
private static final int COMPACT_ACTION_FILE = 1;
private static final int COMPACT_ACTION_ANON = 2;
- private static final int COMPACT_ACTION_FULL = 3;
+ private static final int COMPACT_ACTION_ALL = 3;
private static final String COMPACT_ACTION_STRING[] = {"", "file", "anon", "all"};
@@ -112,7 +112,7 @@ public final class CachedAppOptimizer {
// Defaults for phenotype flags.
@VisibleForTesting static final Boolean DEFAULT_USE_COMPACTION = false;
@VisibleForTesting static final Boolean DEFAULT_USE_FREEZER = true;
- @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_2 = COMPACT_ACTION_FULL;
+ @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_2 = COMPACT_ACTION_ALL;
@VisibleForTesting static final int DEFAULT_COMPACT_ACTION_1 = COMPACT_ACTION_FILE;
@VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_1 = 5_000;
@VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_2 = 10_000;
@@ -147,14 +147,33 @@ public final class CachedAppOptimizer {
@VisibleForTesting
interface ProcessDependencies {
long[] getRss(int pid);
- void performCompaction(String action, int pid) throws IOException;
+ void performCompaction(CompactAction action, int pid) throws IOException;
+ }
+
+ // This indicates the compaction we want to perform
+ public enum CompactProfile {
+ SOME, // File compaction
+ FULL // File+anon compaction
+ }
+
+ // Low level actions that can be performed for compaction
+ // currently determined by the compaction profile
+ public enum CompactAction {
+ NONE, // No compaction
+ FILE, // File+anon compaction
+ ANON,
+ ALL
+ }
+
+ // This indicates the process OOM memory state that initiated the compaction request
+ public enum CompactSource { APP, PERSISTENT, BFGS }
+
+ public enum CancelCompactReason {
+ SCREEN_ON, // screen was turned on which cancels all compactions.
+ OOM_IMPROVEMENT // process moved out of cached state and into a more perceptible state.
}
// Handler constants.
- static final int COMPACT_PROCESS_SOME = 1;
- static final int COMPACT_PROCESS_FULL = 2;
- static final int COMPACT_PROCESS_PERSISTENT = 3;
- static final int COMPACT_PROCESS_BFGS = 4;
static final int COMPACT_PROCESS_MSG = 1;
static final int COMPACT_SYSTEM_MSG = 2;
static final int SET_FROZEN_PROCESS_MSG = 3;
@@ -165,6 +184,12 @@ public final class CachedAppOptimizer {
// on swap resources as file.
static final double COMPACT_DOWNGRADE_FREE_SWAP_THRESHOLD = 0.2;
+ // Size of history for the last 20 compactions for any process
+ static final int LAST_COMPACTED_ANY_PROCESS_STATS_HISTORY_SIZE = 20;
+
+ // Amount of processes supported to record for their last compaction.
+ static final int LAST_COMPACTION_FOR_PROCESS_STATS_SIZE = 256;
+
static final int DO_FREEZE = 1;
static final int REPORT_UNFREEZE = 2;
@@ -273,15 +298,16 @@ public final class CachedAppOptimizer {
private final SettingsContentObserver mSettingsObserver;
- private final Object mPhenotypeFlagLock = new Object();
+ @VisibleForTesting
+ final Object mPhenotypeFlagLock = new Object();
// Configured by phenotype. Updates from the server take effect immediately.
@GuardedBy("mPhenotypeFlagLock")
- @VisibleForTesting volatile String mCompactActionSome =
- compactActionIntToString(DEFAULT_COMPACT_ACTION_1);
+ @VisibleForTesting
+ volatile CompactAction mCompactActionSome = compactActionIntToAction(DEFAULT_COMPACT_ACTION_1);
@GuardedBy("mPhenotypeFlagLock")
- @VisibleForTesting volatile String mCompactActionFull =
- compactActionIntToString(DEFAULT_COMPACT_ACTION_2);
+ @VisibleForTesting
+ volatile CompactAction mCompactActionFull = compactActionIntToAction(DEFAULT_COMPACT_ACTION_2);
@GuardedBy("mPhenotypeFlagLock")
@VisibleForTesting volatile long mCompactThrottleSomeSome = DEFAULT_COMPACT_THROTTLE_1;
@GuardedBy("mPhenotypeFlagLock")
@@ -333,31 +359,139 @@ public final class CachedAppOptimizer {
// facilitate removal of the oldest entry.
@VisibleForTesting
@GuardedBy("mProcLock")
- LinkedHashMap<Integer, LastCompactionStats> mLastCompactionStats =
- new LinkedHashMap<Integer, LastCompactionStats>() {
+ LinkedHashMap<Integer, SingleCompactionStats> mLastCompactionStats =
+ new LinkedHashMap<Integer, SingleCompactionStats>() {
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
- return size() > 100;
+ return size() > LAST_COMPACTION_FOR_PROCESS_STATS_SIZE;
+ }
+ };
+
+ LinkedList<SingleCompactionStats> mCompactionStatsHistory =
+ new LinkedList<SingleCompactionStats>() {
+ @Override
+ public boolean add(SingleCompactionStats e) {
+ if (size() >= LAST_COMPACTED_ANY_PROCESS_STATS_HISTORY_SIZE) {
+ this.remove();
+ }
+ return super.add(e);
}
- };
-
- // Compaction Stats
- private int mSomeCompactionCount;
- private int mFullCompactionCount;
- private int mPersistentCompactionCount;
- private int mBfgsCompactionCount;
- private long mSomeCompactRequest;
- private long mFullCompactRequest;
- private long mPersistentCompactRequest;
- private long mBfgsCompactRequest;
- private long mProcCompactionsRequested;
- private long mProcCompactionsPerformed;
- private long mProcCompactionsNoPidThrottled;
- private long mProcCompactionsOomAdjThrottled;
- private long mProcCompactionsTimeThrottled;
- private long mProcCompactionsRSSThrottled;
- private long mProcCompactionsMiscThrottled;
+ };
+
+ class AggregatedCompactionStats {
+ // Throttling stats
+ public long mFullCompactRequested;
+ public long mSomeCompactRequested;
+ public long mFullCompactPerformed;
+ public long mSomeCompactPerformed;
+ public long mProcCompactionsNoPidThrottled;
+ public long mProcCompactionsOomAdjThrottled;
+ public long mProcCompactionsTimeThrottled;
+ public long mProcCompactionsRSSThrottled;
+ public long mProcCompactionsMiscThrottled;
+
+ // Memory stats
+ public long mTotalDeltaAnonRssKBs;
+ public long mTotalZramConsumedKBs;
+ public long mTotalAnonMemFreedKBs;
+ public long mSumOrigAnonRss;
+ public double mMaxCompactEfficiency;
+
+ // Cpu time
+ public long mTotalCpuTimeMillis;
+
+ public long getThrottledSome() { return mSomeCompactRequested - mSomeCompactPerformed; }
+
+ public long getThrottledFull() { return mFullCompactRequested - mFullCompactPerformed; }
+
+ public void addMemStats(long anonRssSaved, long zramConsumed, long memFreed,
+ long origAnonRss, long totalCpuTimeMillis) {
+ final double compactEfficiency = memFreed / (double) origAnonRss;
+ if (compactEfficiency > mMaxCompactEfficiency) {
+ mMaxCompactEfficiency = compactEfficiency;
+ }
+ mTotalDeltaAnonRssKBs += anonRssSaved;
+ mTotalZramConsumedKBs += zramConsumed;
+ mTotalAnonMemFreedKBs += memFreed;
+ mSumOrigAnonRss += origAnonRss;
+ mTotalCpuTimeMillis += totalCpuTimeMillis;
+ }
+
+ public void dump(PrintWriter pw) {
+ long totalCompactRequested = mSomeCompactRequested + mFullCompactRequested;
+ long totalCompactPerformed = mSomeCompactPerformed + mFullCompactPerformed;
+ pw.println(" Performed / Requested:");
+ pw.println(" Some: (" + mSomeCompactPerformed + "/" + mSomeCompactRequested + ")");
+ pw.println(" Full: (" + mFullCompactPerformed + "/" + mFullCompactRequested + ")");
+
+ long throttledSome = getThrottledSome();
+ long throttledFull = getThrottledFull();
+
+ if (throttledSome > 0 || throttledFull > 0) {
+ pw.println(" Throttled:");
+ pw.println(" Some: " + throttledSome + " Full: " + throttledFull);
+ pw.println(" Throttled by Type:");
+ final long compactionsThrottled = totalCompactRequested - totalCompactPerformed;
+ // Any throttle that was not part of the previous categories
+ final long unaccountedThrottled = compactionsThrottled
+ - mProcCompactionsNoPidThrottled - mProcCompactionsOomAdjThrottled
+ - mProcCompactionsTimeThrottled - mProcCompactionsRSSThrottled
+ - mProcCompactionsMiscThrottled;
+ pw.println(" NoPid: " + mProcCompactionsNoPidThrottled
+ + " OomAdj: " + mProcCompactionsOomAdjThrottled + " Time: "
+ + mProcCompactionsTimeThrottled + " RSS: " + mProcCompactionsRSSThrottled
+ + " Misc: " + mProcCompactionsMiscThrottled
+ + " Unaccounted: " + unaccountedThrottled);
+ final double compactThrottlePercentage =
+ (compactionsThrottled / (double) totalCompactRequested) * 100.0;
+ pw.println(" Throttle Percentage: " + compactThrottlePercentage);
+ }
+
+ if (mFullCompactPerformed > 0) {
+ pw.println(" -----Memory Stats----");
+ pw.println(" Total Delta Anon RSS (KB) : " + mTotalDeltaAnonRssKBs);
+ pw.println(" Total Physical ZRAM Consumed (KB): " + mTotalZramConsumedKBs);
+ pw.println(" Total Anon Memory Freed (KB): " + mTotalAnonMemFreedKBs);
+ // This tells us how much anon memory we were able to free thanks to running
+ // compaction
+ pw.println(" Avg Compaction Efficiency (Anon Freed/Anon RSS): "
+ + (mTotalAnonMemFreedKBs / (double) mSumOrigAnonRss));
+ pw.println(" Max Compaction Efficiency: " + mMaxCompactEfficiency);
+ // This tells us how effective is the compression algorithm in physical memory
+ pw.println(" Avg Compression Ratio (1 - ZRAM Consumed/DeltaAnonRSS): "
+ + (1.0 - mTotalZramConsumedKBs / (double) mTotalDeltaAnonRssKBs));
+ long avgKBsPerProcCompact = mFullCompactPerformed > 0
+ ? (mTotalAnonMemFreedKBs / mFullCompactPerformed)
+ : 0;
+ pw.println(" Avg Anon Mem Freed/Compaction (KB) : " + avgKBsPerProcCompact);
+ double compactionCost =
+ mTotalCpuTimeMillis / (mTotalAnonMemFreedKBs / 1024.0); // ms/MB
+ pw.println(" Compaction Cost (ms/MB): " + compactionCost);
+ }
+ }
+ }
+
+ class AggregatedProcessCompactionStats extends AggregatedCompactionStats {
+ public final String processName;
+
+ AggregatedProcessCompactionStats(String processName) { this.processName = processName; }
+ }
+
+ class AggregatedSourceCompactionStats extends AggregatedCompactionStats {
+ public final CompactSource sourceType;
+
+ AggregatedSourceCompactionStats(CompactSource sourceType) { this.sourceType = sourceType; }
+ }
+
+ private final LinkedHashMap<String, AggregatedProcessCompactionStats> mPerProcessCompactStats =
+ new LinkedHashMap<>(256);
+ private final EnumMap<CompactSource, AggregatedSourceCompactionStats> mPerSourceCompactStats =
+ new EnumMap<>(CompactSource.class);
+ private long mTotalCompactionDowngrades;
private long mSystemCompactionsPerformed;
+ private long mSystemTotalMemFreed;
+ private EnumMap<CancelCompactReason, Integer> mTotalCompactionsCancelled =
+ new EnumMap<>(CancelCompactReason.class);
private final ProcessDependencies mProcessDependencies;
private final ProcLocksReader mProcLocksReader;
@@ -450,34 +584,61 @@ public final class CachedAppOptimizer {
pw.println(" " + KEY_COMPACT_PROC_STATE_THROTTLE + "="
+ Arrays.toString(mProcStateThrottle.toArray(new Integer[0])));
- pw.println(" Requested: " + mSomeCompactRequest + " some, " + mFullCompactRequest
- + " full, " + mPersistentCompactRequest + " persistent, "
- + mBfgsCompactRequest + " BFGS compactions.");
- pw.println(" Performed: " + mSomeCompactionCount + " some, " + mFullCompactionCount
- + " full, " + mPersistentCompactionCount + " persistent, "
- + mBfgsCompactionCount + " BFGS compactions.");
- pw.println(" Process Compactions Requested: " + mProcCompactionsRequested);
- pw.println(" Process Compactions Performed: " + mProcCompactionsPerformed);
- long compactionsThrottled = mProcCompactionsRequested - mProcCompactionsPerformed;
- pw.println(" Process Compactions Throttled: " + compactionsThrottled);
- double compactThrottlePercentage =
- (compactionsThrottled / (double) mProcCompactionsRequested) * 100.0;
- pw.println(" Process Compactions Throttle Percentage: " + compactThrottlePercentage);
- pw.println(" NoPid Throttled: " + mProcCompactionsNoPidThrottled);
- pw.println(" OomAdj Throttled: " + mProcCompactionsOomAdjThrottled);
- pw.println(" Time Throttled: " + mProcCompactionsTimeThrottled);
- pw.println(" RSS Throttled: " + mProcCompactionsRSSThrottled);
- pw.println(" Misc Throttled: " + mProcCompactionsMiscThrottled);
- long unaccountedThrottled = compactionsThrottled - mProcCompactionsNoPidThrottled
- - mProcCompactionsOomAdjThrottled - mProcCompactionsTimeThrottled
- - mProcCompactionsRSSThrottled - mProcCompactionsMiscThrottled;
- // Any throttle that was not part of the previous categories
- pw.println(" Unaccounted Throttled: " + unaccountedThrottled);
-
- pw.println(" System Compactions Performed: " + mSystemCompactionsPerformed);
-
+ pw.println(" Per-Process Compaction Stats");
+ long totalCompactPerformedSome = 0;
+ long totalCompactPerformedFull = 0;
+ for (AggregatedProcessCompactionStats stats : mPerProcessCompactStats.values()) {
+ pw.println("-----" + stats.processName + "-----");
+ totalCompactPerformedSome += stats.mSomeCompactPerformed;
+ totalCompactPerformedFull += stats.mFullCompactPerformed;
+ stats.dump(pw);
+ pw.println();
+ }
+ pw.println();
+ pw.println(" Per-Source Compaction Stats");
+ for (AggregatedSourceCompactionStats stats : mPerSourceCompactStats.values()) {
+ pw.println("-----" + stats.sourceType + "-----");
+ stats.dump(pw);
+ pw.println();
+ }
+ pw.println();
+
+ pw.println("Total Compactions Performed by profile: " + totalCompactPerformedSome
+ + " some, " + totalCompactPerformedFull + " full");
+ pw.println("Total compactions downgraded: " + mTotalCompactionDowngrades);
+ pw.println("Total compactions cancelled by reason: ");
+ for (CancelCompactReason reason : mTotalCompactionsCancelled.keySet()) {
+ pw.println(" " + reason + ": " + mTotalCompactionsCancelled.get(reason));
+ }
+ pw.println();
+
+ pw.println(" System Compaction Memory Stats");
+ pw.println(" Compactions Performed: " + mSystemCompactionsPerformed);
+ pw.println(" Total Memory Freed (KB): " + mSystemTotalMemFreed);
+ double avgKBsPerSystemCompact = mSystemCompactionsPerformed > 0
+ ? mSystemTotalMemFreed / mSystemCompactionsPerformed
+ : 0;
+ pw.println(" Avg Mem Freed per Compact (KB): " + avgKBsPerSystemCompact);
+ pw.println();
pw.println(" Tracking last compaction stats for " + mLastCompactionStats.size()
+ " processes.");
+ pw.println("Last Compaction per process stats:");
+ pw.println(" (ProcessName,Source,DeltaAnonRssKBs,ZramConsumedKBs,AnonMemFreedKBs,"
+ + "CompactEfficiency,CompactCost(ms/MB),procState,oomAdj,oomAdjReason)");
+ for (Map.Entry<Integer, SingleCompactionStats> entry :
+ mLastCompactionStats.entrySet()) {
+ SingleCompactionStats stats = entry.getValue();
+ stats.dump(pw);
+ }
+ pw.println();
+ pw.println("Last 20 Compactions Stats:");
+ pw.println(" (ProcessName,Source,DeltaAnonRssKBs,ZramConsumedKBs,AnonMemFreedKBs,"
+ + "CompactEfficiency,CompactCost(ms/MB),procState,oomAdj,oomAdjReason)");
+ for (SingleCompactionStats stats : mCompactionStatsHistory) {
+ stats.dump(pw);
+ }
+ pw.println();
+
pw.println(" " + KEY_USE_FREEZER + "=" + mUseFreezer);
pw.println(" " + KEY_FREEZER_STATSD_SAMPLE_RATE + "=" + mFreezerStatsdSampleRate);
pw.println(" " + KEY_FREEZER_DEBOUNCE_TIMEOUT + "=" + mFreezerDebounceTimeout);
@@ -500,25 +661,9 @@ public final class CachedAppOptimizer {
}
}
}
- if (DEBUG_COMPACTION) {
- for (Map.Entry<Integer, LastCompactionStats> entry
- : mLastCompactionStats.entrySet()) {
- int pid = entry.getKey();
- LastCompactionStats stats = entry.getValue();
- pw.println(" " + pid + ": "
- + Arrays.toString(stats.getRssAfterCompaction()));
- }
- }
}
}
- @GuardedBy("mProcLock")
- void compactAppSome(ProcessRecord app, boolean force) {
- app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_SOME);
- ++mSomeCompactRequest;
- compactApp(app, force, "some");
- }
-
// This method returns true only if requirements are met. Note, that requirements are different
// from throttles applied at the time a compaction is trying to be executed in the sense that
// these are not subject to change dependent on time or memory as throttles usually do.
@@ -546,46 +691,38 @@ public final class CachedAppOptimizer {
}
@GuardedBy("mProcLock")
- void compactAppFull(ProcessRecord app, boolean force) {
- boolean oomAdjEnteredCached = (app.mState.getSetAdj() < mCompactThrottleMinOomAdj
- || app.mState.getSetAdj() > mCompactThrottleMaxOomAdj)
- && app.mState.getCurAdj() >= mCompactThrottleMinOomAdj
- && app.mState.getCurAdj() <= mCompactThrottleMaxOomAdj;
- if (DEBUG_COMPACTION) {
- Slog.d(TAG_AM,
- " compactAppFull requested for " + app.processName + " force: " + force
- + " oomAdjEnteredCached: " + oomAdjEnteredCached);
- }
- ++mFullCompactRequest;
- // Apply OOM adj score throttle for Full App Compaction.
- if (force || oomAdjEnteredCached) {
- app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_FULL);
- compactApp(app, force, "Full");
- } else {
- if (DEBUG_COMPACTION) {
- Slog.d(TAG_AM, "Skipping full compaction for " + app.processName
- + " oom adj score changed from " + app.mState.getSetAdj()
- + " to " + app.mState.getCurAdj());
- }
+ boolean compactApp(
+ ProcessRecord app, CompactProfile compactProfile, CompactSource source, boolean force) {
+ app.mOptRecord.setReqCompactSource(source);
+ app.mOptRecord.setReqCompactProfile(compactProfile);
+ AggregatedSourceCompactionStats perSourceStats = getPerSourceAggregatedCompactStat(source);
+ AggregatedCompactionStats perProcStats =
+ getPerProcessAggregatedCompactStat(app.processName);
+ switch (compactProfile) {
+ case SOME:
+ ++perProcStats.mSomeCompactRequested;
+ ++perSourceStats.mSomeCompactRequested;
+ break;
+ case FULL:
+ ++perProcStats.mFullCompactRequested;
+ ++perSourceStats.mFullCompactRequested;
+ break;
+ default:
+ Slog.e(TAG_AM,
+ "Unimplemented compaction type, consider adding it.");
+ return false;
}
- }
- @GuardedBy("mProcLock")
- void compactAppPersistent(ProcessRecord app) {
- app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_PERSISTENT);
- ++mPersistentCompactRequest;
- compactApp(app, false, "Persistent");
- }
-
- @GuardedBy("mProcLock")
- boolean compactApp(ProcessRecord app, boolean force, String compactRequestType) {
- if (!app.mOptRecord.hasPendingCompact() && meetsCompactionRequirements(app)) {
+ if (!app.mOptRecord.hasPendingCompact() && (meetsCompactionRequirements(app) || force)) {
final String processName = (app.processName != null ? app.processName : "");
if (DEBUG_COMPACTION) {
- Slog.d(TAG_AM, "compactApp " + compactRequestType + " " + processName);
+ Slog.d(TAG_AM,
+ "compactApp " + app.mOptRecord.getReqCompactSource().name() + " "
+ + app.mOptRecord.getReqCompactProfile().name() + " " + processName);
}
Trace.instantForTrack(Trace.TRACE_TAG_ACTIVITY_MANAGER, ATRACE_COMPACTION_TRACK,
- "compactApp " + compactRequestType + " " + processName);
+ "compactApp " + app.mOptRecord.getReqCompactSource().name() + " "
+ + app.mOptRecord.getReqCompactProfile().name() + " " + processName);
app.mOptRecord.setHasPendingCompact(true);
app.mOptRecord.setForceCompact(force);
mPendingCompactionProcesses.add(app);
@@ -596,14 +733,53 @@ public final class CachedAppOptimizer {
if (DEBUG_COMPACTION) {
Slog.d(TAG_AM,
- " compactApp Skipped for " + app.processName
- + " pendingCompact= " + app.mOptRecord.hasPendingCompact()
- + " meetsCompactionRequirements=" + meetsCompactionRequirements(app)
- + ". Requested compact: " + app.mOptRecord.getReqCompactAction());
+ " compactApp Skipped for " + app.processName + " pendingCompact= "
+ + app.mOptRecord.hasPendingCompact() + " meetsCompactionRequirements="
+ + meetsCompactionRequirements(app) + ". Requested compact profile: "
+ + app.mOptRecord.getReqCompactProfile().name() + ". Compact source "
+ + app.mOptRecord.getReqCompactSource().name());
}
return false;
}
+ private CompactAction resolveCompactActionForProfile(CompactProfile profile) {
+ CompactAction action;
+ switch (profile) {
+ case SOME:
+ action = CompactAction.FILE;
+ break;
+ case FULL:
+ action = CompactAction.ALL;
+ break;
+ default:
+ action = CompactAction.NONE;
+ }
+ return action;
+ }
+
+ private AggregatedProcessCompactionStats getPerProcessAggregatedCompactStat(
+ String processName) {
+ if (processName == null) {
+ processName = "";
+ }
+ AggregatedProcessCompactionStats stats = mPerProcessCompactStats.get(processName);
+ if (stats == null) {
+ stats = new AggregatedProcessCompactionStats(processName);
+ mPerProcessCompactStats.put(processName, stats);
+ }
+ return stats;
+ }
+
+ private AggregatedSourceCompactionStats getPerSourceAggregatedCompactStat(
+ CompactSource source) {
+ AggregatedSourceCompactionStats stats = mPerSourceCompactStats.get(source);
+ if (stats == null) {
+ stats = new AggregatedSourceCompactionStats(source);
+ mPerSourceCompactStats.put(source, stats);
+ }
+ return stats;
+ }
+
@GuardedBy("mProcLock")
boolean shouldCompactPersistent(ProcessRecord app, long now) {
return (app.mOptRecord.getLastCompactTime() == 0
@@ -611,13 +787,6 @@ public final class CachedAppOptimizer {
}
@GuardedBy("mProcLock")
- void compactAppBfgs(ProcessRecord app) {
- ++mBfgsCompactRequest;
- app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_BFGS);
- compactApp(app, false, " Bfgs");
- }
-
- @GuardedBy("mProcLock")
boolean shouldCompactBFGS(ProcessRecord app, long now) {
return (app.mOptRecord.getLastCompactTime() == 0
|| (now - app.mOptRecord.getLastCompactTime()) > mCompactThrottleBFGS);
@@ -648,11 +817,27 @@ public final class CachedAppOptimizer {
static private native void cancelCompaction();
/**
+ * Returns the cpu time for the current thread
+ */
+ static private native long threadCpuTimeNs();
+
+ /**
* Retrieves the free swap percentage.
*/
static private native double getFreeSwapPercent();
/**
+ * Retrieves the total used physical ZRAM
+ */
+ static private native long getUsedZramMemory();
+
+ /**
+ * Amount of memory that has been made free due to compaction.
+ * It represents uncompressed memory size - compressed memory size.
+ */
+ static private native long getMemoryFreedCompaction();
+
+ /**
* Reads the flag value from DeviceConfig to determine whether app compaction
* should be enabled, and starts the freeze/compaction thread if needed.
*/
@@ -851,8 +1036,8 @@ public final class CachedAppOptimizer {
int compactAction2 = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_COMPACT_ACTION_2, DEFAULT_COMPACT_ACTION_2);
- mCompactActionSome = compactActionIntToString(compactAction1);
- mCompactActionFull = compactActionIntToString(compactAction2);
+ mCompactActionSome = compactActionIntToAction(compactAction1);
+ mCompactActionFull = compactActionIntToAction(compactAction2);
}
@GuardedBy("mPhenotypeFlagLock")
@@ -1019,13 +1204,12 @@ public final class CachedAppOptimizer {
return true;
}
- @VisibleForTesting
- static String compactActionIntToString(int action) {
- if (action < 0 || action >= COMPACT_ACTION_STRING.length) {
- return "";
+ static CompactAction compactActionIntToAction(int action) {
+ if (action < 0 || action >= CompactAction.values().length) {
+ return CompactAction.NONE;
}
- return COMPACT_ACTION_STRING[action];
+ return CompactAction.values()[action];
}
// This will ensure app will be out of the freezer for at least mFreezerDebounceTimeout.
@@ -1202,16 +1386,17 @@ public final class CachedAppOptimizer {
if(wakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE) {
// Remove any pending compaction we may have scheduled to happen while screen was off
Slog.e(TAG_AM, "Cancel pending or running compactions as system is awake");
- cancelAllCompactions();
+ cancelAllCompactions(CancelCompactReason.SCREEN_ON);
}
}
- void cancelAllCompactions() {
+ 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.
@@ -1222,53 +1407,64 @@ public final class CachedAppOptimizer {
cancelCompaction();
}
+ @GuardedBy("mProcLock")
+ void cancelCompactionForProcess(ProcessRecord app, CancelCompactReason cancelReason) {
+ boolean cancelled = false;
+ if (!mPendingCompactionProcesses.isEmpty() && mPendingCompactionProcesses.contains(app)) {
+ app.mOptRecord.setHasPendingCompact(false);
+ mPendingCompactionProcesses.remove(app);
+ cancelled = true;
+ }
+ if (DefaultProcessDependencies.mPidCompacting == app.mPid) {
+ cancelCompaction();
+ cancelled = true;
+ }
+ if (cancelled) {
+ if (mTotalCompactionsCancelled.containsKey(cancelReason)) {
+ int count = mTotalCompactionsCancelled.get(cancelReason);
+ mTotalCompactionsCancelled.put(cancelReason, count + 1);
+ } else {
+ mTotalCompactionsCancelled.put(cancelReason, 1);
+ }
+ if (DEBUG_COMPACTION) {
+ Slog.d(TAG_AM,
+ "Cancelled pending or running compactions for process: " +
+ app.processName != null ? app.processName : "" +
+ " reason: " + cancelReason.name());
+ }
+ }
+ }
+
@GuardedBy({"mService", "mProcLock"})
void onOomAdjustChanged(int oldAdj, int newAdj, ProcessRecord app) {
// Cancel any currently executing compactions
// if the process moved out of cached state
- if (DefaultProcessDependencies.mPidCompacting == app.mPid && newAdj < oldAdj
- && newAdj < ProcessList.CACHED_APP_MIN_ADJ) {
- cancelCompaction();
+ if (newAdj < oldAdj && newAdj < ProcessList.CACHED_APP_MIN_ADJ) {
+ cancelCompactionForProcess(app, CancelCompactReason.OOM_IMPROVEMENT);
}
if (oldAdj <= ProcessList.PERCEPTIBLE_APP_ADJ
&& (newAdj == ProcessList.PREVIOUS_APP_ADJ || newAdj == ProcessList.HOME_APP_ADJ)) {
// Perform a minor compaction when a perceptible app becomes the prev/home app
- compactAppSome(app, false);
+ compactApp(app, CompactProfile.SOME, CompactSource.APP, false);
} else if (oldAdj < ProcessList.CACHED_APP_MIN_ADJ
&& newAdj >= ProcessList.CACHED_APP_MIN_ADJ
&& newAdj <= ProcessList.CACHED_APP_MAX_ADJ) {
// Perform a major compaction when any app enters cached
- compactAppFull(app, false);
+ compactApp(app, CompactProfile.FULL, CompactSource.APP, false);
}
}
/**
- * This method resolves which compaction method we should use for the proposed compaction.
+ * Applies a compaction downgrade when swap is low.
*/
- int resolveCompactionAction(int pendingAction) {
- int resolvedAction;
-
- switch (pendingAction) {
- case COMPACT_PROCESS_SOME:
- resolvedAction = COMPACT_ACTION_FILE;
- break;
- // For the time being, treat these as equivalent.
- case COMPACT_PROCESS_FULL:
- case COMPACT_PROCESS_PERSISTENT:
- case COMPACT_PROCESS_BFGS:
- resolvedAction = COMPACT_ACTION_FULL;
- break;
- default:
- resolvedAction = COMPACT_ACTION_NONE;
- break;
- }
-
+ CompactProfile downgradeCompactionIfRequired(CompactProfile profile) {
// Downgrade compaction under swap memory pressure
- if (resolvedAction == COMPACT_ACTION_FULL) {
+ if (profile == CompactProfile.FULL) {
double swapFreePercent = getFreeSwapPercent();
if (swapFreePercent < COMPACT_DOWNGRADE_FREE_SWAP_THRESHOLD) {
- resolvedAction = COMPACT_ACTION_FILE;
+ profile = CompactProfile.SOME;
+ ++mTotalCompactionDowngrades;
if (DEBUG_COMPACTION) {
Slog.d(TAG_AM,
"Downgraded compaction to file only due to low swap."
@@ -1277,20 +1473,69 @@ public final class CachedAppOptimizer {
}
}
- return resolvedAction;
+ return profile;
}
@VisibleForTesting
- static final class LastCompactionStats {
+ static final class SingleCompactionStats {
+ private static final float STATSD_SAMPLE_RATE = 0.1f;
+ private static final Random mRandom = new Random();
private final long[] mRssAfterCompaction;
-
- LastCompactionStats(long[] rss) {
+ public CompactSource mSourceType;
+ public String mProcessName;
+ public final int mUid;
+ public long mDeltaAnonRssKBs;
+ public long mZramConsumedKBs;
+ public long mAnonMemFreedKBs;
+ public float mCpuTimeMillis;
+ public long mOrigAnonRss;
+ public int mProcState;
+ public int mOomAdj;
+ public @OomAdjuster.OomAdjReason int mOomAdjReason;
+
+ SingleCompactionStats(long[] rss, CompactSource source, String processName,
+ long deltaAnonRss, long zramConsumed, long anonMemFreed, long origAnonRss,
+ long cpuTimeMillis, int procState, int oomAdj,
+ @OomAdjuster.OomAdjReason int oomAdjReason, int uid) {
mRssAfterCompaction = rss;
+ mSourceType = source;
+ mProcessName = processName;
+ mUid = uid;
+ mDeltaAnonRssKBs = deltaAnonRss;
+ mZramConsumedKBs = zramConsumed;
+ mAnonMemFreedKBs = anonMemFreed;
+ mCpuTimeMillis = cpuTimeMillis;
+ mOrigAnonRss = origAnonRss;
+ mProcState = procState;
+ mOomAdj = oomAdj;
+ mOomAdjReason = oomAdjReason;
+ }
+
+ double getCompactEfficiency() { return mAnonMemFreedKBs / (double) mOrigAnonRss; }
+
+ double getCompactCost() {
+ // mCpuTimeMillis / (anonMemFreedKBs/1024) and metric is in (ms/MB)
+ return mCpuTimeMillis / (double) mAnonMemFreedKBs * 1024;
}
long[] getRssAfterCompaction() {
return mRssAfterCompaction;
}
+
+ void dump(PrintWriter pw) {
+ pw.println(" (" + mProcessName + "," + mSourceType.name() + "," + mDeltaAnonRssKBs
+ + "," + mZramConsumedKBs + "," + mAnonMemFreedKBs + "," + getCompactEfficiency()
+ + "," + getCompactCost() + "," + mProcState + "," + mOomAdj + ","
+ + OomAdjuster.oomAdjReasonToString(mOomAdjReason) + ")");
+ }
+
+ void sendStat() {
+ if (mRandom.nextFloat() < STATSD_SAMPLE_RATE) {
+ FrameworkStatsLog.write(FrameworkStatsLog.APP_COMPACTED_V2, mUid, mProcState,
+ mOomAdj, mDeltaAnonRssKBs, mZramConsumedKBs, mCpuTimeMillis, mOrigAnonRss,
+ mOomAdjReason);
+ }
+ }
}
private final class MemCompactionHandler extends Handler {
@@ -1298,13 +1543,12 @@ public final class CachedAppOptimizer {
super(mCachedAppOptimizerThread.getLooper());
}
- private boolean shouldOomAdjThrottleCompaction(ProcessRecord proc, int action) {
+ private boolean shouldOomAdjThrottleCompaction(ProcessRecord proc) {
final String name = proc.processName;
// don't compact if the process has returned to perceptible
// and this is only a cached/home/prev compaction
- if ((action == COMPACT_ACTION_FILE || action == COMPACT_ACTION_FULL)
- && (proc.mState.getSetAdj() <= ProcessList.PERCEPTIBLE_APP_ADJ)) {
+ if (proc.mState.getSetAdj() <= ProcessList.PERCEPTIBLE_APP_ADJ) {
if (DEBUG_COMPACTION) {
Slog.d(TAG_AM,
"Skipping compaction as process " + name + " is "
@@ -1316,51 +1560,52 @@ public final class CachedAppOptimizer {
return false;
}
- private boolean shouldTimeThrottleCompaction(
- ProcessRecord proc, long start, int pendingAction) {
+ private boolean shouldTimeThrottleCompaction(ProcessRecord proc, long start,
+ CompactProfile pendingProfile, CompactSource source) {
final ProcessCachedOptimizerRecord opt = proc.mOptRecord;
final String name = proc.processName;
- int lastCompactAction = opt.getLastCompactAction();
+ CompactProfile lastCompactProfile = opt.getLastCompactProfile();
long lastCompactTime = opt.getLastCompactTime();
// basic throttling
- // use the Phenotype flag knobs to determine whether current/prevous
- // compaction combo should be throtted or not
-
+ // use the Phenotype flag knobs to determine whether current/previous
+ // compaction combo should be throttled or not.
// Note that we explicitly don't take mPhenotypeFlagLock here as the flags
// should very seldom change, and taking the risk of using the wrong action is
// preferable to taking the lock for every single compaction action.
if (lastCompactTime != 0) {
- if (pendingAction == COMPACT_PROCESS_SOME) {
- if ((lastCompactAction == COMPACT_PROCESS_SOME
- && (start - lastCompactTime < mCompactThrottleSomeSome))
- || (lastCompactAction == COMPACT_PROCESS_FULL
- && (start - lastCompactTime < mCompactThrottleSomeFull))) {
- if (DEBUG_COMPACTION) {
- Slog.d(TAG_AM,
- "Skipping some compaction for " + name
- + ": too soon. throttle=" + mCompactThrottleSomeSome
- + "/" + mCompactThrottleSomeFull
- + " last=" + (start - lastCompactTime) + "ms ago");
+ if (source == CompactSource.APP) {
+ if (pendingProfile == CompactProfile.SOME) {
+ if ((lastCompactProfile == CompactProfile.SOME
+ && (start - lastCompactTime < mCompactThrottleSomeSome))
+ || (lastCompactProfile == CompactProfile.FULL
+ && (start - lastCompactTime < mCompactThrottleSomeFull))) {
+ if (DEBUG_COMPACTION) {
+ Slog.d(TAG_AM,
+ "Skipping some compaction for " + name
+ + ": too soon. throttle=" + mCompactThrottleSomeSome
+ + "/" + mCompactThrottleSomeFull
+ + " last=" + (start - lastCompactTime) + "ms ago");
+ }
+ return true;
}
- return true;
- }
- } else if (pendingAction == COMPACT_PROCESS_FULL) {
- if ((lastCompactAction == COMPACT_PROCESS_SOME
- && (start - lastCompactTime < mCompactThrottleFullSome))
- || (lastCompactAction == COMPACT_PROCESS_FULL
- && (start - lastCompactTime < mCompactThrottleFullFull))) {
- if (DEBUG_COMPACTION) {
- Slog.d(TAG_AM,
- "Skipping full compaction for " + name
- + ": too soon. throttle=" + mCompactThrottleFullSome
- + "/" + mCompactThrottleFullFull
- + " last=" + (start - lastCompactTime) + "ms ago");
+ } else if (pendingProfile == CompactProfile.FULL) {
+ if ((lastCompactProfile == CompactProfile.SOME
+ && (start - lastCompactTime < mCompactThrottleFullSome))
+ || (lastCompactProfile == CompactProfile.FULL
+ && (start - lastCompactTime < mCompactThrottleFullFull))) {
+ if (DEBUG_COMPACTION) {
+ Slog.d(TAG_AM,
+ "Skipping full compaction for " + name
+ + ": too soon. throttle=" + mCompactThrottleFullSome
+ + "/" + mCompactThrottleFullFull
+ + " last=" + (start - lastCompactTime) + "ms ago");
+ }
+ return true;
}
- return true;
}
- } else if (pendingAction == COMPACT_PROCESS_PERSISTENT) {
+ } else if (source == CompactSource.PERSISTENT) {
if (start - lastCompactTime < mCompactThrottlePersistent) {
if (DEBUG_COMPACTION) {
Slog.d(TAG_AM,
@@ -1370,7 +1615,7 @@ public final class CachedAppOptimizer {
}
return true;
}
- } else if (pendingAction == COMPACT_PROCESS_BFGS) {
+ } else if (source == CompactSource.BFGS) {
if (start - lastCompactTime < mCompactThrottleBFGS) {
if (DEBUG_COMPACTION) {
Slog.d(TAG_AM,
@@ -1386,11 +1631,10 @@ public final class CachedAppOptimizer {
return false;
}
- private boolean shouldThrottleMiscCompaction(
- ProcessRecord proc, int procState, int action) {
- final String name = proc.processName;
+ private boolean shouldThrottleMiscCompaction(ProcessRecord proc, int procState) {
if (mProcStateThrottle.contains(procState)) {
if (DEBUG_COMPACTION) {
+ final String name = proc.processName;
Slog.d(TAG_AM,
"Skipping full compaction for process " + name + "; proc state is "
+ procState);
@@ -1398,21 +1642,13 @@ public final class CachedAppOptimizer {
return true;
}
- if (COMPACT_ACTION_NONE == action) {
- if (DEBUG_COMPACTION) {
- Slog.d(TAG_AM,
- "Skipping compaction for process " + name + "since action is None");
- }
- return true;
- }
-
return false;
}
private boolean shouldRssThrottleCompaction(
- int action, int pid, String name, long[] rssBefore) {
+ CompactProfile profile, int pid, String name, long[] rssBefore) {
long anonRssBefore = rssBefore[RSS_ANON_INDEX];
- LastCompactionStats lastCompactionStats = mLastCompactionStats.get(pid);
+ SingleCompactionStats lastCompactionStats = mLastCompactionStats.get(pid);
if (rssBefore[RSS_TOTAL_INDEX] == 0 && rssBefore[RSS_FILE_INDEX] == 0
&& rssBefore[RSS_ANON_INDEX] == 0 && rssBefore[RSS_SWAP_INDEX] == 0) {
@@ -1424,7 +1660,7 @@ public final class CachedAppOptimizer {
return true;
}
- if (action == COMPACT_ACTION_FULL || action == COMPACT_ACTION_ANON) {
+ if (profile == CompactProfile.FULL) {
if (mFullAnonRssThrottleKb > 0L && anonRssBefore < mFullAnonRssThrottleKb) {
if (DEBUG_COMPACTION) {
Slog.d(TAG_AM,
@@ -1461,13 +1697,16 @@ public final class CachedAppOptimizer {
ProcessRecord proc;
final ProcessCachedOptimizerRecord opt;
int pid;
- String action;
+ CompactAction resolvedAction;
final String name;
- int requestedAction, lastCompactAction;
+ CompactProfile lastCompactProfile;
long lastCompactTime;
- int lastOomAdj = msg.arg1;
+ int newOomAdj = msg.arg1;
int procState = msg.arg2;
boolean forceCompaction;
+ CompactSource compactSource;
+ CompactProfile requestedProfile;
+ int oomAdjReason;
synchronized (mProcLock) {
if (mPendingCompactionProcesses.isEmpty()) {
if (DEBUG_COMPACTION) {
@@ -1479,42 +1718,53 @@ public final class CachedAppOptimizer {
opt = proc.mOptRecord;
forceCompaction = opt.isForceCompact();
opt.setForceCompact(false); // since this is a one-shot operation
-
- requestedAction = opt.getReqCompactAction();
pid = proc.getPid();
name = proc.processName;
opt.setHasPendingCompact(false);
- lastCompactAction = opt.getLastCompactAction();
+ compactSource = opt.getReqCompactSource();
+ requestedProfile = opt.getReqCompactProfile();
+ lastCompactProfile = opt.getLastCompactProfile();
lastCompactTime = opt.getLastCompactTime();
+ oomAdjReason = opt.getLastOomAdjChangeReason();
}
- ++mProcCompactionsRequested;
+ AggregatedSourceCompactionStats perSourceStats =
+ getPerSourceAggregatedCompactStat(opt.getReqCompactSource());
+ AggregatedProcessCompactionStats perProcessStats =
+ getPerProcessAggregatedCompactStat(name);
+
long[] rssBefore;
if (pid == 0) {
// not a real process, either one being launched or one being killed
if (DEBUG_COMPACTION) {
Slog.d(TAG_AM, "Compaction failed, pid is 0");
}
- ++mProcCompactionsNoPidThrottled;
+ ++perSourceStats.mProcCompactionsNoPidThrottled;
+ ++perProcessStats.mProcCompactionsNoPidThrottled;
return;
}
if (!forceCompaction) {
- if (shouldOomAdjThrottleCompaction(proc, requestedAction)) {
- ++mProcCompactionsOomAdjThrottled;
+ if (shouldOomAdjThrottleCompaction(proc)) {
+ ++perProcessStats.mProcCompactionsOomAdjThrottled;
+ ++perSourceStats.mProcCompactionsOomAdjThrottled;
return;
}
- if (shouldTimeThrottleCompaction(proc, start, requestedAction)) {
- ++mProcCompactionsTimeThrottled;
+ if (shouldTimeThrottleCompaction(
+ proc, start, requestedProfile, compactSource)) {
+ ++perProcessStats.mProcCompactionsTimeThrottled;
+ ++perSourceStats.mProcCompactionsTimeThrottled;
return;
}
- if (shouldThrottleMiscCompaction(proc, procState, requestedAction)) {
- ++mProcCompactionsMiscThrottled;
+ if (shouldThrottleMiscCompaction(proc, procState)) {
+ ++perProcessStats.mProcCompactionsMiscThrottled;
+ ++perSourceStats.mProcCompactionsMiscThrottled;
return;
}
rssBefore = mProcessDependencies.getRss(pid);
- if (shouldRssThrottleCompaction(requestedAction, pid, name, rssBefore)) {
- ++mProcCompactionsRSSThrottled;
+ if (shouldRssThrottleCompaction(requestedProfile, pid, name, rssBefore)) {
+ ++perProcessStats.mProcCompactionsRSSThrottled;
+ ++perSourceStats.mProcCompactionsRSSThrottled;
return;
}
} else {
@@ -1524,71 +1774,78 @@ public final class CachedAppOptimizer {
}
}
- // Now we've passed through all the throttles and are going to compact, update
- // bookkeeping.
- switch (requestedAction) {
- case COMPACT_PROCESS_SOME:
- mSomeCompactionCount++;
- break;
- case COMPACT_PROCESS_FULL:
- mFullCompactionCount++;
- break;
- case COMPACT_PROCESS_PERSISTENT:
- mPersistentCompactionCount++;
- break;
- case COMPACT_PROCESS_BFGS:
- mBfgsCompactionCount++;
- break;
- default:
- break;
- }
-
- int resolvedAction = resolveCompactionAction(requestedAction);
- action = compactActionIntToString(resolvedAction);
+ CompactProfile resolvedProfile =
+ downgradeCompactionIfRequired(requestedProfile);
+ resolvedAction = resolveCompactActionForProfile(resolvedProfile);
try {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
- "Compact " + action + ": " + name);
- ++mProcCompactionsPerformed;
- long zramFreeKbBefore = Debug.getZramFreeKb();
- mProcessDependencies.performCompaction(action, pid);
+ "Compact " + resolvedAction.name() + ": " + name
+ + " lastOomAdjReason: " + oomAdjReason);
+ long zramUsedKbBefore = getUsedZramMemory();
+ long startCpuTime = threadCpuTimeNs();
+ mProcessDependencies.performCompaction(resolvedAction, pid);
+ long endCpuTime = threadCpuTimeNs();
long[] rssAfter = mProcessDependencies.getRss(pid);
long end = SystemClock.uptimeMillis();
long time = end - start;
- long zramFreeKbAfter = Debug.getZramFreeKb();
+ long deltaCpuTimeNanos = endCpuTime - startCpuTime;
+ long zramUsedKbAfter = getUsedZramMemory();
long deltaTotalRss = rssAfter[RSS_TOTAL_INDEX] - rssBefore[RSS_TOTAL_INDEX];
long deltaFileRss = rssAfter[RSS_FILE_INDEX] - rssBefore[RSS_FILE_INDEX];
long deltaAnonRss = rssAfter[RSS_ANON_INDEX] - rssBefore[RSS_ANON_INDEX];
long deltaSwapRss = rssAfter[RSS_SWAP_INDEX] - rssBefore[RSS_SWAP_INDEX];
- EventLog.writeEvent(EventLogTags.AM_COMPACT, pid, name, action,
- rssBefore[RSS_TOTAL_INDEX], rssBefore[RSS_FILE_INDEX],
- rssBefore[RSS_ANON_INDEX], rssBefore[RSS_SWAP_INDEX], deltaTotalRss,
- deltaFileRss, deltaAnonRss, deltaSwapRss, time, lastCompactAction,
- lastCompactTime, lastOomAdj, procState, zramFreeKbBefore,
- zramFreeKbAfter - zramFreeKbBefore);
- // Note that as above not taking mPhenoTypeFlagLock here to avoid locking
- // on every single compaction for a flag that will seldom change and the
- // impact of reading the wrong value here is low.
- if (mRandom.nextFloat() < mCompactStatsdSampleRate) {
- FrameworkStatsLog.write(FrameworkStatsLog.APP_COMPACTED, pid, name,
- requestedAction, rssBefore[RSS_TOTAL_INDEX],
- rssBefore[RSS_FILE_INDEX], rssBefore[RSS_ANON_INDEX],
- rssBefore[RSS_SWAP_INDEX], rssAfter[RSS_TOTAL_INDEX],
- rssAfter[RSS_FILE_INDEX], rssAfter[RSS_ANON_INDEX],
- rssAfter[RSS_SWAP_INDEX], time, lastCompactAction,
- lastCompactTime, lastOomAdj,
- ActivityManager.processStateAmToProto(procState),
- zramFreeKbBefore, zramFreeKbAfter);
+ switch (opt.getReqCompactProfile()) {
+ case SOME:
+ ++perSourceStats.mSomeCompactPerformed;
+ ++perProcessStats.mSomeCompactPerformed;
+ break;
+ case FULL:
+ ++perSourceStats.mFullCompactPerformed;
+ ++perProcessStats.mFullCompactPerformed;
+ long anonRssSavings = -deltaAnonRss;
+ long zramConsumed = zramUsedKbAfter - zramUsedKbBefore;
+ long memFreed = anonRssSavings - zramConsumed;
+ long totalCpuTimeMillis = deltaCpuTimeNanos / 1000000;
+ long origAnonRss = rssBefore[RSS_ANON_INDEX];
+
+ // Negative stats would skew averages and will likely be due to
+ // noise of system doing other things so we put a floor at 0 to
+ // avoid negative values.
+ anonRssSavings = anonRssSavings > 0 ? anonRssSavings : 0;
+ zramConsumed = zramConsumed > 0 ? zramConsumed : 0;
+ memFreed = memFreed > 0 ? memFreed : 0;
+
+ perProcessStats.addMemStats(anonRssSavings, zramConsumed, memFreed,
+ origAnonRss, totalCpuTimeMillis);
+ perSourceStats.addMemStats(anonRssSavings, zramConsumed, memFreed,
+ origAnonRss, totalCpuTimeMillis);
+ SingleCompactionStats memStats = new SingleCompactionStats(rssAfter,
+ compactSource, name, anonRssSavings, zramConsumed, memFreed,
+ origAnonRss, totalCpuTimeMillis, procState, newOomAdj,
+ oomAdjReason, proc.uid);
+ mLastCompactionStats.remove(pid);
+ mLastCompactionStats.put(pid, memStats);
+ mCompactionStatsHistory.add(memStats);
+ memStats.sendStat();
+ break;
+ default:
+ // We likely missed adding this category, it needs to be added
+ // if we end up here. In the meantime, gracefully fallback to
+ // attribute to app.
+ Slog.wtf(TAG_AM, "Compaction: Unknown requested action");
+ return;
}
+ EventLog.writeEvent(EventLogTags.AM_COMPACT, pid, name,
+ resolvedAction.name(), rssBefore[RSS_TOTAL_INDEX],
+ rssBefore[RSS_FILE_INDEX], rssBefore[RSS_ANON_INDEX],
+ rssBefore[RSS_SWAP_INDEX], deltaTotalRss, deltaFileRss,
+ deltaAnonRss, deltaSwapRss, time, lastCompactProfile.name(),
+ lastCompactTime, newOomAdj, procState, zramUsedKbBefore,
+ zramUsedKbBefore - zramUsedKbAfter);
synchronized (mProcLock) {
opt.setLastCompactTime(end);
- opt.setLastCompactAction(resolvedAction);
- }
- if (resolvedAction == COMPACT_ACTION_FULL
- || resolvedAction == COMPACT_ACTION_ANON) {
- // Remove entry and insert again to update insertion order.
- mLastCompactionStats.remove(pid);
- mLastCompactionStats.put(pid, new LastCompactionStats(rssAfter));
+ opt.setLastCompactProfile(requestedProfile);
}
} catch (Exception e) {
// nothing to do, presumably the process died
@@ -1603,7 +1860,10 @@ public final class CachedAppOptimizer {
case COMPACT_SYSTEM_MSG: {
++mSystemCompactionsPerformed;
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "compactSystem");
+ long memFreedBefore = getMemoryFreedCompaction();
compactSystem();
+ long memFreedAfter = getMemoryFreedCompaction();
+ mSystemTotalMemFreed += memFreedAfter - memFreedBefore;
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
}
@@ -1809,14 +2069,14 @@ public final class CachedAppOptimizer {
// Compact process.
@Override
- public void performCompaction(String action, int pid) throws IOException {
+ public void performCompaction(CompactAction action, int pid) throws IOException {
mPidCompacting = pid;
- if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FULL])) {
- compactProcess(pid, COMPACT_ACTION_FILE_FLAG | COMPACT_ACTION_ANON_FLAG);
- } else if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FILE])) {
- compactProcess(pid, COMPACT_ACTION_FILE_FLAG);
- } else if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_ANON])) {
- compactProcess(pid, COMPACT_ACTION_ANON_FLAG);
+ if (action == CompactAction.ALL) {
+ compactProcess(pid, COMPACT_ACTION_FILE_FLAG | COMPACT_ACTION_ANON_FLAG);
+ } else if (action == CompactAction.FILE) {
+ compactProcess(pid, COMPACT_ACTION_FILE_FLAG);
+ } else if (action == CompactAction.ANON) {
+ compactProcess(pid, COMPACT_ACTION_ANON_FLAG);
}
mPidCompacting = -1;
}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 9607e401f9c8..8759f23065f0 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -76,9 +76,11 @@ import static com.android.server.am.PlatformCompatCache.CACHED_COMPAT_CHANGE_USE
import static com.android.server.am.ProcessList.TAG_PROCESS_OBSERVERS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityThread;
+import android.app.AppProtoEnums;
import android.app.ApplicationExitInfo;
import android.app.usage.UsageEvents;
import android.compat.annotation.ChangeId;
@@ -113,6 +115,8 @@ import com.android.server.wm.ActivityServiceConnectionsHolder;
import com.android.server.wm.WindowProcessController;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
@@ -122,20 +126,97 @@ import java.util.Arrays;
*/
public class OomAdjuster {
static final String TAG = "OomAdjuster";
- static final String OOM_ADJ_REASON_METHOD = "updateOomAdj";
- static final String OOM_ADJ_REASON_NONE = OOM_ADJ_REASON_METHOD + "_meh";
- static final String OOM_ADJ_REASON_ACTIVITY = OOM_ADJ_REASON_METHOD + "_activityChange";
- static final String OOM_ADJ_REASON_FINISH_RECEIVER = OOM_ADJ_REASON_METHOD + "_finishReceiver";
- static final String OOM_ADJ_REASON_START_RECEIVER = OOM_ADJ_REASON_METHOD + "_startReceiver";
- static final String OOM_ADJ_REASON_BIND_SERVICE = OOM_ADJ_REASON_METHOD + "_bindService";
- static final String OOM_ADJ_REASON_UNBIND_SERVICE = OOM_ADJ_REASON_METHOD + "_unbindService";
- static final String OOM_ADJ_REASON_START_SERVICE = OOM_ADJ_REASON_METHOD + "_startService";
- static final String OOM_ADJ_REASON_GET_PROVIDER = OOM_ADJ_REASON_METHOD + "_getProvider";
- static final String OOM_ADJ_REASON_REMOVE_PROVIDER = OOM_ADJ_REASON_METHOD + "_removeProvider";
- static final String OOM_ADJ_REASON_UI_VISIBILITY = OOM_ADJ_REASON_METHOD + "_uiVisibility";
- static final String OOM_ADJ_REASON_ALLOWLIST = OOM_ADJ_REASON_METHOD + "_allowlistChange";
- static final String OOM_ADJ_REASON_PROCESS_BEGIN = OOM_ADJ_REASON_METHOD + "_processBegin";
- static final String OOM_ADJ_REASON_PROCESS_END = OOM_ADJ_REASON_METHOD + "_processEnd";
+
+ static final int OOM_ADJ_REASON_NONE = 0;
+ static final int OOM_ADJ_REASON_ACTIVITY = 1;
+ static final int OOM_ADJ_REASON_FINISH_RECEIVER = 2;
+ static final int OOM_ADJ_REASON_START_RECEIVER = 3;
+ static final int OOM_ADJ_REASON_BIND_SERVICE = 4;
+ static final int OOM_ADJ_REASON_UNBIND_SERVICE = 5;
+ static final int OOM_ADJ_REASON_START_SERVICE = 6;
+ static final int OOM_ADJ_REASON_GET_PROVIDER = 7;
+ static final int OOM_ADJ_REASON_REMOVE_PROVIDER = 8;
+ static final int OOM_ADJ_REASON_UI_VISIBILITY = 9;
+ static final int OOM_ADJ_REASON_ALLOWLIST = 10;
+ static final int OOM_ADJ_REASON_PROCESS_BEGIN = 11;
+ static final int OOM_ADJ_REASON_PROCESS_END = 12;
+
+ @IntDef(prefix = {"OOM_ADJ_REASON_"},
+ value = {OOM_ADJ_REASON_NONE, OOM_ADJ_REASON_ACTIVITY, OOM_ADJ_REASON_FINISH_RECEIVER,
+ OOM_ADJ_REASON_START_RECEIVER, OOM_ADJ_REASON_BIND_SERVICE,
+ OOM_ADJ_REASON_UNBIND_SERVICE, OOM_ADJ_REASON_START_SERVICE,
+ OOM_ADJ_REASON_GET_PROVIDER, OOM_ADJ_REASON_REMOVE_PROVIDER,
+ OOM_ADJ_REASON_UI_VISIBILITY, OOM_ADJ_REASON_ALLOWLIST,
+ OOM_ADJ_REASON_PROCESS_BEGIN, OOM_ADJ_REASON_PROCESS_END})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface OomAdjReason {}
+
+ public static final int oomAdjReasonToProto(@OomAdjReason int oomReason) {
+ switch (oomReason) {
+ case OOM_ADJ_REASON_NONE:
+ return AppProtoEnums.OOM_ADJ_REASON_NONE;
+ case OOM_ADJ_REASON_ACTIVITY:
+ return AppProtoEnums.OOM_ADJ_REASON_ACTIVITY;
+ case OOM_ADJ_REASON_FINISH_RECEIVER:
+ return AppProtoEnums.OOM_ADJ_REASON_FINISH_RECEIVER;
+ case OOM_ADJ_REASON_START_RECEIVER:
+ return AppProtoEnums.OOM_ADJ_REASON_START_RECEIVER;
+ case OOM_ADJ_REASON_BIND_SERVICE:
+ return AppProtoEnums.OOM_ADJ_REASON_BIND_SERVICE;
+ case OOM_ADJ_REASON_UNBIND_SERVICE:
+ return AppProtoEnums.OOM_ADJ_REASON_UNBIND_SERVICE;
+ case OOM_ADJ_REASON_START_SERVICE:
+ return AppProtoEnums.OOM_ADJ_REASON_START_SERVICE;
+ case OOM_ADJ_REASON_GET_PROVIDER:
+ return AppProtoEnums.OOM_ADJ_REASON_GET_PROVIDER;
+ case OOM_ADJ_REASON_REMOVE_PROVIDER:
+ return AppProtoEnums.OOM_ADJ_REASON_REMOVE_PROVIDER;
+ case OOM_ADJ_REASON_UI_VISIBILITY:
+ return AppProtoEnums.OOM_ADJ_REASON_UI_VISIBILITY;
+ case OOM_ADJ_REASON_ALLOWLIST:
+ return AppProtoEnums.OOM_ADJ_REASON_ALLOWLIST;
+ case OOM_ADJ_REASON_PROCESS_BEGIN:
+ return AppProtoEnums.OOM_ADJ_REASON_PROCESS_BEGIN;
+ case OOM_ADJ_REASON_PROCESS_END:
+ return AppProtoEnums.OOM_ADJ_REASON_PROCESS_END;
+ default:
+ return AppProtoEnums.OOM_ADJ_REASON_UNKNOWN_TO_PROTO;
+ }
+ }
+
+ public static final String oomAdjReasonToString(@OomAdjReason int oomReason) {
+ final String OOM_ADJ_REASON_METHOD = "updateOomAdj";
+ switch (oomReason) {
+ case OOM_ADJ_REASON_NONE:
+ return OOM_ADJ_REASON_METHOD + "_meh";
+ case OOM_ADJ_REASON_ACTIVITY:
+ return OOM_ADJ_REASON_METHOD + "_activityChange";
+ case OOM_ADJ_REASON_FINISH_RECEIVER:
+ return OOM_ADJ_REASON_METHOD + "_finishReceiver";
+ case OOM_ADJ_REASON_START_RECEIVER:
+ return OOM_ADJ_REASON_METHOD + "_startReceiver";
+ case OOM_ADJ_REASON_BIND_SERVICE:
+ return OOM_ADJ_REASON_METHOD + "_bindService";
+ case OOM_ADJ_REASON_UNBIND_SERVICE:
+ return OOM_ADJ_REASON_METHOD + "_unbindService";
+ case OOM_ADJ_REASON_START_SERVICE:
+ return OOM_ADJ_REASON_METHOD + "_startService";
+ case OOM_ADJ_REASON_GET_PROVIDER:
+ return OOM_ADJ_REASON_METHOD + "_getProvider";
+ case OOM_ADJ_REASON_REMOVE_PROVIDER:
+ return OOM_ADJ_REASON_METHOD + "_removeProvider";
+ case OOM_ADJ_REASON_UI_VISIBILITY:
+ return OOM_ADJ_REASON_METHOD + "_uiVisibility";
+ case OOM_ADJ_REASON_ALLOWLIST:
+ return OOM_ADJ_REASON_METHOD + "_allowlistChange";
+ case OOM_ADJ_REASON_PROCESS_BEGIN:
+ return OOM_ADJ_REASON_METHOD + "_processBegin";
+ case OOM_ADJ_REASON_PROCESS_END:
+ return OOM_ADJ_REASON_METHOD + "_processEnd";
+ default:
+ return "_unknown";
+ }
+ }
/**
* Flag {@link android.content.Context#BIND_INCLUDE_CAPABILITIES} is used
@@ -418,14 +499,14 @@ public class OomAdjuster {
* Update OomAdj for all processes in LRU list
*/
@GuardedBy("mService")
- void updateOomAdjLocked(String oomAdjReason) {
+ void updateOomAdjLocked(@OomAdjReason int oomAdjReason) {
synchronized (mProcLock) {
updateOomAdjLSP(oomAdjReason);
}
}
@GuardedBy({"mService", "mProcLock"})
- private void updateOomAdjLSP(String oomAdjReason) {
+ private void updateOomAdjLSP(@OomAdjReason int oomAdjReason) {
if (checkAndEnqueueOomAdjTargetLocked(null)) {
// Simply return as there is an oomAdjUpdate ongoing
return;
@@ -441,7 +522,7 @@ public class OomAdjuster {
}
@GuardedBy({"mService", "mProcLock"})
- private void performUpdateOomAdjLSP(String oomAdjReason) {
+ private void performUpdateOomAdjLSP(@OomAdjReason int oomAdjReason) {
final ProcessRecord topApp = mService.getTopApp();
// Clear any pending ones because we are doing a full update now.
mPendingProcessSet.clear();
@@ -458,14 +539,14 @@ public class OomAdjuster {
* @param oomAdjReason
*/
@GuardedBy("mService")
- boolean updateOomAdjLocked(ProcessRecord app, String oomAdjReason) {
+ boolean updateOomAdjLocked(ProcessRecord app, @OomAdjReason int oomAdjReason) {
synchronized (mProcLock) {
return updateOomAdjLSP(app, oomAdjReason);
}
}
@GuardedBy({"mService", "mProcLock"})
- private boolean updateOomAdjLSP(ProcessRecord app, String oomAdjReason) {
+ private boolean updateOomAdjLSP(ProcessRecord app, @OomAdjReason int oomAdjReason) {
if (app == null || !mConstants.OOMADJ_UPDATE_QUICK) {
updateOomAdjLSP(oomAdjReason);
return true;
@@ -487,10 +568,10 @@ public class OomAdjuster {
}
@GuardedBy({"mService", "mProcLock"})
- private boolean performUpdateOomAdjLSP(ProcessRecord app, String oomAdjReason) {
+ private boolean performUpdateOomAdjLSP(ProcessRecord app, @OomAdjReason int oomAdjReason) {
final ProcessRecord topApp = mService.getTopApp();
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReason);
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
mService.mOomAdjProfiler.oomAdjStarted();
mAdjSeq++;
@@ -509,6 +590,7 @@ public class OomAdjuster {
state.setCurBoundByNonBgRestrictedApp(false);
// Check if this process is in the pending list too, remove from pending list if so.
mPendingProcessSet.remove(app);
+ app.mOptRecord.setLastOomAdjChangeReason(oomAdjReason);
boolean success = performUpdateOomAdjLSP(app, cachedAdj, topApp,
SystemClock.uptimeMillis());
// The 'app' here itself might or might not be in the cycle, for example,
@@ -688,7 +770,7 @@ public class OomAdjuster {
* {@link #enqueueOomAdjTargetLocked}.
*/
@GuardedBy("mService")
- void updateOomAdjPendingTargetsLocked(String oomAdjReason) {
+ void updateOomAdjPendingTargetsLocked(@OomAdjReason int oomAdjReason) {
// First check if there is pending full update
if (mPendingFullOomAdjUpdate) {
mPendingFullOomAdjUpdate = false;
@@ -716,10 +798,11 @@ public class OomAdjuster {
}
@GuardedBy("mService")
- private void performUpdateOomAdjPendingTargetsLocked(String oomAdjReason) {
+ private void performUpdateOomAdjPendingTargetsLocked(
+ @OomAdjuster.OomAdjReason int oomAdjReason) {
final ProcessRecord topApp = mService.getTopApp();
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReason);
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
mService.mOomAdjProfiler.oomAdjStarted();
final ArrayList<ProcessRecord> processes = mTmpProcessList;
@@ -741,11 +824,11 @@ public class OomAdjuster {
* get evaluated recursively here.
*/
@GuardedBy({"mService", "mProcLock"})
- private void updateOomAdjInnerLSP(String oomAdjReason, final ProcessRecord topApp,
+ private void updateOomAdjInnerLSP(@OomAdjReason int oomAdjReason, final ProcessRecord topApp,
ArrayList<ProcessRecord> processes, ActiveUids uids, boolean potentialCycles,
boolean startProfiling) {
if (startProfiling) {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReason);
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
mService.mOomAdjProfiler.oomAdjStarted();
}
final long now = SystemClock.uptimeMillis();
@@ -806,6 +889,7 @@ public class OomAdjuster {
final ProcessStateRecord state = app.mState;
if (!app.isKilledByAm() && app.getThread() != null) {
state.setProcStateChanged(false);
+ app.mOptRecord.setLastOomAdjChangeReason(oomAdjReason);
computeOomAdjLSP(app, ProcessList.UNKNOWN_ADJ, topApp, fullUpdate, now, false,
computeClients); // It won't enter cycle if not computing clients.
// if any app encountered a cycle, we need to perform an additional loop later
@@ -2582,11 +2666,13 @@ public class OomAdjuster {
// processing of the requests. As a result, there is throttling both here
// and in CachedAppOptimizer.
&& mCachedAppOptimizer.shouldCompactPersistent(app, now)) {
- mCachedAppOptimizer.compactAppPersistent(app);
+ mCachedAppOptimizer.compactApp(app, CachedAppOptimizer.CompactProfile.FULL,
+ CachedAppOptimizer.CompactSource.PERSISTENT, false);
} else if (state.getCurProcState()
== ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
&& mCachedAppOptimizer.shouldCompactBFGS(app, now)) {
- mCachedAppOptimizer.compactAppBfgs(app);
+ mCachedAppOptimizer.compactApp(app, CachedAppOptimizer.CompactProfile.FULL,
+ CachedAppOptimizer.CompactSource.BFGS, false);
}
}
}
diff --git a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
index a613729b441f..fb41a39fa2bd 100644
--- a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
+++ b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
@@ -39,16 +39,24 @@ final class ProcessCachedOptimizerRecord {
private long mLastCompactTime;
/**
- * The most recent compaction action requested for this app.
+ * The most recent compaction profile requested for this app.
*/
- @GuardedBy("mProcLock")
- private int mReqCompactAction;
+ @GuardedBy("mProcLock") private CachedAppOptimizer.CompactProfile mReqCompactProfile;
+
+ /**
+ * Source that requested the latest compaction for this app.
+ */
+ @GuardedBy("mProcLock") private CachedAppOptimizer.CompactSource mReqCompactSource;
+
+ /**
+ * Last oom adjust change reason for this app.
+ */
+ @GuardedBy("mProcLock") private @OomAdjuster.OomAdjReason int mLastOomAdjChangeReason;
/**
* The most recent compaction action performed for this app.
*/
- @GuardedBy("mProcLock")
- private int mLastCompactAction;
+ @GuardedBy("mProcLock") private CachedAppOptimizer.CompactProfile mLastCompactProfile;
/**
* This process has been scheduled for a memory compaction.
@@ -105,23 +113,49 @@ final class ProcessCachedOptimizerRecord {
}
@GuardedBy("mProcLock")
- int getReqCompactAction() {
- return mReqCompactAction;
+ CachedAppOptimizer.CompactProfile getReqCompactProfile() {
+ return mReqCompactProfile;
+ }
+
+ @GuardedBy("mProcLock")
+ void setReqCompactProfile(CachedAppOptimizer.CompactProfile reqCompactProfile) {
+ mReqCompactProfile = reqCompactProfile;
}
@GuardedBy("mProcLock")
- void setReqCompactAction(int reqCompactAction) {
- mReqCompactAction = reqCompactAction;
+ CachedAppOptimizer.CompactSource getReqCompactSource() {
+ return mReqCompactSource;
}
@GuardedBy("mProcLock")
- int getLastCompactAction() {
- return mLastCompactAction;
+ void setReqCompactSource(CachedAppOptimizer.CompactSource stat) {
+ mReqCompactSource = stat;
+ }
+
+ @GuardedBy("mProcLock")
+ void setLastOomAdjChangeReason(@OomAdjuster.OomAdjReason int reason) {
+ mLastOomAdjChangeReason = reason;
+ }
+
+ @GuardedBy("mProcLock")
+ @OomAdjuster.OomAdjReason
+ int getLastOomAdjChangeReason() {
+ return mLastOomAdjChangeReason;
+ }
+
+ @GuardedBy("mProcLock")
+ CachedAppOptimizer.CompactProfile getLastCompactProfile() {
+ if (mLastCompactProfile == null) {
+ // The first compaction won't have a previous one, so assign one to avoid crashing.
+ mLastCompactProfile = CachedAppOptimizer.CompactProfile.SOME;
+ }
+
+ return mLastCompactProfile;
}
@GuardedBy("mProcLock")
- void setLastCompactAction(int lastCompactAction) {
- mLastCompactAction = lastCompactAction;
+ void setLastCompactProfile(CachedAppOptimizer.CompactProfile lastCompactProfile) {
+ mLastCompactProfile = lastCompactProfile;
}
@GuardedBy("mProcLock")
@@ -216,7 +250,8 @@ final class ProcessCachedOptimizerRecord {
@GuardedBy("mProcLock")
void dump(PrintWriter pw, String prefix, long nowUptime) {
pw.print(prefix); pw.print("lastCompactTime="); pw.print(mLastCompactTime);
- pw.print(" lastCompactAction="); pw.println(mLastCompactAction);
+ pw.print(" lastCompactProfile=");
+ pw.println(mLastCompactProfile);
pw.print(prefix);
pw.print("hasPendingCompaction=");
pw.print(mPendingCompact);
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index e4224edac790..0c0ae7d8f934 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1872,15 +1872,21 @@ class UserController implements Handler.Callback {
/** Called on handler thread */
@VisibleForTesting
void dispatchUserSwitchComplete(@UserIdInt int userId) {
+ final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+ t.traceBegin("dispatchUserSwitchComplete-" + userId);
mInjector.getWindowManager().setSwitchingUser(false);
final int observerCount = mUserSwitchObservers.beginBroadcast();
for (int i = 0; i < observerCount; i++) {
try {
+ t.traceBegin("onUserSwitchComplete-" + userId + " #" + i + " "
+ + mUserSwitchObservers.getBroadcastCookie(i));
mUserSwitchObservers.getBroadcastItem(i).onUserSwitchComplete(userId);
+ t.traceEnd();
} catch (RemoteException e) {
}
}
mUserSwitchObservers.finishBroadcast();
+ t.traceEnd();
}
private void dispatchLockedBootComplete(@UserIdInt int userId) {
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index dd73cbed06d9..854b818c095f 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -25,20 +25,6 @@ import static com.android.internal.R.styleable.GameModeConfig_allowGameDownscali
import static com.android.internal.R.styleable.GameModeConfig_allowGameFpsOverride;
import static com.android.internal.R.styleable.GameModeConfig_supportsBatteryGameMode;
import static com.android.internal.R.styleable.GameModeConfig_supportsPerformanceGameMode;
-import static com.android.server.wm.CompatModePackages.DOWNSCALED;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_30;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_35;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_40;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_45;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_50;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_55;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_60;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_65;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_70;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_75;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_80;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_85;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_90;
import android.Manifest;
import android.annotation.NonNull;
@@ -48,10 +34,10 @@ import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.GameManager;
import android.app.GameManager.GameMode;
+import android.app.GameManagerInternal;
import android.app.GameModeInfo;
import android.app.GameState;
import android.app.IGameManagerService;
-import android.app.compat.PackageOverride;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -75,9 +61,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.PowerManagerInternal;
import android.os.Process;
-import android.os.RemoteException;
import android.os.ResultReceiver;
-import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.UserManager;
import android.provider.DeviceConfig;
@@ -92,8 +76,6 @@ import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.compat.CompatibilityOverrideConfig;
-import com.android.internal.compat.IPlatformCompat;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
@@ -140,10 +122,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
static final int WRITE_SETTINGS_DELAY = 10 * 1000; // 10 seconds
static final int LOADING_BOOST_MAX_DURATION = 5 * 1000; // 5 seconds
- static final PackageOverride COMPAT_ENABLED = new PackageOverride.Builder().setEnabled(true)
- .build();
- static final PackageOverride COMPAT_DISABLED = new PackageOverride.Builder().setEnabled(false)
- .build();
private static final String PACKAGE_NAME_MSG_KEY = "packageName";
private static final String USER_ID_MSG_KEY = "userId";
private static final String GAME_MODE_INTERVENTION_LIST_FILE_NAME =
@@ -156,7 +134,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
private final Handler mHandler;
private final PackageManager mPackageManager;
private final UserManager mUserManager;
- private final IPlatformCompat mPlatformCompat;
private final PowerManagerInternal mPowerManagerInternal;
private final File mSystemDir;
@VisibleForTesting
@@ -180,8 +157,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
mHandler = new SettingsHandler(looper);
mPackageManager = mContext.getPackageManager();
mUserManager = mContext.getSystemService(UserManager.class);
- mPlatformCompat = IPlatformCompat.Stub.asInterface(
- ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
mSystemDir = new File(Environment.getDataDirectory(), "system");
mSystemDir.mkdirs();
@@ -212,8 +187,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
mHandler = new SettingsHandler(looper);
mPackageManager = mContext.getPackageManager();
mUserManager = mContext.getSystemService(UserManager.class);
- mPlatformCompat = IPlatformCompat.Stub.asInterface(
- ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
mSystemDir = new File(dataDir, "system");
mSystemDir.mkdirs();
@@ -326,14 +299,10 @@ public final class GameManagerService extends IGameManagerService.Stub {
break;
}
case POPULATE_GAME_MODE_SETTINGS: {
- // Scan all game packages and re-enforce the configured compat mode overrides
- // as the DeviceConfig may have be wiped/since last reboot and we can't risk
- // having overrides configured for packages that no longer have any DeviceConfig
- // and thus any way to escape compat mode.
removeMessages(POPULATE_GAME_MODE_SETTINGS, msg.obj);
final int userId = (int) msg.obj;
final String[] packageNames = getInstalledGamePackageNames(userId);
- updateConfigsForUser(userId, packageNames);
+ updateConfigsForUser(userId, false /*checkGamePackage*/, packageNames);
break;
}
case SET_GAME_STATE: {
@@ -402,7 +371,8 @@ public final class GameManagerService extends IGameManagerService.Stub {
@Override
public void onPropertiesChanged(Properties properties) {
final String[] packageNames = properties.getKeyset().toArray(new String[0]);
- updateConfigsForUser(ActivityManager.getCurrentUser(), packageNames);
+ updateConfigsForUser(ActivityManager.getCurrentUser(), true /*checkGamePackage*/,
+ packageNames);
}
@Override
@@ -411,39 +381,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
}
- // Turn the raw string to the corresponding CompatChange id.
- static long getCompatChangeId(String raw) {
- switch (raw) {
- case "0.3":
- return DOWNSCALE_30;
- case "0.35":
- return DOWNSCALE_35;
- case "0.4":
- return DOWNSCALE_40;
- case "0.45":
- return DOWNSCALE_45;
- case "0.5":
- return DOWNSCALE_50;
- case "0.55":
- return DOWNSCALE_55;
- case "0.6":
- return DOWNSCALE_60;
- case "0.65":
- return DOWNSCALE_65;
- case "0.7":
- return DOWNSCALE_70;
- case "0.75":
- return DOWNSCALE_75;
- case "0.8":
- return DOWNSCALE_80;
- case "0.85":
- return DOWNSCALE_85;
- case "0.9":
- return DOWNSCALE_90;
- }
- return 0;
- }
-
public enum FrameRate {
FPS_DEFAULT(0),
FPS_30(30),
@@ -553,7 +490,9 @@ public final class GameManagerService extends IGameManagerService.Stub {
private static final String GAME_MODE_CONFIG_NODE_NAME = "game-mode-config";
private final String mPackageName;
- private final ArrayMap<Integer, GameModeConfiguration> mModeConfigs;
+ private final Object mModeConfigLock = new Object();
+ @GuardedBy("mModeConfigLock")
+ private final ArrayMap<Integer, GameModeConfiguration> mModeConfigs = new ArrayMap<>();
private boolean mPerfModeOptedIn;
private boolean mBatteryModeOptedIn;
private boolean mAllowDownscale;
@@ -562,7 +501,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
GamePackageConfiguration(String packageName, int userId) {
mPackageName = packageName;
- mModeConfigs = new ArrayMap<>();
try {
final ApplicationInfo ai = mPackageManager.getApplicationInfoAsUser(packageName,
PackageManager.GET_META_DATA, userId);
@@ -647,6 +585,13 @@ public final class GameManagerService extends IGameManagerService.Stub {
return xmlFound;
}
+ GameModeConfiguration getOrAddDefaultGameModeConfiguration(int gameMode) {
+ synchronized (mModeConfigLock) {
+ mModeConfigs.putIfAbsent(gameMode, new GameModeConfiguration(gameMode));
+ return mModeConfigs.get(gameMode);
+ }
+ }
+
/**
* GameModeConfiguration contains all the values for all the interventions associated with
* a game mode.
@@ -657,17 +602,26 @@ public final class GameManagerService extends IGameManagerService.Stub {
public static final String MODE_KEY = "mode";
public static final String SCALING_KEY = "downscaleFactor";
public static final String FPS_KEY = "fps";
- public static final String DEFAULT_SCALING = "1.0";
- public static final String DEFAULT_FPS = "";
public static final String ANGLE_KEY = "useAngle";
public static final String LOADING_BOOST_KEY = "loadingBoost";
+ public static final float DEFAULT_SCALING = -1f;
+ public static final String DEFAULT_FPS = "";
+ public static final boolean DEFAULT_USE_ANGLE = false;
+ public static final int DEFAULT_LOADING_BOOST_DURATION = -1;
+
private final @GameMode int mGameMode;
- private String mScaling;
- private String mFps;
+ private float mScaling = DEFAULT_SCALING;
+ private String mFps = DEFAULT_FPS;
private final boolean mUseAngle;
private final int mLoadingBoostDuration;
+ GameModeConfiguration(int gameMode) {
+ mGameMode = gameMode;
+ mUseAngle = DEFAULT_USE_ANGLE;
+ mLoadingBoostDuration = DEFAULT_LOADING_BOOST_DURATION;
+ }
+
GameModeConfiguration(KeyValueListParser parser) {
mGameMode = parser.getInt(MODE_KEY, GameManager.GAME_MODE_UNSUPPORTED);
// isGameModeOptedIn() returns if an app will handle all of the changes necessary
@@ -675,7 +629,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
// GameManagerService) will not do anything for the app (like window scaling or
// using ANGLE).
mScaling = !mAllowDownscale || willGamePerformOptimizations(mGameMode)
- ? DEFAULT_SCALING : parser.getString(SCALING_KEY, DEFAULT_SCALING);
+ ? DEFAULT_SCALING : parser.getFloat(SCALING_KEY, DEFAULT_SCALING);
mFps = mAllowFpsOverride && !willGamePerformOptimizations(mGameMode)
? parser.getString(FPS_KEY, DEFAULT_FPS) : DEFAULT_FPS;
@@ -684,21 +638,22 @@ public final class GameManagerService extends IGameManagerService.Stub {
// - The app has not opted in to performing the work itself AND
// - The Phenotype config has enabled it.
mUseAngle = mAllowAngle && !willGamePerformOptimizations(mGameMode)
- && parser.getBoolean(ANGLE_KEY, false);
+ && parser.getBoolean(ANGLE_KEY, DEFAULT_USE_ANGLE);
- mLoadingBoostDuration = willGamePerformOptimizations(mGameMode) ? -1
- : parser.getInt(LOADING_BOOST_KEY, -1);
+ mLoadingBoostDuration = willGamePerformOptimizations(mGameMode)
+ ? DEFAULT_LOADING_BOOST_DURATION
+ : parser.getInt(LOADING_BOOST_KEY, DEFAULT_LOADING_BOOST_DURATION);
}
public int getGameMode() {
return mGameMode;
}
- public String getScaling() {
+ public synchronized float getScaling() {
return mScaling;
}
- public int getFps() {
+ public synchronized int getFps() {
return GameManagerService.getFpsInt(mFps);
}
@@ -710,15 +665,15 @@ public final class GameManagerService extends IGameManagerService.Stub {
return mLoadingBoostDuration;
}
- public void setScaling(String scaling) {
+ public synchronized void setScaling(float scaling) {
mScaling = scaling;
}
- public void setFpsStr(String fpsStr) {
+ public synchronized void setFpsStr(String fpsStr) {
mFps = fpsStr;
}
- public boolean isValid() {
+ public boolean isActive() {
return (mGameMode == GameManager.GAME_MODE_STANDARD
|| mGameMode == GameManager.GAME_MODE_PERFORMANCE
|| mGameMode == GameManager.GAME_MODE_BATTERY)
@@ -733,13 +688,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
+ mUseAngle + ",Fps:" + mFps + ",Loading Boost Duration:"
+ mLoadingBoostDuration + "]";
}
-
- /**
- * Get the corresponding compat change id for the current scaling string.
- */
- public long getCompatChangeId() {
- return GameManagerService.getCompatChangeId(mScaling);
- }
}
public String getPackageName() {
@@ -761,8 +709,10 @@ public final class GameManagerService extends IGameManagerService.Stub {
private int getAvailableGameModesBitfield() {
int field = 0;
- for (final int mode : mModeConfigs.keySet()) {
- field |= modeToBitmask(mode);
+ synchronized (mModeConfigLock) {
+ for (final int mode : mModeConfigs.keySet()) {
+ field |= modeToBitmask(mode);
+ }
}
if (mBatteryModeOptedIn) {
field |= modeToBitmask(GameManager.GAME_MODE_BATTERY);
@@ -803,27 +753,43 @@ public final class GameManagerService extends IGameManagerService.Stub {
* @return The package's GameModeConfiguration for the provided mode or null if absent
*/
public GameModeConfiguration getGameModeConfiguration(@GameMode int gameMode) {
- return mModeConfigs.get(gameMode);
+ synchronized (mModeConfigLock) {
+ return mModeConfigs.get(gameMode);
+ }
}
/**
- * Insert a new GameModeConfiguration
+ * Inserts a new GameModeConfiguration
*/
public void addModeConfig(GameModeConfiguration config) {
- if (config.isValid()) {
- mModeConfigs.put(config.getGameMode(), config);
+ if (config.isActive()) {
+ synchronized (mModeConfigLock) {
+ mModeConfigs.put(config.getGameMode(), config);
+ }
} else {
- Slog.w(TAG, "Invalid game mode config for "
+ Slog.w(TAG, "Attempt to add inactive game mode config for "
+ mPackageName + ":" + config.toString());
}
}
- public boolean isValid() {
- return mModeConfigs.size() > 0 || mBatteryModeOptedIn || mPerfModeOptedIn;
+ public boolean isActive() {
+ synchronized (mModeConfigLock) {
+ return mModeConfigs.size() > 0 || mBatteryModeOptedIn || mPerfModeOptedIn;
+ }
}
public String toString() {
- return "[Name:" + mPackageName + " Modes: " + mModeConfigs.toString() + "]";
+ synchronized (mModeConfigLock) {
+ return "[Name:" + mPackageName + " Modes: " + mModeConfigs.toString() + "]";
+ }
+ }
+ }
+
+ private final class LocalService extends GameManagerInternal {
+ @Override
+ public float getResolutionScalingFactor(String packageName, int userId) {
+ final int gameMode = getGameModeFromSettings(packageName, userId);
+ return getResolutionScalingFactorInternal(packageName, gameMode, userId);
}
}
@@ -837,13 +803,13 @@ public final class GameManagerService extends IGameManagerService.Stub {
public Lifecycle(Context context) {
super(context);
+ mService = new GameManagerService(context);
}
@Override
public void onStart() {
- final Context context = getContext();
- mService = new GameManagerService(context);
publishBinderService(Context.GAME_SERVICE, mService);
+ mService.publishLocalService();
mService.registerDeviceConfigListener();
mService.registerPackageReceiver();
}
@@ -894,15 +860,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
private @GameMode int[] getAvailableGameModesUnchecked(String packageName) {
- GamePackageConfiguration config = null;
- synchronized (mOverrideConfigLock) {
- config = mOverrideConfigs.get(packageName);
- }
- if (config == null) {
- synchronized (mDeviceConfigLock) {
- config = mConfigs.get(packageName);
- }
- }
+ final GamePackageConfiguration config = getConfig(packageName);
if (config == null) {
return new int[]{};
}
@@ -933,8 +891,8 @@ public final class GameManagerService extends IGameManagerService.Stub {
private @GameMode int getGameModeFromSettings(String packageName, @UserIdInt int userId) {
synchronized (mLock) {
if (!mSettings.containsKey(userId)) {
- Slog.w(TAG, "User ID '" + userId + "' does not have a Game Mode"
- + " selected for package: '" + packageName + "'");
+ Slog.d(TAG, "User ID '" + userId + "' does not have a Game Mode"
+ + " selected for package: '" + packageName + "'");
return GameManager.GAME_MODE_UNSUPPORTED;
}
@@ -1055,19 +1013,19 @@ public final class GameManagerService extends IGameManagerService.Stub {
if (gameMode == GameManager.GAME_MODE_UNSUPPORTED) {
return false;
}
-
+ final GamePackageConfiguration config;
synchronized (mDeviceConfigLock) {
- final GamePackageConfiguration config = mConfigs.get(packageName);
+ config = mConfigs.get(packageName);
if (config == null) {
return false;
}
- GamePackageConfiguration.GameModeConfiguration gameModeConfiguration =
- config.getGameModeConfiguration(gameMode);
- if (gameModeConfiguration == null) {
- return false;
- }
- return gameModeConfiguration.getUseAngle();
}
+ GamePackageConfiguration.GameModeConfiguration gameModeConfiguration =
+ config.getGameModeConfiguration(gameMode);
+ if (gameModeConfiguration == null) {
+ return false;
+ }
+ return gameModeConfiguration.getUseAngle();
}
/**
@@ -1082,19 +1040,19 @@ public final class GameManagerService extends IGameManagerService.Stub {
if (gameMode == GameManager.GAME_MODE_UNSUPPORTED) {
return -1;
}
-
+ final GamePackageConfiguration config;
synchronized (mDeviceConfigLock) {
- final GamePackageConfiguration config = mConfigs.get(packageName);
- if (config == null) {
- return -1;
- }
- GamePackageConfiguration.GameModeConfiguration gameModeConfiguration =
- config.getGameModeConfiguration(gameMode);
- if (gameModeConfiguration == null) {
- return -1;
- }
- return gameModeConfiguration.getLoadingBoostDuration();
+ config = mConfigs.get(packageName);
+ }
+ if (config == null) {
+ return -1;
}
+ GamePackageConfiguration.GameModeConfiguration gameModeConfiguration =
+ config.getGameModeConfiguration(gameMode);
+ if (gameModeConfiguration == null) {
+ return -1;
+ }
+ return gameModeConfiguration.getLoadingBoostDuration();
}
/**
@@ -1159,6 +1117,66 @@ public final class GameManagerService extends IGameManagerService.Stub {
mGameServiceController.setGameServiceProvider(packageName);
}
+
+ /**
+ * Updates the resolution scaling factor for the package's target game mode and activates it.
+ *
+ * @param scalingFactor enable scaling override over any other compat scaling if positive,
+ * or disable the override otherwise
+ * @throws SecurityException if caller doesn't have
+ * {@link android.Manifest.permission#MANAGE_GAME_MODE}
+ * permission.
+ * @throws IllegalArgumentException if the user ID provided doesn't exist.
+ */
+ @Override
+ @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
+ public void updateResolutionScalingFactor(String packageName, int gameMode, float scalingFactor,
+ int userId) throws SecurityException, IllegalArgumentException {
+ checkPermission(Manifest.permission.MANAGE_GAME_MODE);
+ synchronized (mLock) {
+ if (!mSettings.containsKey(userId)) {
+ throw new IllegalArgumentException("User " + userId + " wasn't started");
+ }
+ }
+ setGameModeConfigOverride(packageName, userId, gameMode, null /*fpsStr*/,
+ Float.toString(scalingFactor));
+ }
+
+ /**
+ * Gets the resolution scaling factor for the package's target game mode.
+ *
+ * @return scaling factor for the game mode if exists or negative value otherwise.
+ * @throws SecurityException if caller doesn't have
+ * {@link android.Manifest.permission#MANAGE_GAME_MODE}
+ * permission.
+ * @throws IllegalArgumentException if the user ID provided doesn't exist.
+ */
+ @Override
+ @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
+ public float getResolutionScalingFactor(String packageName, int gameMode, int userId)
+ throws SecurityException, IllegalArgumentException {
+ checkPermission(Manifest.permission.MANAGE_GAME_MODE);
+ synchronized (mLock) {
+ if (!mSettings.containsKey(userId)) {
+ throw new IllegalArgumentException("User " + userId + " wasn't started");
+ }
+ }
+ return getResolutionScalingFactorInternal(packageName, gameMode, userId);
+ }
+
+ float getResolutionScalingFactorInternal(String packageName, int gameMode, int userId) {
+ final GamePackageConfiguration packageConfig = getConfig(packageName);
+ if (packageConfig == null) {
+ return GamePackageConfiguration.GameModeConfiguration.DEFAULT_SCALING;
+ }
+ final GamePackageConfiguration.GameModeConfiguration modeConfig =
+ packageConfig.getGameModeConfiguration(gameMode);
+ if (modeConfig != null) {
+ return modeConfig.getScaling();
+ }
+ return GamePackageConfiguration.GameModeConfiguration.DEFAULT_SCALING;
+ }
+
/**
* Notified when boot is completed.
*/
@@ -1235,28 +1253,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
/**
- * @hide
- */
- @VisibleForTesting
- public void disableCompatScale(String packageName) {
- final long uid = Binder.clearCallingIdentity();
- try {
- Slog.i(TAG, "Disabling downscale for " + packageName);
- final ArrayMap<Long, PackageOverride> overrides = new ArrayMap<>();
- overrides.put(DOWNSCALED, COMPAT_DISABLED);
- final CompatibilityOverrideConfig changeConfig = new CompatibilityOverrideConfig(
- overrides);
- try {
- mPlatformCompat.putOverridesOnReleaseBuilds(changeConfig, packageName);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to call IPlatformCompat#putOverridesOnReleaseBuilds", e);
- }
- } finally {
- Binder.restoreCallingIdentity(uid);
- }
- }
-
- /**
* Remove frame rate override due to mode switch
*/
private void resetFps(String packageName, @UserIdInt int userId) {
@@ -1269,60 +1265,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
}
- private void enableCompatScale(String packageName, long scaleId) {
- final long uid = Binder.clearCallingIdentity();
- try {
- Slog.i(TAG, "Enabling downscale: " + scaleId + " for " + packageName);
- final ArrayMap<Long, PackageOverride> overrides = new ArrayMap<>();
- overrides.put(DOWNSCALED, COMPAT_ENABLED);
- overrides.put(DOWNSCALE_30, COMPAT_DISABLED);
- overrides.put(DOWNSCALE_35, COMPAT_DISABLED);
- overrides.put(DOWNSCALE_40, COMPAT_DISABLED);
- overrides.put(DOWNSCALE_45, COMPAT_DISABLED);
- overrides.put(DOWNSCALE_50, COMPAT_DISABLED);
- overrides.put(DOWNSCALE_55, COMPAT_DISABLED);
- overrides.put(DOWNSCALE_60, COMPAT_DISABLED);
- overrides.put(DOWNSCALE_65, COMPAT_DISABLED);
- overrides.put(DOWNSCALE_70, COMPAT_DISABLED);
- overrides.put(DOWNSCALE_75, COMPAT_DISABLED);
- overrides.put(DOWNSCALE_80, COMPAT_DISABLED);
- overrides.put(DOWNSCALE_85, COMPAT_DISABLED);
- overrides.put(DOWNSCALE_90, COMPAT_DISABLED);
- overrides.put(scaleId, COMPAT_ENABLED);
- final CompatibilityOverrideConfig changeConfig = new CompatibilityOverrideConfig(
- overrides);
- try {
- mPlatformCompat.putOverridesOnReleaseBuilds(changeConfig, packageName);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to call IPlatformCompat#putOverridesOnReleaseBuilds", e);
- }
- } finally {
- Binder.restoreCallingIdentity(uid);
- }
- }
-
- private void updateCompatModeDownscale(GamePackageConfiguration packageConfig,
- String packageName, @GameMode int gameMode) {
-
- if (DEBUG) {
- Slog.v(TAG, dumpDeviceConfigs());
- }
- final GamePackageConfiguration.GameModeConfiguration modeConfig =
- packageConfig.getGameModeConfiguration(gameMode);
- if (modeConfig == null) {
- Slog.i(TAG, "Game mode " + gameMode + " not found for " + packageName);
- return;
- }
- long scaleId = modeConfig.getCompatChangeId();
- if (scaleId == 0) {
- Slog.w(TAG, "Invalid downscaling change id " + scaleId + " for "
- + packageName);
- return;
- }
-
- enableCompatScale(packageName, scaleId);
- }
-
private int modeToBitmask(@GameMode int gameMode) {
return (1 << gameMode);
}
@@ -1360,31 +1302,17 @@ public final class GameManagerService extends IGameManagerService.Stub {
@GameMode int gameMode, @UserIdInt int userId) {
if (gameMode == GameManager.GAME_MODE_STANDARD
|| gameMode == GameManager.GAME_MODE_UNSUPPORTED) {
- disableCompatScale(packageName);
resetFps(packageName, userId);
return;
}
- GamePackageConfiguration packageConfig = null;
-
- synchronized (mOverrideConfigLock) {
- packageConfig = mOverrideConfigs.get(packageName);
- }
-
+ final GamePackageConfiguration packageConfig = getConfig(packageName);
if (packageConfig == null) {
- synchronized (mDeviceConfigLock) {
- packageConfig = mConfigs.get(packageName);
- }
- }
-
- if (packageConfig == null) {
- disableCompatScale(packageName);
Slog.v(TAG, "Package configuration not found for " + packageName);
return;
}
if (packageConfig.willGamePerformOptimizations(gameMode)) {
return;
}
- updateCompatModeDownscale(packageConfig, packageName, gameMode);
updateFps(packageConfig, packageName, gameMode, userId);
updateUseAngle(packageName, gameMode);
}
@@ -1404,34 +1332,31 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
}
// Adding override game mode configuration of the given package name
+ GamePackageConfiguration overrideConfig;
synchronized (mOverrideConfigLock) {
// look for the existing override GamePackageConfiguration
- GamePackageConfiguration overrideConfig = mOverrideConfigs.get(packageName);
+ overrideConfig = mOverrideConfigs.get(packageName);
if (overrideConfig == null) {
overrideConfig = new GamePackageConfiguration(packageName, userId);
mOverrideConfigs.put(packageName, overrideConfig);
}
+ }
+ // modify GameModeConfiguration intervention settings
+ GamePackageConfiguration.GameModeConfiguration overrideModeConfig =
+ overrideConfig.getOrAddDefaultGameModeConfiguration(gameMode);
- // modify GameModeConfiguration intervention settings
- GamePackageConfiguration.GameModeConfiguration overrideModeConfig =
- overrideConfig.getGameModeConfiguration(gameMode);
-
- if (fpsStr != null) {
- overrideModeConfig.setFpsStr(fpsStr);
- } else {
- overrideModeConfig.setFpsStr(
- GamePackageConfiguration.GameModeConfiguration.DEFAULT_FPS);
- }
- if (scaling != null) {
- overrideModeConfig.setScaling(scaling);
- } else {
- overrideModeConfig.setScaling(
- GamePackageConfiguration.GameModeConfiguration.DEFAULT_SCALING);
- }
- Slog.i(TAG, "Package Name: " + packageName
- + " FPS: " + String.valueOf(overrideModeConfig.getFps())
- + " Scaling: " + overrideModeConfig.getScaling());
+ if (fpsStr != null) {
+ overrideModeConfig.setFpsStr(fpsStr);
+ } else {
+ overrideModeConfig.setFpsStr(
+ GamePackageConfiguration.GameModeConfiguration.DEFAULT_FPS);
}
+ if (scaling != null) {
+ overrideModeConfig.setScaling(Float.parseFloat(scaling));
+ }
+ Slog.i(TAG, "Package Name: " + packageName
+ + " FPS: " + String.valueOf(overrideModeConfig.getFps())
+ + " Scaling: " + overrideModeConfig.getScaling());
setGameMode(packageName, gameMode, userId);
}
@@ -1477,7 +1402,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
// If the game mode to reset is the only mode other than standard mode,
- // The override config is removed.
+ // the override config is removed.
if (modes.length <= 2) {
synchronized (mOverrideConfigLock) {
mOverrideConfigs.remove(packageName);
@@ -1497,15 +1422,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
// If not, set the game mode to standard
int gameMode = getGameMode(packageName, userId);
- GamePackageConfiguration config = null;
- synchronized (mOverrideConfigLock) {
- config = mOverrideConfigs.get(packageName);
- }
- if (config == null) {
- synchronized (mDeviceConfigLock) {
- config = mConfigs.get(packageName);
- }
- }
+ final GamePackageConfiguration config = getConfig(packageName);
final int newGameMode = getNewGameMode(gameMode, config);
if (gameMode != newGameMode) {
setGameMode(packageName, GameManager.GAME_MODE_STANDARD, userId);
@@ -1544,18 +1461,8 @@ public final class GameManagerService extends IGameManagerService.Stub {
* Returns the string listing all the interventions currently set to a game.
*/
public String getInterventionList(String packageName) {
- GamePackageConfiguration packageConfig = null;
- synchronized (mOverrideConfigLock) {
- packageConfig = mOverrideConfigs.get(packageName);
- }
-
- if (packageConfig == null) {
- synchronized (mDeviceConfigLock) {
- packageConfig = mConfigs.get(packageName);
- }
- }
-
- StringBuilder listStrSb = new StringBuilder();
+ final GamePackageConfiguration packageConfig = getConfig(packageName);
+ final StringBuilder listStrSb = new StringBuilder();
if (packageConfig == null) {
listStrSb.append("\n No intervention found for package ")
.append(packageName);
@@ -1570,20 +1477,27 @@ public final class GameManagerService extends IGameManagerService.Stub {
* @hide
*/
@VisibleForTesting
- void updateConfigsForUser(@UserIdInt int userId, String... packageNames) {
+ void updateConfigsForUser(@UserIdInt int userId, boolean checkGamePackage,
+ String... packageNames) {
+ if (checkGamePackage) {
+ packageNames = Arrays.stream(packageNames).filter(
+ p -> isPackageGame(p, userId)).toArray(String[]::new);
+ }
try {
synchronized (mDeviceConfigLock) {
for (final String packageName : packageNames) {
final GamePackageConfiguration config =
new GamePackageConfiguration(packageName, userId);
- if (config.isValid()) {
+ if (config.isActive()) {
if (DEBUG) {
Slog.i(TAG, "Adding config: " + config.toString());
}
mConfigs.put(packageName, config);
} else {
- Slog.w(TAG, "Invalid package config for "
- + config.getPackageName() + ":" + config.toString());
+ if (DEBUG) {
+ Slog.w(TAG, "Inactive package config for "
+ + config.getPackageName() + ":" + config.toString());
+ }
mConfigs.remove(packageName);
}
}
@@ -1613,7 +1527,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
}
} catch (Exception e) {
- Slog.e(TAG, "Failed to update compat modes for user " + userId + ": " + e);
+ Slog.e(TAG, "Failed to update configs for user " + userId + ": " + e);
}
final Message msg = mHandler.obtainMessage(WRITE_GAME_MODE_INTERVENTION_LIST_FILE);
@@ -1664,7 +1578,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
final int useAngle = gameModeConfiguration.getUseAngle() ? 1 : 0;
sb.append(TextUtils.formatSimple("angle=%d", useAngle));
sb.append(",");
- final String scaling = gameModeConfiguration.getScaling();
+ final float scaling = gameModeConfiguration.getScaling();
sb.append("scaling=");
sb.append(scaling);
sb.append(",");
@@ -1761,17 +1675,9 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
switch (intent.getAction()) {
case ACTION_PACKAGE_ADDED:
- updateConfigsForUser(userId, packageName);
+ updateConfigsForUser(userId, true /*checkGamePackage*/, packageName);
break;
case ACTION_PACKAGE_REMOVED:
- disableCompatScale(packageName);
- // If EXTRA_REPLACING is true, it means there will be an
- // ACTION_PACKAGE_ADDED triggered after this because this
- // is an updated package that gets installed. Hence, disable
- // resolution downscaling effort but avoid removing the server
- // or commandline overriding configurations because those will
- // not change but the package game mode configurations may change
- // which may opt in and/or opt out some game mode configurations.
if (!intent.getBooleanExtra(EXTRA_REPLACING, false)) {
synchronized (mOverrideConfigLock) {
mOverrideConfigs.remove(packageName);
@@ -1803,6 +1709,10 @@ public final class GameManagerService extends IGameManagerService.Stub {
mDeviceConfigListener = new DeviceConfigListener();
}
+ private void publishLocalService() {
+ LocalServices.addService(GameManagerInternal.class, new LocalService());
+ }
+
private String dumpDeviceConfigs() {
StringBuilder out = new StringBuilder();
for (String key : mConfigs.keySet()) {
diff --git a/services/core/java/com/android/server/app/GameManagerShellCommand.java b/services/core/java/com/android/server/app/GameManagerShellCommand.java
index 470c320dbd7d..487d19aa6aba 100644
--- a/services/core/java/com/android/server/app/GameManagerShellCommand.java
+++ b/services/core/java/com/android/server/app/GameManagerShellCommand.java
@@ -16,21 +16,6 @@
package com.android.server.app;
-import static com.android.server.wm.CompatModePackages.DOWNSCALED;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_30;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_35;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_40;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_45;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_50;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_55;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_60;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_65;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_70;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_75;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_80;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_85;
-import static com.android.server.wm.CompatModePackages.DOWNSCALE_90;
-
import android.app.ActivityManager;
import android.app.GameManager;
import android.app.IGameManagerService;
@@ -39,7 +24,6 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.os.ShellCommand;
-import android.util.ArraySet;
import java.io.PrintWriter;
import java.util.Locale;
@@ -53,23 +37,6 @@ public class GameManagerShellCommand extends ShellCommand {
public GameManagerShellCommand() {}
- private static final ArraySet<Long> DOWNSCALE_CHANGE_IDS = new ArraySet<>(new Long[]{
- DOWNSCALED,
- DOWNSCALE_90,
- DOWNSCALE_85,
- DOWNSCALE_80,
- DOWNSCALE_75,
- DOWNSCALE_70,
- DOWNSCALE_65,
- DOWNSCALE_60,
- DOWNSCALE_55,
- DOWNSCALE_50,
- DOWNSCALE_45,
- DOWNSCALE_40,
- DOWNSCALE_35,
- DOWNSCALE_30,
- });
-
@Override
public int onCommand(String cmd) {
if (cmd == null) {
@@ -212,11 +179,15 @@ public class GameManagerShellCommand extends ShellCommand {
case "--downscale":
if (downscaleRatio == null) {
downscaleRatio = getNextArgRequired();
- if (downscaleRatio != null
- && GameManagerService.getCompatChangeId(downscaleRatio) == 0
- && !downscaleRatio.equals("disable")) {
- pw.println("Invalid scaling ratio '" + downscaleRatio + "'");
- return -1;
+ if ("disable".equals(downscaleRatio)) {
+ downscaleRatio = "-1";
+ } else {
+ try {
+ Float.parseFloat(downscaleRatio);
+ } catch (NumberFormatException e) {
+ pw.println("Invalid scaling ratio '" + downscaleRatio + "'");
+ return -1;
+ }
}
} else {
pw.println("Duplicate option '" + option + "'");
@@ -249,40 +220,16 @@ 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":
- if (perfModeSupported) {
- gameManagerService.setGameModeConfigOverride(packageName, userId,
- GameManager.GAME_MODE_PERFORMANCE, fpsStr, downscaleRatio);
- } else {
- pw.println("Game mode: " + gameMode + " not supported by "
- + packageName);
- return -1;
- }
+ gameManagerService.setGameModeConfigOverride(packageName, userId,
+ GameManager.GAME_MODE_PERFORMANCE, fpsStr, downscaleRatio);
break;
case "3":
case "battery":
- if (batteryModeSupported) {
- gameManagerService.setGameModeConfigOverride(packageName, userId,
- GameManager.GAME_MODE_BATTERY, fpsStr, downscaleRatio);
- } else {
- pw.println("Game mode: " + gameMode + " not supported by "
- + packageName);
- return -1;
- }
+ gameManagerService.setGameModeConfigOverride(packageName, userId,
+ GameManager.GAME_MODE_BATTERY, fpsStr, downscaleRatio);
break;
default:
pw.println("Invalid game mode: " + gameMode);
diff --git a/services/core/java/com/android/server/app/GameTaskInfoProvider.java b/services/core/java/com/android/server/app/GameTaskInfoProvider.java
index f078d98e5950..25a4f377caad 100644
--- a/services/core/java/com/android/server/app/GameTaskInfoProvider.java
+++ b/services/core/java/com/android/server/app/GameTaskInfoProvider.java
@@ -16,6 +16,8 @@
package com.android.server.app;
+import static android.view.Display.INVALID_DISPLAY;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
@@ -93,7 +95,8 @@ final class GameTaskInfoProvider {
runningTaskInfos = mActivityTaskManager.getTasks(
/* maxNum= */ Integer.MAX_VALUE,
/* filterOnlyVisibleRecents= */ false,
- /* keepIntentExtra= */ false);
+ /* keepIntentExtra= */ false,
+ INVALID_DISPLAY);
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to fetch running tasks");
return null;
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 7bef6d520d67..a5bcb0517d25 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -217,7 +217,7 @@ import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Consumer;
-public class AppOpsService extends IAppOpsService.Stub {
+public class AppOpsService extends IAppOpsService.Stub implements PersistenceScheduler {
static final String TAG = "AppOps";
static final boolean DEBUG = false;
@@ -402,6 +402,9 @@ public class AppOpsService extends IAppOpsService.Stub {
/** Package Manager internal. Access via {@link #getPackageManagerInternal()} */
private @Nullable PackageManagerInternal mPackageManagerInternal;
+ /** Interface for app-op modes.*/
+ @VisibleForTesting AppOpsServiceInterface mAppOpsServiceInterface;
+
/**
* An unsynchronized pool of {@link OpEventProxyInfo} objects.
*/
@@ -558,7 +561,6 @@ public class AppOpsService extends IAppOpsService.Stub {
public boolean pendingAppWidgetVisible;
public ArrayMap<String, Ops> pkgOps;
- public SparseIntArray opModes;
// true indicates there is an interested observer, false there isn't but it has such an op
public SparseBooleanArray foregroundOps;
@@ -569,15 +571,43 @@ public class AppOpsService extends IAppOpsService.Stub {
}
public void clear() {
+ mAppOpsServiceInterface.removeUid(uid);
+ if (pkgOps != null) {
+ for (String packageName : pkgOps.keySet()) {
+ mAppOpsServiceInterface.removePackage(packageName);
+ }
+ }
pkgOps = null;
- opModes = null;
}
public boolean isDefault() {
+ boolean areAllPackageModesDefault = true;
+ if (pkgOps != null) {
+ for (String packageName : pkgOps.keySet()) {
+ if (!mAppOpsServiceInterface.arePackageModesDefault(packageName)) {
+ areAllPackageModesDefault = false;
+ break;
+ }
+ }
+ }
return (pkgOps == null || pkgOps.isEmpty())
- && (opModes == null || opModes.size() <= 0)
&& (state == UID_STATE_CACHED
- && (pendingState == UID_STATE_CACHED));
+ && (pendingState == UID_STATE_CACHED))
+ && (mAppOpsServiceInterface.areUidModesDefault(uid)
+ && areAllPackageModesDefault);
+ }
+
+ // Functions for uid mode access and manipulation.
+ public SparseIntArray getNonDefaultUidModes() {
+ return mAppOpsServiceInterface.getNonDefaultUidModes(uid);
+ }
+
+ public int getUidMode(int op) {
+ return mAppOpsServiceInterface.getUidMode(uid, op);
+ }
+
+ public boolean setUidMode(int op, int mode) {
+ return mAppOpsServiceInterface.setUidMode(uid, op, mode);
}
int evalMode(int op, int mode) {
@@ -647,21 +677,20 @@ public class AppOpsService extends IAppOpsService.Stub {
public void evalForegroundOps(SparseArray<ArraySet<ModeCallback>> watchers) {
SparseBooleanArray which = null;
hasForegroundWatchers = false;
- if (opModes != null) {
- for (int i = opModes.size() - 1; i >= 0; i--) {
- if (opModes.valueAt(i) == AppOpsManager.MODE_FOREGROUND) {
- if (which == null) {
- which = new SparseBooleanArray();
- }
- evalForegroundWatchers(opModes.keyAt(i), watchers, which);
+ final SparseIntArray opModes = getNonDefaultUidModes();
+ for (int i = opModes.size() - 1; i >= 0; i--) {
+ if (opModes.valueAt(i) == AppOpsManager.MODE_FOREGROUND) {
+ if (which == null) {
+ which = new SparseBooleanArray();
}
+ evalForegroundWatchers(opModes.keyAt(i), watchers, which);
}
}
if (pkgOps != null) {
for (int i = pkgOps.size() - 1; i >= 0; i--) {
Ops ops = pkgOps.valueAt(i);
for (int j = ops.size() - 1; j >= 0; j--) {
- if (ops.valueAt(j).mode == AppOpsManager.MODE_FOREGROUND) {
+ if (ops.valueAt(j).getMode() == AppOpsManager.MODE_FOREGROUND) {
if (which == null) {
which = new SparseBooleanArray();
}
@@ -1451,8 +1480,6 @@ public class AppOpsService extends IAppOpsService.Stub {
final UidState uidState;
final @NonNull String packageName;
- private @Mode int mode;
-
/** attributionTag -> AttributedOp */
final ArrayMap<String, AttributedOp> mAttributions = new ArrayMap<>(1);
@@ -1461,15 +1488,17 @@ public class AppOpsService extends IAppOpsService.Stub {
this.uid = uid;
this.uidState = uidState;
this.packageName = packageName;
- this.mode = AppOpsManager.opToDefaultMode(op);
}
- int getMode() {
- return mode;
+ @Mode int getMode() {
+ return mAppOpsServiceInterface.getPackageMode(packageName, this.op);
+ }
+ void setMode(@Mode int mode) {
+ mAppOpsServiceInterface.setPackageMode(packageName, this.op, mode);
}
int evalMode() {
- return uidState.evalMode(op, mode);
+ return uidState.evalMode(op, getMode());
}
void removeAttributionsWithNoTime() {
@@ -1503,7 +1532,7 @@ public class AppOpsService extends IAppOpsService.Stub {
mAttributions.valueAt(i).createAttributedOpEntryLocked());
}
- return new OpEntry(op, mode, attributionEntries);
+ return new OpEntry(op, getMode(), attributionEntries);
}
@NonNull OpEntry createSingleAttributionEntryLocked(@Nullable String attributionTag) {
@@ -1518,7 +1547,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
- return new OpEntry(op, mode, attributionEntries);
+ return new OpEntry(op, getMode(), attributionEntries);
}
boolean isRunning() {
@@ -1775,6 +1804,7 @@ public class AppOpsService extends IAppOpsService.Stub {
public AppOpsService(File storagePath, Handler handler, Context context) {
mContext = context;
+ mAppOpsServiceInterface = new LegacyAppOpsServiceInterfaceImpl(this, this);
LockGuard.installLock(this, LockGuard.INDEX_APP_OPS);
mFile = new AtomicFile(storagePath, "appops");
@@ -1815,7 +1845,7 @@ public class AppOpsService extends IAppOpsService.Stub {
if (uidState == null || uidState.pkgOps == null) {
return;
}
-
+ mAppOpsServiceInterface.removePackage(pkgName);
Ops removedOps = uidState.pkgOps.remove(pkgName);
if (removedOps != null) {
scheduleFastWriteLocked();
@@ -2037,25 +2067,27 @@ public class AppOpsService extends IAppOpsService.Stub {
return;
}
- Ops ops = null;
+ Ops removedOps = null;
// Remove any package state if such.
if (uidState.pkgOps != null) {
- ops = uidState.pkgOps.remove(packageName);
+ removedOps = uidState.pkgOps.remove(packageName);
+ mAppOpsServiceInterface.removePackage(packageName);
}
// If we just nuked the last package state check if the UID is valid.
- if (ops != null && uidState.pkgOps.isEmpty()
+ if (removedOps != null && uidState.pkgOps.isEmpty()
&& getPackagesForUid(uid).length <= 0) {
+ uidState.clear();
mUidStates.remove(uid);
}
- if (ops != null) {
+ if (removedOps != null) {
scheduleFastWriteLocked();
- final int numOps = ops.size();
+ final int numOps = removedOps.size();
for (int opNum = 0; opNum < numOps; opNum++) {
- final Op op = ops.valueAt(opNum);
+ final Op op = removedOps.valueAt(opNum);
final int numAttributions = op.mAttributions.size();
for (int attributionNum = 0; attributionNum < numAttributions;
@@ -2080,6 +2112,7 @@ public class AppOpsService extends IAppOpsService.Stub {
public void uidRemoved(int uid) {
synchronized (this) {
if (mUidStates.indexOfKey(uid) >= 0) {
+ mUidStates.get(uid).clear();
mUidStates.remove(uid);
scheduleFastWriteLocked();
}
@@ -2185,12 +2218,11 @@ public class AppOpsService extends IAppOpsService.Stub {
private ArrayList<AppOpsManager.OpEntry> collectOps(Ops pkgOps, int[] ops) {
ArrayList<AppOpsManager.OpEntry> resOps = null;
- final long elapsedNow = SystemClock.elapsedRealtime();
if (ops == null) {
resOps = new ArrayList<>();
for (int j=0; j<pkgOps.size(); j++) {
Op curOp = pkgOps.valueAt(j);
- resOps.add(getOpEntryForResult(curOp, elapsedNow));
+ resOps.add(getOpEntryForResult(curOp));
}
} else {
for (int j=0; j<ops.length; j++) {
@@ -2199,7 +2231,7 @@ public class AppOpsService extends IAppOpsService.Stub {
if (resOps == null) {
resOps = new ArrayList<>();
}
- resOps.add(getOpEntryForResult(curOp, elapsedNow));
+ resOps.add(getOpEntryForResult(curOp));
}
}
}
@@ -2209,11 +2241,12 @@ public class AppOpsService extends IAppOpsService.Stub {
@Nullable
private ArrayList<AppOpsManager.OpEntry> collectUidOps(@NonNull UidState uidState,
@Nullable int[] ops) {
- if (uidState.opModes == null) {
+ final SparseIntArray opModes = uidState.getNonDefaultUidModes();
+ if (opModes == null) {
return null;
}
- int opModeCount = uidState.opModes.size();
+ int opModeCount = opModes.size();
if (opModeCount == 0) {
return null;
}
@@ -2221,25 +2254,24 @@ public class AppOpsService extends IAppOpsService.Stub {
if (ops == null) {
resOps = new ArrayList<>();
for (int i = 0; i < opModeCount; i++) {
- int code = uidState.opModes.keyAt(i);
- resOps.add(new OpEntry(code, uidState.opModes.get(code), Collections.emptyMap()));
+ int code = opModes.keyAt(i);
+ resOps.add(new OpEntry(code, opModes.get(code), Collections.emptyMap()));
}
} else {
for (int j=0; j<ops.length; j++) {
int code = ops[j];
- if (uidState.opModes.indexOfKey(code) >= 0) {
+ if (opModes.indexOfKey(code) >= 0) {
if (resOps == null) {
resOps = new ArrayList<>();
}
- resOps.add(new OpEntry(code, uidState.opModes.get(code),
- Collections.emptyMap()));
+ resOps.add(new OpEntry(code, opModes.get(code), Collections.emptyMap()));
}
}
}
return resOps;
}
- private static @NonNull OpEntry getOpEntryForResult(@NonNull Op op, long elapsedNow) {
+ private static @NonNull OpEntry getOpEntryForResult(@NonNull Op op) {
return op.createEntryLocked();
}
@@ -2484,15 +2516,18 @@ public class AppOpsService extends IAppOpsService.Stub {
Ops ops = getOpsLocked(uid, packageName, null, false, null, /* edit */ false);
if (ops != null) {
ops.remove(op.op);
+ op.setMode(AppOpsManager.opToDefaultMode(op.op));
if (ops.size() <= 0) {
UidState uidState = ops.uidState;
ArrayMap<String, Ops> pkgOps = uidState.pkgOps;
if (pkgOps != null) {
pkgOps.remove(ops.packageName);
+ mAppOpsServiceInterface.removePackage(ops.packageName);
if (pkgOps.isEmpty()) {
uidState.pkgOps = null;
}
if (uidState.isDefault()) {
+ uidState.clear();
mUidStates.remove(uid);
}
}
@@ -2548,33 +2583,18 @@ public class AppOpsService extends IAppOpsService.Stub {
if (mode == defaultMode) {
return;
}
- previousMode = MODE_DEFAULT;
uidState = new UidState(uid);
- uidState.opModes = new SparseIntArray();
- uidState.opModes.put(code, mode);
mUidStates.put(uid, uidState);
- scheduleWriteLocked();
- } else if (uidState.opModes == null) {
- previousMode = MODE_DEFAULT;
- if (mode != defaultMode) {
- uidState.opModes = new SparseIntArray();
- uidState.opModes.put(code, mode);
- scheduleWriteLocked();
- }
+ }
+ if (uidState.getUidMode(code) != AppOpsManager.opToDefaultMode(code)) {
+ previousMode = uidState.getUidMode(code);
} else {
- if (uidState.opModes.indexOfKey(code) >= 0 && uidState.opModes.get(code) == mode) {
- return;
- }
- previousMode = uidState.opModes.get(code);
- if (mode == defaultMode) {
- uidState.opModes.delete(code);
- if (uidState.opModes.size() <= 0) {
- uidState.opModes = null;
- }
- } else {
- uidState.opModes.put(code, mode);
- }
- scheduleWriteLocked();
+ // doesn't look right but is legacy behavior.
+ previousMode = MODE_DEFAULT;
+ }
+
+ if (!uidState.setUidMode(code, mode)) {
+ return;
}
uidState.evalForegroundOps(mOpModeWatchers);
if (mode != MODE_ERRORED && mode != previousMode) {
@@ -2806,9 +2826,10 @@ public class AppOpsService extends IAppOpsService.Stub {
UidState uidState = getUidStateLocked(uid, false);
Op op = getOpLocked(code, uid, packageName, null, false, pvr.bypass, /* edit */ true);
if (op != null) {
- if (op.mode != mode) {
- previousMode = op.mode;
- op.mode = mode;
+ if (op.getMode() != mode) {
+ previousMode = op.getMode();
+ op.setMode(mode);
+
if (uidState != null) {
uidState.evalForegroundOps(mOpModeWatchers);
}
@@ -2976,17 +2997,14 @@ public class AppOpsService extends IAppOpsService.Stub {
for (int i = mUidStates.size() - 1; i >= 0; i--) {
UidState uidState = mUidStates.valueAt(i);
- SparseIntArray opModes = uidState.opModes;
+ SparseIntArray opModes = uidState.getNonDefaultUidModes();
if (opModes != null && (uidState.uid == reqUid || reqUid == -1)) {
final int uidOpCount = opModes.size();
for (int j = uidOpCount - 1; j >= 0; j--) {
final int code = opModes.keyAt(j);
if (AppOpsManager.opAllowsReset(code)) {
int previousMode = opModes.valueAt(j);
- opModes.removeAt(j);
- if (opModes.size() <= 0) {
- uidState.opModes = null;
- }
+ uidState.setUidMode(code, AppOpsManager.opToDefaultMode(code));
for (String packageName : getPackagesForUid(uidState.uid)) {
callbacks = addCallbacks(callbacks, code, uidState.uid, packageName,
previousMode, mOpModeWatchers.get(code));
@@ -3028,9 +3046,9 @@ public class AppOpsService extends IAppOpsService.Stub {
continue;
}
if (AppOpsManager.opAllowsReset(curOp.op)
- && curOp.mode != AppOpsManager.opToDefaultMode(curOp.op)) {
- int previousMode = curOp.mode;
- curOp.mode = AppOpsManager.opToDefaultMode(curOp.op);
+ && curOp.getMode() != AppOpsManager.opToDefaultMode(curOp.op)) {
+ int previousMode = curOp.getMode();
+ curOp.setMode(AppOpsManager.opToDefaultMode(curOp.op));
changed = true;
uidChanged = true;
final int uid = curOp.uidState.uid;
@@ -3049,9 +3067,11 @@ public class AppOpsService extends IAppOpsService.Stub {
}
if (pkgOps.size() == 0) {
it.remove();
+ mAppOpsServiceInterface.removePackage(packageName);
}
}
if (uidState.isDefault()) {
+ uidState.clear();
mUidStates.remove(uidState.uid);
}
if (uidChanged) {
@@ -3267,16 +3287,16 @@ public class AppOpsService extends IAppOpsService.Stub {
}
code = AppOpsManager.opToSwitch(code);
UidState uidState = getUidStateLocked(uid, false);
- if (uidState != null && uidState.opModes != null
- && uidState.opModes.indexOfKey(code) >= 0) {
- final int rawMode = uidState.opModes.get(code);
+ if (uidState != null
+ && uidState.getUidMode(code) != AppOpsManager.opToDefaultMode(code)) {
+ final int rawMode = uidState.getUidMode(code);
return raw ? rawMode : uidState.evalMode(code, rawMode);
}
Op op = getOpLocked(code, uid, packageName, null, false, pvr.bypass, /* edit */ false);
if (op == null) {
return AppOpsManager.opToDefaultMode(code);
}
- return raw ? op.mode : op.evalMode();
+ return raw ? op.getMode() : op.evalMode();
}
}
@@ -3502,8 +3522,8 @@ public class AppOpsService extends IAppOpsService.Stub {
}
// If there is a non-default per UID policy (we set UID op mode only if
// non-default) it takes over, otherwise use the per package policy.
- if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) {
- final int uidMode = uidState.evalMode(code, uidState.opModes.get(switchCode));
+ if (uidState.getUidMode(switchCode) != AppOpsManager.opToDefaultMode(switchCode)) {
+ final int uidMode = uidState.evalMode(code, uidState.getUidMode(switchCode));
if (uidMode != AppOpsManager.MODE_ALLOWED) {
if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
@@ -4018,8 +4038,8 @@ public class AppOpsService extends IAppOpsService.Stub {
final int switchCode = AppOpsManager.opToSwitch(code);
// If there is a non-default per UID policy (we set UID op mode only if
// non-default) it takes over, otherwise use the per package policy.
- if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) {
- final int uidMode = uidState.evalMode(code, uidState.opModes.get(switchCode));
+ if (uidState.getUidMode(switchCode) != AppOpsManager.opToDefaultMode(switchCode)) {
+ final int uidMode = uidState.evalMode(code, uidState.getUidMode(switchCode));
if (!shouldStartForMode(uidMode, startIfModeDefault)) {
if (DEBUG) {
Slog.d(TAG, "startOperation: uid reject #" + uidMode + " for code "
@@ -4516,9 +4536,8 @@ public class AppOpsService extends IAppOpsService.Stub {
continue;
}
- if (uidState.opModes != null
- && uidState.opModes.indexOfKey(code) >= 0
- && uidState.opModes.get(code) == AppOpsManager.MODE_FOREGROUND) {
+ if (uidState.getUidMode(code) != AppOpsManager.opToDefaultMode(code)
+ && uidState.getUidMode(code) == AppOpsManager.MODE_FOREGROUND) {
mHandler.sendMessage(PooledLambda.obtainMessage(
AppOpsService::notifyOpChangedForAllPkgsInUid,
this, code, uidState.uid, true, null));
@@ -4536,7 +4555,7 @@ public class AppOpsService extends IAppOpsService.Stub {
if (op == null) {
continue;
}
- if (op.mode == AppOpsManager.MODE_FOREGROUND) {
+ if (op.getMode() == AppOpsManager.MODE_FOREGROUND) {
mHandler.sendMessage(PooledLambda.obtainMessage(
AppOpsService::notifyOpChanged,
this, callback, code, uidState.uid,
@@ -4811,14 +4830,16 @@ public class AppOpsService extends IAppOpsService.Stub {
return ops;
}
- private void scheduleWriteLocked() {
+ @Override
+ public void scheduleWriteLocked() {
if (!mWriteScheduled) {
mWriteScheduled = true;
mHandler.postDelayed(mWriteRunner, WRITE_DELAY);
}
}
- private void scheduleFastWriteLocked() {
+ @Override
+ public void scheduleFastWriteLocked() {
if (!mFastWriteScheduled) {
mWriteScheduled = true;
mFastWriteScheduled = true;
@@ -4929,6 +4950,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
boolean success = false;
mUidStates.clear();
+ mAppOpsServiceInterface.clearAllModes();
try {
TypedXmlPullParser parser = Xml.resolvePullParser(stream);
int type;
@@ -4977,6 +4999,7 @@ public class AppOpsService extends IAppOpsService.Stub {
} finally {
if (!success) {
mUidStates.clear();
+ mAppOpsServiceInterface.clearAllModes();
}
try {
stream.close();
@@ -4996,11 +5019,12 @@ public class AppOpsService extends IAppOpsService.Stub {
if (uidState == null) {
continue;
}
- if (uidState.opModes != null) {
- final int idx = uidState.opModes.indexOfKey(AppOpsManager.OP_RUN_IN_BACKGROUND);
+ SparseIntArray opModes = uidState.getNonDefaultUidModes();
+ if (opModes != null) {
+ final int idx = opModes.indexOfKey(AppOpsManager.OP_RUN_IN_BACKGROUND);
if (idx >= 0) {
- uidState.opModes.put(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
- uidState.opModes.valueAt(idx));
+ uidState.setUidMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
+ opModes.valueAt(idx));
}
}
if (uidState.pkgOps == null) {
@@ -5011,10 +5035,10 @@ public class AppOpsService extends IAppOpsService.Stub {
Ops ops = uidState.pkgOps.valueAt(j);
if (ops != null) {
final Op op = ops.get(AppOpsManager.OP_RUN_IN_BACKGROUND);
- if (op != null && op.mode != AppOpsManager.opToDefaultMode(op.op)) {
+ if (op != null && op.getMode() != AppOpsManager.opToDefaultMode(op.op)) {
final Op copy = new Op(op.uidState, op.packageName,
AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uidState.uid);
- copy.mode = op.mode;
+ copy.setMode(op.getMode());
ops.put(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, copy);
changed = true;
}
@@ -5142,7 +5166,7 @@ public class AppOpsService extends IAppOpsService.Stub {
Op op = new Op(uidState, pkgName, opCode, uidState.uid);
final int mode = parser.getAttributeInt(null, "m", AppOpsManager.opToDefaultMode(op.op));
- op.mode = mode;
+ op.setMode(mode);
int outerDepth = parser.getDepth();
int type;
@@ -5199,16 +5223,9 @@ public class AppOpsService extends IAppOpsService.Stub {
UidState uidState = mUidStates.valueAt(uidStateNum);
int uid = mUidStates.keyAt(uidStateNum);
- SparseIntArray opModes = uidState.opModes;
+ SparseIntArray opModes = uidState.getNonDefaultUidModes();
if (opModes != null && opModes.size() > 0) {
- uidStatesClone.put(uid, new SparseIntArray(opModes.size()));
-
- final int opCount = opModes.size();
- for (int opCountNum = 0; opCountNum < opCount; opCountNum++) {
- uidStatesClone.get(uid).put(
- opModes.keyAt(opCountNum),
- opModes.valueAt(opCountNum));
- }
+ uidStatesClone.put(uid, opModes);
}
}
}
@@ -6314,15 +6331,15 @@ public class AppOpsService extends IAppOpsService.Stub {
}
for (int i=0; i<mUidStates.size(); i++) {
UidState uidState = mUidStates.valueAt(i);
- final SparseIntArray opModes = uidState.opModes;
+ final SparseIntArray opModes = uidState.getNonDefaultUidModes();
final ArrayMap<String, Ops> pkgOps = uidState.pkgOps;
if (dumpWatchers || dumpHistory) {
continue;
}
if (dumpOp >= 0 || dumpPackage != null || dumpMode >= 0) {
- boolean hasOp = dumpOp < 0 || (uidState.opModes != null
- && uidState.opModes.indexOfKey(dumpOp) >= 0);
+ boolean hasOp = dumpOp < 0 || (opModes != null
+ && opModes.indexOfKey(dumpOp) >= 0);
boolean hasPackage = dumpPackage == null || dumpUid == mUidStates.keyAt(i);
boolean hasMode = dumpMode < 0;
if (!hasMode && opModes != null) {
@@ -6342,7 +6359,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
if (!hasMode) {
for (int opi = 0; !hasMode && opi < ops.size(); opi++) {
- if (ops.valueAt(opi).mode == dumpMode) {
+ if (ops.valueAt(opi).getMode() == dumpMode) {
hasMode = true;
}
}
@@ -6437,7 +6454,7 @@ public class AppOpsService extends IAppOpsService.Stub {
if (dumpOp >= 0 && dumpOp != opCode) {
continue;
}
- if (dumpMode >= 0 && dumpMode != op.mode) {
+ if (dumpMode >= 0 && dumpMode != op.getMode()) {
continue;
}
if (!printedPackage) {
@@ -6445,14 +6462,14 @@ public class AppOpsService extends IAppOpsService.Stub {
printedPackage = true;
}
pw.print(" "); pw.print(AppOpsManager.opToName(opCode));
- pw.print(" ("); pw.print(AppOpsManager.modeToName(op.mode));
+ pw.print(" ("); pw.print(AppOpsManager.modeToName(op.getMode()));
final int switchOp = AppOpsManager.opToSwitch(opCode);
if (switchOp != opCode) {
pw.print(" / switch ");
pw.print(AppOpsManager.opToName(switchOp));
final Op switchObj = ops.get(switchOp);
- int mode = switchObj != null ? switchObj.mode
- : AppOpsManager.opToDefaultMode(switchOp);
+ int mode = switchObj == null
+ ? AppOpsManager.opToDefaultMode(switchOp) : switchObj.getMode();
pw.print("="); pw.print(AppOpsManager.modeToName(mode));
}
pw.println("): ");
@@ -6696,7 +6713,7 @@ public class AppOpsService extends IAppOpsService.Stub {
for (int pkgNum = 0; pkgNum < numPkgOps; pkgNum++) {
Ops ops = uidState.pkgOps.valueAt(pkgNum);
Op op = ops != null ? ops.get(code) : null;
- if (op == null || (op.mode != MODE_ALLOWED && op.mode != MODE_FOREGROUND)) {
+ if (op == null || (op.getMode() != MODE_ALLOWED && op.getMode() != MODE_FOREGROUND)) {
continue;
}
int numAttrTags = op.mAttributions.size();
@@ -6817,6 +6834,7 @@ public class AppOpsService extends IAppOpsService.Stub {
return;
}
Ops removedOps = uidState.pkgOps.remove(packageName);
+ mAppOpsServiceInterface.removePackage(packageName);
if (removedOps != null) {
scheduleFastWriteLocked();
}
@@ -7136,10 +7154,12 @@ public class AppOpsService extends IAppOpsService.Stub {
return false;
}
+ @GuardedBy("this")
private void removeUidsForUserLocked(int userHandle) {
for (int i = mUidStates.size() - 1; i >= 0; --i) {
final int uid = mUidStates.keyAt(i);
if (UserHandle.getUserId(uid) == userHandle) {
+ mUidStates.valueAt(i).clear();
mUidStates.removeAt(i);
}
}
diff --git a/services/core/java/com/android/server/appop/AppOpsServiceInterface.java b/services/core/java/com/android/server/appop/AppOpsServiceInterface.java
new file mode 100644
index 000000000000..cd5ea120f878
--- /dev/null
+++ b/services/core/java/com/android/server/appop/AppOpsServiceInterface.java
@@ -0,0 +1,98 @@
+/*
+ * 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.appop;
+import android.annotation.NonNull;
+import android.app.AppOpsManager.Mode;
+import android.util.SparseIntArray;
+/**
+ * Interface for accessing and modifying modes for app-ops i.e. package and uid modes.
+ * In the future this interface will also include mode callbacks and op restrictions.
+ */
+public interface AppOpsServiceInterface {
+ /**
+ * Returns a copy of non-default app-ops with op as keys and their modes as values for a uid.
+ * Returns an empty SparseIntArray if nothing is set.
+ * @param uid for which we need the app-ops and their modes.
+ */
+ SparseIntArray getNonDefaultUidModes(int uid);
+
+ /**
+ * Returns the app-op mode for a particular app-op of a uid.
+ * Returns default op mode if the op mode for particular uid and op is not set.
+ * @param uid user id for which we need the mode.
+ * @param op app-op for which we need the mode.
+ * @return mode of the app-op.
+ */
+ int getUidMode(int uid, int op);
+
+ /**
+ * Set the app-op mode for a particular uid and op.
+ * The mode is not set if the mode is the same as the default mode for the op.
+ * @param uid user id for which we want to set the mode.
+ * @param op app-op for which we want to set the mode.
+ * @param mode mode for the app-op.
+ * @return true if op mode is changed.
+ */
+ boolean setUidMode(int uid, int op, @Mode int mode);
+
+ /**
+ * Gets the app-op mode for a particular package.
+ * Returns default op mode if the op mode for the particular package is not set.
+ * @param packageName package name for which we need the op mode.
+ * @param op app-op for which we need the mode.
+ * @return the mode of the app-op.
+ */
+ int getPackageMode(@NonNull String packageName, int op);
+
+ /**
+ * Sets the app-op mode for a particular package.
+ * @param packageName package name for which we need to set the op mode.
+ * @param op app-op for which we need to set the mode.
+ * @param mode the mode of the app-op.
+ */
+ void setPackageMode(@NonNull String packageName, int op, @Mode int mode);
+
+ /**
+ * Stop tracking any app-op modes for a package.
+ * @param packageName Name of the package for which we want to remove all mode tracking.
+ */
+ boolean removePackage(@NonNull String packageName);
+
+ /**
+ * Stop tracking any app-op modes for this uid.
+ * @param uid user id for which we want to remove all tracking.
+ */
+ void removeUid(int uid);
+
+ /**
+ * Returns true if all uid modes for this uid are
+ * in default state.
+ * @param uid user id
+ */
+ boolean areUidModesDefault(int uid);
+
+ /**
+ * Returns true if all package modes for this package name are
+ * in default state.
+ * @param packageName package name.
+ */
+ boolean arePackageModesDefault(String packageName);
+
+ /**
+ * Stop tracking app-op modes for all uid and packages.
+ */
+ void clearAllModes();
+}
diff --git a/services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java b/services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java
new file mode 100644
index 000000000000..c27c0d3de5d7
--- /dev/null
+++ b/services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java
@@ -0,0 +1,198 @@
+/*
+ * 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.appop;
+
+import android.annotation.NonNull;
+import android.app.AppOpsManager;
+import android.app.AppOpsManager.Mode;
+import android.util.ArrayMap;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+
+/**
+ * Legacy implementation for App-ops service's app-op mode (uid and package) storage and access.
+ * In the future this class will also include mode callbacks and op restrictions.
+ */
+public class LegacyAppOpsServiceInterfaceImpl implements AppOpsServiceInterface {
+
+ // Should be the same object that the AppOpsService is using for locking.
+ final Object mLock;
+
+ @GuardedBy("mLock")
+ @VisibleForTesting
+ final SparseArray<SparseIntArray> mUidModes = new SparseArray<>();
+
+ @GuardedBy("mLock")
+ final ArrayMap<String, SparseIntArray> mPackageModes = new ArrayMap<>();
+
+ final PersistenceScheduler mPersistenceScheduler;
+
+
+ LegacyAppOpsServiceInterfaceImpl(PersistenceScheduler persistenceScheduler,
+ @NonNull Object lock) {
+ this.mPersistenceScheduler = persistenceScheduler;
+ this.mLock = lock;
+ }
+
+ @Override
+ public SparseIntArray getNonDefaultUidModes(int uid) {
+ synchronized (mLock) {
+ SparseIntArray opModes = mUidModes.get(uid, null);
+ if (opModes == null) {
+ return new SparseIntArray();
+ }
+ return opModes.clone();
+ }
+ }
+
+ @Override
+ public int getUidMode(int uid, int op) {
+ synchronized (mLock) {
+ SparseIntArray opModes = mUidModes.get(uid, null);
+ if (opModes == null) {
+ return AppOpsManager.opToDefaultMode(op);
+ }
+ return opModes.get(op, AppOpsManager.opToDefaultMode(op));
+ }
+ }
+
+ @Override
+ public boolean setUidMode(int uid, int op, int mode) {
+ final int defaultMode = AppOpsManager.opToDefaultMode(op);
+ synchronized (mLock) {
+ SparseIntArray opModes = mUidModes.get(uid, null);
+ if (opModes == null) {
+ if (mode != defaultMode) {
+ opModes = new SparseIntArray();
+ mUidModes.put(uid, opModes);
+ opModes.put(op, mode);
+ mPersistenceScheduler.scheduleWriteLocked();
+ }
+ } else {
+ if (opModes.indexOfKey(op) >= 0 && opModes.get(op) == mode) {
+ return false;
+ }
+ if (mode == defaultMode) {
+ opModes.delete(op);
+ if (opModes.size() <= 0) {
+ opModes = null;
+ mUidModes.delete(uid);
+ }
+ } else {
+ opModes.put(op, mode);
+ }
+ mPersistenceScheduler.scheduleWriteLocked();
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public int getPackageMode(String packageName, int op) {
+ synchronized (mLock) {
+ SparseIntArray opModes = mPackageModes.getOrDefault(packageName, null);
+ if (opModes == null) {
+ return AppOpsManager.opToDefaultMode(op);
+ }
+ return opModes.get(op, AppOpsManager.opToDefaultMode(op));
+ }
+ }
+
+ @Override
+ public void setPackageMode(String packageName, int op, @Mode int mode) {
+ final int defaultMode = AppOpsManager.opToDefaultMode(op);
+ synchronized (mLock) {
+ SparseIntArray opModes = mPackageModes.get(packageName);
+ if (opModes == null) {
+ if (mode != defaultMode) {
+ opModes = new SparseIntArray();
+ mPackageModes.put(packageName, opModes);
+ opModes.put(op, mode);
+ mPersistenceScheduler.scheduleWriteLocked();
+ }
+ } else {
+ if (opModes.indexOfKey(op) >= 0 && opModes.get(op) == mode) {
+ return;
+ }
+ if (mode == defaultMode) {
+ opModes.delete(op);
+ if (opModes.size() <= 0) {
+ opModes = null;
+ mPackageModes.remove(packageName);
+ }
+ } else {
+ opModes.put(op, mode);
+ }
+ mPersistenceScheduler.scheduleWriteLocked();
+ }
+ }
+ }
+
+ @Override
+ public void removeUid(int uid) {
+ synchronized (mLock) {
+ SparseIntArray opModes = mUidModes.get(uid);
+ if (opModes == null) {
+ return;
+ }
+ mUidModes.remove(uid);
+ mPersistenceScheduler.scheduleFastWriteLocked();
+ }
+ }
+
+
+ @Override
+ public boolean areUidModesDefault(int uid) {
+ synchronized (mLock) {
+ SparseIntArray opModes = mUidModes.get(uid);
+ return (opModes == null || opModes.size() <= 0);
+ }
+ }
+
+ @Override
+ public boolean arePackageModesDefault(String packageMode) {
+ synchronized (mLock) {
+ SparseIntArray opModes = mPackageModes.get(packageMode);
+ return (opModes == null || opModes.size() <= 0);
+ }
+ }
+
+ @Override
+ public boolean removePackage(String packageName) {
+ synchronized (mLock) {
+ SparseIntArray ops = mPackageModes.remove(packageName);
+ if (ops != null) {
+ mPersistenceScheduler.scheduleFastWriteLocked();
+ return true;
+ }
+ return false;
+ }
+ }
+
+ @Override
+ public void clearAllModes() {
+ synchronized (mLock) {
+ mUidModes.clear();
+ mPackageModes.clear();
+ }
+ }
+
+}
diff --git a/services/core/java/com/android/server/appop/PersistenceScheduler.java b/services/core/java/com/android/server/appop/PersistenceScheduler.java
new file mode 100644
index 000000000000..e50b6586b4fe
--- /dev/null
+++ b/services/core/java/com/android/server/appop/PersistenceScheduler.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appop;
+
+/**
+ * Interface that allows callers to persist AppOpsService's internal state
+ * to disk.
+ */
+public interface PersistenceScheduler {
+
+ /**
+ * Schedules disk writes for appOpsService and it's internal states.
+ */
+ void scheduleWriteLocked();
+
+ /**
+ * Schedules fast disk writes for appOpsService and it's internal states.
+ */
+ void scheduleFastWriteLocked();
+
+}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index f0a7df40d590..5a20db309f19 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -369,6 +369,7 @@ public class AudioService extends IAudioService.Stub
private static final int MSG_DISPATCH_DEVICE_VOLUME_BEHAVIOR = 47;
private static final int MSG_ROTATION_UPDATE = 48;
private static final int MSG_FOLD_UPDATE = 49;
+ private static final int MSG_RESET_SPATIALIZER = 50;
// start of messages handled under wakelock
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
@@ -376,6 +377,7 @@ public class AudioService extends IAudioService.Stub
private static final int MSG_DISABLE_AUDIO_FOR_UID = 100;
private static final int MSG_INIT_STREAMS_VOLUMES = 101;
private static final int MSG_INIT_SPATIALIZER = 102;
+
// end of messages handled under wakelock
// retry delay in case of failure to indicate system ready to AudioFlinger
@@ -3942,14 +3944,12 @@ public class AudioService extends IAudioService.Stub
case AudioSystem.MODE_IN_COMMUNICATION:
case AudioSystem.MODE_IN_CALL:
case AudioSystem.MODE_NORMAL:
+ case AudioSystem.MODE_CALL_SCREENING:
+ case AudioSystem.MODE_CALL_REDIRECT:
+ case AudioSystem.MODE_COMMUNICATION_REDIRECT:
break;
- case AudioSystem.MODE_RINGTONE:
- // not changing anything for ringtone
- return;
- case AudioSystem.MODE_CURRENT:
- case AudioSystem.MODE_INVALID:
default:
- // don't know what to do in this case, better bail
+ // no-op is enough for all other values
return;
}
@@ -3977,14 +3977,12 @@ public class AudioService extends IAudioService.Stub
case AudioSystem.MODE_IN_COMMUNICATION:
case AudioSystem.MODE_IN_CALL:
case AudioSystem.MODE_NORMAL:
+ case AudioSystem.MODE_CALL_SCREENING:
+ case AudioSystem.MODE_CALL_REDIRECT:
+ case AudioSystem.MODE_COMMUNICATION_REDIRECT:
break;
- case AudioSystem.MODE_RINGTONE:
- // not changing anything for ringtone
- return;
- case AudioSystem.MODE_CURRENT:
- case AudioSystem.MODE_INVALID:
default:
- // don't know what to do in this case, better bail
+ // no-op is enough for all other values
return;
}
@@ -8309,6 +8307,10 @@ public class AudioService extends IAudioService.Stub
onPersistSpatialAudioDeviceSettings();
break;
+ case MSG_RESET_SPATIALIZER:
+ mSpatializerHelper.reset(/* featureEnabled */ mHasSpatializerEffect);
+ break;
+
case MSG_CHECK_MUSIC_ACTIVE:
onCheckMusicActive((String) msg.obj);
break;
@@ -9287,6 +9289,16 @@ public class AudioService extends IAudioService.Stub
/*arg1*/ 0, /*arg2*/ 0, TAG, /*delay*/ 0);
}
+ /**
+ * post a message to schedule a reset of the spatializer state
+ */
+ void postResetSpatializer() {
+ sendMsg(mAudioHandler,
+ MSG_RESET_SPATIALIZER,
+ SENDMSG_REPLACE,
+ /*arg1*/ 0, /*arg2*/ 0, TAG, /*delay*/ 0);
+ }
+
void onInitSpatializer() {
final String settings = mSettings.getSecureStringForUser(mContentResolver,
Settings.Secure.SPATIAL_AUDIO_ENABLED, UserHandle.USER_CURRENT);
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index d9037fe7aca7..66a4527bc2cb 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -53,6 +53,8 @@ public class BtHelper {
private static final String TAG = "AS.BtHelper";
+ private static final int SOURCE_CODEC_TYPE_OPUS = 6; // TODO remove in U
+
private final @NonNull AudioDeviceBroker mDeviceBroker;
BtHelper(@NonNull AudioDeviceBroker broker) {
@@ -911,6 +913,8 @@ public class BtHelper {
return "ENCODING_APTX_HD";
case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC:
return "ENCODING_LDAC";
+ case SOURCE_CODEC_TYPE_OPUS: // TODO update in U
+ return "ENCODING_OPUS";
default:
return "ENCODING_BT_CODEC_TYPE(" + btCodecType + ")";
}
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 9ff72d3ea7f6..4559c56b6de0 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -17,10 +17,12 @@
package com.android.server.audio;
import static android.media.AudioPlaybackConfiguration.EXTRA_PLAYER_EVENT_MUTE;
+import static android.media.AudioPlaybackConfiguration.PLAYER_MUTE_CLIENT_VOLUME;
import static android.media.AudioPlaybackConfiguration.PLAYER_MUTE_MASTER;
import static android.media.AudioPlaybackConfiguration.PLAYER_MUTE_PLAYBACK_RESTRICTED;
import static android.media.AudioPlaybackConfiguration.PLAYER_MUTE_STREAM_MUTED;
import static android.media.AudioPlaybackConfiguration.PLAYER_MUTE_STREAM_VOLUME;
+import static android.media.AudioPlaybackConfiguration.PLAYER_MUTE_VOLUME_SHAPER;
import static android.media.AudioPlaybackConfiguration.PLAYER_PIID_INVALID;
import static android.media.AudioPlaybackConfiguration.PLAYER_UPDATE_MUTED;
@@ -1145,6 +1147,10 @@ public final class PlaybackActivityMonitor
(mEventValue & PLAYER_MUTE_STREAM_MUTED) != 0);
builder.append(" muteFromPlaybackRestricted:").append(
(mEventValue & PLAYER_MUTE_PLAYBACK_RESTRICTED) != 0);
+ builder.append(" muteFromClientVolume:").append(
+ (mEventValue & PLAYER_MUTE_CLIENT_VOLUME) != 0);
+ builder.append(" muteFromVolumeShaper:").append(
+ (mEventValue & PLAYER_MUTE_VOLUME_SHAPER) != 0);
return builder.toString();
default:
return builder.toString();
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 1def72b2987f..e27fb1141850 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -389,10 +389,10 @@ public class SpatializerHelper {
try {
mSpat.setLevel(level);
} catch (RemoteException e) {
- Log.e(TAG, "Can't set spatializer level", e);
- mState = STATE_NOT_SUPPORTED;
- mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
- enabled = false;
+ Log.e(TAG, "onRoutingUpdated() Can't set spatializer level", e);
+ // try to recover by resetting the native spatializer state
+ postReset();
+ return;
}
}
@@ -404,6 +404,10 @@ public class SpatializerHelper {
}
}
+ private void postReset() {
+ mAudioService.postResetSpatializer();
+ }
+
//------------------------------------------------------
// spatializer callback from native
private final class SpatializerCallback extends INativeSpatializerCallback.Stub {
@@ -751,33 +755,29 @@ public class SpatializerHelper {
if (enabled) {
throw (new IllegalStateException("Can't enable when uninitialized"));
}
- return;
+ break;
case STATE_NOT_SUPPORTED:
if (enabled) {
Log.e(TAG, "Can't enable when unsupported");
}
- return;
+ break;
case STATE_DISABLED_UNAVAILABLE:
case STATE_DISABLED_AVAILABLE:
if (enabled) {
createSpat();
onRoutingUpdated();
- break;
- } else {
- // already in disabled state
- return;
- }
+ // onRoutingUpdated() can update the "enabled" state based on context
+ // and will call setDispatchFeatureEnabledState().
+ } // else { nothing to do as already disabled }
+ break;
case STATE_ENABLED_UNAVAILABLE:
case STATE_ENABLED_AVAILABLE:
if (!enabled) {
releaseSpat();
- break;
- } else {
- // already in enabled state
- return;
- }
+ setDispatchFeatureEnabledState(false, "setSpatializerEnabledInt");
+ } // else { nothing to do as already enabled }
+ break;
}
- setDispatchFeatureEnabledState(enabled, "setSpatializerEnabledInt");
}
synchronized int getCapableImmersiveAudioLevel() {
@@ -1161,8 +1161,11 @@ public class SpatializerHelper {
case STATE_DISABLED_AVAILABLE:
case STATE_ENABLED_AVAILABLE:
if (mSpat == null) {
- throw (new IllegalStateException(
- "null Spatializer when calling " + funcName));
+ // try to recover by resetting the native spatializer state
+ Log.e(TAG, "checkSpatForHeadTracking(): "
+ + "native spatializer should not be null in state: " + mState);
+ postReset();
+ return false;
}
break;
}
@@ -1252,8 +1255,8 @@ public class SpatializerHelper {
case STATE_DISABLED_AVAILABLE:
case STATE_ENABLED_AVAILABLE:
if (mSpat == null) {
- throw (new IllegalStateException(
- "null Spatializer for setParameter for key:" + key));
+ Log.e(TAG, "setParameter(" + key + "): null spatializer in state: " + mState);
+ return;
}
break;
}
@@ -1276,8 +1279,8 @@ public class SpatializerHelper {
case STATE_DISABLED_AVAILABLE:
case STATE_ENABLED_AVAILABLE:
if (mSpat == null) {
- throw (new IllegalStateException(
- "null Spatializer for getParameter for key:" + key));
+ Log.e(TAG, "getParameter(" + key + "): null spatializer in state: " + mState);
+ return;
}
break;
}
diff --git a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
index 23f0ffbbf0b8..23183f9011e6 100644
--- a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
@@ -26,11 +26,14 @@ import android.hardware.radio.ITuner;
import android.hardware.radio.ITunerCallback;
import android.hardware.radio.RadioManager;
import android.os.RemoteException;
+import android.util.IndentingPrintWriter;
import android.util.Slog;
import com.android.server.SystemService;
import com.android.server.broadcastradio.hal2.AnnouncementAggregator;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -39,7 +42,6 @@ import java.util.OptionalInt;
public class BroadcastRadioService extends SystemService {
private static final String TAG = "BcRadioSrv";
- private static final boolean DEBUG = false;
private final ServiceImpl mServiceImpl = new ServiceImpl();
@@ -47,7 +49,7 @@ public class BroadcastRadioService extends SystemService {
private final com.android.server.broadcastradio.hal2.BroadcastRadioService mHal2;
private final Object mLock = new Object();
- private List<RadioManager.ModuleProperties> mV1Modules = null;
+ private final List<RadioManager.ModuleProperties> mV1Modules;
public BroadcastRadioService(Context context) {
super(context);
@@ -74,6 +76,7 @@ public class BroadcastRadioService extends SystemService {
@Override
public List<RadioManager.ModuleProperties> listModules() {
+ Slog.v(TAG, "Listing HIDL modules");
enforcePolicyAccess();
List<RadioManager.ModuleProperties> modules = new ArrayList<>();
modules.addAll(mV1Modules);
@@ -84,7 +87,7 @@ public class BroadcastRadioService extends SystemService {
@Override
public ITuner openTuner(int moduleId, RadioManager.BandConfig bandConfig,
boolean withAudio, ITunerCallback callback) throws RemoteException {
- if (DEBUG) Slog.i(TAG, "Opening module " + moduleId);
+ Slog.v(TAG, "Opening module " + moduleId);
enforcePolicyAccess();
if (callback == null) {
throw new IllegalArgumentException("Callback must not be empty");
@@ -101,21 +104,35 @@ public class BroadcastRadioService extends SystemService {
@Override
public ICloseHandle addAnnouncementListener(int[] enabledTypes,
IAnnouncementListener listener) {
- if (DEBUG) {
- Slog.i(TAG, "Adding announcement listener for " + Arrays.toString(enabledTypes));
- }
+ Slog.v(TAG, "Adding announcement listener for " + Arrays.toString(enabledTypes));
Objects.requireNonNull(enabledTypes);
Objects.requireNonNull(listener);
enforcePolicyAccess();
synchronized (mLock) {
if (!mHal2.hasAnyModules()) {
- Slog.i(TAG, "There are no HAL 2.x modules registered");
+ Slog.i(TAG, "There are no HAL 2.0 modules registered");
return new AnnouncementAggregator(listener, mLock);
}
return mHal2.addAnnouncementListener(enabledTypes, listener);
}
}
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ IndentingPrintWriter radioPw = new IndentingPrintWriter(pw);
+ radioPw.printf("BroadcastRadioService\n");
+ radioPw.increaseIndent();
+ radioPw.printf("HAL1: %s\n", mHal1);
+ radioPw.increaseIndent();
+ radioPw.printf("Modules of HAL1: %s\n", mV1Modules);
+ radioPw.decreaseIndent();
+ radioPw.printf("HAL2:\n");
+ radioPw.increaseIndent();
+ mHal2.dumpInfo(radioPw);
+ radioPw.decreaseIndent();
+ radioPw.decreaseIndent();
+ }
}
}
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
index 5c07f76e5011..560573799a3d 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
@@ -29,6 +29,7 @@ import android.hidl.manager.V1_0.IServiceManager;
import android.hidl.manager.V1_0.IServiceNotification;
import android.os.IHwBinder.DeathRecipient;
import android.os.RemoteException;
+import android.util.IndentingPrintWriter;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -132,6 +133,7 @@ public class BroadcastRadioService {
}
public @NonNull Collection<RadioManager.ModuleProperties> listModules() {
+ Slog.v(TAG, "List HIDL 2.0 modules");
synchronized (mLock) {
return mModules.values().stream().map(module -> module.mProperties)
.collect(Collectors.toList());
@@ -152,10 +154,11 @@ public class BroadcastRadioService {
public ITuner openSession(int moduleId, @Nullable RadioManager.BandConfig legacyConfig,
boolean withAudio, @NonNull ITunerCallback callback) throws RemoteException {
+ Slog.v(TAG, "Open HIDL 2.0 session");
Objects.requireNonNull(callback);
if (!withAudio) {
- throw new IllegalArgumentException("Non-audio sessions not supported with HAL 2.x");
+ throw new IllegalArgumentException("Non-audio sessions not supported with HAL 2.0");
}
RadioModule module = null;
@@ -175,6 +178,7 @@ public class BroadcastRadioService {
public ICloseHandle addAnnouncementListener(@NonNull int[] enabledTypes,
@NonNull IAnnouncementListener listener) {
+ Slog.v(TAG, "Add announcementListener");
AnnouncementAggregator aggregator = new AnnouncementAggregator(listener, mLock);
boolean anySupported = false;
synchronized (mLock) {
@@ -192,4 +196,30 @@ public class BroadcastRadioService {
}
return aggregator;
}
+
+ /**
+ * Dump state of broadcastradio service for HIDL HAL 2.0.
+ *
+ * @param pw The file to which BroadcastRadioService state is dumped.
+ */
+ public void dumpInfo(IndentingPrintWriter pw) {
+ synchronized (mLock) {
+ pw.printf("Next module id available: %d\n", mNextModuleId);
+ pw.printf("ServiceName to module id map:\n");
+ pw.increaseIndent();
+ for (Map.Entry<String, Integer> entry : mServiceNameToModuleIdMap.entrySet()) {
+ pw.printf("Service name: %s, module id: %d\n", entry.getKey(), entry.getValue());
+ }
+ pw.decreaseIndent();
+ pw.printf("Radio modules:\n");
+ pw.increaseIndent();
+ for (Map.Entry<Integer, RadioModule> moduleEntry : mModules.entrySet()) {
+ pw.printf("Module id=%d:\n", moduleEntry.getKey());
+ pw.increaseIndent();
+ moduleEntry.getValue().dumpInfo(pw);
+ pw.decreaseIndent();
+ }
+ pw.decreaseIndent();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
index ef7f4c9fc919..852aa66866f0 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
@@ -38,6 +38,7 @@ import android.os.DeadObjectException;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
+import android.util.IndentingPrintWriter;
import android.util.MutableInt;
import android.util.Slog;
@@ -142,8 +143,12 @@ class RadioModule {
public static @Nullable RadioModule tryLoadingModule(int idx, @NonNull String fqName,
Object lock) {
try {
+ Slog.i(TAG, "Try loading module for idx " + idx + ", fqName " + fqName);
IBroadcastRadio service = IBroadcastRadio.getService(fqName);
- if (service == null) return null;
+ if (service == null) {
+ Slog.w(TAG, "No service found for fqName " + fqName);
+ return null;
+ }
Mutable<AmFmRegionConfig> amfmConfig = new Mutable<>();
service.getAmFmRegionConfig(false, (result, config) -> {
@@ -160,7 +165,7 @@ class RadioModule {
return new RadioModule(service, prop, lock);
} catch (RemoteException ex) {
- Slog.e(TAG, "failed to load module " + fqName, ex);
+ Slog.e(TAG, "Failed to load module " + fqName, ex);
return null;
}
}
@@ -171,6 +176,7 @@ class RadioModule {
public @NonNull TunerSession openSession(@NonNull android.hardware.radio.ITunerCallback userCb)
throws RemoteException {
+ Slog.i(TAG, "Open TunerSession");
synchronized (mLock) {
if (mHalTunerSession == null) {
Mutable<ITunerSession> hwSession = new Mutable<>();
@@ -201,6 +207,7 @@ class RadioModule {
// Copy the contents of mAidlTunerSessions into a local array because TunerSession.close()
// must be called without mAidlTunerSessions locked because it can call
// onTunerSessionClosed().
+ Slog.i(TAG, "Close TunerSessions");
TunerSession[] tunerSessions;
synchronized (mLock) {
tunerSessions = new TunerSession[mAidlTunerSessions.size()];
@@ -313,7 +320,7 @@ class RadioModule {
}
onTunerSessionProgramListFilterChanged(null);
if (mAidlTunerSessions.isEmpty() && mHalTunerSession != null) {
- Slog.v(TAG, "closing HAL tuner session");
+ Slog.i(TAG, "Closing HAL tuner session");
try {
mHalTunerSession.close();
} catch (RemoteException ex) {
@@ -365,6 +372,7 @@ class RadioModule {
public android.hardware.radio.ICloseHandle addAnnouncementListener(@NonNull int[] enabledTypes,
@NonNull android.hardware.radio.IAnnouncementListener listener) throws RemoteException {
+ Slog.i(TAG, "Add AnnouncementListener");
ArrayList<Byte> enabledList = new ArrayList<>();
for (int type : enabledTypes) {
enabledList.add((byte)type);
@@ -401,6 +409,7 @@ class RadioModule {
}
Bitmap getImage(int id) {
+ Slog.i(TAG, "Get image for id " + id);
if (id == 0) throw new IllegalArgumentException("Image ID is missing");
byte[] rawImage;
@@ -416,4 +425,30 @@ class RadioModule {
return BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length);
}
+
+ void dumpInfo(IndentingPrintWriter pw) {
+ pw.printf("RadioModule\n");
+ pw.increaseIndent();
+ synchronized (mLock) {
+ pw.printf("BroadcastRadioService: %s\n", mService);
+ pw.printf("Properties: %s\n", mProperties);
+ pw.printf("HIDL2.0 HAL TunerSession: %s\n", mHalTunerSession);
+ pw.printf("Is antenna connected? ");
+ if (mAntennaConnected == null) {
+ pw.printf("null\n");
+ } else {
+ pw.printf("%s\n", mAntennaConnected ? "Yes" : "No");
+ }
+ pw.printf("current ProgramInfo: %s\n", mCurrentProgramInfo);
+ pw.printf("ProgramInfoCache: %s\n", mProgramInfoCache);
+ pw.printf("Union of AIDL ProgramFilters: %s\n", mUnionOfAidlProgramFilters);
+ pw.printf("AIDL TunerSessions:\n");
+ pw.increaseIndent();
+ for (TunerSession aidlTunerSession : mAidlTunerSessions) {
+ aidlTunerSession.dumpInfo(pw);
+ }
+ pw.decreaseIndent();
+ }
+ pw.decreaseIndent();
+ }
}
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
index d476fd60b3ee..41f753c11216 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
@@ -27,6 +27,8 @@ import android.hardware.radio.ProgramList;
import android.hardware.radio.ProgramSelector;
import android.hardware.radio.RadioManager;
import android.os.RemoteException;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
import android.util.MutableBoolean;
import android.util.MutableInt;
import android.util.Slog;
@@ -61,8 +63,13 @@ class TunerSession extends ITuner.Stub {
mLock = Objects.requireNonNull(lock);
}
+ private boolean isDebugEnabled() {
+ return Log.isLoggable(TAG, Log.DEBUG);
+ }
+
@Override
public void close() {
+ if (isDebugEnabled()) Slog.d(TAG, "Close");
close(null);
}
@@ -74,6 +81,7 @@ class TunerSession extends ITuner.Stub {
* @param error Optional error to send to client before session is closed.
*/
public void close(@Nullable Integer error) {
+ if (isDebugEnabled()) Slog.d(TAG, "Close on error " + error);
synchronized (mLock) {
if (mIsClosed) return;
if (error != null) {
@@ -104,7 +112,7 @@ class TunerSession extends ITuner.Stub {
synchronized (mLock) {
checkNotClosedLocked();
mDummyConfig = Objects.requireNonNull(config);
- Slog.i(TAG, "Ignoring setConfiguration - not applicable for broadcastradio HAL 2.x");
+ Slog.i(TAG, "Ignoring setConfiguration - not applicable for broadcastradio HAL 2.0");
mModule.fanoutAidlCallback(cb -> cb.onConfigurationChanged(config));
}
}
@@ -137,6 +145,10 @@ class TunerSession extends ITuner.Stub {
@Override
public void step(boolean directionDown, boolean skipSubChannel) throws RemoteException {
+ if (isDebugEnabled()) {
+ Slog.d(TAG, "Step with directionDown " + directionDown
+ + " skipSubChannel " + skipSubChannel);
+ }
synchronized (mLock) {
checkNotClosedLocked();
int halResult = mHwSession.step(!directionDown);
@@ -146,6 +158,10 @@ class TunerSession extends ITuner.Stub {
@Override
public void scan(boolean directionDown, boolean skipSubChannel) throws RemoteException {
+ if (isDebugEnabled()) {
+ Slog.d(TAG, "Scan with directionDown " + directionDown
+ + " skipSubChannel " + skipSubChannel);
+ }
synchronized (mLock) {
checkNotClosedLocked();
int halResult = mHwSession.scan(!directionDown, skipSubChannel);
@@ -155,6 +171,7 @@ class TunerSession extends ITuner.Stub {
@Override
public void tune(ProgramSelector selector) throws RemoteException {
+ if (isDebugEnabled()) Slog.d(TAG, "Tune with selector " + selector);
synchronized (mLock) {
checkNotClosedLocked();
int halResult = mHwSession.tune(Convert.programSelectorToHal(selector));
@@ -164,6 +181,7 @@ class TunerSession extends ITuner.Stub {
@Override
public void cancel() {
+ Slog.i(TAG, "Cancel");
synchronized (mLock) {
checkNotClosedLocked();
Utils.maybeRethrow(mHwSession::cancel);
@@ -172,23 +190,25 @@ class TunerSession extends ITuner.Stub {
@Override
public void cancelAnnouncement() {
- Slog.i(TAG, "Announcements control doesn't involve cancelling at the HAL level in 2.x");
+ Slog.i(TAG, "Announcements control doesn't involve cancelling at the HAL level in HAL 2.0");
}
@Override
public Bitmap getImage(int id) {
+ if (isDebugEnabled()) Slog.d(TAG, "Get image for " + id);
return mModule.getImage(id);
}
@Override
public boolean startBackgroundScan() {
- Slog.i(TAG, "Explicit background scan trigger is not supported with HAL 2.x");
+ Slog.i(TAG, "Explicit background scan trigger is not supported with HAL 2.0");
mModule.fanoutAidlCallback(cb -> cb.onBackgroundScanComplete());
return true;
}
@Override
public void startProgramListUpdates(ProgramList.Filter filter) throws RemoteException {
+ if (isDebugEnabled()) Slog.d(TAG, "start programList updates " + filter);
// If the AIDL client provides a null filter, it wants all updates, so use the most broad
// filter.
if (filter == null) {
@@ -247,6 +267,7 @@ class TunerSession extends ITuner.Stub {
@Override
public void stopProgramListUpdates() throws RemoteException {
+ if (isDebugEnabled()) Slog.d(TAG, "Stop programList updates");
synchronized (mLock) {
checkNotClosedLocked();
mProgramInfoCache = null;
@@ -270,7 +291,7 @@ class TunerSession extends ITuner.Stub {
@Override
public boolean isConfigFlagSet(int flag) {
- Slog.v(TAG, "isConfigFlagSet " + ConfigFlag.toString(flag));
+ if (isDebugEnabled()) Slog.d(TAG, "Is ConfigFlagSet for " + ConfigFlag.toString(flag));
synchronized (mLock) {
checkNotClosedLocked();
@@ -292,7 +313,9 @@ class TunerSession extends ITuner.Stub {
@Override
public void setConfigFlag(int flag, boolean value) throws RemoteException {
- Slog.v(TAG, "setConfigFlag " + ConfigFlag.toString(flag) + " = " + value);
+ if (isDebugEnabled()) {
+ Slog.d(TAG, "Set ConfigFlag " + ConfigFlag.toString(flag) + " = " + value);
+ }
synchronized (mLock) {
checkNotClosedLocked();
int halResult = mHwSession.setConfigFlag(flag, value);
@@ -317,4 +340,17 @@ class TunerSession extends ITuner.Stub {
() -> mHwSession.getParameters(Convert.listToArrayList(keys))));
}
}
+
+ void dumpInfo(IndentingPrintWriter pw) {
+ pw.printf("TunerSession\n");
+ pw.increaseIndent();
+ synchronized (mLock) {
+ pw.printf("HIDL HAL Session: %s\n", mHwSession);
+ pw.printf("Is session closed? %s\n", mIsClosed ? "Yes" : "No");
+ pw.printf("Is muted? %s\n", mIsMuted ? "Yes" : "No");
+ pw.printf("ProgramInfoCache: %s\n", mProgramInfoCache);
+ pw.printf("Config: %s\n", mDummyConfig);
+ }
+ pw.decreaseIndent();
+ }
}
diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
index f2b4d42c8758..230bfc50d186 100644
--- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
+++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
@@ -16,6 +16,7 @@
package com.android.server.companion.virtual;
+import android.annotation.NonNull;
import android.companion.virtual.IVirtualDevice;
/**
@@ -24,16 +25,41 @@ import android.companion.virtual.IVirtualDevice;
*/
public abstract class VirtualDeviceManagerInternal {
+ /** Interface to listen to the creation and destruction of virtual displays. */
+ public interface VirtualDisplayListener {
+ /** Notifies that a virtual display was created. */
+ void onVirtualDisplayCreated(int displayId);
+
+ /** Notifies that a virtual display was removed. */
+ void onVirtualDisplayRemoved(int displayId);
+ }
+
+ /** Register a listener for the creation and destruction of virtual displays. */
+ public abstract void registerVirtualDisplayListener(
+ @NonNull VirtualDisplayListener listener);
+
+ /** Unregister a listener for the creation and destruction of virtual displays. */
+ public abstract void unregisterVirtualDisplayListener(
+ @NonNull VirtualDisplayListener listener);
+
+
/**
* Validate the virtual device.
*/
public abstract boolean isValidVirtualDevice(IVirtualDevice virtualDevice);
/**
- * Notify a virtual display is removed.
+ * Notifies that a virtual display is created.
+ *
+ * @param displayId The display id of the created virtual display.
+ */
+ public abstract void onVirtualDisplayCreated(int displayId);
+
+ /**
+ * Notifies that a virtual display is removed.
*
* @param virtualDevice The virtual device where the virtual display located.
- * @param displayId The display id of the removed virtual display.
+ * @param displayId The display id of the removed virtual display.
*/
public abstract void onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId);
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 114690a03324..a817cea1a674 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -345,7 +345,9 @@ class AutomaticBrightnessController {
brightnessEvent.setFlags(brightnessEvent.getFlags()
| (!mAmbientLuxValid ? BrightnessEvent.FLAG_INVALID_LUX : 0)
| (mDisplayPolicy == DisplayPowerRequest.POLICY_DOZE
- ? BrightnessEvent.FLAG_DOZE_SCALE : 0));
+ ? BrightnessEvent.FLAG_DOZE_SCALE : 0)
+ | (mCurrentBrightnessMapper.isForIdleMode()
+ ? BrightnessEvent.FLAG_IDLE_CURVE : 0));
}
if (!mAmbientLuxValid) {
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index b5aa7b14792b..74ee6800eb63 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -33,6 +33,7 @@ import android.view.DisplayAddress;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.display.BrightnessSynchronizer;
+import com.android.server.display.config.AutoBrightness;
import com.android.server.display.config.BrightnessThresholds;
import com.android.server.display.config.BrightnessThrottlingMap;
import com.android.server.display.config.BrightnessThrottlingPoint;
@@ -69,9 +70,8 @@ import java.util.List;
import javax.xml.datatype.DatatypeConfigurationException;
/**
- * Reads and stores display-specific configurations.
- * File format:
- * <pre>
+ * Reads and stores display-specific configurations. File format:
+ * <pre>
* {@code
* <displayConfiguration>
* <densityMapping>
@@ -147,6 +147,15 @@ import javax.xml.datatype.DatatypeConfigurationException;
* <quirk>canSetBrightnessViaHwc</quirk>
* </quirks>
*
+ * <autoBrightness>
+ * <brighteningLightDebounceMillis>
+ * 2000
+ * </brighteningLightDebounceMillis>
+ * <darkeningLightDebounceMillis>
+ * 1000
+ * </darkeningLightDebounceMillis>
+ * </autoBrightness>
+ *
* <screenBrightnessRampFastDecrease>0.01</screenBrightnessRampFastDecrease>
* <screenBrightnessRampFastIncrease>0.02</screenBrightnessRampFastIncrease>
* <screenBrightnessRampSlowDecrease>0.03</screenBrightnessRampSlowDecrease>
@@ -224,6 +233,9 @@ public class DisplayDeviceConfig {
// Length of the ambient light horizon used to calculate short-term estimate of ambient light.
private static final int AMBIENT_LIGHT_SHORT_HORIZON_MILLIS = 2000;
+ // Invalid value of AutoBrightness brightening and darkening light debounce
+ private static final int INVALID_AUTO_BRIGHTNESS_LIGHT_DEBOUNCE = -1;
+
@VisibleForTesting
static final float HDR_PERCENT_OF_SCREEN_REQUIRED_DEFAULT = 0.5f;
@@ -281,6 +293,14 @@ public class DisplayDeviceConfig {
private String mLoadedFrom = null;
private Spline mSdrToHdrRatioSpline;
+ // Represents the auto-brightness brightening light debounce.
+ private long mAutoBrightnessBrighteningLightDebounce =
+ INVALID_AUTO_BRIGHTNESS_LIGHT_DEBOUNCE;
+
+ // Represents the auto-brightness darkening light debounce.
+ private long mAutoBrightnessDarkeningLightDebounce =
+ INVALID_AUTO_BRIGHTNESS_LIGHT_DEBOUNCE;
+
// Brightness Throttling data may be updated via the DeviceConfig. Here we store the original
// data, which comes from the ddc, and the current one, which may be the DeviceConfig
// overwritten value.
@@ -293,8 +313,8 @@ public class DisplayDeviceConfig {
}
/**
- * Creates an instance for the specified display.
- * Tries to find a file with identifier in the following priority order:
+ * Creates an instance for the specified display. Tries to find a file with identifier in the
+ * following priority order:
* <ol>
* <li>physicalDisplayId</li>
* <li>physicalDisplayId without a stable flag (old system)</li>
@@ -314,11 +334,12 @@ public class DisplayDeviceConfig {
}
/**
- * Creates an instance using global values since no display device config xml exists.
- * Uses values from config or PowerManager.
+ * Creates an instance using global values since no display device config xml exists. Uses
+ * values from config or PowerManager.
*
- * @param context
- * @param useConfigXml
+ * @param context The context from which the DisplayDeviceConfig is to be constructed.
+ * @param useConfigXml A flag indicating if values are to be loaded from the configuration file,
+ * or the default values.
* @return A configuration instance.
*/
public static DisplayDeviceConfig create(Context context, boolean useConfigXml) {
@@ -450,8 +471,8 @@ public class DisplayDeviceConfig {
}
/**
- * Calculates the backlight value, as recognised by the HAL, from the brightness value
- * given that the rest of the system deals with.
+ * Calculates the backlight value, as recognised by the HAL, from the brightness value given
+ * that the rest of the system deals with.
*
* @param brightness value on the framework scale of 0-1
* @return backlight value on the HAL scale of 0-1
@@ -502,13 +523,13 @@ public class DisplayDeviceConfig {
if (DEBUG) {
Slog.d(TAG, "getHdrBrightnessFromSdr: sdr brightness " + brightness
- + " backlight " + backlight
- + " nits " + nits
- + " ratio " + ratio
- + " hdrNits " + hdrNits
- + " hdrBacklight " + hdrBacklight
- + " hdrBrightness " + hdrBrightness
- );
+ + " backlight " + backlight
+ + " nits " + nits
+ + " ratio " + ratio
+ + " hdrNits " + hdrNits
+ + " hdrBacklight " + hdrBacklight
+ + " hdrBrightness " + hdrBrightness
+ );
}
return hdrBrightness;
}
@@ -590,8 +611,8 @@ public class DisplayDeviceConfig {
/**
* @param quirkValue The quirk to test.
- * @return {@code true} if the specified quirk is present in this configuration,
- * {@code false} otherwise.
+ * @return {@code true} if the specified quirk is present in this configuration, {@code false}
+ * otherwise.
*/
public boolean hasQuirk(String quirkValue) {
return mQuirks != null && mQuirks.contains(quirkValue);
@@ -625,6 +646,20 @@ public class DisplayDeviceConfig {
return BrightnessThrottlingData.create(mBrightnessThrottlingData);
}
+ /**
+ * @return Auto brightness darkening light debounce
+ */
+ public long getAutoBrightnessDarkeningLightDebounce() {
+ return mAutoBrightnessDarkeningLightDebounce;
+ }
+
+ /**
+ * @return Auto brightness brightening light debounce
+ */
+ public long getAutoBrightnessBrighteningLightDebounce() {
+ return mAutoBrightnessBrighteningLightDebounce;
+ }
+
@Override
public String toString() {
return "DisplayDeviceConfig{"
@@ -663,6 +698,10 @@ public class DisplayDeviceConfig {
+ ", mProximitySensor=" + mProximitySensor
+ ", mRefreshRateLimitations= " + Arrays.toString(mRefreshRateLimitations.toArray())
+ ", mDensityMapping= " + mDensityMapping
+ + ", mAutoBrightnessBrighteningLightDebounce= "
+ + mAutoBrightnessBrighteningLightDebounce
+ + ", mAutoBrightnessDarkeningLightDebounce= "
+ + mAutoBrightnessDarkeningLightDebounce
+ "}";
}
@@ -719,6 +758,7 @@ public class DisplayDeviceConfig {
loadProxSensorFromDdc(config);
loadAmbientHorizonFromDdc(config);
loadBrightnessChangeThresholds(config);
+ loadAutoBrightnessConfigValues(config);
} else {
Slog.w(TAG, "DisplayDeviceConfig file is null");
}
@@ -899,8 +939,8 @@ public class DisplayDeviceConfig {
if (i > 0) {
if (nits[i] < nits[i - 1]) {
Slog.e(TAG, "sdrHdrRatioMap must be non-decreasing, ignoring rest "
- + " of configuration. nits: " + nits[i] + " < "
- + nits[i - 1]);
+ + " of configuration. nits: " + nits[i] + " < "
+ + nits[i - 1]);
return null;
}
}
@@ -927,7 +967,7 @@ public class DisplayDeviceConfig {
final List<BrightnessThrottlingPoint> points = map.getBrightnessThrottlingPoint();
// At least 1 point is guaranteed by the display device config schema
List<BrightnessThrottlingData.ThrottlingLevel> throttlingLevels =
- new ArrayList<>(points.size());
+ new ArrayList<>(points.size());
boolean badConfig = false;
for (BrightnessThrottlingPoint point : points) {
@@ -938,7 +978,7 @@ public class DisplayDeviceConfig {
}
throttlingLevels.add(new BrightnessThrottlingData.ThrottlingLevel(
- convertThermalStatus(status), point.getBrightness().floatValue()));
+ convertThermalStatus(status), point.getBrightness().floatValue()));
}
if (!badConfig) {
@@ -947,6 +987,41 @@ public class DisplayDeviceConfig {
}
}
+ private void loadAutoBrightnessConfigValues(DisplayConfiguration config) {
+ loadAutoBrightnessBrighteningLightDebounce(config.getAutoBrightness());
+ loadAutoBrightnessDarkeningLightDebounce(config.getAutoBrightness());
+ }
+
+ /**
+ * Loads the auto-brightness brightening light debounce. Internally, this takes care of loading
+ * the value from the display config, and if not present, falls back to config.xml.
+ */
+ private void loadAutoBrightnessBrighteningLightDebounce(AutoBrightness autoBrightnessConfig) {
+ if (autoBrightnessConfig == null
+ || autoBrightnessConfig.getBrighteningLightDebounceMillis() == null) {
+ mAutoBrightnessBrighteningLightDebounce = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_autoBrightnessBrighteningLightDebounce);
+ } else {
+ mAutoBrightnessBrighteningLightDebounce =
+ autoBrightnessConfig.getBrighteningLightDebounceMillis().intValue();
+ }
+ }
+
+ /**
+ * Loads the auto-brightness darkening light debounce. Internally, this takes care of loading
+ * the value from the display config, and if not present, falls back to config.xml.
+ */
+ private void loadAutoBrightnessDarkeningLightDebounce(AutoBrightness autoBrightnessConfig) {
+ if (autoBrightnessConfig == null
+ || autoBrightnessConfig.getDarkeningLightDebounceMillis() == null) {
+ mAutoBrightnessDarkeningLightDebounce = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_autoBrightnessDarkeningLightDebounce);
+ } else {
+ mAutoBrightnessDarkeningLightDebounce =
+ autoBrightnessConfig.getDarkeningLightDebounceMillis().intValue();
+ }
+ }
+
private void loadBrightnessMapFromConfigXml() {
// Use the config.xml mapping
final Resources res = mContext.getResources();
@@ -1058,17 +1133,17 @@ public class DisplayDeviceConfig {
PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, mBacklight[i]);
}
mBrightnessToBacklightSpline = mInterpolationType == INTERPOLATION_LINEAR
- ? Spline.createLinearSpline(mBrightness, mBacklight)
- : Spline.createSpline(mBrightness, mBacklight);
+ ? Spline.createLinearSpline(mBrightness, mBacklight)
+ : Spline.createSpline(mBrightness, mBacklight);
mBacklightToBrightnessSpline = mInterpolationType == INTERPOLATION_LINEAR
- ? Spline.createLinearSpline(mBacklight, mBrightness)
- : Spline.createSpline(mBacklight, mBrightness);
+ ? Spline.createLinearSpline(mBacklight, mBrightness)
+ : Spline.createSpline(mBacklight, mBrightness);
mBacklightToNitsSpline = mInterpolationType == INTERPOLATION_LINEAR
- ? Spline.createLinearSpline(mBacklight, mNits)
- : Spline.createSpline(mBacklight, mNits);
+ ? Spline.createLinearSpline(mBacklight, mNits)
+ : Spline.createSpline(mBacklight, mNits);
mNitsToBacklightSpline = mInterpolationType == INTERPOLATION_LINEAR
- ? Spline.createLinearSpline(mNits, mBacklight)
- : Spline.createSpline(mNits, mBacklight);
+ ? Spline.createLinearSpline(mNits, mBacklight)
+ : Spline.createSpline(mNits, mBacklight);
}
private void loadQuirks(DisplayConfiguration config) {
@@ -1111,7 +1186,7 @@ public class DisplayDeviceConfig {
if (mHbmData.minimumHdrPercentOfScreen > 1
|| mHbmData.minimumHdrPercentOfScreen < 0) {
Slog.w(TAG, "Invalid minimum HDR percent of screen: "
- + String.valueOf(mHbmData.minimumHdrPercentOfScreen));
+ + String.valueOf(mHbmData.minimumHdrPercentOfScreen));
mHbmData.minimumHdrPercentOfScreen = HDR_PERCENT_OF_SCREEN_REQUIRED_DEFAULT;
}
} else {
@@ -1235,7 +1310,7 @@ public class DisplayDeviceConfig {
ambientBrightnessThresholds.getDarkeningThresholds();
final BigDecimal ambientBrighteningThreshold = brighteningAmbientLux.getMinimum();
- final BigDecimal ambientDarkeningThreshold = darkeningAmbientLux.getMinimum();
+ final BigDecimal ambientDarkeningThreshold = darkeningAmbientLux.getMinimum();
if (ambientBrighteningThreshold != null) {
mAmbientLuxBrighteningMinThreshold = ambientBrighteningThreshold.floatValue();
@@ -1330,8 +1405,8 @@ public class DisplayDeviceConfig {
}
/**
- * @return True if the sensor matches both the specified name and type, or one if only
- * one is specified (not-empty). Always returns false if both parameters are null or empty.
+ * @return True if the sensor matches both the specified name and type, or one if only one
+ * is specified (not-empty). Always returns false if both parameters are null or empty.
*/
public boolean matches(String sensorName, String sensorType) {
final boolean isNameSpecified = !TextUtils.isEmpty(sensorName);
@@ -1446,6 +1521,7 @@ public class DisplayDeviceConfig {
return otherThrottlingLevel.thermalStatus == this.thermalStatus
&& otherThrottlingLevel.brightness == this.brightness;
}
+
@Override
public int hashCode() {
int result = 1;
@@ -1455,8 +1531,11 @@ public class DisplayDeviceConfig {
}
}
- static public BrightnessThrottlingData create(List<ThrottlingLevel> throttlingLevels)
- {
+
+ /**
+ * Creates multiple teperature based throttling levels of brightness
+ */
+ public static BrightnessThrottlingData create(List<ThrottlingLevel> throttlingLevels) {
if (throttlingLevels == null || throttlingLevels.size() == 0) {
Slog.e(TAG, "BrightnessThrottlingData received null or empty throttling levels");
return null;
@@ -1498,8 +1577,9 @@ public class DisplayDeviceConfig {
}
static public BrightnessThrottlingData create(BrightnessThrottlingData other) {
- if (other == null)
+ if (other == null) {
return null;
+ }
return BrightnessThrottlingData.create(other.throttlingLevels);
}
@@ -1508,8 +1588,8 @@ public class DisplayDeviceConfig {
@Override
public String toString() {
return "BrightnessThrottlingData{"
- + "throttlingLevels:" + throttlingLevels
- + "} ";
+ + "throttlingLevels:" + throttlingLevels
+ + "} ";
}
@Override
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index b3434805ee41..2dd3864a9505 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2583,8 +2583,8 @@ public final class DisplayManagerService extends SystemService {
final BrightnessSetting brightnessSetting = new BrightnessSetting(mPersistentDataStore,
display, mSyncRoot);
final DisplayPowerController displayPowerController = new DisplayPowerController(
- mContext, mDisplayPowerCallbacks, mPowerHandler, mSensorManager,
- mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
+ mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
+ mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
() -> handleBrightnessChange(display));
mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController);
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index c01a228bf0cd..90e4596af420 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -48,6 +48,7 @@ import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
+import android.util.FloatProperty;
import android.util.Log;
import android.util.MathUtils;
import android.util.MutableFloat;
@@ -58,6 +59,7 @@ import android.view.Display;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.logging.MetricsLogger;
@@ -97,7 +99,7 @@ import java.io.PrintWriter;
* blocker as long as the display is not ready. So most of the work done here
* does not need to worry about holding a suspend blocker unless it happens
* independently of the display ready signal.
- *
+ *
* For debugging, you can make the color fade and brightness animations run
* slower by changing the "animator duration scale" option in Development Settings.
*/
@@ -160,7 +162,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
private static final int RINGBUFFER_MAX = 100;
- private final String TAG;
+ private final String mTag;
private final Object mLock = new Object();
@@ -256,6 +258,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// to reach the final state.
private final boolean mBrightnessBucketsInDozeConfig;
+ private final Clock mClock;
+ private final Injector mInjector;
+
// Maximum time a ramp animation can take.
private long mBrightnessRampIncreaseMaxTimeMillis;
private long mBrightnessRampDecreaseMaxTimeMillis;
@@ -495,20 +500,22 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
/**
* Creates the display power controller.
*/
- public DisplayPowerController(Context context,
+ DisplayPowerController(Context context, Injector injector,
DisplayPowerCallbacks callbacks, Handler handler,
SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
Runnable onBrightnessChangeRunnable) {
+
+ mInjector = injector != null ? injector : new Injector();
+ mClock = mInjector.getClock();
mLogicalDisplay = logicalDisplay;
mDisplayId = mLogicalDisplay.getDisplayIdLocked();
- final String displayIdStr = "[" + mDisplayId + "]";
- TAG = "DisplayPowerController" + displayIdStr;
- mSuspendBlockerIdUnfinishedBusiness = displayIdStr + "unfinished business";
- mSuspendBlockerIdOnStateChanged = displayIdStr + "on state changed";
- mSuspendBlockerIdProxPositive = displayIdStr + "prox positive";
- mSuspendBlockerIdProxNegative = displayIdStr + "prox negative";
- mSuspendBlockerIdProxDebounce = displayIdStr + "prox debounce";
+ mTag = "DisplayPowerController[" + mDisplayId + "]";
+ mSuspendBlockerIdUnfinishedBusiness = getSuspendBlockerUnfinishedBusinessId(mDisplayId);
+ mSuspendBlockerIdOnStateChanged = getSuspendBlockerOnStateChangedId(mDisplayId);
+ mSuspendBlockerIdProxPositive = getSuspendBlockerProxPositiveId(mDisplayId);
+ mSuspendBlockerIdProxNegative = getSuspendBlockerProxNegativeId(mDisplayId);
+ mSuspendBlockerIdProxDebounce = getSuspendBlockerProxDebounceId(mDisplayId);
mDisplayDevice = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
mUniqueDisplayId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
@@ -593,7 +600,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
displayWhiteBalanceSettings.setCallbacks(this);
displayWhiteBalanceController.setCallbacks(this);
} catch (Exception e) {
- Slog.e(TAG, "failed to set up display white-balance: " + e);
+ Slog.e(mTag, "failed to set up display white-balance: " + e);
}
}
mDisplayWhiteBalanceSettings = displayWhiteBalanceSettings;
@@ -660,7 +667,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
&& mInteractiveModeBrightnessMapper == null)
|| (mAutomaticBrightnessController.isInIdleMode()
&& mIdleModeBrightnessMapper == null)) {
- Log.w(TAG, "No brightness mapping available to recalculate splines for this mode");
+ Log.w(mTag, "No brightness mapping available to recalculate splines for this mode");
return;
}
@@ -681,7 +688,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
/**
* Get the {@link BrightnessChangeEvent}s for the specified user.
- * @param userId userId to fetch data for
+ *
+ * @param userId userId to fetch data for
* @param includePackage if false will null out the package name in events
*/
@Nullable
@@ -723,10 +731,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
* The controller makes a copy of the provided object and then
* begins adjusting the power state to match what was requested.
*
- * @param request The requested power state.
+ * @param request The requested power state.
* @param waitForNegativeProximity If true, issues a request to wait for
- * negative proximity before turning the screen back on, assuming the screen
- * was turned off by the proximity sensor.
+ * negative proximity before turning the screen back on,
+ * assuming the screen
+ * was turned off by the proximity sensor.
* @return True if display is ready, false if there are important changes that must
* be made asynchronously (such as turning the screen on), in which case the caller
* should grab a wake lock, watch for {@link DisplayPowerCallbacks#onStateChanged()}
@@ -735,7 +744,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
public boolean requestPowerState(DisplayPowerRequest request,
boolean waitForNegativeProximity) {
if (DEBUG) {
- Slog.d(TAG, "requestPowerState: "
+ Slog.d(mTag, "requestPowerState: "
+ request + ", waitForNegativeProximity=" + waitForNegativeProximity);
}
@@ -788,7 +797,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
public void onDisplayChanged() {
final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
if (device == null) {
- Slog.wtf(TAG, "Display Device is null in DisplayPowerController for display: "
+ Slog.wtf(mTag, "Display Device is null in DisplayPowerController for display: "
+ mLogicalDisplay.getDisplayIdLocked());
return;
}
@@ -834,7 +843,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
synchronized (mLock) {
mStopped = true;
Message msg = mHandler.obtainMessage(MSG_STOP);
- mHandler.sendMessage(msg);
+ mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
if (mDisplayWhiteBalanceController != null) {
mDisplayWhiteBalanceController.setEnabled(false);
@@ -888,12 +897,12 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
if (!mStopped && !mPendingUpdatePowerStateLocked) {
mPendingUpdatePowerStateLocked = true;
Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE);
- mHandler.sendMessage(msg);
+ mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
}
}
private void initialize(int displayState) {
- mPowerState = new DisplayPowerState(mBlanker,
+ mPowerState = mInjector.getDisplayPowerState(mBlanker,
mColorFadeEnabled ? new ColorFade(mDisplayId) : null, mDisplayId, displayState);
if (mColorFadeEnabled) {
@@ -908,7 +917,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mColorFadeOffAnimator.addListener(mAnimatorListener);
}
- mScreenBrightnessRampAnimator = new DualRampAnimator<>(mPowerState,
+ mScreenBrightnessRampAnimator = mInjector.getDualRampAnimator(mPowerState,
DisplayPowerState.SCREEN_BRIGHTNESS_FLOAT,
DisplayPowerState.SCREEN_SDR_BRIGHTNESS_FLOAT);
mScreenBrightnessRampAnimator.setAnimationTimeLimits(
@@ -986,10 +995,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
screenBrighteningThresholds, screenDarkeningThresholds, screenThresholdLevels,
screenDarkeningMinThreshold, screenBrighteningMinThreshold);
- long brighteningLightDebounce = resources.getInteger(
- com.android.internal.R.integer.config_autoBrightnessBrighteningLightDebounce);
- long darkeningLightDebounce = resources.getInteger(
- com.android.internal.R.integer.config_autoBrightnessDarkeningLightDebounce);
+ long brighteningLightDebounce = mDisplayDeviceConfig
+ .getAutoBrightnessBrighteningLightDebounce();
+ long darkeningLightDebounce = mDisplayDeviceConfig
+ .getAutoBrightnessDarkeningLightDebounce();
boolean autoBrightnessResetAmbientLuxAfterWarmUp = resources.getBoolean(
com.android.internal.R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp);
@@ -1002,7 +1011,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
if (initialLightSensorRate == -1) {
initialLightSensorRate = lightSensorRate;
} else if (initialLightSensorRate > lightSensorRate) {
- Slog.w(TAG, "Expected config_autoBrightnessInitialLightSensorRate ("
+ Slog.w(mTag, "Expected config_autoBrightnessInitialLightSensorRate ("
+ initialLightSensorRate + ") to be less than or equal to "
+ "config_autoBrightnessLightSensorRate (" + lightSensorRate + ").");
}
@@ -1048,7 +1057,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
if (mDisplayDeviceConfig != null && mDisplayDeviceConfig.getNits() != null) {
mNitsRange = mDisplayDeviceConfig.getNits();
} else {
- Slog.w(TAG, "Screen brightness nits configuration is unavailable; falling back");
+ Slog.w(mTag, "Screen brightness nits configuration is unavailable; falling back");
mNitsRange = BrightnessMappingStrategy.getFloatArray(resources
.obtainTypedArray(com.android.internal.R.array.config_screenBrightnessNits));
}
@@ -1077,13 +1086,16 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
@Override
public void onAnimationStart(Animator animation) {
}
+
@Override
public void onAnimationEnd(Animator animation) {
sendUpdatePowerState();
}
+
@Override
public void onAnimationRepeat(Animator animation) {
}
+
@Override
public void onAnimationCancel(Animator animation) {
}
@@ -1124,8 +1136,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mOnProximityNegativeMessages = 0;
final float brightness = mPowerState != null
- ? mPowerState.getScreenBrightness()
- : PowerManager.BRIGHTNESS_MIN;
+ ? mPowerState.getScreenBrightness()
+ : PowerManager.BRIGHTNESS_MIN;
reportStats(brightness);
if (mPowerState != null) {
@@ -1203,8 +1215,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
state = Display.STATE_ON;
break;
}
- assert(state != Display.STATE_UNKNOWN);
+ assert (state != Display.STATE_UNKNOWN);
+ boolean skipRampBecauseOfProximityChangeToNegative = false;
// Apply the proximity sensor.
if (mProximitySensor != null) {
if (mPowerRequest.useProximitySensor && state != Display.STATE_OFF) {
@@ -1242,6 +1255,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// the screen back on. Also turn it back on if we've been asked to ignore the
// prox sensor temporarily.
mScreenOffBecauseOfProximity = false;
+ skipRampBecauseOfProximityChangeToNegative = true;
sendOnProximityNegativeWithWakelock();
}
} else {
@@ -1290,16 +1304,16 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
final boolean autoBrightnessEnabledInDoze =
mAllowAutoBrightnessWhileDozingConfig && Display.isDozeState(state);
final boolean autoBrightnessEnabled = mPowerRequest.useAutoBrightness
- && (state == Display.STATE_ON || autoBrightnessEnabledInDoze)
- && Float.isNaN(brightnessState)
- && mAutomaticBrightnessController != null;
+ && (state == Display.STATE_ON || autoBrightnessEnabledInDoze)
+ && Float.isNaN(brightnessState)
+ && mAutomaticBrightnessController != null;
final boolean autoBrightnessDisabledDueToDisplayOff = mPowerRequest.useAutoBrightness
- && !(state == Display.STATE_ON || autoBrightnessEnabledInDoze);
+ && !(state == Display.STATE_ON || autoBrightnessEnabledInDoze);
final int autoBrightnessState = autoBrightnessEnabled
? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED
: autoBrightnessDisabledDueToDisplayOff
- ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE
- : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
+ ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE
+ : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
final boolean userSetBrightnessChanged = updateUserSetScreenBrightness();
@@ -1524,8 +1538,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
final boolean wasOrWillBeInVr =
(state == Display.STATE_VR || oldState == Display.STATE_VR);
- final boolean initialRampSkip =
- state == Display.STATE_ON && mSkipRampState != RAMP_STATE_SKIP_NONE;
+ final boolean initialRampSkip = (state == Display.STATE_ON && mSkipRampState
+ != RAMP_STATE_SKIP_NONE) || skipRampBecauseOfProximityChangeToNegative;
// While dozing, sometimes the brightness is split into buckets. Rather than animating
// through the buckets, which is unlikely to be smooth in the first place, just jump
// right to the suggested brightness.
@@ -1589,9 +1603,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// AND if we are not in idle screen brightness mode.
if (!brightnessIsTemporary
&& (mAutomaticBrightnessController != null
- && !mAutomaticBrightnessController.isInIdleMode())) {
+ && !mAutomaticBrightnessController.isInIdleMode())) {
if (userInitiatedChange && (mAutomaticBrightnessController == null
- || !mAutomaticBrightnessController.hasValidAmbientLux())) {
+ || !mAutomaticBrightnessController.hasValidAmbientLux())) {
// If we don't have a valid lux reading we can't report a valid
// slider event so notify as if the system changed the brightness.
userInitiatedChange = false;
@@ -1614,13 +1628,13 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// Log any changes to what is currently driving the brightness setting.
if (!mBrightnessReasonTemp.equals(mBrightnessReason) || brightnessAdjustmentFlags != 0) {
- Slog.v(TAG, "Brightness [" + brightnessState + "] reason changing to: '"
+ Slog.v(mTag, "Brightness [" + brightnessState + "] reason changing to: '"
+ mBrightnessReasonTemp.toString(brightnessAdjustmentFlags)
+ "', previous reason: '" + mBrightnessReason + "'.");
mBrightnessReason.set(mBrightnessReasonTemp);
} else if (mBrightnessReasonTemp.getReason() == BrightnessReason.REASON_MANUAL
&& userSetBrightnessChanged) {
- Slog.v(TAG, "Brightness [" + brightnessState + "] manual adjustment.");
+ Slog.v(mTag, "Brightness [" + brightnessState + "] manual adjustment.");
}
@@ -1638,7 +1652,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// we don't spam logcat when the slider is being used.
boolean tempToTempTransition =
mTempBrightnessEvent.getReason().getReason() == BrightnessReason.REASON_TEMPORARY
- && mLastBrightnessEvent.getReason().getReason()
+ && mLastBrightnessEvent.getReason().getReason()
== BrightnessReason.REASON_TEMPORARY;
if ((!mTempBrightnessEvent.equalsMainData(mLastBrightnessEvent) && !tempToTempTransition)
|| brightnessAdjustmentFlags != 0) {
@@ -1650,7 +1664,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
newEvent.setAdjustmentFlags(brightnessAdjustmentFlags);
newEvent.setFlags(newEvent.getFlags() | (userSetBrightnessChanged
? BrightnessEvent.FLAG_USER_SET : 0));
- Slog.i(TAG, newEvent.toString(/* includeTime= */ false));
+ Slog.i(mTag, newEvent.toString(/* includeTime= */ false));
if (mBrightnessEventRingBuffer != null) {
mBrightnessEventRingBuffer.append(newEvent);
@@ -1671,9 +1685,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// Note that we do not wait for the brightness ramp animation to complete before
// reporting the display is ready because we only need to ensure the screen is in the
// right power state even as it continues to converge on the desired brightness.
- final boolean ready = mPendingScreenOnUnblocker == null &&
- (!mColorFadeEnabled ||
- (!mColorFadeOnAnimator.isStarted() && !mColorFadeOffAnimator.isStarted()))
+ final boolean ready = mPendingScreenOnUnblocker == null
+ && (!mColorFadeEnabled || (!mColorFadeOnAnimator.isStarted()
+ && !mColorFadeOffAnimator.isStarted()))
&& mPowerState.waitUntilClean(mCleanListener);
final boolean finished = ready
&& !mScreenBrightnessRampAnimator.isAnimating();
@@ -1688,7 +1702,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// Grab a wake lock if we have unfinished business.
if (!finished && !mUnfinishedBusiness) {
if (DEBUG) {
- Slog.d(TAG, "Unfinished business...");
+ Slog.d(mTag, "Unfinished business...");
}
mCallbacks.acquireSuspendBlocker(mSuspendBlockerIdUnfinishedBusiness);
mUnfinishedBusiness = true;
@@ -1702,7 +1716,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mDisplayReadyLocked = true;
if (DEBUG) {
- Slog.d(TAG, "Display ready!");
+ Slog.d(mTag, "Display ready!");
}
}
}
@@ -1712,7 +1726,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// Release the wake lock when we have no unfinished business.
if (finished && mUnfinishedBusiness) {
if (DEBUG) {
- Slog.d(TAG, "Finished business...");
+ Slog.d(mTag, "Finished business...");
}
mUnfinishedBusiness = false;
mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdUnfinishedBusiness);
@@ -1782,26 +1796,26 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
boolean changed = false;
changed |=
- mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightness,
- brightness);
+ mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightness,
+ brightness);
changed |=
- mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.adjustedBrightness,
- adjustedBrightness);
+ mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.adjustedBrightness,
+ adjustedBrightness);
changed |=
- mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMin,
- minBrightness);
+ mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMin,
+ minBrightness);
changed |=
- mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMax,
- maxBrightness);
+ mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMax,
+ maxBrightness);
changed |=
- mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.hbmMode,
- mHbmController.getHighBrightnessMode());
+ mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.hbmMode,
+ mHbmController.getHighBrightnessMode());
changed |=
- mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.hbmTransitionPoint,
- mHbmController.getTransitionPoint());
+ mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.hbmTransitionPoint,
+ mHbmController.getTransitionPoint());
changed |=
- mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.brightnessMaxReason,
- mBrightnessThrottler.getBrightnessMaxReason());
+ mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.brightnessMaxReason,
+ mBrightnessThrottler.getBrightnessMaxReason());
return changed;
}
@@ -1856,7 +1870,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0);
mPendingScreenOnUnblocker = new ScreenOnUnblocker();
mScreenOnBlockStartRealTime = SystemClock.elapsedRealtime();
- Slog.i(TAG, "Blocking screen on until initial contents have been drawn.");
+ Slog.i(mTag, "Blocking screen on until initial contents have been drawn.");
}
}
@@ -1864,7 +1878,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
if (mPendingScreenOnUnblocker != null) {
mPendingScreenOnUnblocker = null;
long delay = SystemClock.elapsedRealtime() - mScreenOnBlockStartRealTime;
- Slog.i(TAG, "Unblocked screen on after " + delay + " ms");
+ Slog.i(mTag, "Unblocked screen on after " + delay + " ms");
Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0);
}
}
@@ -1874,7 +1888,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_OFF_BLOCKED_TRACE_NAME, 0);
mPendingScreenOffUnblocker = new ScreenOffUnblocker();
mScreenOffBlockStartRealTime = SystemClock.elapsedRealtime();
- Slog.i(TAG, "Blocking screen off");
+ Slog.i(mTag, "Blocking screen off");
}
}
@@ -1882,7 +1896,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
if (mPendingScreenOffUnblocker != null) {
mPendingScreenOffUnblocker = null;
long delay = SystemClock.elapsedRealtime() - mScreenOffBlockStartRealTime;
- Slog.i(TAG, "Unblocked screen off after " + delay + " ms");
+ Slog.i(mTag, "Unblocked screen off after " + delay + " ms");
Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, SCREEN_OFF_BLOCKED_TRACE_NAME, 0);
}
}
@@ -1943,7 +1957,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
if (!isOff
&& (mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_OFF
- || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED)) {
+ || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED)) {
setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_ON);
if (mPowerState.getColorFadeLevel() == 0.0f) {
blockScreenOn();
@@ -2008,7 +2022,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
private void animateScreenBrightness(float target, float sdrTarget, float rate) {
if (DEBUG) {
- Slog.d(TAG, "Animating brightness: target=" + target + ", sdrTarget=" + sdrTarget
+ Slog.d(mTag, "Animating brightness: target=" + target + ", sdrTarget=" + sdrTarget
+ ", rate=" + rate);
}
if (mScreenBrightnessRampAnimator.animateTo(target, sdrTarget, rate)) {
@@ -2021,8 +2035,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
private void animateScreenStateChange(int target, boolean performScreenOffTransition) {
// If there is already an animation in progress, don't interfere with it.
- if (mColorFadeEnabled &&
- (mColorFadeOnAnimator.isStarted() || mColorFadeOffAnimator.isStarted())) {
+ if (mColorFadeEnabled
+ && (mColorFadeOnAnimator.isStarted() || mColorFadeOffAnimator.isStarted())) {
if (target != Display.STATE_ON) {
return;
}
@@ -2069,9 +2083,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
if (mPowerState.getColorFadeLevel() == 1.0f) {
mPowerState.dismissColorFade();
} else if (mPowerState.prepareColorFade(mContext,
- mColorFadeFadesConfig ?
- ColorFade.MODE_FADE :
- ColorFade.MODE_WARM_UP)) {
+ mColorFadeFadesConfig
+ ? ColorFade.MODE_FADE : ColorFade.MODE_WARM_UP)) {
mColorFadeOnAnimator.start();
} else {
mColorFadeOnAnimator.end();
@@ -2173,8 +2186,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mPowerState.dismissColorFadeResources();
} else if (performScreenOffTransition
&& mPowerState.prepareColorFade(mContext,
- mColorFadeFadesConfig ?
- ColorFade.MODE_FADE : ColorFade.MODE_COOL_DOWN)
+ mColorFadeFadesConfig
+ ? ColorFade.MODE_FADE : ColorFade.MODE_COOL_DOWN)
&& mPowerState.getScreenState() != Display.STATE_OFF) {
// Perform the screen off animation.
mColorFadeOffAnimator.start();
@@ -2245,12 +2258,12 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
if (mProximitySensorEnabled
&& mPendingProximity != PROXIMITY_UNKNOWN
&& mPendingProximityDebounceTime >= 0) {
- final long now = SystemClock.uptimeMillis();
+ final long now = mClock.uptimeMillis();
if (mPendingProximityDebounceTime <= now) {
if (mProximity != mPendingProximity) {
// if the status of the sensor changed, stop ignoring.
mIgnoreProximityUntilChanged = false;
- Slog.i(TAG, "No longer ignoring proximity [" + mPendingProximity + "]");
+ Slog.i(mTag, "No longer ignoring proximity [" + mPendingProximity + "]");
}
// Sensor reading accepted. Apply the change then release the wake lock.
mProximity = mPendingProximity;
@@ -2435,7 +2448,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
&& mProximity == PROXIMITY_POSITIVE) {
// Only ignore if it is still reporting positive (near)
mIgnoreProximityUntilChanged = true;
- Slog.i(TAG, "Ignoring proximity");
+ Slog.i(mTag, "Ignoring proximity");
updatePowerState();
}
}
@@ -2505,25 +2518,25 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
pw.println(" mScreenBrightnessForVrRangeMaximum=" + mScreenBrightnessForVrRangeMaximum);
pw.println(" mScreenBrightnessForVrDefault=" + mScreenBrightnessForVrDefault);
pw.println(" mUseSoftwareAutoBrightnessConfig=" + mUseSoftwareAutoBrightnessConfig);
- pw.println(" mAllowAutoBrightnessWhileDozingConfig=" +
- mAllowAutoBrightnessWhileDozingConfig);
+ pw.println(" mAllowAutoBrightnessWhileDozingConfig="
+ + mAllowAutoBrightnessWhileDozingConfig);
pw.println(" mSkipScreenOnBrightnessRamp=" + mSkipScreenOnBrightnessRamp);
pw.println(" mColorFadeFadesConfig=" + mColorFadeFadesConfig);
pw.println(" mColorFadeEnabled=" + mColorFadeEnabled);
synchronized (mCachedBrightnessInfo) {
- pw.println(" mCachedBrightnessInfo.brightness=" +
- mCachedBrightnessInfo.brightness.value);
- pw.println(" mCachedBrightnessInfo.adjustedBrightness=" +
- mCachedBrightnessInfo.adjustedBrightness.value);
- pw.println(" mCachedBrightnessInfo.brightnessMin=" +
- mCachedBrightnessInfo.brightnessMin.value);
- pw.println(" mCachedBrightnessInfo.brightnessMax=" +
- mCachedBrightnessInfo.brightnessMax.value);
+ pw.println(" mCachedBrightnessInfo.brightness="
+ + mCachedBrightnessInfo.brightness.value);
+ pw.println(" mCachedBrightnessInfo.adjustedBrightness="
+ + mCachedBrightnessInfo.adjustedBrightness.value);
+ pw.println(" mCachedBrightnessInfo.brightnessMin="
+ + mCachedBrightnessInfo.brightnessMin.value);
+ pw.println(" mCachedBrightnessInfo.brightnessMax="
+ + mCachedBrightnessInfo.brightnessMax.value);
pw.println(" mCachedBrightnessInfo.hbmMode=" + mCachedBrightnessInfo.hbmMode.value);
- pw.println(" mCachedBrightnessInfo.hbmTransitionPoint=" +
- mCachedBrightnessInfo.hbmTransitionPoint.value);
- pw.println(" mCachedBrightnessInfo.brightnessMaxReason =" +
- mCachedBrightnessInfo.brightnessMaxReason .value);
+ pw.println(" mCachedBrightnessInfo.hbmTransitionPoint="
+ + mCachedBrightnessInfo.hbmTransitionPoint.value);
+ pw.println(" mCachedBrightnessInfo.brightnessMaxReason ="
+ + mCachedBrightnessInfo.brightnessMaxReason.value);
}
pw.println(" mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig);
pw.println(" mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig);
@@ -2706,7 +2719,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
float hbmTransitionPoint = PowerManager.BRIGHTNESS_MAX;
- synchronized(mCachedBrightnessInfo) {
+ synchronized (mCachedBrightnessInfo) {
if (mCachedBrightnessInfo.hbmTransitionPoint == null) {
return;
}
@@ -2740,10 +2753,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
}
-
-
private final class DisplayControllerHandler extends Handler {
- public DisplayControllerHandler(Looper looper) {
+ DisplayControllerHandler(Looper looper) {
super(looper, null, true /*async*/);
}
@@ -2806,7 +2817,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
break;
case MSG_BRIGHTNESS_RAMP_DONE:
- if (mPowerState != null) {
+ if (mPowerState != null) {
final float brightness = mPowerState.getScreenBrightness();
reportStats(brightness);
}
@@ -2823,7 +2834,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
@Override
public void onSensorChanged(SensorEvent event) {
if (mProximitySensorEnabled) {
- final long time = SystemClock.uptimeMillis();
+ final long time = mClock.uptimeMillis();
final float distance = event.values[0];
boolean positive = distance >= 0.0f && distance < mProximityThreshold;
handleProximitySensorEvent(time, positive);
@@ -2838,7 +2849,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
private final class SettingsObserver extends ContentObserver {
- public SettingsObserver(Handler handler) {
+ SettingsObserver(Handler handler) {
super(handler);
}
@@ -2892,19 +2903,68 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
}
+ @VisibleForTesting
+ String getSuspendBlockerUnfinishedBusinessId(int displayId) {
+ return "[" + displayId + "]unfinished business";
+ }
+
+ String getSuspendBlockerOnStateChangedId(int displayId) {
+ return "[" + displayId + "]on state changed";
+ }
+
+ String getSuspendBlockerProxPositiveId(int displayId) {
+ return "[" + displayId + "]prox positive";
+ }
+
+ String getSuspendBlockerProxNegativeId(int displayId) {
+ return "[" + displayId + "]prox negative";
+ }
+
+ @VisibleForTesting
+ String getSuspendBlockerProxDebounceId(int displayId) {
+ return "[" + displayId + "]prox debounce";
+ }
+
+ /** Functional interface for providing time. */
+ @VisibleForTesting
+ interface Clock {
+ /**
+ * Returns current time in milliseconds since boot, not counting time spent in deep sleep.
+ */
+ long uptimeMillis();
+ }
+
+ @VisibleForTesting
+ static class Injector {
+ Clock getClock() {
+ return SystemClock::uptimeMillis;
+ }
+
+ DisplayPowerState getDisplayPowerState(DisplayBlanker blanker, ColorFade colorFade,
+ int displayId, int displayState) {
+ return new DisplayPowerState(blanker, colorFade, displayId, displayState);
+ }
+
+ DualRampAnimator<DisplayPowerState> getDualRampAnimator(DisplayPowerState dps,
+ FloatProperty<DisplayPowerState> firstProperty,
+ FloatProperty<DisplayPowerState> secondProperty) {
+ return new DualRampAnimator(dps, firstProperty, secondProperty);
+ }
+ }
+
static class CachedBrightnessInfo {
public MutableFloat brightness = new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
public MutableFloat adjustedBrightness =
- new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
public MutableFloat brightnessMin =
- new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
public MutableFloat brightnessMax =
- new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
public MutableInt hbmMode = new MutableInt(BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF);
public MutableFloat hbmTransitionPoint =
- new MutableFloat(HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID);
+ new MutableFloat(HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID);
public MutableInt brightnessMaxReason =
- new MutableInt(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE);
+ new MutableInt(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE);
public boolean checkAndSetFloat(MutableFloat mf, float f) {
if (mf.value != f) {
diff --git a/services/core/java/com/android/server/display/RampAnimator.java b/services/core/java/com/android/server/display/RampAnimator.java
index 690ec3fb5aaf..52b92c4c7ca6 100644
--- a/services/core/java/com/android/server/display/RampAnimator.java
+++ b/services/core/java/com/android/server/display/RampAnimator.java
@@ -29,7 +29,6 @@ import android.view.Choreographer;
class RampAnimator<T> {
private final T mObject;
private final FloatProperty<T> mProperty;
- private final Choreographer mChoreographer;
private float mCurrentValue;
private float mTargetValue;
@@ -43,18 +42,16 @@ class RampAnimator<T> {
private boolean mFirstTime = true;
- private Listener mListener;
RampAnimator(T object, FloatProperty<T> property) {
mObject = object;
mProperty = property;
- mChoreographer = Choreographer.getInstance();
}
/**
* Sets the maximum time that a brightness animation can take.
*/
- public void setAnimationTimeLimits(long animationRampIncreaseMaxTimeMillis,
+ void setAnimationTimeLimits(long animationRampIncreaseMaxTimeMillis,
long animationRampDecreaseMaxTimeMillis) {
mAnimationIncreaseMaxTimeSecs = (animationRampIncreaseMaxTimeMillis > 0)
? (animationRampIncreaseMaxTimeMillis / 1000.0f) : 0.0f;
@@ -63,7 +60,7 @@ class RampAnimator<T> {
}
/**
- * Starts animating towards the specified value.
+ * Sets the animation target and the rate of this ramp animator.
*
* If this is the first time the property is being set or if the rate is 0,
* the value jumps directly to the target.
@@ -72,7 +69,7 @@ class RampAnimator<T> {
* @param rate The convergence rate in units per second, or 0 to set the value immediately.
* @return True if the target differs from the previous target.
*/
- public boolean animateTo(float targetLinear, float rate) {
+ boolean setAnimationTarget(float targetLinear, float rate) {
// Convert the target from the linear into the HLG space.
final float target = BrightnessUtils.convertLinearToGamma(targetLinear);
@@ -84,13 +81,7 @@ class RampAnimator<T> {
mTargetValue = target;
mCurrentValue = target;
setPropertyValue(target);
- if (mAnimating) {
- mAnimating = false;
- cancelAnimationCallback();
- }
- if (mListener != null) {
- mListener.onAnimationEnd();
- }
+ mAnimating = false;
return true;
}
return false;
@@ -127,7 +118,6 @@ class RampAnimator<T> {
mAnimating = true;
mAnimatedValue = mCurrentValue;
mLastFrameTimeNanos = System.nanoTime();
- postAnimationCallback();
}
return changed;
@@ -136,18 +126,11 @@ class RampAnimator<T> {
/**
* Returns true if the animation is running.
*/
- public boolean isAnimating() {
+ boolean isAnimating() {
return mAnimating;
}
/**
- * Sets a listener to watch for animation events.
- */
- public void setListener(Listener listener) {
- mListener = listener;
- }
-
- /**
* Sets the brightness property by converting the given value from HLG space
* into linear space.
*/
@@ -156,78 +139,53 @@ class RampAnimator<T> {
mProperty.setValue(mObject, linearVal);
}
- private void postAnimationCallback() {
- mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimationCallback, null);
- }
-
- private void cancelAnimationCallback() {
- mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION, mAnimationCallback, null);
- }
-
- private final Runnable mAnimationCallback = new Runnable() {
- @Override // Choreographer callback
- public void run() {
- final long frameTimeNanos = mChoreographer.getFrameTimeNanos();
- final float timeDelta = (frameTimeNanos - mLastFrameTimeNanos)
- * 0.000000001f;
- mLastFrameTimeNanos = frameTimeNanos;
+ void performNextAnimationStep(long frameTimeNanos) {
+ final float timeDelta = (frameTimeNanos - mLastFrameTimeNanos) * 0.000000001f;
+ mLastFrameTimeNanos = frameTimeNanos;
- // Advance the animated value towards the target at the specified rate
- // and clamp to the target. This gives us the new current value but
- // we keep the animated value around to allow for fractional increments
- // towards the target.
- final float scale = ValueAnimator.getDurationScale();
- if (scale == 0) {
- // Animation off.
- mAnimatedValue = mTargetValue;
+ // Advance the animated value towards the target at the specified rate
+ // and clamp to the target. This gives us the new current value but
+ // we keep the animated value around to allow for fractional increments
+ // towards the target.
+ final float scale = ValueAnimator.getDurationScale();
+ if (scale == 0) {
+ // Animation off.
+ mAnimatedValue = mTargetValue;
+ } else {
+ final float amount = timeDelta * mRate / scale;
+ if (mTargetValue > mCurrentValue) {
+ mAnimatedValue = Math.min(mAnimatedValue + amount, mTargetValue);
} else {
- final float amount = timeDelta * mRate / scale;
- if (mTargetValue > mCurrentValue) {
- mAnimatedValue = Math.min(mAnimatedValue + amount, mTargetValue);
- } else {
- mAnimatedValue = Math.max(mAnimatedValue - amount, mTargetValue);
- }
- }
- final float oldCurrentValue = mCurrentValue;
- mCurrentValue = mAnimatedValue;
- if (oldCurrentValue != mCurrentValue) {
- setPropertyValue(mCurrentValue);
- }
- if (mTargetValue != mCurrentValue) {
- postAnimationCallback();
- } else {
- mAnimating = false;
- if (mListener != null) {
- mListener.onAnimationEnd();
- }
+ mAnimatedValue = Math.max(mAnimatedValue - amount, mTargetValue);
}
}
- };
+ final float oldCurrentValue = mCurrentValue;
+ mCurrentValue = mAnimatedValue;
+ if (oldCurrentValue != mCurrentValue) {
+ setPropertyValue(mCurrentValue);
+ }
+ if (mTargetValue == mCurrentValue) {
+ mAnimating = false;
+ }
+ }
public interface Listener {
void onAnimationEnd();
}
static class DualRampAnimator<T> {
+ private final Choreographer mChoreographer;
private final RampAnimator<T> mFirst;
private final RampAnimator<T> mSecond;
- private final Listener mInternalListener = new Listener() {
- @Override
- public void onAnimationEnd() {
- if (mListener != null && !isAnimating()) {
- mListener.onAnimationEnd();
- }
- }
- };
private Listener mListener;
+ private boolean mAwaitingCallback;
DualRampAnimator(T object, FloatProperty<T> firstProperty,
FloatProperty<T> secondProperty) {
- mFirst = new RampAnimator(object, firstProperty);
- mFirst.setListener(mInternalListener);
- mSecond = new RampAnimator(object, secondProperty);
- mSecond.setListener(mInternalListener);
+ mChoreographer = Choreographer.getInstance();
+ mFirst = new RampAnimator<>(object, firstProperty);
+ mSecond = new RampAnimator<>(object, secondProperty);
}
/**
@@ -253,17 +211,56 @@ class RampAnimator<T> {
* @return True if either target differs from the previous target.
*/
public boolean animateTo(float linearFirstTarget, float linearSecondTarget, float rate) {
- final boolean firstRetval = mFirst.animateTo(linearFirstTarget, rate);
- final boolean secondRetval = mSecond.animateTo(linearSecondTarget, rate);
- return firstRetval && secondRetval;
+ boolean animationTargetChanged = mFirst.setAnimationTarget(linearFirstTarget, rate);
+ animationTargetChanged |= mSecond.setAnimationTarget(linearSecondTarget, rate);
+ boolean shouldBeAnimating = isAnimating();
+
+ if (shouldBeAnimating != mAwaitingCallback) {
+ if (shouldBeAnimating) {
+ mAwaitingCallback = true;
+ postAnimationCallback();
+ } else if (mAwaitingCallback) {
+ mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION,
+ mAnimationCallback, null);
+ mAwaitingCallback = false;
+ }
+ }
+ return animationTargetChanged;
}
+ /**
+ * Sets a listener to watch for animation events.
+ */
public void setListener(Listener listener) {
mListener = listener;
}
+ /**
+ * Returns true if the animation is running.
+ */
public boolean isAnimating() {
- return mFirst.isAnimating() && mSecond.isAnimating();
+ return mFirst.isAnimating() || mSecond.isAnimating();
}
+
+ private void postAnimationCallback() {
+ mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimationCallback, null);
+ }
+
+ private final Runnable mAnimationCallback = new Runnable() {
+ @Override // Choreographer callback
+ public void run() {
+ long frameTimeNanos = mChoreographer.getFrameTimeNanos();
+ mFirst.performNextAnimationStep(frameTimeNanos);
+ mSecond.performNextAnimationStep(frameTimeNanos);
+ if (isAnimating()) {
+ postAnimationCallback();
+ } else {
+ if (mListener != null) {
+ mListener.onAnimationEnd();
+ }
+ mAwaitingCallback = false;
+ }
+ }
+ };
}
}
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 9f447657cd83..479629eae7ac 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -35,6 +35,7 @@ import static com.android.server.display.DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROU
import static com.android.server.display.DisplayDeviceInfo.FLAG_TOUCH_FEEDBACK_DISABLED;
import static com.android.server.display.DisplayDeviceInfo.FLAG_TRUSTED;
+import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Point;
import android.hardware.display.IVirtualDisplayCallback;
@@ -110,15 +111,20 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
} else {
uniqueId = UNIQUE_ID_PREFIX + ownerPackageName + ":" + uniqueId;
}
+ MediaProjectionCallback mediaProjectionCallback = null;
+ if (projection != null) {
+ mediaProjectionCallback = new MediaProjectionCallback(appToken);
+ }
VirtualDisplayDevice device = new VirtualDisplayDevice(displayToken, appToken,
- ownerUid, ownerPackageName, surface, flags, new Callback(callback, mHandler),
+ ownerUid, ownerPackageName, surface, flags,
+ new Callback(callback, mHandler), projection, mediaProjectionCallback,
uniqueId, uniqueIndex, virtualDisplayConfig);
mVirtualDisplayDevices.put(appToken, device);
try {
if (projection != null) {
- projection.registerCallback(new MediaProjectionCallback(appToken));
+ projection.registerCallback(mediaProjectionCallback);
}
appToken.linkToDeath(device, 0);
} catch (RemoteException ex) {
@@ -223,6 +229,8 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
final String mName;
private final int mFlags;
private final Callback mCallback;
+ @Nullable private final IMediaProjection mProjection;
+ @Nullable private final IMediaProjectionCallback mMediaProjectionCallback;
private int mWidth;
private int mHeight;
@@ -240,7 +248,8 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
public VirtualDisplayDevice(IBinder displayToken, IBinder appToken,
int ownerUid, String ownerPackageName, Surface surface, int flags,
- Callback callback, String uniqueId, int uniqueIndex,
+ Callback callback, IMediaProjection projection,
+ IMediaProjectionCallback mediaProjectionCallback, String uniqueId, int uniqueIndex,
VirtualDisplayConfig virtualDisplayConfig) {
super(VirtualDisplayAdapter.this, displayToken, uniqueId, getContext());
mAppToken = appToken;
@@ -254,6 +263,8 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
mSurface = surface;
mFlags = flags;
mCallback = callback;
+ mProjection = projection;
+ mMediaProjectionCallback = mediaProjectionCallback;
mDisplayState = Display.STATE_UNKNOWN;
mPendingChanges |= PENDING_SURFACE_CHANGE;
mUniqueIndex = uniqueIndex;
@@ -269,6 +280,13 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
Slog.i(TAG, "Virtual display device released because application token died: "
+ mOwnerPackageName);
destroyLocked(false);
+ if (mProjection != null && mMediaProjectionCallback != null) {
+ try {
+ mProjection.unregisterCallback(mMediaProjectionCallback);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to unregister callback in binderDied", e);
+ }
+ }
sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_REMOVED);
}
}
@@ -279,6 +297,13 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
mSurface = null;
}
SurfaceControl.destroyDisplay(getDisplayTokenLocked());
+ if (mProjection != null && mMediaProjectionCallback != null) {
+ try {
+ mProjection.unregisterCallback(mMediaProjectionCallback);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to unregister callback in destroy", e);
+ }
+ }
if (binderAlive) {
mCallback.dispatchDisplayStopped();
}
diff --git a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
index 6698612dc652..d831dbd4bb23 100644
--- a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
+++ b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
@@ -27,8 +27,9 @@ import android.util.TimeUtils;
public final class BrightnessEvent {
public static final int FLAG_RBC = 0x1;
public static final int FLAG_INVALID_LUX = 0x2;
- public static final int FLAG_DOZE_SCALE = 0x3;
- public static final int FLAG_USER_SET = 0x4;
+ public static final int FLAG_DOZE_SCALE = 0x4;
+ public static final int FLAG_USER_SET = 0x8;
+ public static final int FLAG_IDLE_CURVE = 0x16;
private BrightnessReason mReason = new BrightnessReason();
private int mDisplayId;
@@ -257,6 +258,7 @@ public final class BrightnessEvent {
return ((mFlags & FLAG_USER_SET) != 0 ? "user_set " : "")
+ ((mFlags & FLAG_RBC) != 0 ? "rbc " : "")
+ ((mFlags & FLAG_INVALID_LUX) != 0 ? "invalid_lux " : "")
- + ((mFlags & FLAG_DOZE_SCALE) != 0 ? "doze_scale " : "");
+ + ((mFlags & FLAG_DOZE_SCALE) != 0 ? "doze_scale " : "")
+ + ((mFlags & FLAG_IDLE_CURVE) != 0 ? "idle_curve " : "");
}
}
diff --git a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
index 8180e66166d9..0c889c2765bb 100644
--- a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
+++ b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
@@ -32,11 +32,10 @@ final class HandwritingEventReceiverSurface {
public static final String TAG = HandwritingEventReceiverSurface.class.getSimpleName();
static final boolean DEBUG = HandwritingModeController.DEBUG;
- // Place the layer below the highest layer to place it under gesture monitors. If the surface
- // is above gesture monitors, then edge-back and swipe-up gestures won't work when this surface
- // is intercepting.
+ // Place the layer at the highest layer so stylus cannot trigger gesture monitors
+ // (e.g. navigation bar, edge-back, etc) while handwriting is ongoing.
// TODO(b/217538817): Specify the ordering in WM by usage.
- private static final int HANDWRITING_SURFACE_LAYER = Integer.MAX_VALUE - 1;
+ private static final int HANDWRITING_SURFACE_LAYER = Integer.MAX_VALUE;
private final InputWindowHandle mWindowHandle;
private final InputChannel mClientChannel;
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
index 6c75dbf033d6..eb73234549c9 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
@@ -223,6 +223,17 @@ final class IInputMethodInvoker {
}
@AnyThread
+ boolean updateEditorToolType(int toolType) {
+ try {
+ mTarget.updateEditorToolType(toolType);
+ } catch (RemoteException e) {
+ logRemoteException(e);
+ return false;
+ }
+ return true;
+ }
+
+ @AnyThread
void changeInputMethodSubtype(InputMethodSubtype subtype) {
try {
mTarget.changeInputMethodSubtype(subtype);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 6f37b516baea..d48acb18722a 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -132,6 +132,7 @@ import android.view.DisplayInfo;
import android.view.IWindowManager;
import android.view.InputChannel;
import android.view.InputDevice;
+import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowManager.DisplayImePolicy;
@@ -3320,7 +3321,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@Override
public boolean showSoftInput(IInputMethodClient client, IBinder windowToken, int flags,
- ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+ int lastClickTooType, ResultReceiver resultReceiver,
+ @SoftInputShowHideReason int reason) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showSoftInput");
int uid = Binder.getCallingUid();
ImeTracing.getInstance().triggerManagerServiceDump(
@@ -3332,7 +3334,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
final long ident = Binder.clearCallingIdentity();
try {
if (DEBUG) Slog.v(TAG, "Client requesting input be shown");
- return showCurrentInputLocked(windowToken, flags, resultReceiver, reason);
+ return showCurrentInputLocked(
+ windowToken, flags, lastClickTooType, resultReceiver, reason);
} finally {
Binder.restoreCallingIdentity(ident);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -3404,8 +3407,15 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@GuardedBy("ImfLock.class")
- boolean showCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason) {
+ boolean showCurrentInputLocked(IBinder windowToken, int flags,
+ ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+ return showCurrentInputLocked(
+ windowToken, flags, MotionEvent.TOOL_TYPE_UNKNOWN, resultReceiver, reason);
+ }
+
+ @GuardedBy("ImfLock.class")
+ private boolean showCurrentInputLocked(IBinder windowToken, int flags, int lastClickToolType,
+ ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
mShowRequested = true;
if (mAccessibilityRequestingNoSoftKeyboard || mImeHiddenByDisplayPolicy) {
return false;
@@ -3434,6 +3444,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
+ ", " + showFlags + ", " + resultReceiver + ") for reason: "
+ InputMethodDebug.softInputDisplayReasonToString(reason));
}
+
+ if (lastClickToolType != MotionEvent.TOOL_TYPE_UNKNOWN) {
+ curMethod.updateEditorToolType(lastClickToolType);
+ }
// TODO(b/192412909): Check if we can always call onShowHideSoftInputRequested() or not.
if (curMethod.showSoftInput(showInputToken, showFlags, resultReceiver)) {
onShowHideSoftInputRequested(true /* show */, windowToken, reason);
diff --git a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
index 2bdeab4703a8..f144cf8efbba 100644
--- a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
+++ b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
@@ -44,7 +44,6 @@ import java.util.Set;
public class BiometricDeferredQueue {
private static final String TAG = "BiometricDeferredQueue";
- @NonNull private final Context mContext;
@NonNull private final SyntheticPasswordManager mSpManager;
@NonNull private final Handler mHandler;
@Nullable private FingerprintManager mFingerprintManager;
@@ -131,9 +130,7 @@ public class BiometricDeferredQueue {
mFaceResetLockoutTask = null;
};
- BiometricDeferredQueue(@NonNull Context context, @NonNull SyntheticPasswordManager spManager,
- @NonNull Handler handler) {
- mContext = context;
+ BiometricDeferredQueue(@NonNull SyntheticPasswordManager spManager, @NonNull Handler handler) {
mSpManager = spManager;
mHandler = handler;
mPendingResetLockoutsForFingerprint = new ArrayList<>();
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index b86fa7af8bd8..c5f73625379f 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -67,7 +67,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
-import android.content.res.Resources;
import android.database.ContentObserver;
import android.database.sqlite.SQLiteDatabase;
import android.hardware.authsecret.V1_0.IAuthSecret;
@@ -238,7 +237,6 @@ public class LockSettingsService extends ILockSettings.Stub {
@VisibleForTesting
protected final SyntheticPasswordManager mSpManager;
- private final KeyStore mKeyStore;
private final java.security.KeyStore mJavaKeyStore;
private final RecoverableKeyStoreManager mRecoverableKeyStoreManager;
private ManagedProfilePasswordCache mManagedProfilePasswordCache;
@@ -570,7 +568,6 @@ public class LockSettingsService extends ILockSettings.Stub {
protected LockSettingsService(Injector injector) {
mInjector = injector;
mContext = injector.getContext();
- mKeyStore = injector.getKeyStore();
mJavaKeyStore = injector.getJavaKeyStore();
mRecoverableKeyStoreManager = injector.getRecoverableKeyStoreManager();
mHandler = injector.getHandler(injector.getServiceThread());
@@ -595,7 +592,7 @@ public class LockSettingsService extends ILockSettings.Stub {
mSpManager = injector.getSyntheticPasswordManager(mStorage);
mManagedProfilePasswordCache = injector.getManagedProfilePasswordCache(mJavaKeyStore);
- mBiometricDeferredQueue = new BiometricDeferredQueue(mContext, mSpManager, mHandler);
+ mBiometricDeferredQueue = new BiometricDeferredQueue(mSpManager, mHandler);
mRebootEscrowManager = injector.getRebootEscrowManager(new RebootEscrowCallbacks(),
mStorage);
@@ -637,7 +634,6 @@ public class LockSettingsService extends ILockSettings.Stub {
}
private void showEncryptionNotificationForProfile(UserHandle user, String reason) {
- Resources r = mContext.getResources();
CharSequence title = getEncryptionNotificationTitle();
CharSequence message = getEncryptionNotificationMessage();
CharSequence detail = getEncryptionNotificationDetail();
@@ -657,7 +653,7 @@ public class LockSettingsService extends ILockSettings.Stub {
PendingIntent intent = PendingIntent.getActivity(mContext, 0, unlockIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
- Slog.d(TAG, String.format("showing encryption notification, user: %d; reason: %s",
+ Slog.d(TAG, TextUtils.formatSimple("showing encryption notification, user: %d; reason: %s",
user.getIdentifier(), reason));
showEncryptionNotification(user, title, message, detail, intent);
@@ -839,7 +835,7 @@ public class LockSettingsService extends ILockSettings.Stub {
if (mContext.checkCallingOrSelfPermission(PERMISSION) != PERMISSION_GRANTED) {
EventLog.writeEvent(0x534e4554, "28251513", getCallingUid(), ""); // SafetyNet
}
- checkWritePermission(UserHandle.USER_SYSTEM);
+ checkWritePermission();
mHasSecureLockScreen = mContext.getPackageManager()
.hasSystemFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN);
@@ -979,7 +975,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
- private final void checkWritePermission(int userId) {
+ private final void checkWritePermission() {
mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsWrite");
}
@@ -987,7 +983,7 @@ public class LockSettingsService extends ILockSettings.Stub {
mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsRead");
}
- private final void checkPasswordHavePermission(int userId) {
+ private final void checkPasswordHavePermission() {
if (mContext.checkCallingOrSelfPermission(PERMISSION) != PERMISSION_GRANTED) {
EventLog.writeEvent(0x534e4554, "28251513", getCallingUid(), ""); // SafetyNet
}
@@ -1056,7 +1052,7 @@ public class LockSettingsService extends ILockSettings.Stub {
@Override
public void setSeparateProfileChallengeEnabled(int userId, boolean enabled,
LockscreenCredential profileUserPassword) {
- checkWritePermission(userId);
+ checkWritePermission();
if (!mHasSecureLockScreen
&& profileUserPassword != null
&& profileUserPassword.getType() != CREDENTIAL_TYPE_NONE) {
@@ -1103,19 +1099,19 @@ public class LockSettingsService extends ILockSettings.Stub {
@Override
public void setBoolean(String key, boolean value, int userId) {
- checkWritePermission(userId);
+ checkWritePermission();
mStorage.setBoolean(key, value, userId);
}
@Override
public void setLong(String key, long value, int userId) {
- checkWritePermission(userId);
+ checkWritePermission();
mStorage.setLong(key, value, userId);
}
@Override
public void setString(String key, String value, int userId) {
- checkWritePermission(userId);
+ checkWritePermission();
mStorage.setString(key, value, userId);
}
@@ -1154,7 +1150,7 @@ public class LockSettingsService extends ILockSettings.Stub {
*/
@Override
public int getCredentialType(int userId) {
- checkPasswordHavePermission(userId);
+ checkPasswordHavePermission();
return getCredentialTypeInternal(userId);
}
@@ -1627,7 +1623,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
onSyntheticPasswordKnown(userId, sp);
- setLockCredentialWithSpLocked(credential, sp, userId, isLockTiedToParent);
+ setLockCredentialWithSpLocked(credential, sp, userId);
sendCredentialsOnChangeIfRequired(credential, userId, isLockTiedToParent);
return true;
}
@@ -1641,15 +1637,18 @@ public class LockSettingsService extends ILockSettings.Stub {
if (newCredential.isPattern()) {
setBoolean(LockPatternUtils.PATTERN_EVER_CHOSEN_KEY, true, userHandle);
}
+ updatePasswordHistory(newCredential, userHandle);
mContext.getSystemService(TrustManager.class).reportEnabledTrustAgentsChanged(userHandle);
}
/**
* Store the hash of the new password in the password history list, if device policy enforces
* a password history requirement.
+ *
+ * This must not be called while the mSpManager lock is held, as this calls into
+ * DevicePolicyManagerService to get the requested password history length.
*/
- private void updatePasswordHistory(SyntheticPassword sp, LockscreenCredential password,
- int userHandle, boolean isLockTiedToParent) {
+ private void updatePasswordHistory(LockscreenCredential password, int userHandle) {
if (password.isNone()) {
return;
}
@@ -1657,10 +1656,6 @@ public class LockSettingsService extends ILockSettings.Stub {
// Do not keep track of historical patterns
return;
}
- if (isLockTiedToParent) {
- // Do not keep track of historical auto-generated profile passwords
- return;
- }
// Add the password to the password history.
String passwordHistory = getString(
LockPatternUtils.PASSWORD_HISTORY_KEY, /* defaultValue= */ null, userHandle);
@@ -1671,9 +1666,16 @@ public class LockSettingsService extends ILockSettings.Stub {
if (passwordHistoryLength == 0) {
passwordHistory = "";
} else {
- final byte[] hashFactor = sp.derivePasswordHashFactor();
+ final byte[] hashFactor = getHashFactor(password, userHandle);
final byte[] salt = getSalt(userHandle).getBytes();
String hash = password.passwordToHistoryHash(salt, hashFactor);
+ if (hash == null) {
+ // This should never happen, as all information needed to compute the hash should be
+ // available. In particular, unwrapping the SP in getHashFactor() should always
+ // succeed, as we're using the LSKF that was just set.
+ Slog.e(TAG, "Failed to compute password hash; password history won't be updated");
+ return;
+ }
if (TextUtils.isEmpty(passwordHistory)) {
passwordHistory = hash;
} else {
@@ -1961,7 +1963,7 @@ public class LockSettingsService extends ILockSettings.Stub {
@Override
public void resetKeyStore(int userId) {
- checkWritePermission(userId);
+ checkWritePermission();
if (DEBUG) Slog.v(TAG, "Reset keystore for user: " + userId);
List<Integer> profileUserIds = new ArrayList<>();
List<LockscreenCredential> profileUserDecryptedPasswords = new ArrayList<>();
@@ -2269,7 +2271,7 @@ public class LockSettingsService extends ILockSettings.Stub {
@Override
public void requireStrongAuth(int strongAuthReason, int userId) {
- checkWritePermission(userId);
+ checkWritePermission();
mStrongAuth.requireStrongAuth(strongAuthReason, userId);
}
@@ -2287,7 +2289,7 @@ public class LockSettingsService extends ILockSettings.Stub {
@Override
public void userPresent(int userId) {
- checkWritePermission(userId);
+ checkWritePermission();
mStrongAuth.reportUnlock(userId);
}
@@ -2315,9 +2317,9 @@ public class LockSettingsService extends ILockSettings.Stub {
final int origPid = Binder.getCallingPid();
final int origUid = Binder.getCallingUid();
+ Slog.e(TAG, "Caller pid " + origPid + " Caller uid " + origUid);
// The original identity is an opaque integer.
final long origId = Binder.clearCallingIdentity();
- Slog.e(TAG, "Caller pid " + origPid + " Caller uid " + origUid);
try {
final LockSettingsShellCommand command =
new LockSettingsShellCommand(new LockPatternUtils(mContext), mContext, origPid,
@@ -2644,7 +2646,7 @@ public class LockSettingsService extends ILockSettings.Stub {
*/
@GuardedBy("mSpManager")
private long setLockCredentialWithSpLocked(LockscreenCredential credential,
- SyntheticPassword sp, int userId, boolean isLockTiedToParent) {
+ SyntheticPassword sp, int userId) {
if (DEBUG) Slog.d(TAG, "setLockCredentialWithSpLocked: user=" + userId);
final int savedCredentialType = getCredentialTypeInternal(userId);
final long oldProtectorId = getCurrentLskfBasedProtectorId(userId);
@@ -2682,7 +2684,6 @@ public class LockSettingsService extends ILockSettings.Stub {
LockPatternUtils.invalidateCredentialTypeCache();
synchronizeUnifiedWorkChallengeForProfiles(userId, profilePasswords);
- updatePasswordHistory(sp, credential, userId, isLockTiedToParent);
setUserPasswordMetrics(credential, userId);
mManagedProfilePasswordCache.removePassword(userId);
if (savedCredentialType != CREDENTIAL_TYPE_NONE) {
@@ -2842,7 +2843,7 @@ public class LockSettingsService extends ILockSettings.Stub {
synchronized (mSpManager) {
disableEscrowTokenOnNonManagedDevicesIfNeeded(userId);
for (long handle : mSpManager.getPendingTokensForUser(userId)) {
- Slog.i(TAG, String.format("activateEscrowTokens: %x %d ", handle, userId));
+ Slog.i(TAG, TextUtils.formatSimple("activateEscrowTokens: %x %d ", handle, userId));
mSpManager.createTokenBasedProtector(handle, sp, userId);
}
}
@@ -2928,8 +2929,7 @@ public class LockSettingsService extends ILockSettings.Stub {
return false;
}
onSyntheticPasswordKnown(userId, result.syntheticPassword);
- setLockCredentialWithSpLocked(credential, result.syntheticPassword, userId,
- /* isLockTiedToParent= */ false);
+ setLockCredentialWithSpLocked(credential, result.syntheticPassword, userId);
return true;
}
@@ -3004,14 +3004,14 @@ public class LockSettingsService extends ILockSettings.Stub {
pw.println("User " + userId);
pw.increaseIndent();
synchronized (mSpManager) {
- pw.println(String.format("LSKF-based SP protector ID: %x",
+ pw.println(TextUtils.formatSimple("LSKF-based SP protector ID: %x",
getCurrentLskfBasedProtectorId(userId)));
- pw.println(String.format("LSKF last changed: %s (previous protector: %x)",
+ pw.println(TextUtils.formatSimple("LSKF last changed: %s (previous protector: %x)",
timestampToString(getLong(LSKF_LAST_CHANGED_TIME_KEY, 0, userId)),
getLong(PREV_LSKF_BASED_PROTECTOR_ID_KEY, 0, userId)));
}
try {
- pw.println(String.format("SID: %x",
+ pw.println(TextUtils.formatSimple("SID: %x",
getGateKeeperService().getSecureUserId(userId)));
} catch (RemoteException e) {
// ignore.
@@ -3022,7 +3022,7 @@ public class LockSettingsService extends ILockSettings.Stub {
pw.println("CredentialType: " + credentialTypeToString(
getCredentialTypeInternal(userId)));
pw.println("SeparateChallenge: " + getSeparateProfileChallengeEnabledInternal(userId));
- pw.println(String.format("Metrics: %s",
+ pw.println(TextUtils.formatSimple("Metrics: %s",
getUserPasswordMetrics(userId) != null ? "known" : "unknown"));
pw.decreaseIndent();
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index e5b50362b03d..db036b06a77a 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -17,7 +17,6 @@
package com.android.server.locksettings;
import static android.content.Context.USER_SERVICE;
-import static android.text.TextUtils.formatSimple;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
import static com.android.internal.widget.LockPatternUtils.USER_FRP;
@@ -437,7 +436,7 @@ class LockSettingsStorage {
}
private File getSyntheticPasswordStateFileForUser(int userId, long protectorId, String name) {
- String fileName = formatSimple("%016x.%s", protectorId, name);
+ String fileName = TextUtils.formatSimple("%016x.%s", protectorId, name);
return new File(getSyntheticPasswordDirectoryForUser(userId), fileName);
}
@@ -643,13 +642,13 @@ class LockSettingsStorage {
final UserManager um = UserManager.get(mContext);
for (UserInfo user : um.getUsers()) {
File userPath = getSyntheticPasswordDirectoryForUser(user.id);
- pw.println(String.format("User %d [%s]:", user.id, userPath));
+ pw.println(TextUtils.formatSimple("User %d [%s]:", user.id, userPath));
pw.increaseIndent();
File[] files = userPath.listFiles();
if (files != null) {
Arrays.sort(files);
for (File file : files) {
- pw.println(String.format("%6d %s %s", file.length(),
+ pw.println(TextUtils.formatSimple("%6d %s %s", file.length(),
LockSettingsService.timestampToString(file.lastModified()),
file.getName()));
}
diff --git a/services/core/java/com/android/server/locksettings/PasswordSlotManager.java b/services/core/java/com/android/server/locksettings/PasswordSlotManager.java
index 17aca1576e40..21fb403a41c9 100644
--- a/services/core/java/com/android/server/locksettings/PasswordSlotManager.java
+++ b/services/core/java/com/android/server/locksettings/PasswordSlotManager.java
@@ -72,8 +72,6 @@ public class PasswordSlotManager {
/**
* Notify the manager of which slots are definitively in use by the current OS image.
- *
- * @throws RuntimeException
*/
public void refreshActiveSlots(Set<Integer> activeSlots) throws RuntimeException {
if (mSlotMap == null) {
@@ -103,8 +101,6 @@ public class PasswordSlotManager {
/**
* Mark the given slot as in use by the current OS image.
- *
- * @throws RuntimeException
*/
public void markSlotInUse(int slot) throws RuntimeException {
ensureSlotMapLoaded();
@@ -117,8 +113,6 @@ public class PasswordSlotManager {
/**
* Mark the given slot as no longer in use by the current OS image.
- *
- * @throws RuntimeException
*/
public void markSlotDeleted(int slot) throws RuntimeException {
ensureSlotMapLoaded();
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowKeyStoreManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowKeyStoreManager.java
index da29368df082..41cdb42cd8e2 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowKeyStoreManager.java
@@ -64,9 +64,9 @@ public class RebootEscrowKeyStoreManager {
private SecretKey getKeyStoreEncryptionKeyLocked() {
try {
KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
- KeyStore.LoadStoreParameter loadStoreParameter = null;
// Load from the specific namespace if keystore2 is enabled.
- loadStoreParameter = new AndroidKeyStoreLoadStoreParameter(KEY_STORE_NAMESPACE);
+ KeyStore.LoadStoreParameter loadStoreParameter =
+ new AndroidKeyStoreLoadStoreParameter(KEY_STORE_NAMESPACE);
keyStore.load(loadStoreParameter);
return (SecretKey) keyStore.getKey(REBOOT_ESCROW_KEY_STORE_ENCRYPTION_KEY_NAME,
null);
@@ -86,9 +86,9 @@ public class RebootEscrowKeyStoreManager {
synchronized (mKeyStoreLock) {
try {
KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
- KeyStore.LoadStoreParameter loadStoreParameter = null;
// Load from the specific namespace if keystore2 is enabled.
- loadStoreParameter = new AndroidKeyStoreLoadStoreParameter(KEY_STORE_NAMESPACE);
+ KeyStore.LoadStoreParameter loadStoreParameter =
+ new AndroidKeyStoreLoadStoreParameter(KEY_STORE_NAMESPACE);
keyStore.load(loadStoreParameter);
keyStore.deleteEntry(REBOOT_ESCROW_KEY_STORE_ENCRYPTION_KEY_NAME);
} catch (IOException | GeneralSecurityException e) {
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
index a9318449c8a0..2a6ae44a99e8 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
@@ -23,6 +23,7 @@ import android.security.keystore.KeyProtection;
import android.security.keystore2.AndroidKeyStoreLoadStoreParameter;
import android.system.keystore2.Domain;
import android.system.keystore2.KeyDescriptor;
+import android.text.TextUtils;
import android.util.Slog;
import com.android.internal.util.ArrayUtils;
@@ -301,7 +302,7 @@ public class SyntheticPasswordCrypto {
// Treat this as a success so we don't migrate again.
return true;
} else {
- Slog.e(TAG, String.format("Failed to migrate key: %d", err));
+ Slog.e(TAG, TextUtils.formatSimple("Failed to migrate key: %d", err));
return false;
}
}
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index d9cf353b476c..f1afb96866eb 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -36,6 +36,7 @@ import android.security.GateKeeper;
import android.security.Scrypt;
import android.service.gatekeeper.GateKeeperResponse;
import android.service.gatekeeper.IGateKeeperService;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
@@ -144,7 +145,7 @@ public class SyntheticPasswordManager {
private static final int PASSWORD_SCRYPT_LOG_R = 3;
private static final int PASSWORD_SCRYPT_LOG_P = 1;
private static final int PASSWORD_SALT_LENGTH = 16;
- private static final int PASSWORD_TOKEN_LENGTH = 32;
+ private static final int STRETCHED_LSKF_LENGTH = 32;
private static final String TAG = "SyntheticPasswordManager";
private static final byte[] PERSONALIZATION_SECDISCARDABLE = "secdiscardable-transform".getBytes();
@@ -590,7 +591,7 @@ public class SyntheticPasswordManager {
// Remove potential persistent state (in RPMB), to prevent them from accumulating and
// causing problems.
try {
- gatekeeper.clearSecureUserId(fakeUid(userId));
+ gatekeeper.clearSecureUserId(fakeUserId(userId));
} catch (RemoteException ignore) {
Slog.w(TAG, "Failed to clear SID from gatekeeper");
}
@@ -772,7 +773,7 @@ public class SyntheticPasswordManager {
LockscreenCredential credential, SyntheticPassword sp, int userId) {
long protectorId = generateProtectorId();
PasswordData pwd = PasswordData.create(credential.getType());
- byte[] pwdToken = computePasswordToken(credential, pwd);
+ byte[] stretchedLskf = stretchLskf(credential, pwd);
final long sid;
final byte[] protectorSecret;
@@ -780,7 +781,7 @@ public class SyntheticPasswordManager {
// Protector uses Weaver to verify the LSKF
int weaverSlot = getNextAvailableWeaverSlot();
Slog.i(TAG, "Weaver enroll password to slot " + weaverSlot + " for user " + userId);
- byte[] weaverSecret = weaverEnroll(weaverSlot, passwordTokenToWeaverKey(pwdToken),
+ byte[] weaverSecret = weaverEnroll(weaverSlot, stretchedLskfToWeaverKey(stretchedLskf),
null);
if (weaverSecret == null) {
throw new IllegalStateException(
@@ -793,21 +794,21 @@ public class SyntheticPasswordManager {
pwd.passwordHandle = null;
sid = GateKeeper.INVALID_SECURE_USER_ID;
- protectorSecret = transformUnderWeaverSecret(pwdToken, weaverSecret);
+ protectorSecret = transformUnderWeaverSecret(stretchedLskf, weaverSecret);
} else {
// Protector uses Gatekeeper to verify the LSKF
// In case GK enrollment leaves persistent state around (in RPMB), this will nuke them
// to prevent them from accumulating and causing problems.
try {
- gatekeeper.clearSecureUserId(fakeUid(userId));
+ gatekeeper.clearSecureUserId(fakeUserId(userId));
} catch (RemoteException ignore) {
Slog.w(TAG, "Failed to clear SID from gatekeeper");
}
GateKeeperResponse response;
try {
- response = gatekeeper.enroll(fakeUid(userId), null, null,
- passwordTokenToGkInput(pwdToken));
+ response = gatekeeper.enroll(fakeUserId(userId), null, null,
+ stretchedLskfToGkPassword(stretchedLskf));
} catch (RemoteException e) {
throw new IllegalStateException("Failed to enroll LSKF for new SP protector for "
+ "user " + userId, e);
@@ -818,7 +819,7 @@ public class SyntheticPasswordManager {
}
pwd.passwordHandle = response.getPayload();
sid = sidFromPasswordHandle(pwd.passwordHandle);
- protectorSecret = transformUnderSecdiscardable(pwdToken,
+ protectorSecret = transformUnderSecdiscardable(stretchedLskf,
createSecdiscardable(protectorId, userId));
// No need to pass in quality since the credential type already encodes sufficient info
synchronizeFrpPassword(pwd, 0, userId);
@@ -836,12 +837,13 @@ public class SyntheticPasswordManager {
PersistentData persistentData = mStorage.readPersistentDataBlock();
if (persistentData.type == PersistentData.TYPE_SP) {
PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
- byte[] pwdToken = computePasswordToken(userCredential, pwd);
+ byte[] stretchedLskf = stretchLskf(userCredential, pwd);
GateKeeperResponse response;
try {
- response = gatekeeper.verifyChallenge(fakeUid(persistentData.userId),
- 0 /* challenge */, pwd.passwordHandle, passwordTokenToGkInput(pwdToken));
+ response = gatekeeper.verifyChallenge(fakeUserId(persistentData.userId),
+ 0 /* challenge */, pwd.passwordHandle,
+ stretchedLskfToGkPassword(stretchedLskf));
} catch (RemoteException e) {
Slog.e(TAG, "FRP verifyChallenge failed", e);
return VerifyCredentialResponse.ERROR;
@@ -853,10 +855,10 @@ public class SyntheticPasswordManager {
return VerifyCredentialResponse.ERROR;
}
PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
- byte[] pwdToken = computePasswordToken(userCredential, pwd);
+ byte[] stretchedLskf = stretchLskf(userCredential, pwd);
int weaverSlot = persistentData.userId;
- return weaverVerify(weaverSlot, passwordTokenToWeaverKey(pwdToken)).stripPayload();
+ return weaverVerify(weaverSlot, stretchedLskfToWeaverKey(stretchedLskf)).stripPayload();
} else {
Slog.e(TAG, "persistentData.type must be TYPE_SP or TYPE_SP_WEAVER, but is "
+ persistentData.type);
@@ -1028,13 +1030,13 @@ public class SyntheticPasswordManager {
userId));
if (!credential.checkAgainstStoredType(pwd.credentialType)) {
- Slog.e(TAG, String.format("Credential type mismatch: expected %d actual %d",
+ Slog.e(TAG, TextUtils.formatSimple("Credential type mismatch: expected %d actual %d",
pwd.credentialType, credential.getType()));
result.gkResponse = VerifyCredentialResponse.ERROR;
return result;
}
- byte[] pwdToken = computePasswordToken(credential, pwd);
+ byte[] stretchedLskf = stretchLskf(credential, pwd);
final byte[] protectorSecret;
final long sid;
@@ -1046,20 +1048,20 @@ public class SyntheticPasswordManager {
result.gkResponse = VerifyCredentialResponse.ERROR;
return result;
}
- result.gkResponse = weaverVerify(weaverSlot, passwordTokenToWeaverKey(pwdToken));
+ result.gkResponse = weaverVerify(weaverSlot, stretchedLskfToWeaverKey(stretchedLskf));
if (result.gkResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
return result;
}
sid = GateKeeper.INVALID_SECURE_USER_ID;
- protectorSecret = transformUnderWeaverSecret(pwdToken,
+ protectorSecret = transformUnderWeaverSecret(stretchedLskf,
result.gkResponse.getGatekeeperHAT());
} else {
// Protector uses Gatekeeper to verify the LSKF
- byte[] gkPwdToken = passwordTokenToGkInput(pwdToken);
+ byte[] gkPassword = stretchedLskfToGkPassword(stretchedLskf);
GateKeeperResponse response;
try {
- response = gatekeeper.verifyChallenge(fakeUid(userId), 0L,
- pwd.passwordHandle, gkPwdToken);
+ response = gatekeeper.verifyChallenge(fakeUserId(userId), 0L,
+ pwd.passwordHandle, gkPassword);
} catch (RemoteException e) {
Slog.e(TAG, "gatekeeper verify failed", e);
result.gkResponse = VerifyCredentialResponse.ERROR;
@@ -1071,8 +1073,8 @@ public class SyntheticPasswordManager {
if (response.getShouldReEnroll()) {
GateKeeperResponse reenrollResponse;
try {
- reenrollResponse = gatekeeper.enroll(fakeUid(userId),
- pwd.passwordHandle, gkPwdToken, gkPwdToken);
+ reenrollResponse = gatekeeper.enroll(fakeUserId(userId),
+ pwd.passwordHandle, gkPassword, gkPassword);
} catch (RemoteException e) {
Slog.w(TAG, "Fail to invoke gatekeeper.enroll", e);
reenrollResponse = GateKeeperResponse.ERROR;
@@ -1098,7 +1100,7 @@ public class SyntheticPasswordManager {
return result;
}
sid = sidFromPasswordHandle(pwd.passwordHandle);
- protectorSecret = transformUnderSecdiscardable(pwdToken,
+ protectorSecret = transformUnderSecdiscardable(stretchedLskf,
loadSecdiscardable(protectorId, userId));
}
// Supplied credential passes first stage weaver/gatekeeper check so it should be correct.
@@ -1451,8 +1453,8 @@ public class SyntheticPasswordManager {
return result;
}
- private int fakeUid(int uid) {
- return 100000 + uid;
+ private int fakeUserId(int userId) {
+ return 100000 + userId;
}
protected static byte[] secureRandom(int length) {
@@ -1465,21 +1467,23 @@ public class SyntheticPasswordManager {
}
private String getProtectorKeyAlias(long protectorId) {
- return String.format("%s%x", PROTECTOR_KEY_ALIAS_PREFIX, protectorId);
+ return TextUtils.formatSimple("%s%x", PROTECTOR_KEY_ALIAS_PREFIX, protectorId);
}
- private byte[] computePasswordToken(LockscreenCredential credential, PasswordData data) {
+ private byte[] stretchLskf(LockscreenCredential credential, PasswordData data) {
final byte[] password = credential.isNone() ? DEFAULT_PASSWORD : credential.getCredential();
return scrypt(password, data.salt, 1 << data.scryptLogN, 1 << data.scryptLogR,
- 1 << data.scryptLogP, PASSWORD_TOKEN_LENGTH);
+ 1 << data.scryptLogP, STRETCHED_LSKF_LENGTH);
}
- private byte[] passwordTokenToGkInput(byte[] token) {
- return SyntheticPasswordCrypto.personalizedHash(PERSONALIZATION_USER_GK_AUTH, token);
+ private byte[] stretchedLskfToGkPassword(byte[] stretchedLskf) {
+ return SyntheticPasswordCrypto.personalizedHash(PERSONALIZATION_USER_GK_AUTH,
+ stretchedLskf);
}
- private byte[] passwordTokenToWeaverKey(byte[] token) {
- byte[] key = SyntheticPasswordCrypto.personalizedHash(PERSONALIZATION_WEAVER_KEY, token);
+ private byte[] stretchedLskfToWeaverKey(byte[] stretchedLskf) {
+ byte[] key = SyntheticPasswordCrypto.personalizedHash(PERSONALIZATION_WEAVER_KEY,
+ stretchedLskf);
if (key.length < mWeaverConfig.keySize) {
throw new IllegalArgumentException("weaver key length too small");
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
index f32af5434c43..7009a41726e2 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
@@ -75,7 +75,6 @@ public class PlatformKeyManager {
"com.android.server.locksettings.recoverablekeystore/platform/";
private static final String ENCRYPT_KEY_ALIAS_SUFFIX = "encrypt";
private static final String DECRYPT_KEY_ALIAS_SUFFIX = "decrypt";
- private static final int USER_AUTHENTICATION_VALIDITY_DURATION_SECONDS = 15;
private static final String KEY_WRAP_CIPHER_ALGORITHM = "AES/GCM/NoPadding";
private static final int GCM_TAG_LENGTH_BITS = 128;
// Only used for checking if a key is usable
@@ -184,7 +183,6 @@ public class PlatformKeyManager {
invalidatePlatformKey(userId, generationId);
nextId = generationId + 1;
}
- generationId = Math.max(generationId, MIN_GENERATION_ID_FOR_UNLOCKED_DEVICE_REQUIRED);
generateAndLoadKey(userId, nextId);
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/CleanupManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/CleanupManager.java
index be35b50c361e..7d5fd652ef38 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/CleanupManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/CleanupManager.java
@@ -35,7 +35,6 @@ import java.util.Map;
public class CleanupManager {
private static final String TAG = "CleanupManager";
- private final Context mContext;
private final UserManager mUserManager;
private final RecoverableKeyStoreDb mDatabase;
private final RecoverySnapshotStorage mSnapshotStorage;
@@ -54,7 +53,6 @@ public class CleanupManager {
RecoverableKeyStoreDb recoverableKeyStoreDb,
ApplicationKeyStorage applicationKeyStorage) {
return new CleanupManager(
- context,
snapshotStorage,
recoverableKeyStoreDb,
UserManager.get(context),
@@ -63,12 +61,10 @@ public class CleanupManager {
@VisibleForTesting
CleanupManager(
- Context context,
RecoverySnapshotStorage snapshotStorage,
RecoverableKeyStoreDb recoverableKeyStoreDb,
UserManager userManager,
ApplicationKeyStorage applicationKeyStorage) {
- mContext = context;
mSnapshotStorage = snapshotStorage;
mDatabase = recoverableKeyStoreDb;
mUserManager = userManager;
diff --git a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
index a5c762a1df0b..c12dc8e9e92e 100644
--- a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
+++ b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
@@ -119,7 +119,7 @@ final class MediaButtonReceiverHolder {
ComponentName componentName = getComponentName(pendingIntent, componentType);
if (componentName != null) {
if (!TextUtils.equals(componentName.getPackageName(), sessionPackageName)) {
- EventLog.writeEvent(0x534e4554, "238177121", -1, "");
+ EventLog.writeEvent(0x534e4554, "238177121", -1, ""); // SafetyNet Logging.
throw new IllegalArgumentException("ComponentName does not belong to "
+ "sessionPackageName. sessionPackageName = " + sessionPackageName
+ ", ComponentName pkg = " + componentName.getPackageName());
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 1ee9a873fff9..4f8771a3ddef 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -914,16 +914,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
}
@Override
- public void setMediaButtonReceiver(PendingIntent pi, String sessionPackageName)
- throws RemoteException {
- //mPackageName has been verified in MediaSessionService.enforcePackageName().
- if (!TextUtils.equals(sessionPackageName, mPackageName)) {
- EventLog.writeEvent(0x534e4554, "238177121", -1, "");
- throw new IllegalArgumentException("sessionPackageName name does not match "
- + "package name provided to MediaSessionRecord. sessionPackageName = "
- + sessionPackageName + ", pkg = "
- + mPackageName);
- }
+ public void setMediaButtonReceiver(PendingIntent pi) throws RemoteException {
final long token = Binder.clearCallingIdentity();
try {
if ((mPolicies & MediaSessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER)
@@ -931,7 +922,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
return;
}
mMediaButtonReceiverHolder =
- MediaButtonReceiverHolder.create(mContext, mUserId, pi, sessionPackageName);
+ MediaButtonReceiverHolder.create(mContext, mUserId, pi, mPackageName);
mService.onMediaButtonReceiverChanged(MediaSessionRecord.this);
} finally {
Binder.restoreCallingIdentity(token);
@@ -945,7 +936,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
//mPackageName has been verified in MediaSessionService.enforcePackageName().
if (receiver != null && !TextUtils.equals(
mPackageName, receiver.getPackageName())) {
- EventLog.writeEvent(0x534e4554, "238177121", -1, "");
+ EventLog.writeEvent(0x534e4554, "238177121", -1, ""); // SafetyNet Logging.
throw new IllegalArgumentException("receiver does not belong to "
+ "package name provided to MediaSessionRecord. Pkg = " + mPackageName
+ ", Receiver Pkg = " + receiver.getPackageName());
diff --git a/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java b/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java
index f5910fa3143c..718756f85860 100644
--- a/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java
+++ b/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java
@@ -16,6 +16,7 @@
package com.android.server.pm;
+import android.annotation.IntDef;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.UserHandle;
@@ -30,6 +31,8 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* The {@link PackageManagerService} maintains some {@link CrossProfileIntentFilter}s for each user.
@@ -41,13 +44,52 @@ class CrossProfileIntentFilter extends WatchedIntentFilter {
private static final String ATTR_FLAGS = "flags";
private static final String ATTR_OWNER_PACKAGE = "ownerPackage";
private static final String ATTR_FILTER = "filter";
+ private static final String ATTR_ACCESS_CONTROL = "accessControl";
private static final String TAG = "CrossProfileIntentFilter";
+ /**
+ * AccessControlLevel provides level of access for user to create/modify
+ * {@link CrossProfileIntentFilter} in {@link com.android.server.pm.Settings}.
+ * Each AccessControlLevel have value assigned, the higher the value
+ * implies higher restriction for creation/modification.
+ * AccessControlLevel allows us to protect against malicious changes in user's
+ * {@link CrossProfileIntentFilter}s, which might add/remove {@link CrossProfileIntentFilter}
+ * leading to unprecedented results.
+ */
+ @IntDef(prefix = {"ACCESS_LEVEL_"}, value = {
+ ACCESS_LEVEL_ALL,
+ ACCESS_LEVEL_SYSTEM,
+ ACCESS_LEVEL_SYSTEM_ADD_ONLY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AccessControlLevel {
+ }
+
+ /**
+ * ACCESS_LEVEL_ALL signifies that irrespective of user we would allow
+ * access(addition/modification/removal) for CrossProfileIntentFilter.
+ * This is the default access control level.
+ */
+ public static final int ACCESS_LEVEL_ALL = 0;
+
+ /**
+ * ACCESS_LEVEL_SYSTEM signifies that only system/root user would be able to
+ * access(addition/modification/removal) CrossProfileIntentFilter.
+ */
+ public static final int ACCESS_LEVEL_SYSTEM = 10;
+
+ /**
+ * ACCESS_LEVEL_SYSTEM_ADD_ONLY signifies that only system/root user would be able to add
+ * CrossProfileIntentFilter but not modify/remove. Once added, it cannot be modified or removed.
+ */
+ public static final int ACCESS_LEVEL_SYSTEM_ADD_ONLY = 20;
+
// If the intent matches the IntentFilter, then it can be forwarded to this userId.
final int mTargetUserId;
final String mOwnerPackage; // packageName of the app.
final int mFlags;
+ final int mAccessControlLevel;
// The cache for snapshots, so they are not rebuilt if the base object has not
// changed.
@@ -65,10 +107,16 @@ class CrossProfileIntentFilter extends WatchedIntentFilter {
CrossProfileIntentFilter(IntentFilter filter, String ownerPackage, int targetUserId,
int flags) {
+ this(filter, ownerPackage, targetUserId, flags, ACCESS_LEVEL_ALL);
+ }
+
+ CrossProfileIntentFilter(IntentFilter filter, String ownerPackage, int targetUserId,
+ int flags, @AccessControlLevel int accessControlLevel) {
super(filter);
mTargetUserId = targetUserId;
mOwnerPackage = ownerPackage;
mFlags = flags;
+ mAccessControlLevel = accessControlLevel;
mSnapshot = makeCache();
}
@@ -77,12 +125,18 @@ class CrossProfileIntentFilter extends WatchedIntentFilter {
this(filter.mFilter, ownerPackage, targetUserId, flags);
}
+ CrossProfileIntentFilter(WatchedIntentFilter filter, String ownerPackage, int targetUserId,
+ int flags, @AccessControlLevel int accessControlLevel) {
+ this(filter.mFilter, ownerPackage, targetUserId, flags, accessControlLevel);
+ }
+
// Copy constructor used only to create a snapshot.
private CrossProfileIntentFilter(CrossProfileIntentFilter f) {
super(f);
mTargetUserId = f.mTargetUserId;
mOwnerPackage = f.mOwnerPackage;
mFlags = f.mFlags;
+ mAccessControlLevel = f.mAccessControlLevel;
mSnapshot = new SnapshotCache.Sealed();
}
@@ -98,9 +152,14 @@ class CrossProfileIntentFilter extends WatchedIntentFilter {
return mOwnerPackage;
}
+ public @AccessControlLevel int getAccessControlLevel() {
+ return mAccessControlLevel;
+ }
+
CrossProfileIntentFilter(TypedXmlPullParser parser) throws XmlPullParserException, IOException {
mTargetUserId = parser.getAttributeInt(null, ATTR_TARGET_USER_ID, UserHandle.USER_NULL);
mOwnerPackage = getStringFromXml(parser, ATTR_OWNER_PACKAGE, "");
+ mAccessControlLevel = parser.getAttributeInt(null, ATTR_ACCESS_CONTROL, ACCESS_LEVEL_ALL);
mFlags = parser.getAttributeInt(null, ATTR_FLAGS, 0);
mSnapshot = makeCache();
@@ -151,6 +210,7 @@ class CrossProfileIntentFilter extends WatchedIntentFilter {
serializer.attributeInt(null, ATTR_TARGET_USER_ID, mTargetUserId);
serializer.attributeInt(null, ATTR_FLAGS, mFlags);
serializer.attribute(null, ATTR_OWNER_PACKAGE, mOwnerPackage);
+ serializer.attributeInt(null, ATTR_ACCESS_CONTROL, mAccessControlLevel);
serializer.startTag(null, ATTR_FILTER);
mFilter.writeToXml(serializer);
serializer.endTag(null, ATTR_FILTER);
@@ -165,7 +225,8 @@ class CrossProfileIntentFilter extends WatchedIntentFilter {
boolean equalsIgnoreFilter(CrossProfileIntentFilter other) {
return mTargetUserId == other.mTargetUserId
&& mOwnerPackage.equals(other.mOwnerPackage)
- && mFlags == other.mFlags;
+ && mFlags == other.mFlags
+ && mAccessControlLevel == other.mAccessControlLevel;
}
public CrossProfileIntentFilter snapshot() {
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 48ec436761e9..bda7b823adc3 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -816,9 +816,10 @@ final class DeletePackageHelper {
}
// Allow package verifier to silently uninstall.
- if (mPm.mRequiredVerifierPackage != null && callingUid == snapshot
- .getPackageUid(mPm.mRequiredVerifierPackage, 0, callingUserId)) {
- return true;
+ for (String verifierPackageName : mPm.mRequiredVerifierPackages) {
+ if (callingUid == snapshot.getPackageUid(verifierPackageName, 0, callingUserId)) {
+ return true;
+ }
}
// Allow package uninstaller to silently uninstall.
diff --git a/services/core/java/com/android/server/pm/DumpHelper.java b/services/core/java/com/android/server/pm/DumpHelper.java
index 7352018cb4d0..d2a5e3233a8b 100644
--- a/services/core/java/com/android/server/pm/DumpHelper.java
+++ b/services/core/java/com/android/server/pm/DumpHelper.java
@@ -54,7 +54,7 @@ final class DumpHelper {
private final StorageEventHelper mStorageEventHelper;
private final DomainVerificationManagerInternal mDomainVerificationManager;
private final PackageInstallerService mInstallerService;
- private final String mRequiredVerifierPackage;
+ private final String[] mRequiredVerifierPackages;
private final KnownPackages mKnownPackages;
private final ChangedPackagesTracker mChangedPackagesTracker;
private final ArrayMap<String, FeatureInfo> mAvailableFeatures;
@@ -65,7 +65,7 @@ final class DumpHelper {
PermissionManagerServiceInternal permissionManager,
StorageEventHelper storageEventHelper,
DomainVerificationManagerInternal domainVerificationManager,
- PackageInstallerService installerService, String requiredVerifierPackage,
+ PackageInstallerService installerService, String[] requiredVerifierPackages,
KnownPackages knownPackages,
ChangedPackagesTracker changedPackagesTracker,
ArrayMap<String, FeatureInfo> availableFeatures,
@@ -75,7 +75,7 @@ final class DumpHelper {
mStorageEventHelper = storageEventHelper;
mDomainVerificationManager = domainVerificationManager;
mInstallerService = installerService;
- mRequiredVerifierPackage = requiredVerifierPackage;
+ mRequiredVerifierPackages = requiredVerifierPackages;
mKnownPackages = knownPackages;
mChangedPackagesTracker = changedPackagesTracker;
mAvailableFeatures = availableFeatures;
@@ -313,26 +313,28 @@ final class DumpHelper {
ipw.decreaseIndent();
}
- if (dumpState.isDumping(DumpState.DUMP_VERIFIERS)
- && packageName == null) {
- final String requiredVerifierPackage = mRequiredVerifierPackage;
- if (!checkin) {
+ if (dumpState.isDumping(DumpState.DUMP_VERIFIERS) && packageName == null) {
+ if (!checkin && mRequiredVerifierPackages.length > 0) {
if (dumpState.onTitlePrinted()) {
pw.println();
}
pw.println("Verifiers:");
- pw.print(" Required: ");
- pw.print(requiredVerifierPackage);
- pw.print(" (uid=");
- pw.print(snapshot.getPackageUid(requiredVerifierPackage,
- MATCH_DEBUG_TRIAGED_MISSING, UserHandle.USER_SYSTEM));
- pw.println(")");
- } else if (requiredVerifierPackage != null) {
- pw.print("vrfy,");
- pw.print(requiredVerifierPackage);
- pw.print(",");
- pw.println(snapshot.getPackageUid(requiredVerifierPackage,
- MATCH_DEBUG_TRIAGED_MISSING, UserHandle.USER_SYSTEM));
+ }
+ for (String requiredVerifierPackage : mRequiredVerifierPackages) {
+ if (!checkin) {
+ pw.print(" Required: ");
+ pw.print(requiredVerifierPackage);
+ pw.print(" (uid=");
+ pw.print(snapshot.getPackageUid(requiredVerifierPackage,
+ MATCH_DEBUG_TRIAGED_MISSING, UserHandle.USER_SYSTEM));
+ pw.println(")");
+ } else {
+ pw.print("vrfy,");
+ pw.print(requiredVerifierPackage);
+ pw.print(",");
+ pw.println(snapshot.getPackageUid(requiredVerifierPackage,
+ MATCH_DEBUG_TRIAGED_MISSING, UserHandle.USER_SYSTEM));
+ }
}
}
@@ -642,17 +644,19 @@ final class DumpHelper {
private void dumpProto(Computer snapshot, FileDescriptor fd) {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
- final long requiredVerifierPackageToken =
- proto.start(PackageServiceDumpProto.REQUIRED_VERIFIER_PACKAGE);
- proto.write(PackageServiceDumpProto.PackageShortProto.NAME,
- mRequiredVerifierPackage);
- proto.write(
- PackageServiceDumpProto.PackageShortProto.UID,
- snapshot.getPackageUid(
- mRequiredVerifierPackage,
- MATCH_DEBUG_TRIAGED_MISSING,
- UserHandle.USER_SYSTEM));
- proto.end(requiredVerifierPackageToken);
+ for (String requiredVerifierPackage : mRequiredVerifierPackages) {
+ final long requiredVerifierPackageToken =
+ proto.start(PackageServiceDumpProto.REQUIRED_VERIFIER_PACKAGE);
+ proto.write(PackageServiceDumpProto.PackageShortProto.NAME,
+ requiredVerifierPackage);
+ proto.write(
+ PackageServiceDumpProto.PackageShortProto.UID,
+ snapshot.getPackageUid(
+ requiredVerifierPackage,
+ MATCH_DEBUG_TRIAGED_MISSING,
+ UserHandle.USER_SYSTEM));
+ proto.end(requiredVerifierPackageToken);
+ }
DomainVerificationProxy proxy = mDomainVerificationManager.getProxy();
ComponentName verifierComponent = proxy.getComponentName();
diff --git a/services/core/java/com/android/server/pm/InitAppsHelper.java b/services/core/java/com/android/server/pm/InitAppsHelper.java
index 122ab10f43d9..52e3fd910a7f 100644
--- a/services/core/java/com/android/server/pm/InitAppsHelper.java
+++ b/services/core/java/com/android/server/pm/InitAppsHelper.java
@@ -46,6 +46,7 @@ import android.util.EventLog;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.om.OverlayConfig;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.EventLogTags;
@@ -401,4 +402,9 @@ final class InitAppsHelper {
public List<ScanPartition> getDirsToScanAsSystem() {
return mDirsToScanAsSystem;
}
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public int getSystemScanFlags() {
+ return mSystemScanFlags;
+ }
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 8bb9b841e1a2..728d2d154078 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -2719,15 +2719,16 @@ final class InstallPackageHelper {
installerPackageName, null /*finishedReceiver*/,
updateUserIds, instantUserIds, null /* broadcastAllowList */, null);
}
- // if the required verifier is defined, but, is not the installer of record
- // for the package, it gets notified
- final boolean notifyVerifier = mPm.mRequiredVerifierPackage != null
- && !mPm.mRequiredVerifierPackage.equals(installerPackageName);
- if (notifyVerifier) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
- extras, 0 /*flags*/,
- mPm.mRequiredVerifierPackage, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, null /* broadcastAllowList */, null);
+ // Notify required verifier(s) that are not the installer of record for the package.
+ for (String verifierPackageName : mPm.mRequiredVerifierPackages) {
+ if (verifierPackageName != null && !verifierPackageName.equals(
+ installerPackageName)) {
+ mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
+ extras, 0 /*flags*/,
+ verifierPackageName, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, null /* broadcastAllowList */,
+ null);
+ }
}
// If package installer is defined, notify package installer about new
// app installed
@@ -2752,12 +2753,14 @@ final class InstallPackageHelper {
updateUserIds, instantUserIds, null /*broadcastAllowList*/,
null);
}
- if (notifyVerifier) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
- extras, 0 /*flags*/,
- mPm.mRequiredVerifierPackage, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, null /*broadcastAllowList*/,
- null);
+ for (String verifierPackageName : mPm.mRequiredVerifierPackages) {
+ if (verifierPackageName != null && !verifierPackageName.equals(
+ installerPackageName)) {
+ mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
+ packageName, extras, 0 /*flags*/, verifierPackageName,
+ null /*finishedReceiver*/, updateUserIds, instantUserIds,
+ null /*broadcastAllowList*/, null);
+ }
}
mPm.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
null /*package*/, null /*extras*/, 0 /*flags*/,
diff --git a/services/core/java/com/android/server/pm/KnownPackages.java b/services/core/java/com/android/server/pm/KnownPackages.java
index 04376b448e32..dcf715265138 100644
--- a/services/core/java/com/android/server/pm/KnownPackages.java
+++ b/services/core/java/com/android/server/pm/KnownPackages.java
@@ -79,7 +79,7 @@ public final class KnownPackages {
private final String mRequiredInstallerPackage;
private final String mRequiredUninstallerPackage;
private final String mSetupWizardPackage;
- private final String mRequiredVerifierPackage;
+ private final String[] mRequiredVerifierPackages;
private final String mDefaultTextClassifierPackage;
private final String mSystemTextClassifierPackageName;
private final String mRequiredPermissionControllerPackage;
@@ -94,7 +94,7 @@ public final class KnownPackages {
KnownPackages(DefaultAppProvider defaultAppProvider, String requiredInstallerPackage,
String requiredUninstallerPackage, String setupWizardPackage,
- String requiredVerifierPackage, String defaultTextClassifierPackage,
+ String[] requiredVerifierPackages, String defaultTextClassifierPackage,
String systemTextClassifierPackageName, String requiredPermissionControllerPackage,
String configuratorPackage, String incidentReportApproverPackage,
String ambientContextDetectionPackage, String appPredictionServicePackage,
@@ -104,7 +104,7 @@ public final class KnownPackages {
mRequiredInstallerPackage = requiredInstallerPackage;
mRequiredUninstallerPackage = requiredUninstallerPackage;
mSetupWizardPackage = setupWizardPackage;
- mRequiredVerifierPackage = requiredVerifierPackage;
+ mRequiredVerifierPackages = requiredVerifierPackages;
mDefaultTextClassifierPackage = defaultTextClassifierPackage;
mSystemTextClassifierPackageName = systemTextClassifierPackageName;
mRequiredPermissionControllerPackage = requiredPermissionControllerPackage;
@@ -182,7 +182,7 @@ public final class KnownPackages {
case PACKAGE_SYSTEM:
return new String[]{"android"};
case PACKAGE_VERIFIER:
- return snapshot.filterOnlySystemPackages(mRequiredVerifierPackage);
+ return snapshot.filterOnlySystemPackages(mRequiredVerifierPackages);
case PACKAGE_SYSTEM_TEXT_CLASSIFIER:
return snapshot.filterOnlySystemPackages(
mDefaultTextClassifierPackage, mSystemTextClassifierPackageName);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 32b3e6a4ab76..e798adf1c22a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -238,6 +238,7 @@ import com.android.server.utils.Watcher;
import dalvik.system.VMRuntime;
+import libcore.util.EmptyArray;
import libcore.util.HexEncoding;
import java.io.ByteArrayInputStream;
@@ -521,6 +522,9 @@ public class PackageManagerService implements PackageSender, TestUtilityService
private static final String COMPANION_PACKAGE_NAME = "com.android.companiondevicemanager";
+ // How many required verifiers can be on the system.
+ private static final int REQUIRED_VERIFIERS_MAX_COUNT = 2;
+
// Compilation reasons.
public static final int REASON_FIRST_BOOT = 0;
public static final int REASON_BOOT_AFTER_OTA = 1;
@@ -906,7 +910,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
final SparseArray<PostInstallData> mRunningInstalls = new SparseArray<>();
int mNextInstallToken = 1; // nonzero; will be wrapped back to 1 when ++ overflows
- final @Nullable String mRequiredVerifierPackage;
+ final @NonNull String[] mRequiredVerifierPackages;
final @NonNull String mRequiredInstallerPackage;
final @NonNull String mRequiredUninstallerPackage;
final @NonNull String mRequiredPermissionControllerPackage;
@@ -1642,7 +1646,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
mProtectedPackages = testParams.protectedPackages;
mSeparateProcesses = testParams.separateProcesses;
mViewCompiler = testParams.viewCompiler;
- mRequiredVerifierPackage = testParams.requiredVerifierPackage;
+ mRequiredVerifierPackages = testParams.requiredVerifierPackages;
mRequiredInstallerPackage = testParams.requiredInstallerPackage;
mRequiredUninstallerPackage = testParams.requiredUninstallerPackage;
mRequiredPermissionControllerPackage = testParams.requiredPermissionControllerPackage;
@@ -2126,7 +2130,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
SystemClock.uptimeMillis());
- mRequiredVerifierPackage = getRequiredButNotReallyRequiredVerifierLPr(computer);
+ mRequiredVerifierPackages = getRequiredButNotReallyRequiredVerifiersLPr(computer);
mRequiredInstallerPackage = getRequiredInstallerLPr(computer);
mRequiredUninstallerPackage = getRequiredUninstallerLPr(computer);
ComponentName intentFilterVerifierComponent =
@@ -2265,8 +2269,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService
"persist.pm.mock-upgrade", false /* default */);
}
- @Nullable
- private String getRequiredButNotReallyRequiredVerifierLPr(@NonNull Computer computer) {
+ @NonNull
+ private String[] getRequiredButNotReallyRequiredVerifiersLPr(@NonNull Computer computer) {
final Intent intent = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
final List<ResolveInfo> matches =
@@ -2274,13 +2278,23 @@ public class PackageManagerService implements PackageSender, TestUtilityService
PACKAGE_MIME_TYPE,
MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
UserHandle.USER_SYSTEM, Binder.getCallingUid());
- if (matches.size() == 1) {
- return matches.get(0).getComponentInfo().packageName;
- } else if (matches.size() == 0) {
+ final int size = matches.size();
+ if (size == 0) {
Log.w(TAG, "There should probably be a verifier, but, none were found");
- return null;
+ return EmptyArray.STRING;
+ } else if (size <= REQUIRED_VERIFIERS_MAX_COUNT) {
+ String[] verifiers = new String[size];
+ for (int i = 0; i < size; ++i) {
+ verifiers[i] = matches.get(i).getComponentInfo().packageName;
+ if (TextUtils.isEmpty(verifiers[i])) {
+ throw new RuntimeException("Invalid verifier: " + matches);
+ }
+ }
+ return verifiers;
}
- throw new RuntimeException("There must be exactly one verifier; found " + matches);
+ throw new RuntimeException(
+ "There must be no more than " + REQUIRED_VERIFIERS_MAX_COUNT + " verifiers; found "
+ + matches);
}
@NonNull
@@ -3153,8 +3167,12 @@ public class PackageManagerService implements PackageSender, TestUtilityService
boolean isCallerVerifier(@NonNull Computer snapshot, int callingUid) {
final int callingUserId = UserHandle.getUserId(callingUid);
- return mRequiredVerifierPackage != null && callingUid == snapshot.getPackageUid(
- mRequiredVerifierPackage, 0, callingUserId);
+ for (String requiredVerifierPackage : mRequiredVerifierPackages) {
+ if (callingUid == snapshot.getPackageUid(requiredVerifierPackage, 0, callingUserId)) {
+ return true;
+ }
+ }
+ return false;
}
public boolean isPackageDeviceAdminOnAnyUser(@NonNull Computer snapshot, String packageName) {
@@ -3332,6 +3350,12 @@ public class PackageManagerService implements PackageSender, TestUtilityService
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
int callingUid = Binder.getCallingUid();
enforceOwnerRights(snapshot, ownerPackage, callingUid);
+
+ // Verifying that current calling uid should be able to add {@link CrossProfileIntentFilter}
+ // for source and target user
+ mUserManager.enforceCrossProfileIntentFilterAccess(sourceUserId, targetUserId, callingUid,
+ /* addCrossProfileIntentFilter */ true);
+
PackageManagerServiceUtils.enforceShellRestriction(mInjector.getUserManagerInternal(),
UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId);
if (intentFilter.countActions() == 0) {
@@ -3340,7 +3364,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService
}
synchronized (mLock) {
CrossProfileIntentFilter newFilter = new CrossProfileIntentFilter(intentFilter,
- ownerPackage, targetUserId, flags);
+ ownerPackage, targetUserId, flags, mUserManager
+ .getCrossProfileIntentFilterAccessControl(sourceUserId, targetUserId));
CrossProfileIntentResolver resolver =
mSettings.editCrossProfileIntentResolverLPw(sourceUserId);
ArrayList<CrossProfileIntentFilter> existing = resolver.findFilters(intentFilter);
@@ -4554,7 +4579,11 @@ public class PackageManagerService implements PackageSender, TestUtilityService
ArraySet<CrossProfileIntentFilter> set =
new ArraySet<>(resolver.filterSet());
for (CrossProfileIntentFilter filter : set) {
- if (filter.getOwnerPackage().equals(ownerPackage)) {
+ //Only remove if calling user is allowed based on access control of
+ // {@link CrossProfileIntentFilter}
+ if (filter.getOwnerPackage().equals(ownerPackage)
+ && mUserManager.isCrossProfileIntentFilterAccessible(sourceUserId,
+ filter.mTargetUserId, /* addCrossProfileIntentFilter */ false)) {
resolver.removeFilter(filter);
}
}
@@ -4665,15 +4694,28 @@ public class PackageManagerService implements PackageSender, TestUtilityService
}
@Override
- public void extendVerificationTimeout(int id, int verificationCodeAtTimeout,
+ public void extendVerificationTimeout(int verificationId, int verificationCodeAtTimeout,
long millisecondsToDelay) {
- mContext.enforceCallingOrSelfPermission(
- Manifest.permission.PACKAGE_VERIFICATION_AGENT,
- "Only package verification agents can extend verification timeouts");
+ // Negative ids correspond to testing verifiers and will be silently enforced in
+ // the handler thread.
+ if (verificationId >= 0) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.PACKAGE_VERIFICATION_AGENT,
+ "Only package verification agents can extend verification timeouts");
+ }
final int callingUid = Binder.getCallingUid();
mHandler.post(() -> {
+ final int id = verificationId >= 0 ? verificationId : -verificationId;
final PackageVerificationState state = mPendingVerification.get(id);
+ if (state == null || state.timeoutExtended() || !state.checkRequiredVerifierUid(
+ callingUid)) {
+ // Only allow calls from required verifiers.
+ return;
+ }
+
+ state.extendTimeout();
+
final PackageVerificationResponse response = new PackageVerificationResponse(
verificationCodeAtTimeout, callingUid);
@@ -4685,14 +4727,10 @@ public class PackageManagerService implements PackageSender, TestUtilityService
delay = 0;
}
- if ((state != null) && !state.timeoutExtended()) {
- state.extendTimeout();
-
- final Message msg = mHandler.obtainMessage(PackageManagerService.PACKAGE_VERIFIED);
- msg.arg1 = id;
- msg.obj = response;
- mHandler.sendMessageDelayed(msg, delay);
- }
+ final Message msg = mHandler.obtainMessage(PackageManagerService.PACKAGE_VERIFIED);
+ msg.arg1 = id;
+ msg.obj = response;
+ mHandler.sendMessageDelayed(msg, delay);
});
}
@@ -5434,13 +5472,19 @@ public class PackageManagerService implements PackageSender, TestUtilityService
final long callingId = Binder.clearCallingIdentity();
try {
- final PackageStateInternal packageState =
- snapshot.getPackageStateForInstalledAndFiltered(
- packageName, callingUid, userId);
+ final PackageStateInternal packageState = snapshot.getPackageStateInternal(
+ packageName);
if (packageState == null) {
return false;
}
+ final PackageUserStateInternal userState = packageState.getUserStateOrDefault(
+ userId);
+ if (userState.isHidden() == hidden || !userState.isInstalled()
+ || snapshot.shouldFilterApplication(packageState, callingUid, userId)) {
+ return false;
+ }
+
// Cannot hide static shared libs as they are considered
// a part of the using app (emulating static linking). Also
// static libs are installed always on internal storage.
@@ -5470,10 +5514,6 @@ public class PackageManagerService implements PackageSender, TestUtilityService
return false;
}
- if (packageState.getUserStateOrDefault(userId).isHidden() == hidden) {
- return false;
- }
-
commitPackageStateMutation(null, packageName, packageState1 ->
packageState1.userState(userId).setHidden(hidden));
@@ -5862,18 +5902,32 @@ public class PackageManagerService implements PackageSender, TestUtilityService
}
@Override
- public void verifyPendingInstall(int id, int verificationCode) throws RemoteException {
- mContext.enforceCallingOrSelfPermission(
- Manifest.permission.PACKAGE_VERIFICATION_AGENT,
- "Only package verification agents can verify applications");
+ public void verifyPendingInstall(int verificationId, int verificationCode)
+ throws RemoteException {
+ // Negative ids correspond to testing verifiers and will be silently enforced in
+ // the handler thread.
+ if (verificationId >= 0) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.PACKAGE_VERIFICATION_AGENT,
+ "Only package verification agents can verify applications");
+ }
final int callingUid = Binder.getCallingUid();
- final Message msg = mHandler.obtainMessage(PackageManagerService.PACKAGE_VERIFIED);
- final PackageVerificationResponse response = new PackageVerificationResponse(
- verificationCode, callingUid);
- msg.arg1 = id;
- msg.obj = response;
- mHandler.sendMessage(msg);
+ mHandler.post(() -> {
+ final int id = verificationId >= 0 ? verificationId : -verificationId;
+ final PackageVerificationState state = mPendingVerification.get(id);
+ if (state == null || !state.checkRequiredVerifierUid(callingUid)) {
+ // Only allow calls from required verifiers.
+ return;
+ }
+
+ final Message msg = mHandler.obtainMessage(PackageManagerService.PACKAGE_VERIFIED);
+ final PackageVerificationResponse response = new PackageVerificationResponse(
+ verificationCode, callingUid);
+ msg.arg1 = id;
+ msg.obj = response;
+ mHandler.sendMessage(msg);
+ });
}
@Override
@@ -5928,7 +5982,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
mRequiredInstallerPackage,
mRequiredUninstallerPackage,
mSetupWizardPackage,
- mRequiredVerifierPackage,
+ mRequiredVerifierPackages,
mDefaultTextClassifierPackage,
mSystemTextClassifierPackageName,
mRequiredPermissionControllerPackage,
@@ -5949,7 +6003,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
protectedBroadcasts = new ArraySet<>(mProtectedBroadcasts);
}
new DumpHelper(mPermissionManager, mStorageEventHelper,
- mDomainVerificationManager, mInstallerService, mRequiredVerifierPackage,
+ mDomainVerificationManager, mInstallerService, mRequiredVerifierPackages,
knownPackages, mChangedPackagesTracker, availableFeatures, protectedBroadcasts,
getPerUidReadTimeouts(snapshot)
).doDump(snapshot, fd, pw, args);
@@ -6956,7 +7010,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
mRequiredInstallerPackage,
mRequiredUninstallerPackage,
mSetupWizardPackage,
- mRequiredVerifierPackage,
+ mRequiredVerifierPackages,
mDefaultTextClassifierPackage,
mSystemTextClassifierPackageName,
mRequiredPermissionControllerPackage,
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index f44d922ea716..ec1b29eda69e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -80,7 +80,7 @@ public final class PackageManagerServiceTestParams {
public @NonNull String requiredInstallerPackage;
public @NonNull String requiredPermissionControllerPackage;
public @NonNull String requiredUninstallerPackage;
- public @Nullable String requiredVerifierPackage;
+ public @NonNull String[] requiredVerifierPackages;
public String[] separateProcesses;
public @NonNull String servicesExtensionPackageName;
public @Nullable String setupWizardPackage;
diff --git a/services/core/java/com/android/server/pm/PackageVerificationState.java b/services/core/java/com/android/server/pm/PackageVerificationState.java
index d105fc4f609b..a574fc9225a8 100644
--- a/services/core/java/com/android/server/pm/PackageVerificationState.java
+++ b/services/core/java/com/android/server/pm/PackageVerificationState.java
@@ -30,7 +30,7 @@ class PackageVerificationState {
private final SparseBooleanArray mSufficientVerifierUids;
- private int mRequiredVerifierUid;
+ private final SparseBooleanArray mRequiredVerifierUids;
private boolean mSufficientVerificationComplete;
@@ -51,6 +51,9 @@ class PackageVerificationState {
PackageVerificationState(VerifyingSession verifyingSession) {
mVerifyingSession = verifyingSession;
mSufficientVerifierUids = new SparseBooleanArray();
+ mRequiredVerifierUids = new SparseBooleanArray();
+ mRequiredVerificationComplete = false;
+ mRequiredVerificationPassed = true;
mExtendedTimeout = false;
}
@@ -58,9 +61,14 @@ class PackageVerificationState {
return mVerifyingSession;
}
- /** Sets the user ID of the required package verifier. */
- void setRequiredVerifierUid(int uid) {
- mRequiredVerifierUid = uid;
+ /** Add the user ID of the required package verifier. */
+ void addRequiredVerifierUid(int uid) {
+ mRequiredVerifierUids.put(uid, true);
+ }
+
+ /** Returns true if the uid a required verifier. */
+ boolean checkRequiredVerifierUid(int uid) {
+ return mRequiredVerifierUids.get(uid, false);
}
/**
@@ -80,39 +88,55 @@ class PackageVerificationState {
* @return {@code true} if the verifying agent actually exists in our list
*/
boolean setVerifierResponse(int uid, int code) {
- if (uid == mRequiredVerifierUid) {
- mRequiredVerificationComplete = true;
+ if (mRequiredVerifierUids.get(uid)) {
switch (code) {
case PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT:
mSufficientVerifierUids.clear();
// fall through
case PackageManager.VERIFICATION_ALLOW:
- mRequiredVerificationPassed = true;
+ // Two possible options:
+ // - verification result is true,
+ // - another verifier set it to false.
+ // In both cases we don't need to assign anything, just exit.
break;
default:
mRequiredVerificationPassed = false;
}
+
+ mRequiredVerifierUids.delete(uid);
+ if (mRequiredVerifierUids.size() == 0) {
+ mRequiredVerificationComplete = true;
+ }
return true;
- } else {
- if (mSufficientVerifierUids.get(uid)) {
- if (code == PackageManager.VERIFICATION_ALLOW) {
- mSufficientVerificationComplete = true;
- mSufficientVerificationPassed = true;
- }
-
- mSufficientVerifierUids.delete(uid);
- if (mSufficientVerifierUids.size() == 0) {
- mSufficientVerificationComplete = true;
- }
-
- return true;
+ } else if (mSufficientVerifierUids.get(uid)) {
+ if (code == PackageManager.VERIFICATION_ALLOW) {
+ mSufficientVerificationPassed = true;
+ mSufficientVerificationComplete = true;
}
+
+ mSufficientVerifierUids.delete(uid);
+ if (mSufficientVerifierUids.size() == 0) {
+ mSufficientVerificationComplete = true;
+ }
+
+ return true;
}
return false;
}
/**
+ * Mark the session as passed required verification.
+ */
+ void passRequiredVerification() {
+ if (mRequiredVerifierUids.size() > 0) {
+ throw new RuntimeException("Required verifiers still present.");
+ }
+ mRequiredVerificationPassed = true;
+ mRequiredVerificationComplete = true;
+ }
+
+ /**
* Returns whether verification is considered complete. This means that the required verifier
* and at least one of the sufficient verifiers has returned a positive verification.
*
@@ -137,15 +161,15 @@ class PackageVerificationState {
* @return {@code true} if installation should be allowed
*/
boolean isInstallAllowed() {
- if (!mRequiredVerificationComplete) {
+ if (!mRequiredVerificationComplete || !mRequiredVerificationPassed) {
return false;
}
if (mSufficientVerificationComplete) {
- return mRequiredVerificationPassed && mSufficientVerificationPassed;
+ return mSufficientVerificationPassed;
}
- return mRequiredVerificationPassed;
+ return true;
}
/** Extend the timeout for this Package to be verified. */
diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
index f931ba8720db..01aecd95afb9 100644
--- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java
+++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
@@ -126,10 +126,9 @@ public final class SuspendPackageHelper {
unmodifiablePackages.add(packageName);
continue;
}
- final PackageStateInternal packageState =
- snapshot.getPackageStateForInstalledAndFiltered(
- packageName, callingUid, userId);
- if (packageState == null) {
+ final PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName);
+ if (packageState == null || !packageState.getUserStateOrDefault(userId).isInstalled()
+ || snapshot.shouldFilterApplication(packageState, callingUid, userId)) {
Slog.w(TAG, "Could not find package setting for package: " + packageName
+ ". Skipping suspending/un-suspending.");
unmodifiablePackages.add(packageName);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index b7a5f7031046..a0335e89ceec 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -89,6 +89,7 @@ import android.os.storage.StorageManagerInternal;
import android.provider.Settings;
import android.security.GateKeeper;
import android.service.gatekeeper.IGateKeeperService;
+import android.service.voice.VoiceInteractionManagerInternal;
import android.stats.devicepolicy.DevicePolicyEnums;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -1649,11 +1650,40 @@ public class UserManagerService extends IUserManager.Stub {
}
int currentUser = Binder.withCleanCallingIdentity(() -> ActivityManager.getCurrentUser());
- // TODO(b/179163496): should return true for profile users of the current user as well
return currentUser == userId;
}
@Override
+ public boolean isUserVisible(@UserIdInt int userId) {
+ int callingUserId = UserHandle.getCallingUserId();
+ if (callingUserId != userId
+ && !hasManageUsersOrPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)) {
+ throw new SecurityException("Caller from user " + callingUserId + " needs MANAGE_USERS "
+ + "or INTERACT_ACROSS_USERS permission to check if another user (" + userId
+ + ") is visible");
+ }
+
+ // First check current foreground user (on main display)
+ int currentUserId = Binder.withCleanCallingIdentity(() -> ActivityManager.getCurrentUser());
+
+ if (currentUserId == userId) {
+ return true;
+ }
+
+ // Then profiles of current user
+ // TODO(b/239824814): consider using AMInternal.isCurrentProfile() instead
+ if (isProfile(userId)) {
+ int parentId = Binder.withCleanCallingIdentity(() -> getProfileParentId(userId));
+ if (parentId == currentUserId) {
+ return isUserRunning(userId);
+ }
+ }
+
+ // TODO(b/239824814): support background users on secondary display (and their profiles)
+ return false;
+ }
+
+ @Override
public @NonNull String getUserName() {
final int callingUid = Binder.getCallingUid();
if (!hasQueryOrCreateUsersPermission()
@@ -1865,6 +1895,103 @@ public class UserManagerService extends IUserManager.Stub {
return mLocalService.exists(userId);
}
+ /**
+ * Returns user's {@link CrossProfileIntentFilter.AccessControlLevel}, which is derived from
+ * {@link UserTypeDetails}. If user does not have defined their access control level,
+ * returns default {@link CrossProfileIntentFilter#ACCESS_LEVEL_ALL}
+ */
+ private @CrossProfileIntentFilter.AccessControlLevel int
+ getCrossProfileIntentFilterAccessControl(@UserIdInt int userId) {
+ final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
+ return userTypeDetails != null ? userTypeDetails.getCrossProfileIntentFilterAccessControl()
+ : CrossProfileIntentFilter.ACCESS_LEVEL_ALL;
+ }
+
+ /**
+ * Verifies if calling user is allowed to access {@link CrossProfileIntentFilter} between given
+ * source and target user.
+ * @param sourceUserId userId for which CrossProfileIntentFilter would be configured
+ * @param targetUserId target user where we can resolve given intent filter
+ * @param callingUid user accessing api
+ * @param addCrossProfileIntentFilter if the operation is addition or not.
+ * @throws SecurityException is calling user is not allowed to access.
+ */
+ public void enforceCrossProfileIntentFilterAccess(
+ int sourceUserId, int targetUserId,
+ int callingUid, boolean addCrossProfileIntentFilter) {
+ if (!isCrossProfileIntentFilterAccessible(sourceUserId, targetUserId,
+ addCrossProfileIntentFilter)) {
+ throw new SecurityException("CrossProfileIntentFilter cannot be accessed by user "
+ + callingUid);
+ }
+ }
+
+ /**
+ * Checks if {@link CrossProfileIntentFilter} can be accessed by calling user for given source
+ * and target user. There are following rules of access
+ * 1. For {@link CrossProfileIntentFilter#ACCESS_LEVEL_ALL},
+ * irrespective of user we would allow access(addition/modification/removal)
+ * 2. For {@link CrossProfileIntentFilter#ACCESS_LEVEL_SYSTEM},
+ * only system/root user would be able to access(addition/modification/removal)
+ * 3. For {@link CrossProfileIntentFilter#ACCESS_LEVEL_SYSTEM_ADD_ONLY},
+ * only system/root user would be able to add but not modify/remove. Once added, it cannot be
+ * modified or removed
+ * @param sourceUserId userId for which CrossProfileIntentFilter would be configured
+ * @param targetUserId target user where we can resolve given intent filter
+ * @param addCrossProfileIntentFilter if the operation is addition or not.
+ * @return true if {@link CrossProfileIntentFilter} can be accessed by calling user
+ */
+ public boolean isCrossProfileIntentFilterAccessible(int sourceUserId, int targetUserId,
+ boolean addCrossProfileIntentFilter) {
+ int effectiveAccessControl =
+ getCrossProfileIntentFilterAccessControl(sourceUserId, targetUserId);
+
+ /*
+ For {@link CrossProfileIntentFilter#ACCESS_LEVEL_SYSTEM}, if accessing user is not
+ system or root disallowing access to {@link CrossProfileIntentFilter}
+ */
+ if (CrossProfileIntentFilter.ACCESS_LEVEL_SYSTEM == effectiveAccessControl
+ && !PackageManagerServiceUtils.isSystemOrRoot()) {
+ return false;
+ }
+
+ /*
+ For {@link CrossProfileIntentFilter#ACCESS_LEVEL_SYSTEM_ADD_ONLY}, allowing only
+ system user to add {@link CrossProfileIntentFilter}. All users(including system) are
+ disallowed to modify/remove.
+ */
+ if (CrossProfileIntentFilter.ACCESS_LEVEL_SYSTEM_ADD_ONLY == effectiveAccessControl
+ && (!addCrossProfileIntentFilter || !PackageManagerServiceUtils.isSystemOrRoot())) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns {@link CrossProfileIntentFilter.AccessControlLevel}
+ * that should be assigned to {@link CrossProfileIntentFilter}
+ * computed from source user's and target user's
+ * {@link CrossProfileIntentFilter.AccessControlLevel}.
+ * The Access Level is configured per {@link CrossProfileIntentFilter} and its property of edge
+ * between source and target user e.g. for all {@link CrossProfileIntentFilter}s configured
+ * between Primary user and Clone profile should have access level of
+ * {@link CrossProfileIntentFilter#ACCESS_LEVEL_SYSTEM} which is driven by highest
+ * access value from source or target. The higher value means higher restrictions.
+ * @param sourceUserId userId of source user for whom CrossProfileIntentFilter will be stored
+ * @param targetUserId userId of target user for whom Cross Profile access would be allowed
+ * @return least privileged {@link CrossProfileIntentFilter.AccessControlLevel} from source or
+ * target user.
+ */
+ public @CrossProfileIntentFilter.AccessControlLevel int
+ getCrossProfileIntentFilterAccessControl(int sourceUserId, int targetUserId) {
+ int sourceAccessControlLevel,
+ targetAccessControlLevel, effectiveAccessControl;
+ sourceAccessControlLevel = getCrossProfileIntentFilterAccessControl(sourceUserId);
+ targetAccessControlLevel = getCrossProfileIntentFilterAccessControl(targetUserId);
+ effectiveAccessControl = Math.max(sourceAccessControlLevel, targetAccessControlLevel);
+ return effectiveAccessControl;
+ }
+
@Override
public void setUserName(@UserIdInt int userId, String name) {
checkManageUsersPermission("rename users");
@@ -4366,6 +4493,11 @@ public class UserManagerService extends IUserManager.Stub {
Binder.withCleanCallingIdentity(() -> {
mPm.onNewUserCreated(preCreatedUser.id, /* convertedFromPreCreated= */ true);
dispatchUserAdded(preCreatedUser, token);
+ VoiceInteractionManagerInternal vimi = LocalServices
+ .getService(VoiceInteractionManagerInternal.class);
+ if (vimi != null) {
+ vimi.onPreCreatedUserConversion(preCreatedUser.id);
+ }
});
return preCreatedUser;
}
@@ -4550,34 +4682,31 @@ public class UserManagerService extends IUserManager.Stub {
}
final List<UserInfo> users = getUsersInternal(true, true, true);
final int size = users.size();
- for (int idx = 0; idx < size; idx++) {
- final UserInfo user = users.get(idx);
- if (user.id == UserHandle.USER_SYSTEM) {
- // Skip user 0. It's not interesting. We already know it exists, is running, and (if
- // we know the device configuration) its userType.
- continue;
- }
-
- final int userTypeStandard = UserManager.getUserTypeForStatsd(user.userType);
- final String userTypeCustom = (userTypeStandard ==
- FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN) ?
- user.userType : null;
+ if (size > 1) {
+ for (int idx = 0; idx < size; idx++) {
+ final UserInfo user = users.get(idx);
+ final int userTypeStandard = UserManager.getUserTypeForStatsd(user.userType);
+ final String userTypeCustom = (userTypeStandard == FrameworkStatsLog
+ .USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN)
+ ?
+ user.userType : null;
+
+ boolean isUserRunningUnlocked;
+ synchronized (mUserStates) {
+ isUserRunningUnlocked =
+ mUserStates.get(user.id, -1) == UserState.STATE_RUNNING_UNLOCKED;
+ }
- boolean isUserRunningUnlocked;
- synchronized (mUserStates) {
- isUserRunningUnlocked =
- mUserStates.get(user.id, -1) == UserState.STATE_RUNNING_UNLOCKED;
+ data.add(FrameworkStatsLog.buildStatsEvent(FrameworkStatsLog.USER_INFO,
+ user.id,
+ userTypeStandard,
+ userTypeCustom,
+ user.flags,
+ user.creationTime,
+ user.lastLoggedInTime,
+ isUserRunningUnlocked
+ ));
}
-
- data.add(FrameworkStatsLog.buildStatsEvent(FrameworkStatsLog.USER_INFO,
- user.id,
- userTypeStandard,
- userTypeCustom,
- user.flags,
- user.creationTime,
- user.lastLoggedInTime,
- isUserRunningUnlocked
- ));
}
return android.app.StatsManager.PULL_SUCCESS;
}
@@ -6004,6 +6133,10 @@ public class UserManagerService extends IUserManager.Stub {
} // synchronized (mPackagesLock)
+ // Multiple Users on Multiple Display info
+ pw.println(" Supports users on secondary displays: "
+ + UserManager.isUsersOnSecondaryDisplaysEnabled());
+
// Dump some capabilities
pw.println();
pw.print(" Max users: " + UserManager.getMaxSupportedUsers());
diff --git a/services/core/java/com/android/server/pm/UserTypeDetails.java b/services/core/java/com/android/server/pm/UserTypeDetails.java
index 4aad1a7f3881..2f3ca664c126 100644
--- a/services/core/java/com/android/server/pm/UserTypeDetails.java
+++ b/services/core/java/com/android/server/pm/UserTypeDetails.java
@@ -163,6 +163,14 @@ public final class UserTypeDetails {
*/
private final boolean mIsCredentialSharableWithParent;
+ /**
+ * Denotes the default access control for {@link CrossProfileIntentFilter} of user profile.
+ *
+ * <p> Default value is {@link CrossProfileIntentFilter#ACCESS_LEVEL_ALL}
+ */
+ private final @CrossProfileIntentFilter.AccessControlLevel int
+ mCrossProfileIntentFilterAccessControl;
+
private UserTypeDetails(@NonNull String name, boolean enabled, int maxAllowed,
@UserInfoFlag int baseType, @UserInfoFlag int defaultUserInfoPropertyFlags, int label,
int maxAllowedPerParent,
@@ -174,7 +182,8 @@ public final class UserTypeDetails {
@Nullable Bundle defaultSecureSettings,
@Nullable List<DefaultCrossProfileIntentFilter> defaultCrossProfileIntentFilters,
boolean isMediaSharedWithParent,
- boolean isCredentialSharableWithParent) {
+ boolean isCredentialSharableWithParent,
+ @CrossProfileIntentFilter.AccessControlLevel int accessControlLevel) {
this.mName = name;
this.mEnabled = enabled;
this.mMaxAllowed = maxAllowed;
@@ -195,6 +204,7 @@ public final class UserTypeDetails {
this.mDarkThemeBadgeColors = darkThemeBadgeColors;
this.mIsMediaSharedWithParent = isMediaSharedWithParent;
this.mIsCredentialSharableWithParent = isCredentialSharableWithParent;
+ this.mCrossProfileIntentFilterAccessControl = accessControlLevel;
}
/**
@@ -327,6 +337,16 @@ public final class UserTypeDetails {
return mIsCredentialSharableWithParent;
}
+ /**
+ * Returning user's {@link CrossProfileIntentFilter.AccessControlLevel}. If not explicitly
+ * configured, default value is {@link CrossProfileIntentFilter#ACCESS_LEVEL_ALL}
+ * @return user's {@link CrossProfileIntentFilter.AccessControlLevel}
+ */
+ public @CrossProfileIntentFilter.AccessControlLevel int
+ getCrossProfileIntentFilterAccessControl() {
+ return mCrossProfileIntentFilterAccessControl;
+ }
+
/** Returns a {@link Bundle} representing the default user restrictions. */
@NonNull Bundle getDefaultRestrictions() {
return BundleUtils.clone(mDefaultRestrictions);
@@ -420,6 +440,8 @@ public final class UserTypeDetails {
private @DrawableRes int mBadgeNoBackground = Resources.ID_NULL;
private boolean mIsMediaSharedWithParent = false;
private boolean mIsCredentialSharableWithParent = false;
+ private @CrossProfileIntentFilter.AccessControlLevel int
+ mCrossProfileIntentFilterAccessControl = CrossProfileIntentFilter.ACCESS_LEVEL_ALL;
public Builder setName(String name) {
mName = name;
@@ -520,6 +542,16 @@ public final class UserTypeDetails {
}
/**
+ * Sets {@link CrossProfileIntentFilter.AccessControlLevel} for the user.
+ * @param accessControlLevel default access control for user
+ */
+ public Builder setCrossProfileIntentFilterAccessControl(
+ @CrossProfileIntentFilter.AccessControlLevel int accessControlLevel) {
+ mCrossProfileIntentFilterAccessControl = accessControlLevel;
+ return this;
+ }
+
+ /**
* Sets shared media property for the user.
* @param isCredentialSharableWithParent the value to be set, true or false
*/
@@ -571,7 +603,8 @@ public final class UserTypeDetails {
mDefaultSecureSettings,
mDefaultCrossProfileIntentFilters,
mIsMediaSharedWithParent,
- mIsCredentialSharableWithParent);
+ mIsCredentialSharableWithParent,
+ mCrossProfileIntentFilterAccessControl);
}
private boolean hasBadge() {
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 0878da49df5b..857a9757b03a 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -122,6 +122,8 @@ public final class UserTypeFactory {
.setLabel(0)
.setDefaultRestrictions(null)
.setIsMediaSharedWithParent(true)
+ .setCrossProfileIntentFilterAccessControl(
+ CrossProfileIntentFilter.ACCESS_LEVEL_SYSTEM)
.setIsCredentialSharableWithParent(true);
}
diff --git a/services/core/java/com/android/server/pm/VerifyingSession.java b/services/core/java/com/android/server/pm/VerifyingSession.java
index 9bad4a82a0a0..0a39e64ffcf2 100644
--- a/services/core/java/com/android/server/pm/VerifyingSession.java
+++ b/services/core/java/com/android/server/pm/VerifyingSession.java
@@ -353,45 +353,54 @@ final class VerifyingSession {
}
final int verifierUserId = verifierUser.getIdentifier();
- String requiredVerifierPackage = mPm.mRequiredVerifierPackage;
- boolean requiredVerifierPackageOverridden = false;
+ String[] requiredVerifierPackages = mPm.mRequiredVerifierPackages;
+ boolean requiredVerifierPackagesOverridden = false;
// Allow verifier override for ADB installations which could already be unverified using
// PackageManager.INSTALL_DISABLE_VERIFICATION flag.
if ((mInstallFlags & PackageManager.INSTALL_FROM_ADB) != 0
&& (mInstallFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) == 0) {
- final String adbVerifierOverridePackage = SystemProperties.get(
- "debug.pm.adb_verifier_override_package", "");
- // Check if the package installed.
- if (!TextUtils.isEmpty(adbVerifierOverridePackage)
- && packageExists(adbVerifierOverridePackage)) {
- // Pretend we requested to disable verification from command line.
- boolean requestedDisableVerification = true;
- // If this returns false then the caller can already skip verification, so we are
- // not adding a new way to disable verifications.
- if (!isAdbVerificationEnabled(pkgLite, verifierUserId,
- requestedDisableVerification)) {
- requiredVerifierPackage = adbVerifierOverridePackage;
- requiredVerifierPackageOverridden = true;
+ String property = SystemProperties.get("debug.pm.adb_verifier_override_packages", "");
+ if (!TextUtils.isEmpty(property)) {
+ String[] verifierPackages = property.split(";");
+ List<String> adbVerifierOverridePackages = new ArrayList<>();
+ for (String verifierPackage : verifierPackages) {
+ if (!TextUtils.isEmpty(verifierPackage) && packageExists(verifierPackage)) {
+ adbVerifierOverridePackages.add(verifierPackage);
+ }
+ }
+ // Check if the package installed.
+ if (adbVerifierOverridePackages.size() > 0) {
+ // Pretend we requested to disable verification from command line.
+ boolean requestedDisableVerification = true;
+ // If this returns false then the caller can already skip verification, so we
+ // are not adding a new way to disable verifications.
+ if (!isAdbVerificationEnabled(pkgLite, verifierUserId,
+ requestedDisableVerification)) {
+ requiredVerifierPackages = adbVerifierOverridePackages.toArray(
+ new String[adbVerifierOverridePackages.size()]);
+ requiredVerifierPackagesOverridden = true;
+ }
}
}
}
+ if (mOriginInfo.mExisting || !isVerificationEnabled(pkgLite, verifierUserId,
+ requiredVerifierPackages)) {
+ verificationState.passRequiredVerification();
+ return;
+ }
+
/*
* Determine if we have any installed package verifiers. If we
* do, then we'll defer to them to verify the packages.
*/
final Computer snapshot = mPm.snapshotComputer();
- final int requiredUid = requiredVerifierPackage == null ? -1
- : snapshot.getPackageUid(requiredVerifierPackage,
- MATCH_DEBUG_TRIAGED_MISSING, verifierUserId);
- verificationState.setRequiredVerifierUid(requiredUid);
- final boolean isVerificationEnabled = isVerificationEnabled(pkgLite,
- verifierUserId);
-
- if (mOriginInfo.mExisting || !isVerificationEnabled) {
- verificationState.setVerifierResponse(requiredUid, PackageManager.VERIFICATION_ALLOW);
- return;
+
+ for (String requiredVerifierPackage : requiredVerifierPackages) {
+ final int requiredUid = snapshot.getPackageUid(requiredVerifierPackage,
+ MATCH_DEBUG_TRIAGED_MISSING, verifierUserId);
+ verificationState.addRequiredVerifierUid(requiredUid);
}
final Intent verification = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
@@ -428,11 +437,13 @@ final class VerifyingSession {
final String baseCodePath = mPackageLite.getBaseApkPath();
final String[] splitCodePaths = mPackageLite.getSplitApkPaths();
+ final String rootHashString;
if (IncrementalManager.isIncrementalPath(baseCodePath)) {
- final String rootHashString =
- PackageManagerServiceUtils.buildVerificationRootHashString(
- baseCodePath, splitCodePaths);
+ rootHashString = PackageManagerServiceUtils.buildVerificationRootHashString(
+ baseCodePath, splitCodePaths);
verification.putExtra(PackageManager.EXTRA_VERIFICATION_ROOT_HASH, rootHashString);
+ } else {
+ rootHashString = null;
}
verification.putExtra(PackageInstaller.EXTRA_DATA_LOADER_TYPE, mDataLoaderType);
@@ -503,8 +514,8 @@ final class VerifyingSession {
}
}
- if (requiredVerifierPackage == null) {
- Slog.e(TAG, "Required verifier is null");
+ if (requiredVerifierPackages.length == 0) {
+ Slog.e(TAG, "No required verifiers");
return;
}
@@ -514,47 +525,75 @@ final class VerifyingSession {
} else {
verificationCodeAtTimeout = PackageManager.VERIFICATION_REJECT;
}
- final PackageVerificationResponse response = new PackageVerificationResponse(
- verificationCodeAtTimeout, requiredUid);
- /*
- * Send the intent to the required verification agent,
- * but only start the verification timeout after the
- * target BroadcastReceivers have run.
- */
- if (!requiredVerifierPackageOverridden) {
- final ComponentName requiredVerifierComponent = matchComponentForVerifier(
- requiredVerifierPackage, receivers.getList());
- verification.setComponent(requiredVerifierComponent);
- } else {
- verification.setPackage(requiredVerifierPackage);
- }
- idleController.addPowerSaveTempWhitelistApp(Process.myUid(),
- requiredVerifierPackage, verificationTimeout,
- verifierUserId, false,
- REASON_PACKAGE_VERIFIER, "package verifier");
-
- if (streaming) {
- // For streaming installations, count verification timeout from the broadcast.
- startVerificationTimeoutCountdown(verificationId, streaming, response,
- verificationTimeout);
- }
+ for (String requiredVerifierPackage : requiredVerifierPackages) {
+ final int requiredUid = snapshot.getPackageUid(requiredVerifierPackage,
+ MATCH_DEBUG_TRIAGED_MISSING, verifierUserId);
+
+ final Intent requiredIntent;
+ final String receiverPermission;
+ if (!requiredVerifierPackagesOverridden || requiredVerifierPackages.length == 1) {
+ // Prod code OR test code+single verifier.
+ requiredIntent = new Intent(verification);
+ if (!requiredVerifierPackagesOverridden) {
+ ComponentName requiredVerifierComponent = matchComponentForVerifier(
+ requiredVerifierPackage, receivers.getList());
+ requiredIntent.setComponent(requiredVerifierComponent);
+ } else {
+ requiredIntent.setPackage(requiredVerifierPackage);
+ }
+ receiverPermission = android.Manifest.permission.PACKAGE_VERIFICATION_AGENT;
+ } else {
+ // Test code+multiple verifiers.
+ // Recreate the intent to contain test-only information.
+ requiredIntent = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
+ requiredIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ requiredIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ requiredIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
+ requiredIntent.setDataAndType(Uri.fromFile(new File(mOriginInfo.mResolvedPath)),
+ PACKAGE_MIME_TYPE);
+ requiredIntent.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);
+ requiredIntent.putExtra(PackageInstaller.EXTRA_DATA_LOADER_TYPE, mDataLoaderType);
+ if (rootHashString != null) {
+ requiredIntent.putExtra(PackageManager.EXTRA_VERIFICATION_ROOT_HASH,
+ rootHashString);
+ }
+ requiredIntent.setPackage(requiredVerifierPackage);
+ // Negative verification id.
+ requiredIntent.putExtra(PackageManager.EXTRA_VERIFICATION_ID, -verificationId);
+ // OK not to have permission.
+ receiverPermission = null;
+ }
- mPm.mContext.sendOrderedBroadcastAsUser(verification, verifierUser,
- android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
- /* appOp= */ AppOpsManager.OP_NONE,
- /* options= */ options.toBundle(),
- new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (!streaming) {
- // For NON-streaming installations, count verification timeout from
- // the broadcast was processed by all receivers.
- startVerificationTimeoutCountdown(verificationId, streaming, response,
- verificationTimeout);
+ idleController.addPowerSaveTempWhitelistApp(Process.myUid(), requiredVerifierPackage,
+ verificationTimeout, verifierUserId, false, REASON_PACKAGE_VERIFIER,
+ "package verifier");
+
+ final PackageVerificationResponse response = new PackageVerificationResponse(
+ verificationCodeAtTimeout, requiredUid);
+
+ if (streaming) {
+ // For streaming installations, count verification timeout from the broadcast.
+ startVerificationTimeoutCountdown(verificationId, streaming, response,
+ verificationTimeout);
+ }
+
+ // Send the intent to the required verification agent, but only start the
+ // verification timeout after the target BroadcastReceivers have run.
+ mPm.mContext.sendOrderedBroadcastAsUser(requiredIntent, verifierUser,
+ receiverPermission, AppOpsManager.OP_NONE, options.toBundle(),
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!streaming) {
+ // For NON-streaming installations, count verification timeout from
+ // the broadcast was processed by all receivers.
+ startVerificationTimeoutCountdown(verificationId, streaming,
+ response, verificationTimeout);
+ }
}
- }
- }, null, 0, null, null);
+ }, null, 0, null, null);
+ }
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
@@ -617,7 +656,8 @@ final class VerifyingSession {
*
* @return true if verification should be performed
*/
- private boolean isVerificationEnabled(PackageInfoLite pkgInfoLite, int userId) {
+ private boolean isVerificationEnabled(PackageInfoLite pkgInfoLite, int userId,
+ String[] requiredVerifierPackages) {
if (!DEFAULT_VERIFY_ENABLE) {
return false;
}
@@ -634,18 +674,21 @@ final class VerifyingSession {
// only when not installed from ADB, skip verification for instant apps when
// the installer and verifier are the same.
- if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
- if (mPm.mInstantAppInstallerActivity != null
- && mPm.mInstantAppInstallerActivity.packageName.equals(
- mPm.mRequiredVerifierPackage)) {
- try {
- mPm.mInjector.getSystemService(AppOpsManager.class)
- .checkPackage(installerUid, mPm.mRequiredVerifierPackage);
- if (DEBUG_VERIFY) {
- Slog.i(TAG, "disable verification for instant app");
+ if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0
+ && mPm.mInstantAppInstallerActivity != null) {
+ String installerPackage = mPm.mInstantAppInstallerActivity.packageName;
+ for (String requiredVerifierPackage : requiredVerifierPackages) {
+ if (installerPackage.equals(requiredVerifierPackage)) {
+ try {
+ mPm.mInjector.getSystemService(AppOpsManager.class)
+ .checkPackage(installerUid, requiredVerifierPackage);
+ if (DEBUG_VERIFY) {
+ Slog.i(TAG, "disable verification for instant app");
+ }
+ return false;
+ } catch (SecurityException ignore) {
}
- return false;
- } catch (SecurityException ignore) { }
+ }
}
}
return true;
diff --git a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
index d49227dec454..905bcf969eaf 100644
--- a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
+++ b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
@@ -64,7 +64,7 @@ public class ArtStatsLogUtils {
COMPILATION_REASON_MAP.put(PackageManagerService.REASON_FIRST_BOOT, ArtStatsLog.
ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_FIRST_BOOT);
COMPILATION_REASON_MAP.put(PackageManagerService.REASON_BOOT_AFTER_OTA, ArtStatsLog.
- ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_BOOT);
+ ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_BOOT_AFTER_OTA);
COMPILATION_REASON_MAP.put(PackageManagerService.REASON_POST_BOOT, ArtStatsLog.
ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_POST_BOOT);
COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL, ArtStatsLog.
diff --git a/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java b/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java
new file mode 100644
index 000000000000..14976848fe61
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java
@@ -0,0 +1,131 @@
+/*
+ * 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.pm.pkg.component;
+
+
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.Build;
+import android.util.ArraySet;
+
+import com.android.internal.R;
+import com.android.server.SystemConfig;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Set;
+
+/**
+ * Utility methods for handling the tag {@code <install-constraints/>}
+ *
+ * @hide
+ */
+public class InstallConstraintsTagParser {
+
+ private static final String TAG_FINGERPRINT_PREFIX = "fingerprint-prefix";
+
+ /**
+ * @hide
+ */
+ public static ParseResult<ParsingPackage> parseInstallConstraints(
+ ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)
+ throws XmlPullParserException, IOException {
+ Set<String> allowlist = SystemConfig.getInstance().getInstallConstraintsAllowlist();
+ if (!allowlist.contains(pkg.getPackageName())) {
+ return input.skip("install-constraints cannot be used by this package");
+ }
+
+ ParseResult<Set<String>> prefixes = parseFingerprintPrefixes(input, res, parser);
+ if (prefixes.isSuccess()) {
+ if (validateFingerprintPrefixes(prefixes.getResult())) {
+ return input.success(pkg);
+ } else {
+ return input.skip(
+ "Install of this package is restricted on this device; device fingerprint"
+ + " does not start with one of the allowed prefixes");
+ }
+ }
+ return input.skip(prefixes.getErrorMessage());
+ }
+
+ private static ParseResult<Set<String>> parseFingerprintPrefixes(
+ ParseInput input, Resources res, XmlResourceParser parser)
+ throws XmlPullParserException, IOException {
+ Set<String> prefixes = new ArraySet<>();
+ int type;
+ while (true) {
+ // move to the tag that contains the next prefix
+ type = parser.next();
+ if (type == XmlPullParser.END_TAG) {
+ if (prefixes.size() == 0) {
+ return input.error("install-constraints must contain at least one constraint");
+ }
+ return input.success(prefixes);
+ } else if (type == XmlPullParser.START_TAG) {
+ if (parser.getName().equals(TAG_FINGERPRINT_PREFIX)) {
+ ParseResult<String> parsedPrefix =
+ readFingerprintPrefixValue(input, res, parser);
+ if (parsedPrefix.isSuccess()) {
+ prefixes.add(parsedPrefix.getResult());
+ } else {
+ return input.error(parsedPrefix.getErrorMessage());
+ }
+ } else {
+ return input.error("Unexpected tag: " + parser.getName());
+ }
+
+ // consume the end tag of this attribute
+ type = parser.next();
+ if (type != XmlPullParser.END_TAG) {
+ return input.error("Expected end tag; instead got " + type);
+ }
+ }
+ }
+ }
+
+ private static ParseResult<String> readFingerprintPrefixValue(ParseInput input, Resources res,
+ XmlResourceParser parser) {
+ TypedArray sa = res.obtainAttributes(parser,
+ R.styleable.AndroidManifestInstallConstraintsFingerprintPrefix);
+ try {
+ String value = sa.getString(
+ R.styleable.AndroidManifestInstallConstraintsFingerprintPrefix_value);
+ if (value == null) {
+ return input.error("Failed to specify prefix value");
+ }
+ return input.success(value);
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ private static boolean validateFingerprintPrefixes(Set<String> prefixes) {
+ String fingerprint = Build.FINGERPRINT;
+ for (String prefix : prefixes) {
+ if (fingerprint.startsWith(prefix)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java
index ee59810134f0..8f953e7719e5 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java
@@ -293,6 +293,10 @@ public class PackageInfoWithoutStateUtils {
pi.requestedPermissionsFlags[i] |=
PackageInfo.REQUESTED_PERMISSION_NEVER_FOR_LOCATION;
}
+ if (pkg.getImplicitPermissions().contains(pi.requestedPermissions[i])) {
+ pi.requestedPermissionsFlags[i] |=
+ PackageInfo.REQUESTED_PERMISSION_IMPLICIT;
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
index b60b9a04d620..5b8f473bad2e 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
@@ -94,6 +94,7 @@ import com.android.server.pm.SharedUidMigration;
import com.android.server.pm.permission.CompatibilityPermissionInfo;
import com.android.server.pm.pkg.component.ComponentMutateUtils;
import com.android.server.pm.pkg.component.ComponentParseUtils;
+import com.android.server.pm.pkg.component.InstallConstraintsTagParser;
import com.android.server.pm.pkg.component.ParsedActivity;
import com.android.server.pm.pkg.component.ParsedActivityUtils;
import com.android.server.pm.pkg.component.ParsedApexSystemService;
@@ -168,9 +169,11 @@ public class ParsingPackageUtils {
public static final String TAG_ADOPT_PERMISSIONS = "adopt-permissions";
public static final String TAG_APPLICATION = "application";
+ public static final String TAG_ATTRIBUTION = "attribution";
public static final String TAG_COMPATIBLE_SCREENS = "compatible-screens";
public static final String TAG_EAT_COMMENT = "eat-comment";
public static final String TAG_FEATURE_GROUP = "feature-group";
+ public static final String TAG_INSTALL_CONSTRAINTS = "install-constraints";
public static final String TAG_INSTRUMENTATION = "instrumentation";
public static final String TAG_KEY_SETS = "key-sets";
public static final String TAG_MANIFEST = "manifest";
@@ -178,15 +181,16 @@ public class ParsingPackageUtils {
public static final String TAG_OVERLAY = "overlay";
public static final String TAG_PACKAGE = "package";
public static final String TAG_PACKAGE_VERIFIER = "package-verifier";
- public static final String TAG_ATTRIBUTION = "attribution";
public static final String TAG_PERMISSION = "permission";
public static final String TAG_PERMISSION_GROUP = "permission-group";
public static final String TAG_PERMISSION_TREE = "permission-tree";
+ public static final String TAG_PROFILEABLE = "profileable";
public static final String TAG_PROTECTED_BROADCAST = "protected-broadcast";
public static final String TAG_QUERIES = "queries";
+ public static final String TAG_RECEIVER = "receiver";
public static final String TAG_RESTRICT_UPDATE = "restrict-update";
- public static final String TAG_SUPPORT_SCREENS = "supports-screens";
public static final String TAG_SUPPORTS_INPUT = "supports-input";
+ public static final String TAG_SUPPORT_SCREENS = "supports-screens";
public static final String TAG_USES_CONFIGURATION = "uses-configuration";
public static final String TAG_USES_FEATURE = "uses-feature";
public static final String TAG_USES_GL_TEXTURE = "uses-gl-texture";
@@ -195,8 +199,6 @@ public class ParsingPackageUtils {
public static final String TAG_USES_PERMISSION_SDK_M = "uses-permission-sdk-m";
public static final String TAG_USES_SDK = "uses-sdk";
public static final String TAG_USES_SPLIT = "uses-split";
- public static final String TAG_PROFILEABLE = "profileable";
- public static final String TAG_RECEIVER = "receiver";
public static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect";
public static final String METADATA_SUPPORTS_SIZE_CHANGES = "android.supports_size_changes";
@@ -1026,6 +1028,8 @@ public class ParsingPackageUtils {
return input.success(pkg);
case TAG_RESTRICT_UPDATE:
return parseRestrictUpdateHash(flags, input, pkg, res, parser);
+ case TAG_INSTALL_CONSTRAINTS:
+ return parseInstallConstraints(input, pkg, res, parser);
case TAG_QUERIES:
return parseQueries(input, pkg, res, parser);
default:
@@ -1719,6 +1723,12 @@ public class ParsingPackageUtils {
return input.success(pkg);
}
+ private static ParseResult<ParsingPackage> parseInstallConstraints(
+ ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)
+ throws IOException, XmlPullParserException {
+ return InstallConstraintsTagParser.parseInstallConstraints(input, pkg, res, parser);
+ }
+
private static ParseResult<ParsingPackage> parseQueries(ParseInput input, ParsingPackage pkg,
Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException {
final int depth = parser.getDepth();
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/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index d2a00af245f6..cceb58d74562 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -1020,6 +1020,7 @@ class ActivityClientController extends IActivityClientController.Stub {
public boolean showAssistFromActivity(IBinder token, Bundle args) {
final long ident = Binder.clearCallingIdentity();
try {
+ final String callingAttributionTag;
synchronized (mGlobalLock) {
final ActivityRecord caller = ActivityRecord.forTokenLocked(token);
final Task topRootTask = mService.getTopDisplayFocusedRootTask();
@@ -1035,9 +1036,10 @@ class ActivityClientController extends IActivityClientController.Stub {
+ " is not visible");
return false;
}
+ callingAttributionTag = top.launchedFromFeatureId;
}
return mAssistUtils.showSessionForActiveService(args, SHOW_SOURCE_APPLICATION,
- null /* showCallback */, token);
+ callingAttributionTag, null /* showCallback */, token);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -1054,6 +1056,7 @@ class ActivityClientController extends IActivityClientController.Stub {
@Override
public void startLocalVoiceInteraction(IBinder callingActivity, Bundle options) {
Slog.i(TAG, "Activity tried to startLocalVoiceInteraction");
+ final String callingAttributionTag;
synchronized (mGlobalLock) {
final Task topRootTask = mService.getTopDisplayFocusedRootTask();
final ActivityRecord activity = topRootTask != null
@@ -1071,9 +1074,10 @@ class ActivityClientController extends IActivityClientController.Stub {
return;
}
activity.pendingVoiceInteractionStart = true;
+ callingAttributionTag = activity.launchedFromFeatureId;
}
LocalServices.getService(VoiceInteractionManagerInternal.class)
- .startLocalVoiceInteraction(callingActivity, options);
+ .startLocalVoiceInteraction(callingActivity, callingAttributionTag, options);
}
@Override
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 111d8a060f78..135cf5d59dc4 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -4956,8 +4956,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
ActivityOptions takeOptions() {
if (DEBUG_TRANSITION) Slog.i(TAG, "Taking options for " + this + " callers="
+ Debug.getCallers(6));
+ if (mPendingOptions == null) return null;
final ActivityOptions opts = mPendingOptions;
mPendingOptions = null;
+ // Strip sensitive information from options before sending it to app.
+ opts.setRemoteTransition(null);
+ opts.setRemoteAnimationAdapter(null);
return opts;
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index e062c3896cf3..abedd96d1cde 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -131,6 +131,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.HeavyWeightSwitcherActivity;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.am.PendingIntentRecord;
import com.android.server.pm.InstantAppResolver;
import com.android.server.power.ShutdownCheckPoints;
@@ -2095,6 +2096,49 @@ class ActivityStarter {
}
}
+ // Log activity starts which violate one of the following rules of the
+ // activity security model (ASM):
+ // 1. Only the top activity on a task can start activities on that task
+ // 2. Only the top activity on the top task can create new (top) tasks
+ // We don't currently block, but these checks may later become blocks
+ // TODO(b/236234252): Shift to BackgroundActivityStartController once
+ // class is ready
+ if (mSourceRecord != null) {
+ int callerUid = mSourceRecord.getUid();
+ ActivityRecord targetTopActivity =
+ targetTask != null ? targetTask.getTopNonFinishingActivity() : null;
+ boolean passesAsmChecks = newTask
+ ? mRootWindowContainer.hasResumedActivity(callerUid)
+ : targetTopActivity != null && targetTopActivity.getUid() == callerUid;
+
+ if (!passesAsmChecks) {
+ Slog.i(TAG, "Launching r: " + r
+ + " from background: " + mSourceRecord
+ + ". New task: " + newTask);
+ boolean newOrEmptyTask = newTask || (targetTopActivity == null);
+ FrameworkStatsLog.write(FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED,
+ /* caller_uid */
+ callerUid,
+ /* caller_activity_class_name */
+ mSourceRecord.info.name,
+ /* target_task_top_activity_uid */
+ newOrEmptyTask ? -1 : targetTopActivity.getUid(),
+ /* target_task_top_activity_class_name */
+ newOrEmptyTask ? null : targetTopActivity.info.name,
+ /* target_task_is_different */
+ newTask || !mSourceRecord.getTask().equals(targetTask),
+ /* target_activity_uid */
+ r.getUid(),
+ /* target_activity_class_name */
+ r.info.name,
+ /* target_intent_action */
+ r.intent.getAction(),
+ /* target_intent_flags */
+ r.intent.getFlags()
+ );
+ }
+ }
+
return START_SUCCESS;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 471424bd86e4..87a4fe9b9817 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2311,16 +2311,25 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
* @return a list of {@link ActivityManager.RunningTaskInfo} with up to {@code maxNum} items
*/
public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) {
- return getTasks(maxNum, false /* filterForVisibleRecents */, false /* keepIntentExtra */);
+ return getTasks(maxNum, false /* filterForVisibleRecents */, false /* keepIntentExtra */,
+ INVALID_DISPLAY);
}
/**
* @param filterOnlyVisibleRecents whether to filter the tasks based on whether they would ever
* be visible in the recent task list in systemui
*/
- @Override
public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum,
boolean filterOnlyVisibleRecents, boolean keepIntentExtra) {
+ return getTasks(maxNum, filterOnlyVisibleRecents, keepIntentExtra, INVALID_DISPLAY);
+ }
+
+ /**
+ * @param displayId the target display id, or {@link INVALID_DISPLAY} not to filter by displayId
+ */
+ @Override
+ public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum,
+ boolean filterOnlyVisibleRecents, boolean keepIntentExtra, int displayId) {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
@@ -2342,7 +2351,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final boolean allowed = isGetTasksAllowed("getTasks", callingPid, callingUid);
flags |= (allowed ? RunningTasks.FLAG_ALLOWED : 0);
mRootWindowContainer.getRunningTasks(
- maxNum, list, flags, callingUid, callingProfileIds);
+ maxNum, list, flags, callingUid, callingProfileIds, displayId);
}
return list;
@@ -2568,6 +2577,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
task = r.getTask();
}
+ // If {@code isSystemCaller} is {@code true}, it means the user intends to stop
+ // pinned mode through UI; otherwise, it's called by an app and we need to stop
+ // locked or pinned mode, subject to checks.
getLockTaskController().stopLockTaskMode(task, isSystemCaller, callingUid);
}
// Launch in-call UI if a call is ongoing. This is necessary to allow stopping the lock
@@ -2986,7 +2998,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
@Override
public boolean requestAssistDataForTask(IAssistDataReceiver receiver, int taskId,
- String callingPackageName) {
+ String callingPackageName, @Nullable String callingAttributionTag) {
mAmInternal.enforceCallingPermission(android.Manifest.permission.GET_TOP_ACTIVITY_INFO,
"requestAssistDataForTask()");
final long callingId = Binder.clearCallingIdentity();
@@ -3013,7 +3025,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
requester.requestAssistData(topActivityToken, true /* fetchData */,
false /* fetchScreenshot */, false /* fetchStructure */, true /* allowFetchData */,
false /* allowFetchScreenshot*/, true /* ignoreFocusCheck */,
- Binder.getCallingUid(), callingPackageName);
+ Binder.getCallingUid(), callingPackageName, callingAttributionTag);
return true;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 345ec117b03a..be995a887fc2 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -886,11 +886,12 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
logIfTransactionTooLarge(r.intent, r.getSavedState());
- if (r.isEmbedded()) {
+ final TaskFragment organizedTaskFragment = r.getOrganizedTaskFragment();
+ if (organizedTaskFragment != null) {
// Sending TaskFragmentInfo to client to ensure the info is updated before
// the activity creation.
mService.mTaskFragmentOrganizerController.dispatchPendingInfoChangedEvent(
- r.getOrganizedTaskFragment());
+ organizedTaskFragment);
}
// Create activity launch transaction.
@@ -1874,6 +1875,9 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
// End power mode launch before going sleep
mService.endLaunchPowerMode(ActivityTaskManagerService.POWER_MODE_REASON_ALL);
+ // Rank task layers to make sure the {@link Task#mLayerRank} is updated.
+ mRootWindowContainer.rankTaskLayers();
+
removeSleepTimeouts();
if (mGoingToSleepWakeLock.isHeld()) {
diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java
index 2ea043a5c623..6f1945033af9 100644
--- a/services/core/java/com/android/server/wm/CompatModePackages.java
+++ b/services/core/java/com/android/server/wm/CompatModePackages.java
@@ -24,6 +24,7 @@ import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import android.app.ActivityManager;
import android.app.AppGlobals;
+import android.app.GameManagerInternal;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
@@ -48,6 +49,7 @@ import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.server.LocalServices;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -64,6 +66,7 @@ public final class CompatModePackages {
private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
private final ActivityTaskManagerService mService;
+ private GameManagerInternal mGameManager;
private final AtomicFile mFile;
// Compatibility state: no longer ask user to select the mode.
@@ -375,6 +378,18 @@ public final class CompatModePackages {
float getCompatScale(String packageName, int uid) {
final UserHandle userHandle = UserHandle.getUserHandleForUid(uid);
+ if (mGameManager == null) {
+ mGameManager = LocalServices.getService(GameManagerInternal.class);
+ }
+ if (mGameManager != null) {
+ final int userId = userHandle.getIdentifier();
+ final float scalingFactor = mGameManager.getResolutionScalingFactor(packageName,
+ userId);
+ if (scalingFactor > 0) {
+ return 1f / scalingFactor;
+ }
+ }
+
if (CompatChanges.isChangeEnabled(DOWNSCALED, packageName, userHandle)) {
if (CompatChanges.isChangeEnabled(DOWNSCALE_90, packageName, userHandle)) {
return 1f / 0.9f;
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 7aa05412ec88..07694065cbd7 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1725,16 +1725,6 @@ public class DisplayPolicy {
win.mAttrs.insetsFlags.appearance & APPEARANCE_LIGHT_STATUS_BARS,
new Rect(win.getFrame())));
mStatusBarColorCheckedBounds.union(sTmpRect);
- // Check if current activity is letterboxed in order create a LetterboxDetails
- // component to be passed to SysUI for status bar treatment
- final ActivityRecord currentActivity = win.getActivityRecord();
- if (currentActivity != null) {
- final LetterboxDetails currentLetterboxDetails = currentActivity
- .mLetterboxUiController.getLetterboxDetails();
- if (currentLetterboxDetails != null) {
- mLetterboxDetails.add(currentLetterboxDetails);
- }
- }
}
}
@@ -1752,6 +1742,17 @@ public class DisplayPolicy {
mNavBarBackgroundWindow = win;
}
}
+
+ // Check if current activity is letterboxed in order create a LetterboxDetails
+ // component to be passed to SysUI for status bar treatment
+ final ActivityRecord currentActivity = win.getActivityRecord();
+ if (currentActivity != null) {
+ final LetterboxDetails currentLetterboxDetails = currentActivity
+ .mLetterboxUiController.getLetterboxDetails();
+ if (currentLetterboxDetails != null) {
+ mLetterboxDetails.add(currentLetterboxDetails);
+ }
+ }
} else if (win.isDimming()) {
if (mStatusBar != null) {
addStatusBarAppearanceRegionsForDimmingWindow(
@@ -1900,8 +1901,10 @@ public class DisplayPolicy {
/**
* Called when the resource overlays change.
*/
- public void onOverlayChangedLw() {
+ void onOverlayChanged() {
updateCurrentUserResources();
+ // Update the latest display size, cutout.
+ mDisplayContent.updateDisplayInfo();
onConfigurationChanged();
mSystemGestures.onConfigurationChanged();
}
@@ -2933,7 +2936,10 @@ public class DisplayPolicy {
return;
}
- mDisplayContent.unregisterPointerEventListener(mPointerLocationView);
+ if (!mDisplayContent.isRemoved()) {
+ mDisplayContent.unregisterPointerEventListener(mPointerLocationView);
+ }
+
final WindowManager wm = mContext.getSystemService(WindowManager.class);
wm.removeView(mPointerLocationView);
mPointerLocationView = null;
@@ -2958,6 +2964,9 @@ public class DisplayPolicy {
mHandler.post(mGestureNavigationSettingsObserver::unregister);
mHandler.post(mForceShowNavBarSettingsObserver::unregister);
mImmersiveModeConfirmation.release();
+ if (mService.mPointerLocationEnabled) {
+ setPointerLocationEnabled(false);
+ }
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 3e2d7c928936..7a9e6a977dfa 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -398,16 +398,18 @@ class InsetsPolicy {
if (WindowConfiguration.isFloating(windowingMode)
|| (windowingMode == WINDOWING_MODE_MULTI_WINDOW && isAlwaysOnTop)) {
- if (!stateCopied) {
- state = new InsetsState(state);
- stateCopied = true;
+ InsetsState newState = new InsetsState();
+
+ // Only caption and IME are needed.
+ if (state.peekSource(ITYPE_CAPTION_BAR) != null) {
+ newState.addSource(state.peekSource(ITYPE_CAPTION_BAR));
}
- state.removeSource(ITYPE_STATUS_BAR);
- state.removeSource(ITYPE_NAVIGATION_BAR);
- state.removeSource(ITYPE_EXTRA_NAVIGATION_BAR);
- if (windowingMode == WINDOWING_MODE_PINNED) {
- state.removeSource(ITYPE_IME);
+ if (windowingMode != WINDOWING_MODE_PINNED && state.peekSource(ITYPE_IME) != null) {
+ newState.addSource(state.peekSource(ITYPE_IME));
}
+
+ state = newState;
+ stateCopied = true;
}
return state;
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index 7a055d2948ad..f11c2a7da840 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -481,27 +481,24 @@ public class LockTaskController {
*
* @param task the task that requested the end of lock task mode ({@code null} for quitting app
* pinning mode)
- * @param isSystemCaller indicates whether this request comes from the system via
- * {@link ActivityTaskManagerService#stopSystemLockTaskMode()}. If
- * {@code true}, it means the user intends to stop pinned mode through UI;
- * otherwise, it's called by an app and we need to stop locked or pinned
- * mode, subject to checks.
+ * @param stopAppPinning indicates whether to stop app pinning mode or to stop a task from
+ * being locked.
* @param callingUid the caller that requested the end of lock task mode.
* @throws IllegalArgumentException if the calling task is invalid (e.g., {@code null} or not in
* foreground)
* @throws SecurityException if the caller is not authorized to stop the lock task mode, i.e. if
* they differ from the one that launched lock task mode.
*/
- void stopLockTaskMode(@Nullable Task task, boolean isSystemCaller, int callingUid) {
+ void stopLockTaskMode(@Nullable Task task, boolean stopAppPinning, int callingUid) {
if (mLockTaskModeState == LOCK_TASK_MODE_NONE) {
return;
}
- if (isSystemCaller) {
+ if (stopAppPinning) {
if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
clearLockedTasks("stopAppPinning");
} else {
- Slog.e(TAG_LOCKTASK, "Attempted to stop LockTask with isSystemCaller=true");
+ Slog.e(TAG_LOCKTASK, "Attempted to stop app pinning while fully locked");
showLockTaskToast();
}
@@ -642,6 +639,10 @@ public class LockTaskController {
* @param callingUid the caller that requested the launch of lock task mode.
*/
void startLockTaskMode(@NonNull Task task, boolean isSystemCaller, int callingUid) {
+ if (task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) {
+ ProtoLog.w(WM_DEBUG_LOCKTASK, "startLockTaskMode: Can't lock due to auth");
+ return;
+ }
if (!isSystemCaller) {
task.mLockTaskUid = callingUid;
if (task.mLockTaskAuth == LOCK_TASK_AUTH_PINNABLE) {
@@ -654,6 +655,11 @@ public class LockTaskController {
statusBarManager.showScreenPinningRequest(task.mTaskId);
}
return;
+ } else if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
+ // startLockTask() called by app, and app is part of lock task allowlist.
+ // Deactivate the currently pinned task before doing so.
+ Slog.i(TAG, "Stop app pinning before entering full lock task mode");
+ stopLockTaskMode(/* task= */ null, /* stopAppPinning= */ true, callingUid);
}
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index d8c3795abb51..552c6a530544 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1820,6 +1820,10 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
return getItemFromTaskDisplayAreas(TaskDisplayArea::getFocusedActivity);
}
+ boolean hasResumedActivity(int uid) {
+ return forAllActivities(ar -> ar.isState(RESUMED) && ar.getUid() == uid);
+ }
+
boolean isTopDisplayFocusedRootTask(Task task) {
return task != null && task == getTopDisplayFocusedRootTask();
}
@@ -3326,9 +3330,16 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
@VisibleForTesting
void getRunningTasks(int maxNum, List<ActivityManager.RunningTaskInfo> list,
- int flags, int callingUid, ArraySet<Integer> profileIds) {
- mTaskSupervisor.getRunningTasks().getTasks(maxNum, list, flags, this, callingUid,
- profileIds);
+ int flags, int callingUid, ArraySet<Integer> profileIds, int displayId) {
+ WindowContainer root = this;
+ if (displayId != INVALID_DISPLAY) {
+ root = getDisplayContent(displayId);
+ if (root == null) {
+ return;
+ }
+ }
+ mTaskSupervisor.getRunningTasks().getTasks(maxNum, list, flags, mService.getRecentTasks(),
+ root, callingUid, profileIds);
}
void startPowerModeLaunchIfNeeded(boolean forceSend, ActivityRecord targetActivity) {
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index 1ec191ed7c05..120fec0fe0e6 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -60,8 +60,8 @@ class RunningTasks {
private RecentTasks mRecentTasks;
private boolean mKeepIntentExtra;
- void getTasks(int maxNum, List<RunningTaskInfo> list, int flags,
- RootWindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
+ void getTasks(int maxNum, List<RunningTaskInfo> list, int flags, RecentTasks recentTasks,
+ WindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
// Return early if there are no tasks to fetch
if (maxNum <= 0) {
return;
@@ -76,7 +76,7 @@ class RunningTasks {
mAllowed = (flags & FLAG_ALLOWED) == FLAG_ALLOWED;
mFilterOnlyVisibleRecents =
(flags & FLAG_FILTER_ONLY_VISIBLE_RECENTS) == FLAG_FILTER_ONLY_VISIBLE_RECENTS;
- mRecentTasks = root.mService.getRecentTasks();
+ mRecentTasks = recentTasks;
mKeepIntentExtra = (flags & FLAG_KEEP_INTENT_EXTRA) == FLAG_KEEP_INTENT_EXTRA;
final PooledConsumer c = PooledLambda.obtainConsumer(RunningTasks::processTask, this,
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index e65f0bbc1e55..8de556ac4667 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1109,6 +1109,9 @@ class Task extends TaskFragment {
// as the one in the task because either one of them could be the alias activity.
if (Objects.equals(realActivity, r.mActivityComponent) && this.intent != null) {
intent.setComponent(this.intent.getComponent());
+ // Make sure the package name the same to prevent one of the intent is set while the
+ // other one is not.
+ intent.setPackage(this.intent.getPackage());
}
return intent.filterEquals(this.intent);
}
@@ -5319,7 +5322,23 @@ class Task extends TaskFragment {
parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TASK ||
parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TOP ||
(destIntentFlags & Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
- parent.deliverNewIntentLocked(callingUid, destIntent, destGrants, srec.packageName);
+ boolean abort;
+ try {
+ abort = !mTaskSupervisor.checkStartAnyActivityPermission(destIntent,
+ parent.info, null /* resultWho */, -1 /* requestCode */, srec.getPid(),
+ callingUid, srec.info.packageName, null /* callingFeatureId */,
+ false /* ignoreTargetSecurity */, false /* launchingInTask */, srec.app,
+ null /* resultRecord */, null /* resultRootTask */);
+ } catch (SecurityException e) {
+ abort = true;
+ }
+ if (abort) {
+ Slog.e(TAG, "Cannot navigateUpTo, intent =" + destIntent);
+ foundParentInTask = false;
+ } else {
+ parent.deliverNewIntentLocked(callingUid, destIntent, destGrants,
+ srec.packageName);
+ }
} else {
try {
ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 589618e49a68..e6afdd6c5876 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -586,7 +586,10 @@ class TaskFragment extends WindowContainer<WindowContainer> {
}
// Cannot embed activity across TaskFragments for activity result.
- if (a.resultTo != null && a.resultTo.getTaskFragment() != this) {
+ // If the activity that started for result is finishing, it's likely that this start mode
+ // is used to place an activity in the same task. Since the finishing activity won't be
+ // able to get the results, so it's OK to embed in a different TaskFragment.
+ if (a.resultTo != null && !a.resultTo.finishing && a.resultTo.getTaskFragment() != this) {
return EMBEDDING_DISALLOWED_NEW_TASK_FRAGMENT;
}
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 602579fb4f9f..6ca564833d41 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -16,12 +16,21 @@
package com.android.server.wm;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.window.TaskFragmentOrganizer.putErrorInfoInBundle;
+import static android.window.TaskFragmentTransaction.TYPE_ACTIVITY_REPARENT_TO_TASK;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_APPEARED;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_ERROR;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_INFO_CHANGED;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
import static com.android.server.wm.WindowOrganizerController.configurationsAreEqualForOrganizer;
+import static java.util.Objects.requireNonNull;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -32,18 +41,21 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.view.RemoteAnimationDefinition;
import android.window.ITaskFragmentOrganizer;
import android.window.ITaskFragmentOrganizerController;
import android.window.TaskFragmentInfo;
+import android.window.TaskFragmentTransaction;
import com.android.internal.protolog.common.ProtoLog;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
@@ -63,10 +75,12 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
private final ArrayMap<IBinder, TaskFragmentOrganizerState> mTaskFragmentOrganizerState =
new ArrayMap<>();
/**
- * A List which manages the TaskFragment pending event {@link PendingTaskFragmentEvent}
+ * Map from {@link ITaskFragmentOrganizer} to a list of related {@link PendingTaskFragmentEvent}
*/
- private final ArrayList<PendingTaskFragmentEvent> mPendingTaskFragmentEvents =
- new ArrayList<>();
+ private final ArrayMap<IBinder, List<PendingTaskFragmentEvent>> mPendingTaskFragmentEvents =
+ new ArrayMap<>();
+
+ private final ArraySet<Task> mTmpTaskSet = new ArraySet<>();
TaskFragmentOrganizerController(ActivityTaskManagerService atm) {
mAtmService = atm;
@@ -82,10 +96,30 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
private final ITaskFragmentOrganizer mOrganizer;
private final int mOrganizerPid;
private final int mOrganizerUid;
+
+ /**
+ * Map from {@link TaskFragment} to the last {@link TaskFragmentInfo} sent to the
+ * organizer.
+ */
private final Map<TaskFragment, TaskFragmentInfo> mLastSentTaskFragmentInfos =
new WeakHashMap<>();
- private final Map<TaskFragment, Configuration> mLastSentTaskFragmentParentConfigs =
- new WeakHashMap<>();
+
+ /**
+ * Map from {@link TaskFragment} to its leaf {@link Task#mTaskId}. Embedded
+ * {@link TaskFragment} will not be reparented until it is removed.
+ */
+ private final Map<TaskFragment, Integer> mTaskFragmentTaskIds = new WeakHashMap<>();
+
+ /**
+ * Map from {@link Task#mTaskId} to the last Task {@link Configuration} sent to the
+ * organizer.
+ */
+ private final SparseArray<Configuration> mLastSentTaskFragmentParentConfigs =
+ new SparseArray<>();
+
+ /**
+ * Map from temporary activity token to the corresponding {@link ActivityRecord}.
+ */
private final Map<IBinder, ActivityRecord> mTemporaryActivityTokens =
new WeakHashMap<>();
@@ -145,107 +179,120 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
mOrganizer.asBinder().unlinkToDeath(this, 0 /*flags*/);
}
- void onTaskFragmentAppeared(TaskFragment tf) {
+ @NonNull
+ TaskFragmentTransaction.Change prepareTaskFragmentAppeared(@NonNull TaskFragment tf) {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment appeared name=%s", tf.getName());
final TaskFragmentInfo info = tf.getTaskFragmentInfo();
- try {
- mOrganizer.onTaskFragmentAppeared(info);
- mLastSentTaskFragmentInfos.put(tf, info);
- tf.mTaskFragmentAppearedSent = true;
- } catch (RemoteException e) {
- Slog.d(TAG, "Exception sending onTaskFragmentAppeared callback", e);
- }
- onTaskFragmentParentInfoChanged(tf);
- }
-
- void onTaskFragmentVanished(TaskFragment tf) {
+ final int taskId = tf.getTask().mTaskId;
+ tf.mTaskFragmentAppearedSent = true;
+ mLastSentTaskFragmentInfos.put(tf, info);
+ mTaskFragmentTaskIds.put(tf, taskId);
+ return new TaskFragmentTransaction.Change(
+ TYPE_TASK_FRAGMENT_APPEARED)
+ .setTaskFragmentToken(tf.getFragmentToken())
+ .setTaskFragmentInfo(info)
+ .setTaskId(taskId);
+ }
+
+ @NonNull
+ TaskFragmentTransaction.Change prepareTaskFragmentVanished(@NonNull TaskFragment tf) {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment vanished name=%s", tf.getName());
- try {
- mOrganizer.onTaskFragmentVanished(tf.getTaskFragmentInfo());
- } catch (RemoteException e) {
- Slog.d(TAG, "Exception sending onTaskFragmentVanished callback", e);
- }
tf.mTaskFragmentAppearedSent = false;
mLastSentTaskFragmentInfos.remove(tf);
- mLastSentTaskFragmentParentConfigs.remove(tf);
- }
- void onTaskFragmentInfoChanged(TaskFragment tf) {
- // Parent config may have changed. The controller will check if there is any important
- // config change for the organizer.
- onTaskFragmentParentInfoChanged(tf);
+ // Cleanup TaskFragmentParentConfig if this is the last TaskFragment in the Task.
+ final int taskId;
+ if (mTaskFragmentTaskIds.containsKey(tf)) {
+ taskId = mTaskFragmentTaskIds.remove(tf);
+ if (!mTaskFragmentTaskIds.containsValue(taskId)) {
+ // No more TaskFragment in the Task.
+ mLastSentTaskFragmentParentConfigs.remove(taskId);
+ }
+ } else {
+ // This can happen if the appeared wasn't sent before remove.
+ taskId = INVALID_TASK_ID;
+ }
+ return new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_VANISHED)
+ .setTaskFragmentToken(tf.getFragmentToken())
+ .setTaskFragmentInfo(tf.getTaskFragmentInfo())
+ .setTaskId(taskId);
+ }
+
+ @Nullable
+ TaskFragmentTransaction.Change prepareTaskFragmentInfoChanged(
+ @NonNull TaskFragment tf) {
// Check if the info is different from the last reported info.
final TaskFragmentInfo info = tf.getTaskFragmentInfo();
final TaskFragmentInfo lastInfo = mLastSentTaskFragmentInfos.get(tf);
if (info.equalsForTaskFragmentOrganizer(lastInfo) && configurationsAreEqualForOrganizer(
info.getConfiguration(), lastInfo.getConfiguration())) {
- return;
+ return null;
}
+
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment info changed name=%s",
tf.getName());
- try {
- mOrganizer.onTaskFragmentInfoChanged(info);
- mLastSentTaskFragmentInfos.put(tf, info);
- } catch (RemoteException e) {
- Slog.d(TAG, "Exception sending onTaskFragmentInfoChanged callback", e);
- }
+ mLastSentTaskFragmentInfos.put(tf, info);
+ return new TaskFragmentTransaction.Change(
+ TYPE_TASK_FRAGMENT_INFO_CHANGED)
+ .setTaskFragmentToken(tf.getFragmentToken())
+ .setTaskFragmentInfo(info)
+ .setTaskId(tf.getTask().mTaskId);
}
- void onTaskFragmentParentInfoChanged(TaskFragment tf) {
+ @Nullable
+ TaskFragmentTransaction.Change prepareTaskFragmentParentInfoChanged(
+ @NonNull Task task) {
+ final int taskId = task.mTaskId;
// Check if the parent info is different from the last reported parent info.
- if (tf.getParent() == null || tf.getParent().asTask() == null) {
- mLastSentTaskFragmentParentConfigs.remove(tf);
- return;
- }
- final Task parent = tf.getParent().asTask();
- final Configuration parentConfig = parent.getConfiguration();
- final Configuration lastParentConfig = mLastSentTaskFragmentParentConfigs.get(tf);
- if (configurationsAreEqualForOrganizer(parentConfig, lastParentConfig)
- && parentConfig.windowConfiguration.getWindowingMode()
+ final Configuration taskConfig = task.getConfiguration();
+ final Configuration lastParentConfig = mLastSentTaskFragmentParentConfigs.get(taskId);
+ if (configurationsAreEqualForOrganizer(taskConfig, lastParentConfig)
+ && taskConfig.windowConfiguration.getWindowingMode()
== lastParentConfig.windowConfiguration.getWindowingMode()) {
- return;
+ return null;
}
+
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
"TaskFragment parent info changed name=%s parentTaskId=%d",
- tf.getName(), parent.mTaskId);
- try {
- mOrganizer.onTaskFragmentParentInfoChanged(tf.getFragmentToken(), parentConfig);
- mLastSentTaskFragmentParentConfigs.put(tf, new Configuration(parentConfig));
- } catch (RemoteException e) {
- Slog.d(TAG, "Exception sending onTaskFragmentParentInfoChanged callback", e);
- }
+ task.getName(), taskId);
+ mLastSentTaskFragmentParentConfigs.put(taskId, new Configuration(taskConfig));
+ return new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED)
+ .setTaskId(taskId)
+ .setTaskConfiguration(taskConfig);
}
- void onTaskFragmentError(IBinder errorCallbackToken, @Nullable TaskFragment taskFragment,
- int opType, Throwable exception) {
+ @NonNull
+ TaskFragmentTransaction.Change prepareTaskFragmentError(
+ @Nullable IBinder errorCallbackToken, @Nullable TaskFragment taskFragment,
+ int opType, @NonNull Throwable exception) {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
"Sending TaskFragment error exception=%s", exception.toString());
final TaskFragmentInfo info =
taskFragment != null ? taskFragment.getTaskFragmentInfo() : null;
final Bundle errorBundle = putErrorInfoInBundle(exception, info, opType);
- try {
- mOrganizer.onTaskFragmentError(errorCallbackToken, errorBundle);
- } catch (RemoteException e) {
- Slog.d(TAG, "Exception sending onTaskFragmentError callback", e);
- }
+ return new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_ERROR)
+ .setErrorCallbackToken(errorCallbackToken)
+ .setErrorBundle(errorBundle);
}
- void onActivityReparentToTask(ActivityRecord activity) {
+ @Nullable
+ TaskFragmentTransaction.Change prepareActivityReparentToTask(
+ @NonNull ActivityRecord activity) {
if (activity.finishing) {
Slog.d(TAG, "Reparent activity=" + activity.token + " is finishing");
- return;
+ return null;
}
final Task task = activity.getTask();
if (task == null || task.effectiveUid != mOrganizerUid) {
Slog.d(TAG, "Reparent activity=" + activity.token
+ " is not in a task belong to the organizer app.");
- return;
+ return null;
}
if (task.isAllowedToEmbedActivity(activity, mOrganizerUid) != EMBEDDING_ALLOWED) {
Slog.d(TAG, "Reparent activity=" + activity.token
+ " is not allowed to be embedded.");
- return;
+ return null;
}
final IBinder activityToken;
@@ -268,11 +315,10 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
}
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Activity=%s reparent to taskId=%d",
activity.token, task.mTaskId);
- try {
- mOrganizer.onActivityReparentToTask(task.mTaskId, activity.intent, activityToken);
- } catch (RemoteException e) {
- Slog.d(TAG, "Exception sending onActivityReparentToTask callback", e);
- }
+ return new TaskFragmentTransaction.Change(TYPE_ACTIVITY_REPARENT_TO_TASK)
+ .setTaskId(task.mTaskId)
+ .setActivityIntent(activity.intent)
+ .setActivityToken(activityToken);
}
}
@@ -303,6 +349,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
}
mTaskFragmentOrganizerState.put(organizer.asBinder(),
new TaskFragmentOrganizerState(organizer, pid, uid));
+ mPendingTaskFragmentEvents.put(organizer.asBinder(), new ArrayList<>());
}
}
@@ -375,7 +422,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
*/
@Nullable
public RemoteAnimationDefinition getRemoteAnimationDefinition(
- ITaskFragmentOrganizer organizer, int taskId) {
+ @NonNull ITaskFragmentOrganizer organizer, int taskId) {
synchronized (mGlobalLock) {
final TaskFragmentOrganizerState organizerState =
mTaskFragmentOrganizerState.get(organizer.asBinder());
@@ -385,12 +432,18 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
}
}
- int getTaskFragmentOrganizerUid(ITaskFragmentOrganizer organizer) {
+ int getTaskFragmentOrganizerUid(@NonNull ITaskFragmentOrganizer organizer) {
final TaskFragmentOrganizerState state = validateAndGetState(organizer);
return state.mOrganizerUid;
}
- void onTaskFragmentAppeared(ITaskFragmentOrganizer organizer, TaskFragment taskFragment) {
+ void onTaskFragmentAppeared(@NonNull ITaskFragmentOrganizer organizer,
+ @NonNull TaskFragment taskFragment) {
+ if (taskFragment.getTask() == null) {
+ Slog.w(TAG, "onTaskFragmentAppeared failed because it is not attached tf="
+ + taskFragment);
+ return;
+ }
final TaskFragmentOrganizerState state = validateAndGetState(organizer);
if (!state.addTaskFragment(taskFragment)) {
return;
@@ -398,27 +451,15 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
PendingTaskFragmentEvent pendingEvent = getPendingTaskFragmentEvent(taskFragment,
PendingTaskFragmentEvent.EVENT_APPEARED);
if (pendingEvent == null) {
- pendingEvent = new PendingTaskFragmentEvent.Builder(
+ addPendingEvent(new PendingTaskFragmentEvent.Builder(
PendingTaskFragmentEvent.EVENT_APPEARED, organizer)
.setTaskFragment(taskFragment)
- .build();
- mPendingTaskFragmentEvents.add(pendingEvent);
+ .build());
}
}
- void onTaskFragmentInfoChanged(ITaskFragmentOrganizer organizer, TaskFragment taskFragment) {
- handleTaskFragmentInfoChanged(organizer, taskFragment,
- PendingTaskFragmentEvent.EVENT_INFO_CHANGED);
- }
-
- void onTaskFragmentParentInfoChanged(ITaskFragmentOrganizer organizer,
- TaskFragment taskFragment) {
- handleTaskFragmentInfoChanged(organizer, taskFragment,
- PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED);
- }
-
- private void handleTaskFragmentInfoChanged(ITaskFragmentOrganizer organizer,
- TaskFragment taskFragment, int eventType) {
+ void onTaskFragmentInfoChanged(@NonNull ITaskFragmentOrganizer organizer,
+ @NonNull TaskFragment taskFragment) {
validateAndGetState(organizer);
if (!taskFragment.mTaskFragmentAppearedSent) {
// Skip if TaskFragment still not appeared.
@@ -426,64 +467,61 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
}
PendingTaskFragmentEvent pendingEvent = getLastPendingLifecycleEvent(taskFragment);
if (pendingEvent == null) {
- pendingEvent = new PendingTaskFragmentEvent.Builder(eventType, organizer)
- .setTaskFragment(taskFragment)
- .build();
+ pendingEvent = new PendingTaskFragmentEvent.Builder(
+ PendingTaskFragmentEvent.EVENT_INFO_CHANGED, organizer)
+ .setTaskFragment(taskFragment)
+ .build();
} else {
if (pendingEvent.mEventType == PendingTaskFragmentEvent.EVENT_VANISHED) {
// Skipped the info changed event if vanished event is pending.
return;
}
// Remove and add for re-ordering.
- mPendingTaskFragmentEvents.remove(pendingEvent);
+ removePendingEvent(pendingEvent);
// Reset the defer time when TaskFragment is changed, so that it can check again if
// the event should be sent to the organizer, for example the TaskFragment may become
// empty.
pendingEvent.mDeferTime = 0;
}
- mPendingTaskFragmentEvents.add(pendingEvent);
+ addPendingEvent(pendingEvent);
}
- void onTaskFragmentVanished(ITaskFragmentOrganizer organizer, TaskFragment taskFragment) {
+ void onTaskFragmentVanished(@NonNull ITaskFragmentOrganizer organizer,
+ @NonNull TaskFragment taskFragment) {
final TaskFragmentOrganizerState state = validateAndGetState(organizer);
- for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) {
- PendingTaskFragmentEvent entry = mPendingTaskFragmentEvents.get(i);
- if (taskFragment == entry.mTaskFragment) {
- mPendingTaskFragmentEvents.remove(i);
- if (entry.mEventType == PendingTaskFragmentEvent.EVENT_APPEARED) {
- // If taskFragment appeared callback is pending, ignore the vanished request.
- return;
- }
+ final List<PendingTaskFragmentEvent> pendingEvents = mPendingTaskFragmentEvents
+ .get(organizer.asBinder());
+ // Remove any pending events since this TaskFragment is being removed.
+ for (int i = pendingEvents.size() - 1; i >= 0; i--) {
+ final PendingTaskFragmentEvent event = pendingEvents.get(i);
+ if (taskFragment == event.mTaskFragment) {
+ pendingEvents.remove(i);
}
}
- if (!taskFragment.mTaskFragmentAppearedSent) {
- return;
- }
- final PendingTaskFragmentEvent pendingEvent = new PendingTaskFragmentEvent.Builder(
+ addPendingEvent(new PendingTaskFragmentEvent.Builder(
PendingTaskFragmentEvent.EVENT_VANISHED, organizer)
.setTaskFragment(taskFragment)
- .build();
- mPendingTaskFragmentEvents.add(pendingEvent);
+ .build());
state.removeTaskFragment(taskFragment);
}
- void onTaskFragmentError(ITaskFragmentOrganizer organizer, IBinder errorCallbackToken,
- TaskFragment taskFragment, int opType, Throwable exception) {
+ void onTaskFragmentError(@NonNull ITaskFragmentOrganizer organizer,
+ @Nullable IBinder errorCallbackToken, @Nullable TaskFragment taskFragment,
+ int opType, @NonNull Throwable exception) {
validateAndGetState(organizer);
Slog.w(TAG, "onTaskFragmentError ", exception);
- final PendingTaskFragmentEvent pendingEvent = new PendingTaskFragmentEvent.Builder(
+ addPendingEvent(new PendingTaskFragmentEvent.Builder(
PendingTaskFragmentEvent.EVENT_ERROR, organizer)
.setErrorCallbackToken(errorCallbackToken)
.setTaskFragment(taskFragment)
.setException(exception)
.setOpType(opType)
- .build();
- mPendingTaskFragmentEvents.add(pendingEvent);
+ .build());
// Make sure the error event will be dispatched if there are no other changes.
mAtmService.mWindowManager.mWindowPlacerLocked.requestTraversal();
}
- void onActivityReparentToTask(ActivityRecord activity) {
+ void onActivityReparentToTask(@NonNull ActivityRecord activity) {
final ITaskFragmentOrganizer organizer;
if (activity.mLastTaskFragmentOrganizerBeforePip != null) {
// If the activity is previously embedded in an organized TaskFragment.
@@ -508,28 +546,30 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
Slog.w(TAG, "The last TaskFragmentOrganizer no longer exists");
return;
}
- final PendingTaskFragmentEvent pendingEvent = new PendingTaskFragmentEvent.Builder(
+ addPendingEvent(new PendingTaskFragmentEvent.Builder(
PendingTaskFragmentEvent.EVENT_ACTIVITY_REPARENT_TO_TASK, organizer)
.setActivity(activity)
- .build();
- mPendingTaskFragmentEvents.add(pendingEvent);
+ .build());
}
- boolean isOrganizerRegistered(ITaskFragmentOrganizer organizer) {
+ private void addPendingEvent(@NonNull PendingTaskFragmentEvent event) {
+ mPendingTaskFragmentEvents.get(event.mTaskFragmentOrg.asBinder()).add(event);
+ }
+
+ private void removePendingEvent(@NonNull PendingTaskFragmentEvent event) {
+ mPendingTaskFragmentEvents.get(event.mTaskFragmentOrg.asBinder()).remove(event);
+ }
+
+ boolean isOrganizerRegistered(@NonNull ITaskFragmentOrganizer organizer) {
return mTaskFragmentOrganizerState.containsKey(organizer.asBinder());
}
- private void removeOrganizer(ITaskFragmentOrganizer organizer) {
+ private void removeOrganizer(@NonNull ITaskFragmentOrganizer organizer) {
final TaskFragmentOrganizerState state = validateAndGetState(organizer);
// remove all of the children of the organized TaskFragment
state.dispose();
// Remove any pending event of this organizer.
- for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) {
- final PendingTaskFragmentEvent event = mPendingTaskFragmentEvents.get(i);
- if (event.mTaskFragmentOrg.asBinder().equals(organizer.asBinder())) {
- mPendingTaskFragmentEvents.remove(i);
- }
- }
+ mPendingTaskFragmentEvents.remove(organizer.asBinder());
mTaskFragmentOrganizerState.remove(organizer.asBinder());
}
@@ -539,7 +579,9 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
* we wouldn't register {@link DeathRecipient} for the organizer, and might not remove the
* {@link TaskFragment} after the organizer process died.
*/
- private TaskFragmentOrganizerState validateAndGetState(ITaskFragmentOrganizer organizer) {
+ @NonNull
+ private TaskFragmentOrganizerState validateAndGetState(
+ @NonNull ITaskFragmentOrganizer organizer) {
final TaskFragmentOrganizerState state =
mTaskFragmentOrganizerState.get(organizer.asBinder());
if (state == null) {
@@ -583,6 +625,8 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
private final Throwable mException;
@Nullable
private final ActivityRecord mActivity;
+ @Nullable
+ private final Task mTask;
// Set when the event is deferred due to the host task is invisible. The defer time will
// be the last active time of the host task.
private long mDeferTime;
@@ -594,6 +638,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
@Nullable IBinder errorCallbackToken,
@Nullable Throwable exception,
@Nullable ActivityRecord activity,
+ @Nullable Task task,
int opType) {
mEventType = eventType;
mTaskFragmentOrg = taskFragmentOrg;
@@ -601,6 +646,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
mErrorCallbackToken = errorCallbackToken;
mException = exception;
mActivity = activity;
+ mTask = task;
mOpType = opType;
}
@@ -632,11 +678,13 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
private Throwable mException;
@Nullable
private ActivityRecord mActivity;
+ @Nullable
+ private Task mTask;
private int mOpType;
- Builder(@EventType int eventType, ITaskFragmentOrganizer taskFragmentOrg) {
+ Builder(@EventType int eventType, @NonNull ITaskFragmentOrganizer taskFragmentOrg) {
mEventType = eventType;
- mTaskFragmentOrg = taskFragmentOrg;
+ mTaskFragmentOrg = requireNonNull(taskFragmentOrg);
}
Builder setTaskFragment(@Nullable TaskFragment taskFragment) {
@@ -649,13 +697,18 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
return this;
}
- Builder setException(@Nullable Throwable exception) {
- mException = exception;
+ Builder setException(@NonNull Throwable exception) {
+ mException = requireNonNull(exception);
return this;
}
- Builder setActivity(@Nullable ActivityRecord activity) {
- mActivity = activity;
+ Builder setActivity(@NonNull ActivityRecord activity) {
+ mActivity = requireNonNull(activity);
+ return this;
+ }
+
+ Builder setTask(@NonNull Task task) {
+ mTask = requireNonNull(task);
return this;
}
@@ -666,29 +719,35 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
PendingTaskFragmentEvent build() {
return new PendingTaskFragmentEvent(mEventType, mTaskFragmentOrg, mTaskFragment,
- mErrorCallbackToken, mException, mActivity, mOpType);
+ mErrorCallbackToken, mException, mActivity, mTask, mOpType);
}
}
}
@Nullable
- private PendingTaskFragmentEvent getLastPendingLifecycleEvent(TaskFragment tf) {
- for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) {
- PendingTaskFragmentEvent entry = mPendingTaskFragmentEvents.get(i);
- if (tf == entry.mTaskFragment && entry.isLifecycleEvent()) {
- return entry;
+ private PendingTaskFragmentEvent getLastPendingLifecycleEvent(@NonNull TaskFragment tf) {
+ final ITaskFragmentOrganizer organizer = tf.getTaskFragmentOrganizer();
+ final List<PendingTaskFragmentEvent> events = mPendingTaskFragmentEvents
+ .get(organizer.asBinder());
+ for (int i = events.size() - 1; i >= 0; i--) {
+ final PendingTaskFragmentEvent event = events.get(i);
+ if (tf == event.mTaskFragment && event.isLifecycleEvent()) {
+ return event;
}
}
return null;
}
@Nullable
- private PendingTaskFragmentEvent getPendingTaskFragmentEvent(TaskFragment taskFragment,
+ private PendingTaskFragmentEvent getPendingTaskFragmentEvent(@NonNull TaskFragment taskFragment,
int type) {
- for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) {
- PendingTaskFragmentEvent entry = mPendingTaskFragmentEvents.get(i);
- if (taskFragment == entry.mTaskFragment && type == entry.mEventType) {
- return entry;
+ final ITaskFragmentOrganizer organizer = taskFragment.getTaskFragmentOrganizer();
+ final List<PendingTaskFragmentEvent> events = mPendingTaskFragmentEvents
+ .get(organizer.asBinder());
+ for (int i = events.size() - 1; i >= 0; i--) {
+ final PendingTaskFragmentEvent event = events.get(i);
+ if (taskFragment == event.mTaskFragment && type == event.mEventType) {
+ return event;
}
}
return null;
@@ -714,12 +773,25 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|| mPendingTaskFragmentEvents.isEmpty()) {
return;
}
+ final int organizerNum = mPendingTaskFragmentEvents.size();
+ for (int i = 0; i < organizerNum; i++) {
+ final ITaskFragmentOrganizer organizer = mTaskFragmentOrganizerState.get(
+ mPendingTaskFragmentEvents.keyAt(i)).mOrganizer;
+ dispatchPendingEvents(organizer, mPendingTaskFragmentEvents.valueAt(i));
+ }
+ }
+
+ void dispatchPendingEvents(@NonNull ITaskFragmentOrganizer organizer,
+ @NonNull List<PendingTaskFragmentEvent> pendingEvents) {
+ if (pendingEvents.isEmpty()) {
+ return;
+ }
final ArrayList<Task> visibleTasks = new ArrayList<>();
final ArrayList<Task> invisibleTasks = new ArrayList<>();
final ArrayList<PendingTaskFragmentEvent> candidateEvents = new ArrayList<>();
- for (int i = 0, n = mPendingTaskFragmentEvents.size(); i < n; i++) {
- final PendingTaskFragmentEvent event = mPendingTaskFragmentEvents.get(i);
+ for (int i = 0, n = pendingEvents.size(); i < n; i++) {
+ final PendingTaskFragmentEvent event = pendingEvents.get(i);
final Task task = event.mTaskFragment != null ? event.mTaskFragment.getTask() : null;
if (task != null && (task.lastActiveTime <= event.mDeferTime
|| !(isTaskVisible(task, visibleTasks, invisibleTasks)
@@ -731,16 +803,35 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
candidateEvents.add(event);
}
final int numEvents = candidateEvents.size();
- for (int i = 0; i < numEvents; i++) {
- dispatchEvent(candidateEvents.get(i));
+ if (numEvents == 0) {
+ return;
}
- if (numEvents > 0) {
- mPendingTaskFragmentEvents.removeAll(candidateEvents);
+
+ mTmpTaskSet.clear();
+ final TaskFragmentTransaction transaction = new TaskFragmentTransaction();
+ for (int i = 0; i < numEvents; i++) {
+ final PendingTaskFragmentEvent event = candidateEvents.get(i);
+ if (event.mEventType == PendingTaskFragmentEvent.EVENT_APPEARED
+ || event.mEventType == PendingTaskFragmentEvent.EVENT_INFO_CHANGED) {
+ final Task task = event.mTaskFragment.getTask();
+ if (mTmpTaskSet.add(task)) {
+ // Make sure the organizer know about the Task config.
+ transaction.addChange(prepareChange(new PendingTaskFragmentEvent.Builder(
+ PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED, organizer)
+ .setTask(task)
+ .build()));
+ }
+ }
+ transaction.addChange(prepareChange(event));
}
+ mTmpTaskSet.clear();
+ dispatchTransactionInfo(organizer, transaction);
+ pendingEvents.removeAll(candidateEvents);
}
- private static boolean isTaskVisible(Task task, ArrayList<Task> knownVisibleTasks,
- ArrayList<Task> knownInvisibleTasks) {
+ private static boolean isTaskVisible(@NonNull Task task,
+ @NonNull ArrayList<Task> knownVisibleTasks,
+ @NonNull ArrayList<Task> knownInvisibleTasks) {
if (knownVisibleTasks.contains(task)) {
return true;
}
@@ -756,44 +847,63 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
}
}
- void dispatchPendingInfoChangedEvent(TaskFragment taskFragment) {
- PendingTaskFragmentEvent event = getPendingTaskFragmentEvent(taskFragment,
+ void dispatchPendingInfoChangedEvent(@NonNull TaskFragment taskFragment) {
+ final PendingTaskFragmentEvent event = getPendingTaskFragmentEvent(taskFragment,
PendingTaskFragmentEvent.EVENT_INFO_CHANGED);
if (event == null) {
return;
}
- dispatchEvent(event);
- mPendingTaskFragmentEvents.remove(event);
+ final ITaskFragmentOrganizer organizer = taskFragment.getTaskFragmentOrganizer();
+ final TaskFragmentTransaction transaction = new TaskFragmentTransaction();
+ // Make sure the organizer know about the Task config.
+ transaction.addChange(prepareChange(new PendingTaskFragmentEvent.Builder(
+ PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED, organizer)
+ .setTask(taskFragment.getTask())
+ .build()));
+ transaction.addChange(prepareChange(event));
+ dispatchTransactionInfo(event.mTaskFragmentOrg, transaction);
+ mPendingTaskFragmentEvents.get(organizer.asBinder()).remove(event);
+ }
+
+ private void dispatchTransactionInfo(@NonNull ITaskFragmentOrganizer organizer,
+ @NonNull TaskFragmentTransaction transaction) {
+ if (transaction.isEmpty()) {
+ return;
+ }
+ try {
+ organizer.onTransactionReady(transaction);
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Exception sending TaskFragmentTransaction", e);
+ }
}
- private void dispatchEvent(PendingTaskFragmentEvent event) {
+ @Nullable
+ private TaskFragmentTransaction.Change prepareChange(
+ @NonNull PendingTaskFragmentEvent event) {
final ITaskFragmentOrganizer taskFragmentOrg = event.mTaskFragmentOrg;
final TaskFragment taskFragment = event.mTaskFragment;
final TaskFragmentOrganizerState state =
mTaskFragmentOrganizerState.get(taskFragmentOrg.asBinder());
if (state == null) {
- return;
+ return null;
}
switch (event.mEventType) {
case PendingTaskFragmentEvent.EVENT_APPEARED:
- state.onTaskFragmentAppeared(taskFragment);
- break;
+ return state.prepareTaskFragmentAppeared(taskFragment);
case PendingTaskFragmentEvent.EVENT_VANISHED:
- state.onTaskFragmentVanished(taskFragment);
- break;
+ return state.prepareTaskFragmentVanished(taskFragment);
case PendingTaskFragmentEvent.EVENT_INFO_CHANGED:
- state.onTaskFragmentInfoChanged(taskFragment);
- break;
+ return state.prepareTaskFragmentInfoChanged(taskFragment);
case PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED:
- state.onTaskFragmentParentInfoChanged(taskFragment);
- break;
+ return state.prepareTaskFragmentParentInfoChanged(event.mTask);
case PendingTaskFragmentEvent.EVENT_ERROR:
- state.onTaskFragmentError(event.mErrorCallbackToken, taskFragment, event.mOpType,
- event.mException);
- break;
+ return state.prepareTaskFragmentError(event.mErrorCallbackToken, taskFragment,
+ event.mOpType, event.mException);
case PendingTaskFragmentEvent.EVENT_ACTIVITY_REPARENT_TO_TASK:
- state.onActivityReparentToTask(event.mActivity);
+ return state.prepareActivityReparentToTask(event.mActivity);
+ default:
+ throw new IllegalArgumentException("Unknown TaskFragmentEvent=" + event.mEventType);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index adb8bf696dd3..70dd9f3179f5 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -3389,6 +3389,13 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
pw.println(prefix + "mLastOrientationSource=" + mLastOrientationSource);
pw.println(prefix + "deepestLastOrientationSource=" + getLastOrientationSource());
}
+ if (mLocalInsetsSourceProviders != null && mLocalInsetsSourceProviders.size() != 0) {
+ pw.println(prefix + mLocalInsetsSourceProviders.size() + " LocalInsetsSourceProviders");
+ final String childPrefix = prefix + " ";
+ for (int i = 0; i < mLocalInsetsSourceProviders.size(); ++i) {
+ mLocalInsetsSourceProviders.valueAt(i).dump(pw, childPrefix);
+ }
+ }
}
final void updateSurfacePositionNonOrganized() {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 6527bbb67099..79037ab6a787 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7041,13 +7041,14 @@ public class WindowManagerService extends IWindowManager.Stub
}
public void onOverlayChanged() {
- synchronized (mGlobalLock) {
- mRoot.forAllDisplays(displayContent -> {
- displayContent.getDisplayPolicy().onOverlayChangedLw();
- displayContent.updateDisplayInfo();
- });
- requestTraversal();
- }
+ // Post to display thread so it can get the latest display info.
+ mH.post(() -> {
+ synchronized (mGlobalLock) {
+ mAtmService.deferWindowLayout();
+ mRoot.forAllDisplays(dc -> dc.getDisplayPolicy().onOverlayChanged());
+ mAtmService.continueWindowLayout();
+ }
+ });
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 3bc1e3588acc..283010ea5767 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2452,8 +2452,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
dc.setImeLayeringTarget(null);
dc.computeImeTarget(true /* updateImeTarget */);
}
- if (dc.getImeInputTarget() == this
- && (mActivityRecord == null || !mActivityRecord.isRelaunching())) {
+ if (dc.getImeInputTarget() == this && !inRelaunchingActivity()) {
dc.updateImeInputAndControlTarget(null);
}
@@ -2580,7 +2579,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// usually unnoticeable (e.g. covered by rotation animation) and the animation
// bounds could be inconsistent, such as depending on when the window applies
// its draw transaction with new rotation.
- final boolean allowExitAnimation = !getDisplayContent().inTransition();
+ final boolean allowExitAnimation = !getDisplayContent().inTransition()
+ // There will be a new window so the exit animation may not be visible or
+ // look weird if its orientation is changed.
+ && !inRelaunchingActivity();
if (wasVisible) {
final int transit = (!startingWindow) ? TRANSIT_EXIT : TRANSIT_PREVIEW_DONE;
@@ -3870,7 +3872,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// If the activity is scheduled to relaunch, skip sending the resized to ViewRootImpl now
// since it will be destroyed anyway. This also prevents the client from receiving
// windowing mode change before it is destroyed.
- if (mActivityRecord != null && mActivityRecord.isRelaunching()) {
+ if (inRelaunchingActivity()) {
return;
}
// If this is an activity or wallpaper and is invisible or going invisible, don't report
@@ -3956,6 +3958,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
+ boolean inRelaunchingActivity() {
+ return mActivityRecord != null && mActivityRecord.isRelaunching();
+ }
+
boolean isClientLocal() {
return mClient instanceof IWindow.Stub;
}
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index bbb21f8122c5..72e7e65ae43a 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -46,6 +46,7 @@ import android.view.DisplayInfo;
import android.view.InsetsState;
import android.view.Surface;
import android.view.SurfaceControl;
+import android.view.WindowManager;
import android.view.WindowManager.LayoutParams.WindowType;
import android.window.WindowContext;
@@ -558,6 +559,12 @@ class WindowToken extends WindowContainer<WindowState> {
// The window may be detached or detaching.
return;
}
+ if (mTransitionController.isShellTransitionsEnabled()
+ && asActivityRecord() != null && isVisible()) {
+ // Trigger an activity level rotation transition.
+ mTransitionController.requestTransitionIfNeeded(WindowManager.TRANSIT_CHANGE, this);
+ mTransitionController.setReady(this);
+ }
final int originalRotation = getWindowConfiguration().getRotation();
onConfigurationChanged(parent.getConfiguration());
onCancelFixedRotationTransform(originalRotation);
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/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
index af5718f076d6..1d560780dc81 100644
--- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
+++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
@@ -29,8 +29,10 @@
#include <dirent.h>
#include <jni.h>
#include <linux/errno.h>
+#include <linux/time.h>
#include <log/log.h>
#include <meminfo/procmeminfo.h>
+#include <meminfo/sysmeminfo.h>
#include <nativehelper/JNIHelp.h>
#include <processgroup/processgroup.h>
#include <stddef.h>
@@ -42,6 +44,7 @@
#include <sys/sysinfo.h>
#include <sys/types.h>
#include <unistd.h>
+#include <utils/Timers.h>
#include <utils/Trace.h>
#include <algorithm>
@@ -457,6 +460,12 @@ static void com_android_server_am_CachedAppOptimizer_cancelCompaction(JNIEnv*, j
ATRACE_INSTANT_FOR_TRACK(ATRACE_COMPACTION_TRACK, "Cancel compaction");
}
+static jlong com_android_server_am_CachedAppOptimizer_threadCpuTimeNs(JNIEnv*, jobject) {
+ int64_t currentCpuTime = systemTime(CLOCK_THREAD_CPUTIME_ID);
+
+ return currentCpuTime;
+}
+
static jdouble com_android_server_am_CachedAppOptimizer_getFreeSwapPercent(JNIEnv*, jobject) {
struct sysinfo memoryInfo;
int error = sysinfo(&memoryInfo);
@@ -467,6 +476,16 @@ static jdouble com_android_server_am_CachedAppOptimizer_getFreeSwapPercent(JNIEn
return (double)memoryInfo.freeswap / (double)memoryInfo.totalswap;
}
+static jlong com_android_server_am_CachedAppOptimizer_getUsedZramMemory() {
+ android::meminfo::SysMemInfo sysmeminfo;
+ return sysmeminfo.mem_zram_kb();
+}
+
+static jlong com_android_server_am_CachedAppOptimizer_getMemoryFreedCompaction() {
+ android::meminfo::SysMemInfo sysmeminfo;
+ return sysmeminfo.mem_compacted_kb("/sys/block/zram0/");
+}
+
static void com_android_server_am_CachedAppOptimizer_compactProcess(JNIEnv*, jobject, jint pid,
jint compactionFlags) {
compactProcessOrFallback(pid, compactionFlags);
@@ -520,8 +539,13 @@ static const JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"cancelCompaction", "()V",
(void*)com_android_server_am_CachedAppOptimizer_cancelCompaction},
+ {"threadCpuTimeNs", "()J", (void*)com_android_server_am_CachedAppOptimizer_threadCpuTimeNs},
{"getFreeSwapPercent", "()D",
(void*)com_android_server_am_CachedAppOptimizer_getFreeSwapPercent},
+ {"getUsedZramMemory", "()J",
+ (void*)com_android_server_am_CachedAppOptimizer_getUsedZramMemory},
+ {"getMemoryFreedCompaction", "()J",
+ (void*)com_android_server_am_CachedAppOptimizer_getMemoryFreedCompaction},
{"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem},
{"compactProcess", "(II)V", (void*)com_android_server_am_CachedAppOptimizer_compactProcess},
{"freezeBinder", "(IZ)I", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder},
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 74ed3dfec7ba..891bc0f06f2c 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -462,10 +462,10 @@ void NativeInputManager::dump(std::string& dump) {
mInputManager->getReader().dump(dump);
dump += "\n";
- mInputManager->getUnwantedInteractionBlocker().dump(dump);
+ mInputManager->getBlocker().dump(dump);
dump += "\n";
- mInputManager->getClassifier().dump(dump);
+ mInputManager->getProcessor().dump(dump);
dump += "\n";
mInputManager->getDispatcher().dump(dump);
@@ -702,7 +702,7 @@ void NativeInputManager::ensureSpriteControllerLocked() REQUIRES(mLock) {
void NativeInputManager::notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) {
ATRACE_CALL();
- mInputManager->getUnwantedInteractionBlocker().notifyInputDevicesChanged(inputDevices);
+ mInputManager->getBlocker().notifyInputDevicesChanged(inputDevices);
JNIEnv* env = jniEnv();
size_t count = inputDevices.size();
@@ -1483,7 +1483,7 @@ PointerIconStyle NativeInputManager::getCustomPointerIconId() {
}
void NativeInputManager::setMotionClassifierEnabled(bool enabled) {
- mInputManager->getClassifier().setMotionClassifierEnabled(enabled);
+ mInputManager->getProcessor().setMotionClassifierEnabled(enabled);
}
bool NativeInputManager::isPerDisplayTouchModeEnabled() {
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index 09044e72f60b..073b131cc819 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -45,6 +45,8 @@
<xs:element type="highBrightnessMode" name="highBrightnessMode" minOccurs="0"
maxOccurs="1"/>
<xs:element type="displayQuirks" name="quirks" minOccurs="0" maxOccurs="1" />
+ <xs:element type="autoBrightness" name="autoBrightness" minOccurs="0"
+ maxOccurs="1" />
<xs:element type="nonNegativeDecimal" name="screenBrightnessRampFastDecrease">
<xs:annotation name="final"/>
</xs:element>
@@ -101,6 +103,21 @@
<!-- Type definitions -->
+ <xs:complexType name="autoBrightness">
+ <xs:sequence>
+ <!-- Sets the debounce for autoBrightness brightening in millis-->
+ <xs:element name="brighteningLightDebounceMillis" type="xs:nonNegativeInteger"
+ minOccurs="0" maxOccurs="1">
+ <xs:annotation name="final"/>
+ </xs:element>
+ <!-- Sets the debounce for autoBrightness darkening in millis-->
+ <xs:element name="darkeningLightDebounceMillis" type="xs:nonNegativeInteger"
+ minOccurs="0" maxOccurs="1">
+ <xs:annotation name="final"/>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+
<xs:complexType name="displayQuirks">
<xs:sequence>
<xs:element name="quirk" type="xs:string" minOccurs="0" maxOccurs="unbounded" />
@@ -341,5 +358,4 @@
<xs:annotation name="final"/>
</xs:element>
</xs:complexType>
-
</xs:schema>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index e8b13ca6356e..e9a926946764 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -1,6 +1,14 @@
// Signature format: 2.0
package com.android.server.display.config {
+ public class AutoBrightness {
+ ctor public AutoBrightness();
+ method public final java.math.BigInteger getBrighteningLightDebounceMillis();
+ method public final java.math.BigInteger getDarkeningLightDebounceMillis();
+ method public final void setBrighteningLightDebounceMillis(java.math.BigInteger);
+ method public final void setDarkeningLightDebounceMillis(java.math.BigInteger);
+ }
+
public class BrightnessThresholds {
ctor public BrightnessThresholds();
method @NonNull public final java.math.BigDecimal getMinimum();
@@ -40,6 +48,7 @@ package com.android.server.display.config {
method @NonNull public final com.android.server.display.config.Thresholds getAmbientBrightnessChangeThresholds();
method public final java.math.BigInteger getAmbientLightHorizonLong();
method public final java.math.BigInteger getAmbientLightHorizonShort();
+ method public com.android.server.display.config.AutoBrightness getAutoBrightness();
method @Nullable public final com.android.server.display.config.DensityMapping getDensityMapping();
method @NonNull public final com.android.server.display.config.Thresholds getDisplayBrightnessChangeThresholds();
method public com.android.server.display.config.HighBrightnessMode getHighBrightnessMode();
@@ -58,6 +67,7 @@ package com.android.server.display.config {
method public final void setAmbientBrightnessChangeThresholds(@NonNull com.android.server.display.config.Thresholds);
method public final void setAmbientLightHorizonLong(java.math.BigInteger);
method public final void setAmbientLightHorizonShort(java.math.BigInteger);
+ method public void setAutoBrightness(com.android.server.display.config.AutoBrightness);
method public final void setDensityMapping(@Nullable com.android.server.display.config.DensityMapping);
method public final void setDisplayBrightnessChangeThresholds(@NonNull com.android.server.display.config.Thresholds);
method public void setHighBrightnessMode(com.android.server.display.config.HighBrightnessMode);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index b4cade344c00..c1c7bceaebd9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3552,11 +3552,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
- if (mInjector.getPackageManagerInternal().filterAppAccess(packageName, caller.getUid(),
- userHandle)) {
- return false;
- }
-
synchronized (getLockObject()) {
DevicePolicyData policy = getUserData(userHandle);
final int N = policy.mAdminList.size();
diff --git a/services/proguard.flags b/services/proguard.flags
index c648f7d3ac45..c9303462fb6a 100644
--- a/services/proguard.flags
+++ b/services/proguard.flags
@@ -64,6 +64,12 @@
# Referenced via CarServiceHelperService in car-frameworks-service (avoid removing)
-keep public class com.android.server.utils.Slogf { *; }
+# Referenced in wear-service
+# HIDL interfaces
+-keep public class android.hidl.base.** { *; }
+-keep public class android.hidl.manager.** { *; }
+-keep public class com.android.server.wm.WindowManagerInternal { *; }
+
# Notification extractors
# TODO(b/210510433): Revisit and consider generating from frameworks/base/core/res/res/values/config.xml.
-keep,allowoptimization,allowaccessmodification public class com.android.server.notification.** implements com.android.server.notification.NotificationSignalExtractor
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt
index 45e0d09a1753..4ef6875dd310 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt
@@ -628,6 +628,7 @@ class SystemStubMultiUserDisableUninstallTest : BaseHostJUnit4Test() {
CodePath.SAME, CodePath.DIFFERENT ->
throw AssertionError("secondDataPath cannot be a data path")
CodePath.SYSTEM -> assertThat(codePaths[1]).isEqualTo(stubFile.parent.toString())
+ else -> {}
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
index 2baa1ec6cdc2..05cad160c7fa 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
@@ -17,12 +17,9 @@
package com.android.server.am;
import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
-
import static com.android.server.am.ActivityManagerService.Injector;
-import static com.android.server.am.CachedAppOptimizer.compactActionIntToString;
-
+import static com.android.server.am.CachedAppOptimizer.compactActionIntToAction;
import static com.google.common.truth.Truth.assertThat;
-
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
@@ -37,15 +34,18 @@ import android.os.Process;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
import android.text.TextUtils;
-
import androidx.test.platform.app.InstrumentationRegistry;
-
import com.android.modules.utils.testing.TestableDeviceConfig;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.appop.AppOpsService;
import com.android.server.wm.ActivityTaskManagerService;
-
+import java.io.File;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Assume;
import org.junit.Before;
@@ -55,13 +55,6 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
-import java.io.File;
-import java.io.IOException;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
/**
* Tests for {@link CachedAppOptimizer}.
*
@@ -147,36 +140,40 @@ public final class CachedAppOptimizerTest {
@Test
public void init_setsDefaults() {
mCachedAppOptimizerUnderTest.init();
- assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo(
- CachedAppOptimizer.DEFAULT_USE_COMPACTION);
- assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo(
- compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
- assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo(
- compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
- assertThat(mCachedAppOptimizerUnderTest.mCompactStatsdSampleRate).isEqualTo(
- CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
- assertThat(mCachedAppOptimizerUnderTest.mFreezerStatsdSampleRate).isEqualTo(
- CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6);
- assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
- assertThat(mCachedAppOptimizerUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMinOomAdj).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ);
- assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMaxOomAdj).isEqualTo(
- CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ);
+ synchronized (mCachedAppOptimizerUnderTest.mPhenotypeFlagLock) {
+ assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo(
+ CachedAppOptimizer.DEFAULT_USE_COMPACTION);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome)
+ .isEqualTo(
+ compactActionIntToAction(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
+ assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull)
+ .isEqualTo(
+ compactActionIntToAction(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_2);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullSome).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_3);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleFullFull).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactStatsdSampleRate).isEqualTo(
+ CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
+ assertThat(mCachedAppOptimizerUnderTest.mFreezerStatsdSampleRate).isEqualTo(
+ CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6);
+ assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
+ assertThat(mCachedAppOptimizerUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMinOomAdj).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ);
+ assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMaxOomAdj).isEqualTo(
+ CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ);
+ }
Set<Integer> expected = new HashSet<>();
@@ -192,6 +189,7 @@ public final class CachedAppOptimizerTest {
CachedAppOptimizer.DEFAULT_USE_FREEZER);
}
+ @SuppressWarnings("GuardedBy")
@Test
public void init_withDeviceConfigSetsParameters() {
// When the DeviceConfig already has a flag value stored (note this test will need to
@@ -256,11 +254,11 @@ public final class CachedAppOptimizerTest {
assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isTrue();
assertThat(mCachedAppOptimizerUnderTest.mCachedAppOptimizerThread.isAlive()).isTrue();
- assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo(
- compactActionIntToString(
+ assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome)
+ .isEqualTo(compactActionIntToAction(
(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1));
- assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo(
- compactActionIntToString(
+ assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull)
+ .isEqualTo(compactActionIntToAction(
(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1));
assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1 + 1);
@@ -412,10 +410,12 @@ public final class CachedAppOptimizerTest {
assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
// Then the updates are reflected in the flags.
- assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo(
- compactActionIntToString(expectedSome));
- assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo(
- compactActionIntToString(expectedFull));
+ synchronized (mCachedAppOptimizerUnderTest.mPhenotypeFlagLock) {
+ assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome)
+ .isEqualTo(compactActionIntToAction(expectedSome));
+ assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull)
+ .isEqualTo(compactActionIntToAction(expectedFull));
+ }
}
}
@@ -431,11 +431,15 @@ public final class CachedAppOptimizerTest {
CachedAppOptimizer.KEY_COMPACT_ACTION_2, "foo", false);
assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
- // Then the default values are reflected in the flag
- assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo(
- compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
- assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo(
- compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
+ synchronized (mCachedAppOptimizerUnderTest.mPhenotypeFlagLock) {
+ // Then the default values are reflected in the flag
+ assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome)
+ .isEqualTo(
+ compactActionIntToAction(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
+ assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull)
+ .isEqualTo(
+ compactActionIntToAction(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
+ }
mCountDown = new CountDownLatch(2);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -444,10 +448,14 @@ public final class CachedAppOptimizerTest {
CachedAppOptimizer.KEY_COMPACT_ACTION_2, "", false);
assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
- assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo(
- compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
- assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo(
- compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
+ synchronized (mCachedAppOptimizerUnderTest.mPhenotypeFlagLock) {
+ assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome)
+ .isEqualTo(
+ compactActionIntToAction(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
+ assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull)
+ .isEqualTo(
+ compactActionIntToAction(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
+ }
}
@Test
@@ -893,7 +901,9 @@ public final class CachedAppOptimizerTest {
mProcessDependencies.setRss(rssBefore1);
mProcessDependencies.setRssAfterCompaction(rssAfter1); //
// WHEN we try to run compaction
- mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
+ mCachedAppOptimizerUnderTest.compactApp(processRecord,
+ CachedAppOptimizer.CompactProfile.FULL, CachedAppOptimizer.CompactSource.APP,
+ false);
waitForHandler();
// THEN process IS compacted.
assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
@@ -908,7 +918,9 @@ public final class CachedAppOptimizerTest {
processRecord.mOptRecord.setLastCompactTime(
processRecord.mOptRecord.getLastCompactTime() - 10_000);
// WHEN we try to run compaction.
- mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
+ mCachedAppOptimizerUnderTest.compactApp(processRecord,
+ CachedAppOptimizer.CompactProfile.FULL, CachedAppOptimizer.CompactSource.APP,
+ false);
waitForHandler();
// THEN process IS NOT compacted - values after compaction for process 1 should remain the
// same as from the last compaction.
@@ -924,7 +936,9 @@ public final class CachedAppOptimizerTest {
processRecord.mOptRecord.setLastCompactTime(
processRecord.mOptRecord.getLastCompactTime() - 10_000);
// WHEN we try to run compaction
- mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
+ mCachedAppOptimizerUnderTest.compactApp(processRecord,
+ CachedAppOptimizer.CompactProfile.FULL, CachedAppOptimizer.CompactSource.APP,
+ false);
waitForHandler();
// THEN process IS compacted - values after compaction for process 1 should be updated.
assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
@@ -965,7 +979,9 @@ public final class CachedAppOptimizerTest {
mProcessDependencies.setRss(rssBelowThreshold);
mProcessDependencies.setRssAfterCompaction(rssBelowThresholdAfter);
// WHEN we try to run compaction
- mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
+ mCachedAppOptimizerUnderTest.compactApp(processRecord,
+ CachedAppOptimizer.CompactProfile.FULL, CachedAppOptimizer.CompactSource.APP,
+ false);
waitForHandler();
// THEN process IS NOT compacted.
assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNull();
@@ -974,7 +990,9 @@ public final class CachedAppOptimizerTest {
mProcessDependencies.setRss(rssAboveThreshold);
mProcessDependencies.setRssAfterCompaction(rssAboveThresholdAfter);
// WHEN we try to run compaction
- mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
+ mCachedAppOptimizerUnderTest.compactApp(processRecord,
+ CachedAppOptimizer.CompactProfile.FULL, CachedAppOptimizer.CompactSource.APP,
+ false);
waitForHandler();
// THEN process IS compacted.
assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
@@ -1008,22 +1026,16 @@ public final class CachedAppOptimizerTest {
mProcessDependencies.setRss(rssBefore);
mProcessDependencies.setRssAfterCompaction(rssAfter);
- // Compaction should occur if (setAdj < min for process || setAdj > max for process) &&
- // (MIN < curAdj < MAX)
- // GIVEN OomAdj score below threshold.
- processRecord.mState.setSetAdj(899);
- processRecord.mState.setCurAdj(970);
- // WHEN we try to run compaction
- mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
+ // When moving within cached state
+ mCachedAppOptimizerUnderTest.onOomAdjustChanged(
+ ProcessList.CACHED_APP_MIN_ADJ, ProcessList.CACHED_APP_MIN_ADJ + 1, processRecord);
waitForHandler();
// THEN process IS NOT compacted.
assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNull();
- // GIVEN (setAdj < MIN || setAdj > MAX) && (MIN < curAdj < MAX)
- processRecord.mState.setSetAdj(910);
- processRecord.mState.setCurAdj(930);
- // WHEN we try to run compaction
- mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
+ // When moving into cached state
+ mCachedAppOptimizerUnderTest.onOomAdjustChanged(ProcessList.CACHED_APP_MIN_ADJ - 1,
+ ProcessList.CACHED_APP_MIN_ADJ + 1, processRecord);
waitForHandler();
// THEN process IS compacted.
assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
@@ -1057,13 +1069,17 @@ public final class CachedAppOptimizerTest {
processRecord.mState.setCurAdj(100);
// Compact process full
- mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
+ mCachedAppOptimizerUnderTest.compactApp(processRecord,
+ CachedAppOptimizer.CompactProfile.FULL, CachedAppOptimizer.CompactSource.APP,
+ false);
waitForHandler();
// the process is not compacted
assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNull();
// Compact process some
- mCachedAppOptimizerUnderTest.compactAppSome(processRecord, false);
+ mCachedAppOptimizerUnderTest.compactApp(processRecord,
+ CachedAppOptimizer.CompactProfile.SOME, CachedAppOptimizer.CompactSource.APP,
+ false);
waitForHandler();
// the process is not compacted
assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNull();
@@ -1072,7 +1088,9 @@ public final class CachedAppOptimizerTest {
processRecord.mState.setCurAdj(100);
// We force a full compaction
- mCachedAppOptimizerUnderTest.compactAppFull(processRecord, true);
+ mCachedAppOptimizerUnderTest.compactApp(processRecord,
+ CachedAppOptimizer.CompactProfile.FULL, CachedAppOptimizer.CompactSource.APP,
+ true);
waitForHandler();
// then process is compacted.
assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
@@ -1080,13 +1098,13 @@ public final class CachedAppOptimizerTest {
mCachedAppOptimizerUnderTest.mLastCompactionStats.clear();
// We force a some compaction
- mCachedAppOptimizerUnderTest.compactAppSome(processRecord, true);
+ mCachedAppOptimizerUnderTest.compactApp(processRecord,
+ CachedAppOptimizer.CompactProfile.SOME, CachedAppOptimizer.CompactSource.APP, true);
waitForHandler();
// then process is compacted.
- String executedCompactAction =
- compactActionIntToString(processRecord.mOptRecord.getLastCompactAction());
- assertThat(executedCompactAction)
- .isEqualTo(mCachedAppOptimizerUnderTest.mCompactActionSome);
+ CachedAppOptimizer.CompactProfile executedCompactProfile =
+ processRecord.mOptRecord.getLastCompactProfile();
+ assertThat(executedCompactProfile).isEqualTo(CachedAppOptimizer.CompactProfile.SOME);
}
private void setFlag(String key, String value, boolean defaultValue) throws Exception {
@@ -1162,7 +1180,8 @@ public final class CachedAppOptimizerTest {
}
@Override
- public void performCompaction(String action, int pid) throws IOException {
+ public void performCompaction(CachedAppOptimizer.CompactAction action, int pid)
+ throws IOException {
mRss = mRssAfterCompaction;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 8d59dce29bd9..598c6b0d9ab5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -40,6 +40,7 @@ import static android.app.ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static com.android.server.am.OomAdjuster.OOM_ADJ_REASON_ACTIVITY;
import static com.android.server.am.ProcessList.BACKUP_APP_ADJ;
import static com.android.server.am.ProcessList.CACHED_APP_MAX_ADJ;
import static com.android.server.am.ProcessList.CACHED_APP_MIN_ADJ;
@@ -146,6 +147,7 @@ public class MockingOomAdjusterTests {
private static PackageManagerInternal sPackageManagerInternal;
private static ActivityManagerService sService;
+ @SuppressWarnings("GuardedBy")
@BeforeClass
public static void setUpOnce() {
sContext = getInstrumentation().getTargetContext();
@@ -196,7 +198,7 @@ public class MockingOomAdjusterTests {
new ActivityManagerService.Injector(sContext));
doReturn(mock(AppOpsManager.class)).when(sService).getAppOpsManager();
doCallRealMethod().when(sService).enqueueOomAdjTargetLocked(any(ProcessRecord.class));
- doCallRealMethod().when(sService).updateOomAdjPendingTargetsLocked(any(String.class));
+ doCallRealMethod().when(sService).updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_ACTIVITY);
setFieldValue(AppProfiler.class, profiler, "mProfilerLock", new Object());
doReturn(new ActivityManagerService.ProcessChangeItem()).when(pr)
.enqueueProcessChangeItemLocked(anyInt(), anyInt());
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
index d675b0aa4973..b53a2c69708d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -201,7 +201,6 @@ public class GameManagerServiceTests {
public void tearDown() throws Exception {
LocalServices.removeServiceForTest(PowerManagerInternal.class);
GameManagerService gameManagerService = new GameManagerService(mMockContext);
- gameManagerService.disableCompatScale(mPackageName);
if (mMockingSession != null) {
mMockingSession.finishMocking();
}
@@ -450,13 +449,13 @@ public class GameManagerServiceTests {
startUser(gameManagerService, USER_ID_1);
- gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+ gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
mockModifyGameModeGranted();
assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
gameManagerService.getGameMode(mPackageName, USER_ID_1));
// We need to make sure the mode is supported before setting it.
mockDeviceConfigAll();
- gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+ gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_STANDARD, USER_ID_1);
assertEquals(GameManager.GAME_MODE_STANDARD,
gameManagerService.getGameMode(mPackageName, USER_ID_1));
@@ -534,8 +533,8 @@ public class GameManagerServiceTests {
startUser(gameManagerService, USER_ID_1);
startUser(gameManagerService, USER_ID_2);
- gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
- gameManagerService.updateConfigsForUser(USER_ID_2, mPackageName);
+ gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
+ gameManagerService.updateConfigsForUser(USER_ID_2, true, mPackageName);
// Set User 1 to Standard
gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_STANDARD, USER_ID_1);
@@ -563,7 +562,7 @@ public class GameManagerServiceTests {
if (gameManagerService == null) {
gameManagerService = new GameManagerService(mMockContext, mTestLooper.getLooper());
startUser(gameManagerService, USER_ID_1);
- gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+ gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
}
ArraySet<Integer> reportedModes = new ArraySet<>();
int[] modes = gameManagerService.getAvailableGameModes(mPackageName);
@@ -578,20 +577,20 @@ public class GameManagerServiceTests {
}
private void checkDownscaling(GameManagerService gameManagerService,
- int gameMode, String scaling) {
+ int gameMode, float scaling) {
if (gameManagerService == null) {
gameManagerService = new GameManagerService(mMockContext, mTestLooper.getLooper());
startUser(gameManagerService, USER_ID_1);
- gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+ gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
}
GameManagerService.GamePackageConfiguration config =
gameManagerService.getConfig(mPackageName);
- assertEquals(config.getGameModeConfiguration(gameMode).getScaling(), scaling);
+ assertEquals(scaling, config.getGameModeConfiguration(gameMode).getScaling(), 0.01f);
}
private void checkAngleEnabled(GameManagerService gameManagerService, int gameMode,
boolean angleEnabled) {
- gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+ gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
// Validate GamePackageConfiguration returns the correct value.
GameManagerService.GamePackageConfiguration config =
@@ -604,7 +603,7 @@ public class GameManagerServiceTests {
private void checkLoadingBoost(GameManagerService gameManagerService, int gameMode,
int loadingBoost) {
- gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+ gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
// Validate GamePackageConfiguration returns the correct value.
GameManagerService.GamePackageConfiguration config =
@@ -621,7 +620,7 @@ public class GameManagerServiceTests {
if (gameManagerService == null) {
gameManagerService = new GameManagerService(mMockContext, mTestLooper.getLooper());
startUser(gameManagerService, USER_ID_1);
- gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+ gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
}
GameManagerService.GamePackageConfiguration config =
gameManagerService.getConfig(mPackageName);
@@ -715,7 +714,7 @@ public class GameManagerServiceTests {
checkReportedModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE,
GameManager.GAME_MODE_STANDARD);
- checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, "0.3");
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0.3f);
checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 120);
}
@@ -735,7 +734,7 @@ public class GameManagerServiceTests {
checkReportedModes(gameManagerService, GameManager.GAME_MODE_BATTERY,
GameManager.GAME_MODE_STANDARD);
- checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, "0.5");
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.5f);
checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 60);
}
@@ -757,9 +756,9 @@ public class GameManagerServiceTests {
checkReportedModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE,
GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD);
- checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, "0.3");
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0.3f);
checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 120);
- checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, "0.5");
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.5f);
checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 60);
}
@@ -782,7 +781,7 @@ public class GameManagerServiceTests {
checkReportedModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE,
GameManager.GAME_MODE_STANDARD);
- checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, "0.5");
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0.5f);
checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 90);
}
@@ -805,7 +804,7 @@ public class GameManagerServiceTests {
checkReportedModes(gameManagerService, GameManager.GAME_MODE_BATTERY,
GameManager.GAME_MODE_STANDARD);
- checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, "0.7");
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.7f);
checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 30);
}
@@ -829,9 +828,9 @@ public class GameManagerServiceTests {
checkReportedModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE,
GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD);
- checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, "0.5");
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0.5f);
checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 90);
- checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, "0.7");
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.7f);
checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 30);
}
@@ -858,9 +857,9 @@ public class GameManagerServiceTests {
checkReportedModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE,
GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD);
- checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, "0.3");
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 0.3f);
checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 120);
- checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, "0.7");
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, 0.7f);
checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 30);
}
@@ -933,7 +932,7 @@ public class GameManagerServiceTests {
public void testInterventionAllowScalingDefault() throws Exception {
mockDeviceConfigPerformance();
mockModifyGameModeGranted();
- checkDownscaling(null, GameManager.GAME_MODE_PERFORMANCE, "0.5");
+ checkDownscaling(null, GameManager.GAME_MODE_PERFORMANCE, 0.5f);
}
/**
@@ -944,7 +943,7 @@ public class GameManagerServiceTests {
mockDeviceConfigPerformance();
mockInterventionAllowDownscaleFalse();
mockModifyGameModeGranted();
- checkDownscaling(null, GameManager.GAME_MODE_PERFORMANCE, "1.0");
+ checkDownscaling(null, GameManager.GAME_MODE_PERFORMANCE, -1.0f);
}
/**
@@ -956,7 +955,7 @@ public class GameManagerServiceTests {
mockDeviceConfigPerformance();
mockInterventionAllowDownscaleTrue();
mockModifyGameModeGranted();
- checkDownscaling(null, GameManager.GAME_MODE_PERFORMANCE, "0.5");
+ checkDownscaling(null, GameManager.GAME_MODE_PERFORMANCE, 0.5f);
}
/**
@@ -1091,7 +1090,7 @@ public class GameManagerServiceTests {
GameManagerService gameManagerService =
new GameManagerService(mMockContext, mTestLooper.getLooper());
startUser(gameManagerService, USER_ID_1);
- gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+ gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
GameManagerService.GamePackageConfiguration config =
gameManagerService.getConfig(mPackageName);
assertNull(config.getGameModeConfiguration(GameManager.GAME_MODE_PERFORMANCE));
@@ -1109,7 +1108,7 @@ public class GameManagerServiceTests {
new GameManagerService(mMockContext, mTestLooper.getLooper());
startUser(gameManagerService, USER_ID_1);
gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1);
- gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+ gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
gameManagerService.getGameMode(mPackageName, USER_ID_1));
}
@@ -1126,7 +1125,7 @@ public class GameManagerServiceTests {
new GameManagerService(mMockContext, mTestLooper.getLooper());
startUser(gameManagerService, USER_ID_1);
gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_BATTERY, USER_ID_1);
- gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+ gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
assertEquals(GameManager.GAME_MODE_STANDARD,
gameManagerService.getGameMode(mPackageName, USER_ID_1));
}
@@ -1143,7 +1142,7 @@ public class GameManagerServiceTests {
new GameManagerService(mMockContext, mTestLooper.getLooper());
startUser(gameManagerService, USER_ID_1);
gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_UNSUPPORTED, USER_ID_1);
- gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+ gameManagerService.updateConfigsForUser(USER_ID_1, true, mPackageName);
assertEquals(GameManager.GAME_MODE_STANDARD,
gameManagerService.getGameMode(mPackageName, USER_ID_1));
}
@@ -1404,4 +1403,102 @@ public class GameManagerServiceTests {
assertEquals(splitLine[6], "angle=0,scaling=0.7,fps=30");
}
+
+ @Test
+ public void testUpdateResolutionScalingFactor() {
+ mockModifyGameModeGranted();
+ mockDeviceConfigBattery();
+ GameManagerService gameManagerService =
+ new GameManagerService(mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ float scalingFactor = 0.123f;
+ gameManagerService.updateResolutionScalingFactor(mPackageName,
+ GameManager.GAME_MODE_BATTERY, scalingFactor,
+ USER_ID_1);
+ assertEquals(scalingFactor, gameManagerService.getResolutionScalingFactor(mPackageName,
+ GameManager.GAME_MODE_BATTERY, USER_ID_1), 0.001f);
+ scalingFactor = 0.321f;
+ gameManagerService.updateResolutionScalingFactor(mPackageName,
+ GameManager.GAME_MODE_BATTERY, scalingFactor,
+ USER_ID_1);
+ assertEquals(scalingFactor, gameManagerService.getResolutionScalingFactor(mPackageName,
+ GameManager.GAME_MODE_BATTERY, USER_ID_1), 0.001f);
+ }
+
+ @Test
+ public void testUpdateResolutionScalingFactor_noDeviceConfig() {
+ mockModifyGameModeGranted();
+ GameManagerService gameManagerService =
+ new GameManagerService(mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ float scalingFactor = 0.123f;
+ gameManagerService.updateResolutionScalingFactor(mPackageName,
+ GameManager.GAME_MODE_BATTERY, scalingFactor,
+ USER_ID_1);
+ assertEquals(scalingFactor, gameManagerService.getResolutionScalingFactor(mPackageName,
+ GameManager.GAME_MODE_BATTERY, USER_ID_1), 0.001f);
+ scalingFactor = 0.321f;
+ gameManagerService.updateResolutionScalingFactor(mPackageName,
+ GameManager.GAME_MODE_BATTERY, scalingFactor,
+ USER_ID_1);
+ assertEquals(scalingFactor, gameManagerService.getResolutionScalingFactor(mPackageName,
+ GameManager.GAME_MODE_BATTERY,
+ USER_ID_1), 0.001f);
+ }
+
+ @Test
+ public void testUpdateResolutionScalingFactor_permissionDenied() {
+ mockModifyGameModeDenied();
+ mockDeviceConfigAll();
+ GameManagerService gameManagerService =
+ new GameManagerService(mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ float scalingFactor = 0.123f;
+ assertThrows(SecurityException.class, () -> {
+ gameManagerService.updateResolutionScalingFactor(mPackageName,
+ GameManager.GAME_MODE_BATTERY, scalingFactor,
+ USER_ID_1);
+ });
+ mockModifyGameModeGranted();
+ assertEquals(0.7f, gameManagerService.getResolutionScalingFactor(mPackageName,
+ GameManager.GAME_MODE_BATTERY, USER_ID_1), 0.001f);
+ }
+
+ @Test
+ public void testUpdateResolutionScalingFactor_noUserId() {
+ mockModifyGameModeGranted();
+ GameManagerService gameManagerService =
+ new GameManagerService(mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_2);
+ final float scalingFactor = 0.123f;
+ assertThrows(IllegalArgumentException.class, () -> {
+ gameManagerService.updateResolutionScalingFactor(mPackageName,
+ GameManager.GAME_MODE_BATTERY, scalingFactor,
+ USER_ID_1);
+ });
+ }
+
+ @Test
+ public void testGetResolutionScalingFactor_permissionDenied() {
+ mockModifyGameModeDenied();
+ mockDeviceConfigAll();
+ GameManagerService gameManagerService =
+ new GameManagerService(mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ assertThrows(SecurityException.class, () -> {
+ gameManagerService.getResolutionScalingFactor(mPackageName,
+ GameManager.GAME_MODE_BATTERY, USER_ID_1);
+ });
+ }
+
+ @Test
+ public void testGetResolutionScalingFactor_noUserId() {
+ mockModifyGameModeDenied();
+ mockDeviceConfigAll();
+ GameManagerService gameManagerService =
+ new GameManagerService(mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_2);
+ assertEquals(-1f, gameManagerService.getResolutionScalingFactor(mPackageName,
+ GameManager.GAME_MODE_BATTERY, USER_ID_1), 0.001f);
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
index a8b340c6a391..e4f9eaf091e9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
@@ -218,8 +218,8 @@ public final class GameServiceProviderInstanceImplTest {
}).when(mMockWindowManagerInternal).unregisterTaskSystemBarsListener(any());
mRunningTaskInfos = new ArrayList<>();
- when(mMockActivityTaskManager.getTasks(anyInt(), anyBoolean(), anyBoolean())).thenReturn(
- mRunningTaskInfos);
+ when(mMockActivityTaskManager.getTasks(anyInt(), anyBoolean(), anyBoolean(), anyInt()))
+ .thenReturn(mRunningTaskInfos);
final UserHandle userHandle = new UserHandle(USER_ID);
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
index 99693d28f378..7111047956bf 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
@@ -35,6 +35,7 @@ import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import android.util.TypedXmlPullParser;
import android.util.Xml;
@@ -97,9 +98,10 @@ public class AppOpsUpgradeTest {
final int defaultModeOp2 = AppOpsManager.opToDefaultMode(op2);
for(int i = 0; i < uidStates.size(); i++) {
final AppOpsService.UidState uidState = uidStates.valueAt(i);
- if (uidState.opModes != null) {
- final int uidMode1 = uidState.opModes.get(op1, defaultModeOp1);
- final int uidMode2 = uidState.opModes.get(op2, defaultModeOp2);
+ SparseIntArray opModes = uidState.getNonDefaultUidModes();
+ if (opModes != null) {
+ final int uidMode1 = opModes.get(op1, defaultModeOp1);
+ final int uidMode2 = opModes.get(op2, defaultModeOp2);
assertEquals(uidMode1, uidMode2);
if (uidMode1 != defaultModeOp1) {
numberOfNonDefaultOps++;
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
new file mode 100644
index 000000000000..6a18dc181393
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -0,0 +1,236 @@
+/*
+ * 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.display;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
+
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isA;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.res.Resources;
+import android.hardware.Sensor;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
+import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.os.test.TestLooper;
+import android.util.FloatProperty;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+import com.android.server.display.RampAnimator.DualRampAnimator;
+import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.testutils.OffsettableClock;
+
+import org.junit.After;
+import org.junit.Before;
+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 java.util.List;
+
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class DisplayPowerControllerTest {
+ private static final String UNIQUE_DISPLAY_ID = "unique_id_test123";
+ private static final int DISPLAY_ID = 42;
+
+ private OffsettableClock mClock;
+ private TestLooper mTestLooper;
+ private Handler mHandler;
+ private DisplayPowerController.Injector mInjector;
+ private Context mContextSpy;
+
+ @Mock
+ private DisplayPowerCallbacks mDisplayPowerCallbacksMock;
+ @Mock
+ private SensorManager mSensorManagerMock;
+ @Mock
+ private DisplayBlanker mDisplayBlankerMock;
+ @Mock
+ private LogicalDisplay mLogicalDisplayMock;
+ @Mock
+ private DisplayDevice mDisplayDeviceMock;
+ @Mock
+ private BrightnessTracker mBrightnessTrackerMock;
+ @Mock
+ private BrightnessSetting mBrightnessSettingMock;
+ @Mock
+ private WindowManagerPolicy mWindowManagerPolicyMock;
+ @Mock
+ private PowerManager mPowerManagerMock;
+ @Mock
+ private Resources mResourcesMock;
+ @Mock
+ private DisplayDeviceConfig mDisplayDeviceConfigMock;
+ @Mock
+ private DisplayPowerState mDisplayPowerStateMock;
+ @Mock
+ private DualRampAnimator<DisplayPowerState> mDualRampAnimatorMock;
+
+ @Captor
+ private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
+ mClock = new OffsettableClock.Stopped();
+ mTestLooper = new TestLooper(mClock::now);
+ mHandler = new Handler(mTestLooper.getLooper());
+ mInjector = new DisplayPowerController.Injector() {
+ @Override
+ DisplayPowerController.Clock getClock() {
+ return mClock::now;
+ }
+
+ @Override
+ DisplayPowerState getDisplayPowerState(DisplayBlanker blanker, ColorFade colorFade,
+ int displayId, int displayState) {
+ return mDisplayPowerStateMock;
+ }
+
+ @Override
+ DualRampAnimator<DisplayPowerState> getDualRampAnimator(DisplayPowerState dps,
+ FloatProperty<DisplayPowerState> firstProperty,
+ FloatProperty<DisplayPowerState> secondProperty) {
+ return mDualRampAnimatorMock;
+ }
+ };
+
+ addLocalServiceMock(WindowManagerPolicy.class, mWindowManagerPolicyMock);
+
+ when(mContextSpy.getSystemService(eq(PowerManager.class))).thenReturn(mPowerManagerMock);
+ when(mContextSpy.getResources()).thenReturn(mResourcesMock);
+ }
+
+ @After
+ public void tearDown() {
+ LocalServices.removeServiceForTest(WindowManagerPolicy.class);
+ }
+
+ @Test
+ public void testReleaseProxSuspendBlockersOnExit() throws Exception {
+ setUpDisplay(DISPLAY_ID, UNIQUE_DISPLAY_ID);
+
+ Sensor proxSensor = setUpProxSensor();
+
+ DisplayPowerController dpc = new DisplayPowerController(
+ mContextSpy, mInjector, mDisplayPowerCallbacksMock, mHandler,
+ mSensorManagerMock, mDisplayBlankerMock, mLogicalDisplayMock,
+ mBrightnessTrackerMock, mBrightnessSettingMock, () -> {
+ });
+
+ when(mDisplayPowerStateMock.getScreenState()).thenReturn(Display.STATE_ON);
+ // send a display power request
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
+ dpr.useProximitySensor = true;
+ dpc.requestPowerState(dpr, false /* waitForNegativeProximity */);
+
+ // Run updatePowerState to start listener for the prox sensor
+ advanceTime(1);
+
+ SensorEventListener listener = getSensorEventListener(proxSensor);
+ assertNotNull(listener);
+
+ listener.onSensorChanged(TestUtils.createSensorEvent(proxSensor, 5 /* lux */));
+ advanceTime(1);
+
+ // two times, one for unfinished business and one for proximity
+ verify(mDisplayPowerCallbacksMock).acquireSuspendBlocker(
+ dpc.getSuspendBlockerUnfinishedBusinessId(DISPLAY_ID));
+ verify(mDisplayPowerCallbacksMock).acquireSuspendBlocker(
+ dpc.getSuspendBlockerProxDebounceId(DISPLAY_ID));
+
+ dpc.stop();
+ advanceTime(1);
+
+ // two times, one for unfinished business and one for proximity
+ verify(mDisplayPowerCallbacksMock).releaseSuspendBlocker(
+ dpc.getSuspendBlockerUnfinishedBusinessId(DISPLAY_ID));
+ verify(mDisplayPowerCallbacksMock).releaseSuspendBlocker(
+ dpc.getSuspendBlockerProxDebounceId(DISPLAY_ID));
+ }
+
+ /**
+ * Creates a mock and registers it to {@link LocalServices}.
+ */
+ private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
+ LocalServices.removeServiceForTest(clazz);
+ LocalServices.addService(clazz, mock);
+ }
+
+ private void advanceTime(long timeMs) {
+ mClock.fastForward(timeMs);
+ mTestLooper.dispatchAll();
+ }
+
+ private Sensor setUpProxSensor() throws Exception {
+ Sensor proxSensor = TestUtils.createSensor(
+ Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_PROXIMITY);
+ when(mSensorManagerMock.getSensorList(eq(Sensor.TYPE_ALL)))
+ .thenReturn(List.of(proxSensor));
+ return proxSensor;
+ }
+
+ private SensorEventListener getSensorEventListener(Sensor sensor) {
+ verify(mSensorManagerMock).registerListener(mSensorEventListenerCaptor.capture(),
+ eq(sensor), eq(SensorManager.SENSOR_DELAY_NORMAL), isA(Handler.class));
+ return mSensorEventListenerCaptor.getValue();
+ }
+
+ private void setUpDisplay(int displayId, String uniqueId) {
+ DisplayInfo info = new DisplayInfo();
+ DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo();
+
+ when(mLogicalDisplayMock.getDisplayIdLocked()).thenReturn(displayId);
+ when(mLogicalDisplayMock.getPrimaryDisplayDeviceLocked()).thenReturn(mDisplayDeviceMock);
+ when(mLogicalDisplayMock.getDisplayInfoLocked()).thenReturn(info);
+ when(mLogicalDisplayMock.isEnabled()).thenReturn(true);
+ when(mLogicalDisplayMock.getPhase()).thenReturn(LogicalDisplay.DISPLAY_PHASE_ENABLED);
+ when(mDisplayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo);
+ when(mDisplayDeviceMock.getUniqueId()).thenReturn(uniqueId);
+ when(mDisplayDeviceMock.getDisplayDeviceConfig()).thenReturn(mDisplayDeviceConfigMock);
+ when(mDisplayDeviceConfigMock.getProximitySensor()).thenReturn(
+ new DisplayDeviceConfig.SensorData() {
+ {
+ type = Sensor.STRING_TYPE_PROXIMITY;
+ name = null;
+ }
+ });
+ when(mDisplayDeviceConfigMock.getNits()).thenReturn(new float[]{2, 500});
+ }
+}
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 5f9f1b226958..bcdfc3500e94 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
@@ -24,6 +24,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import static com.android.server.job.JobSchedulerService.sSystemClock;
@@ -185,13 +186,17 @@ public class PrefetchControllerTest {
JobStatus js = JobStatus.createFromJobInfo(
jobInfo, callingUid, packageName, SOURCE_USER_ID, testTag);
js.serviceInfo = mock(ServiceInfo.class);
+ js.setStandbyBucket(FREQUENT_INDEX);
// Make sure Doze and background-not-restricted don't affect tests.
js.setDeviceNotDozingConstraintSatisfied(/* nowElapsed */ sElapsedRealtimeClock.millis(),
/* state */ true, /* allowlisted */false);
js.setBackgroundNotRestrictedConstraintSatisfied(
sElapsedRealtimeClock.millis(), true, false);
+ js.setQuotaConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
js.setTareWealthConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
+ js.setExpeditedJobQuotaApproved(sElapsedRealtimeClock.millis(), true);
js.setExpeditedJobTareApproved(sElapsedRealtimeClock.millis(), true);
+ js.setFlexibilityConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
return js;
}
@@ -294,6 +299,7 @@ public class PrefetchControllerTest {
verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS))
.getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID);
assertFalse(job.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ assertFalse(job.isReady());
}
@Test
@@ -309,6 +315,7 @@ public class PrefetchControllerTest {
.getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID);
verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS)).onControllerStateChanged(any());
assertTrue(job.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ assertTrue(job.isReady());
}
@Test
@@ -331,19 +338,25 @@ public class PrefetchControllerTest {
inOrder.verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS))
.onControllerStateChanged(any());
assertTrue(jobPending.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ assertTrue(jobPending.isReady());
assertTrue(jobRunning.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ assertTrue(jobRunning.isReady());
setUidBias(uid, JobInfo.BIAS_TOP_APP);
// Processing happens on the handler, so wait until we're sure the change has been processed
inOrder.verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS))
.onControllerStateChanged(any());
// Already running job should continue but pending job must wait.
assertFalse(jobPending.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ assertFalse(jobPending.isReady());
assertTrue(jobRunning.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ assertTrue(jobRunning.isReady());
setUidBias(uid, JobInfo.BIAS_DEFAULT);
inOrder.verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS))
.onControllerStateChanged(any());
assertTrue(jobPending.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ assertTrue(jobPending.isReady());
assertTrue(jobRunning.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ assertTrue(jobRunning.isReady());
}
@Test
@@ -367,11 +380,13 @@ public class PrefetchControllerTest {
verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS))
.getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID);
assertFalse(jobNonWidget.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ assertFalse(jobNonWidget.isReady());
when(appWidgetManager.isBoundWidgetPackage(SOURCE_PACKAGE, SOURCE_USER_ID))
.thenReturn(true);
trackJobs(jobWidget);
assertTrue(jobWidget.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ assertTrue(jobWidget.isReady());
}
@Test
@@ -390,6 +405,7 @@ public class PrefetchControllerTest {
.getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID);
verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS)).onControllerStateChanged(any());
assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ assertTrue(jobStatus.isReady());
mEstimatedLaunchTimeChangedListener.onEstimatedLaunchTimeChanged(SOURCE_USER_ID,
SOURCE_PACKAGE, sSystemClock.millis() + 10 * HOUR_IN_MILLIS);
@@ -401,6 +417,7 @@ public class PrefetchControllerTest {
anyInt(), eq(sElapsedRealtimeClock.millis() + 3 * HOUR_IN_MILLIS),
anyLong(), eq(TAG_PREFETCH), any(), any());
assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ assertFalse(jobStatus.isReady());
}
@Test
@@ -418,6 +435,7 @@ public class PrefetchControllerTest {
inOrder.verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS))
.getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID);
assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ assertFalse(jobStatus.isReady());
mEstimatedLaunchTimeChangedListener.onEstimatedLaunchTimeChanged(SOURCE_USER_ID,
SOURCE_PACKAGE, sSystemClock.millis() + MINUTE_IN_MILLIS);
@@ -426,6 +444,7 @@ public class PrefetchControllerTest {
.getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID);
verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS)).onControllerStateChanged(any());
assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ assertTrue(jobStatus.isReady());
}
@Test
@@ -448,6 +467,7 @@ public class PrefetchControllerTest {
anyInt(), eq(sElapsedRealtimeClock.millis() + 3 * HOUR_IN_MILLIS),
anyLong(), eq(TAG_PREFETCH), any(), any());
assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ assertFalse(jobStatus.isReady());
mEstimatedLaunchTimeChangedListener.onEstimatedLaunchTimeChanged(SOURCE_USER_ID,
SOURCE_PACKAGE, sSystemClock.millis() + HOUR_IN_MILLIS);
@@ -456,6 +476,7 @@ public class PrefetchControllerTest {
.getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID);
verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS)).onControllerStateChanged(any());
assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ assertTrue(jobStatus.isReady());
sSystemClock = getShiftedClock(sSystemClock, HOUR_IN_MILLIS + MINUTE_IN_MILLIS);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/InitAppsHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/InitAppsHelperTest.kt
new file mode 100644
index 000000000000..2165301f56c2
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/InitAppsHelperTest.kt
@@ -0,0 +1,135 @@
+/*
+ * 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.pm
+
+import android.os.Build
+import android.os.SystemProperties
+import com.android.server.extendedtestutils.wheneverStatic
+import com.android.server.testutils.spy
+import com.android.server.testutils.whenever
+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.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class InitAppsHelperTest {
+ @Rule
+ @JvmField
+ val rule = MockSystemRule()
+
+ @Before
+ fun setUp() {
+ rule.system().stageNominalSystemState()
+ }
+
+ private fun mockNoFirstBoot() {
+ // To mock that this is not the first boot
+ rule.system().stageScanExistingPackage("a.data.package", 1L,
+ rule.system().dataAppDirectory)
+ }
+
+ private fun mockFingerprintChanged() {
+ val versionInfo = Settings.VersionInfo()
+ versionInfo.fingerprint = "mockFingerprintForTesting"
+ whenever(rule.mocks().settings.internalVersion) {
+ versionInfo
+ }
+ }
+
+ private fun mockFingerprintUnchanged() {
+ // To mock that this is not the first boot
+ val versionInfo = Settings.VersionInfo()
+ versionInfo.fingerprint = MockSystem.DEFAULT_VERSION_INFO.fingerprint
+ whenever(rule.mocks().settings.internalVersion) {
+ versionInfo
+ }
+ }
+
+ private fun mockOta() {
+ wheneverStatic {
+ SystemProperties.getBoolean("persist.pm.mock-upgrade", false)
+ }.thenReturn(true)
+ }
+
+ private fun createPackageManagerService(): PackageManagerService {
+ return spy(PackageManagerService(rule.mocks().injector,
+ false /*factoryTest*/,
+ MockSystem.DEFAULT_VERSION_INFO.fingerprint,
+ false /*isEngBuild*/,
+ false /*isUserDebugBuild*/,
+ Build.VERSION_CODES.CUR_DEVELOPMENT,
+ Build.VERSION.INCREMENTAL))
+ }
+
+ @Test
+ fun testSystemScanFlagOnFirstBoot() {
+ val pms = createPackageManagerService()
+ assertThat(pms.isFirstBoot).isEqualTo(true)
+ assertThat(pms.isDeviceUpgrading).isEqualTo(false)
+ val initAppsHelper = InitAppsHelper(pms, rule.mocks().apexManager, null, null,
+ listOf<ScanPartition>())
+ assertThat(
+ initAppsHelper.systemScanFlags and PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE)
+ .isEqualTo(PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE)
+ }
+
+ @Test
+ fun testSystemScanFlagWithMockOTA() {
+ mockNoFirstBoot()
+ mockFingerprintUnchanged()
+ mockOta()
+ val pms = createPackageManagerService()
+ assertThat(pms.isFirstBoot).isEqualTo(false)
+ assertThat(pms.isDeviceUpgrading).isEqualTo(true)
+ val initAppsHelper = InitAppsHelper(pms, rule.mocks().apexManager, null, null,
+ listOf<ScanPartition>())
+ assertThat(
+ initAppsHelper.systemScanFlags and PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE)
+ .isEqualTo(PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE)
+ }
+
+ @Test
+ fun testSystemScanFlagNoOTA() {
+ mockNoFirstBoot()
+ mockFingerprintUnchanged()
+ val pms = createPackageManagerService()
+ assertThat(pms.isFirstBoot).isEqualTo(false)
+ assertThat(pms.isDeviceUpgrading).isEqualTo(false)
+ val initAppsHelper = InitAppsHelper(pms, rule.mocks().apexManager, null, null,
+ listOf<ScanPartition>())
+ assertThat(
+ initAppsHelper.systemScanFlags and PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE)
+ .isEqualTo(0)
+ }
+
+ @Test
+ fun testSystemScanFlagWithFingerprintChanged() {
+ mockNoFirstBoot()
+ mockFingerprintChanged()
+ val pms = createPackageManagerService()
+ assertThat(pms.isFirstBoot).isEqualTo(false)
+ assertThat(pms.isDeviceUpgrading).isEqualTo(true)
+ val initAppsHelper = InitAppsHelper(pms, rule.mocks().apexManager, null, null,
+ listOf<ScanPartition>())
+ assertThat(
+ initAppsHelper.systemScanFlags and PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE)
+ .isEqualTo(PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE)
+ }
+}
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index a9ee84f1feed..1921ce70d6cd 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -135,8 +135,9 @@ android_test {
java_library {
name: "servicestests-core-utils",
srcs: [
- "src/com/android/server/pm/PackageSettingBuilder.java",
"src/com/android/server/am/DeviceConfigSession.java",
+ "src/com/android/server/display/TestUtils.java",
+ "src/com/android/server/pm/PackageSettingBuilder.java",
],
static_libs: [
"services.core",
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index c80547ce61c0..966df4f92726 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -124,6 +124,8 @@ public class VirtualDeviceManagerServiceTest {
private VirtualDeviceImpl mDeviceImpl;
private InputController mInputController;
private AssociationInfo mAssociationInfo;
+ private VirtualDeviceManagerService mVdms;
+ private VirtualDeviceManagerInternal mLocalService;
@Mock
private InputController.NativeWrapper mNativeWrapperMock;
@Mock
@@ -139,6 +141,8 @@ public class VirtualDeviceManagerServiceTest {
@Mock
private Consumer<ArraySet<Integer>> mRunningAppsChangedCallback;
@Mock
+ private VirtualDeviceManagerInternal.VirtualDisplayListener mDisplayListener;
+ @Mock
IPowerManager mIPowerManagerMock;
@Mock
IThermalService mIThermalServiceMock;
@@ -215,6 +219,9 @@ public class VirtualDeviceManagerServiceTest {
mAssociationInfo = new AssociationInfo(1, 0, null,
MacAddress.BROADCAST_ADDRESS, "", null, true, false, false, 0, 0);
+ mVdms = new VirtualDeviceManagerService(mContext);
+ mLocalService = mVdms.getLocalServiceInstance();
+
VirtualDeviceParams params = new VirtualDeviceParams
.Builder()
.setBlockedActivities(getBlockedActivities())
@@ -235,6 +242,28 @@ public class VirtualDeviceManagerServiceTest {
}
@Test
+ public void onVirtualDisplayCreatedLocked_listenersNotified() throws RemoteException {
+ mLocalService.registerVirtualDisplayListener(mDisplayListener);
+
+ mLocalService.onVirtualDisplayCreated(DISPLAY_ID);
+ TestableLooper.get(this).processAllMessages();
+
+ verify(mDisplayListener).onVirtualDisplayCreated(DISPLAY_ID);
+ }
+
+ @Test
+ public void onVirtualDisplayRemovedLocked_listenersNotified() throws RemoteException {
+ mLocalService.registerVirtualDisplayListener(mDisplayListener);
+ mDeviceImpl.onVirtualDisplayCreatedLocked(
+ mDeviceImpl.createWindowPolicyController(), DISPLAY_ID);
+
+ mLocalService.onVirtualDisplayRemoved(mDeviceImpl, DISPLAY_ID);
+ TestableLooper.get(this).processAllMessages();
+
+ verify(mDisplayListener).onVirtualDisplayRemoved(DISPLAY_ID);
+ }
+
+ @Test
public void onVirtualDisplayCreatedLocked_wakeLockIsAcquired() throws RemoteException {
verify(mIPowerManagerMock, never()).acquireWakeLock(any(Binder.class), anyInt(),
nullable(String.class), nullable(String.class), nullable(WorkSource.class),
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
index 793930395daa..03ea6137074d 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -86,6 +86,8 @@ public final class DisplayDeviceConfigTest {
0.0f);
assertEquals(mDisplayDeviceConfig.getScreenBrighteningMinThreshold(), 0.001, 0.000001f);
assertEquals(mDisplayDeviceConfig.getScreenDarkeningMinThreshold(), 0.002, 0.000001f);
+ assertEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLightDebounce(), 2000);
+ assertEquals(mDisplayDeviceConfig.getAutoBrightnessDarkeningLightDebounce(), 1000);
// Todo(brup): Add asserts for BrightnessThrottlingData, DensityMapping,
// HighBrightnessModeData AmbientLightSensor, RefreshRateLimitations and ProximitySensor.
@@ -109,6 +111,10 @@ public final class DisplayDeviceConfigTest {
+ "<nits>800.0</nits>\n"
+ "</point>\n"
+ "</screenBrightnessMap>\n"
+ + "<autoBrightness>\n"
+ + "<brighteningLightDebounceMillis>2000</brighteningLightDebounceMillis>\n"
+ + "<darkeningLightDebounceMillis>1000</darkeningLightDebounceMillis>\n"
+ + "</autoBrightness>\n"
+ "<highBrightnessMode enabled=\"true\">\n"
+ "<transitionPoint>0.62</transitionPoint>\n"
+ "<minimumLux>10000</minimumLux>\n"
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/FakeGateKeeperService.java b/services/tests/servicestests/src/com/android/server/locksettings/FakeGateKeeperService.java
index 094b7af0fb8e..b044d7af9acd 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/FakeGateKeeperService.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/FakeGateKeeperService.java
@@ -42,7 +42,7 @@ public class FakeGateKeeperService implements IGateKeeperService {
ByteBuffer buffer = ByteBuffer.allocate(handle.length);
buffer.put(handle, 0, handle.length);
buffer.flip();
- int version = buffer.get();
+ buffer.get(); // version
sid = buffer.getLong();
password = new byte[buffer.remaining()];
buffer.get(password);
@@ -50,7 +50,7 @@ public class FakeGateKeeperService implements IGateKeeperService {
public byte[] toBytes() {
ByteBuffer buffer = ByteBuffer.allocate(1 + Long.BYTES + password.length);
- buffer.put((byte)0);
+ buffer.put((byte)0); // version
buffer.putLong(sid);
buffer.put(password);
return buffer.array();
@@ -70,14 +70,14 @@ public class FakeGateKeeperService implements IGateKeeperService {
ByteBuffer buffer = ByteBuffer.allocate(handle.length);
buffer.put(handle, 0, handle.length);
buffer.flip();
- int version = buffer.get();
+ buffer.get(); // version
challenge = buffer.getLong();
sid = buffer.getLong();
}
public byte[] toBytes() {
ByteBuffer buffer = ByteBuffer.allocate(1 + Long.BYTES + Long.BYTES);
- buffer.put((byte)0);
+ buffer.put((byte)0); // version
buffer.putLong(challenge);
buffer.putLong(sid);
return buffer.array();
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStrongAuthTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStrongAuthTest.java
index 6de7fddf6ccd..ec708adb993b 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStrongAuthTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStrongAuthTest.java
@@ -250,8 +250,8 @@ public class LockSettingsStrongAuthTest {
// schedule (a) an alarm for non-strong biometric fallback timeout and (b) an alarm for
// non-strong biometric idle timeout, so later we can verify that unlocking with
// strong biometric or primary auth will cancel those alarms
- mStrongAuth.reportSuccessfulBiometricUnlock(false /* isStrongBiometric */, PRIMARY_USER_ID);
- mStrongAuth.scheduleNonStrongBiometricIdleTimeout(PRIMARY_USER_ID);
+ mStrongAuth.reportSuccessfulBiometricUnlock(false /* isStrongBiometric */, userId);
+ mStrongAuth.scheduleNonStrongBiometricIdleTimeout(userId);
}
private void verifyAlarmsCancelledAndNonStrongBiometricAllowed(int userId) {
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java b/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java
index 186a04f9f546..8cb18a88c840 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java
@@ -35,7 +35,6 @@ public class MockSyntheticPasswordManager extends SyntheticPasswordManager {
private FakeGateKeeperService mGateKeeper;
private IWeaver mWeaverService;
- private PasswordSlotManagerTestable mPasswordSlotManager;
public MockSyntheticPasswordManager(Context context, LockSettingsStorage storage,
FakeGateKeeperService gatekeeper, UserManager userManager,
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/PasswordSlotManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/PasswordSlotManagerTests.java
index 0f24fb2aac5f..2faf6a2b29d1 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/PasswordSlotManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/PasswordSlotManagerTests.java
@@ -16,8 +16,9 @@
package com.android.server.locksettings;
+import static org.junit.Assert.assertEquals;
+
import android.platform.test.annotations.Presubmit;
-import android.test.AndroidTestCase;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -37,7 +38,7 @@ import java.util.Set;
@SmallTest
@Presubmit
@RunWith(AndroidJUnit4.class)
-public class PasswordSlotManagerTests extends AndroidTestCase {
+public class PasswordSlotManagerTests {
PasswordSlotManagerTestable mManager;
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
index b01c1c8ead28..858f658b52f0 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -125,7 +125,6 @@ public class RebootEscrowManagerTests {
static class MockInjector extends RebootEscrowManager.Injector {
private final IRebootEscrow mRebootEscrow;
- private final ResumeOnRebootServiceConnection mServiceConnection;
private final RebootEscrowProviderInterface mDefaultRebootEscrowProvider;
private final UserManager mUserManager;
private final MockableRebootEscrowInjected mInjected;
@@ -140,7 +139,6 @@ public class RebootEscrowManagerTests {
MockableRebootEscrowInjected injected) {
super(context, storage);
mRebootEscrow = rebootEscrow;
- mServiceConnection = null;
mServerBased = false;
RebootEscrowProviderHalImpl.Injector halInjector =
new RebootEscrowProviderHalImpl.Injector() {
@@ -161,7 +159,6 @@ public class RebootEscrowManagerTests {
LockSettingsStorageTestable storage,
MockableRebootEscrowInjected injected) {
super(context, storage);
- mServiceConnection = serviceConnection;
mRebootEscrow = null;
mServerBased = true;
RebootEscrowProviderServerBasedImpl.Injector injector =
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index c2e83f23ae86..ea5caa865666 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -678,8 +678,7 @@ public class KeySyncTaskTest {
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
- SecretKey applicationKey =
- addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
+ addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
mKeySyncTask.run();
@@ -710,8 +709,7 @@ public class KeySyncTaskTest {
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
- SecretKey applicationKey =
- addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
+ addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
mKeySyncTask.run();
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
index 3fd2c97075ac..c546a741e76a 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
@@ -587,7 +587,7 @@ public class PlatformKeyManagerTest {
return keyGenerator.generateKey();
}
- class PlatformKeyManagerTestable extends PlatformKeyManager {
+ static class PlatformKeyManagerTestable extends PlatformKeyManager {
private IGateKeeperService mGateKeeperService;
PlatformKeyManagerTestable(
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index aceae61b8b9d..281195de4b35 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -134,7 +134,6 @@ public class RecoverableKeyStoreManagerTest {
"V1 reencrypted_recovery_key".getBytes(StandardCharsets.UTF_8);
private static final String TEST_ALIAS = "nick";
private static final String TEST_ALIAS2 = "bob";
- private static final int RECOVERABLE_KEY_SIZE_BYTES = 32;
private static final int APPLICATION_KEY_SIZE_BYTES = 32;
private static final int GENERATION_ID = 1;
private static final byte[] NONCE = getUtf8Bytes("nonce");
@@ -503,8 +502,6 @@ public class RecoverableKeyStoreManagerTest {
@Test
public void initRecoveryService_throwsExceptionOnSmallerSerial() throws Exception {
- int uid = Binder.getCallingUid();
- int userId = UserHandle.getCallingUserId();
long certSerial = 1000L;
mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
@@ -636,7 +633,6 @@ public class RecoverableKeyStoreManagerTest {
throws Exception {
int uid = Binder.getCallingUid();
int userId = UserHandle.getCallingUserId();
- long certSerial = 1000L;
mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile(
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java
index 5d4be1bee105..4bf99e0951b6 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java
@@ -39,7 +39,6 @@ public final class TestData {
private static final String KEY_ALGORITHM = "AES";
private static final long DEFAULT_SERIAL = 10001;
- private static final String CERT_PATH_ENCODING = "PkiPath";
private static final String CERT_PATH_1_BASE64 = ""
+ "MIIIXTCCBRowggMCoAMCAQICEB35ZwzVpI9ssXg9SAehnU0wDQYJKoZIhvcNAQEL"
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/CleanupManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/CleanupManagerTest.java
index 0b15a126e98a..1c9c6dc8d0db 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/CleanupManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/CleanupManagerTest.java
@@ -21,11 +21,9 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.content.Context;
import android.os.UserHandle;
import android.os.UserManager;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -49,7 +47,6 @@ public class CleanupManagerTest {
private static final long USER_SERIAL_NUMBER = 101L;
private static final long USER_SERIAL_NUMBER_2 = 202L;
- private Context mContext;
private CleanupManager mManager;
@Mock private RecoverableKeyStoreDb mDatabase;
@@ -60,8 +57,7 @@ public class CleanupManagerTest {
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mContext = InstrumentationRegistry.getTargetContext();
- mManager = new CleanupManager(mContext, mRecoverySnapshotStorage, mDatabase, mUserManager,
+ mManager = new CleanupManager(mRecoverySnapshotStorage, mDatabase, mUserManager,
mApplicationKeyStorage);
}
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 51498a6ca719..a62569fc81c3 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageVerificationStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageVerificationStateTest.java
@@ -23,7 +23,9 @@ import android.test.AndroidTestCase;
@Presubmit
public class PackageVerificationStateTest extends AndroidTestCase {
- private static final int REQUIRED_UID = 1948;
+ private static final int REQUIRED_UID_1 = 1948;
+
+ private static final int REQUIRED_UID_2 = 1949;
private static final int SUFFICIENT_UID_1 = 1005;
@@ -31,12 +33,12 @@ public class PackageVerificationStateTest extends AndroidTestCase {
public void testPackageVerificationState_OnlyRequiredVerifier_AllowedInstall() {
PackageVerificationState state = new PackageVerificationState(null);
- state.setRequiredVerifierUid(REQUIRED_UID);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
assertFalse("Verification should not be marked as complete yet",
state.isVerificationComplete());
- state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_ALLOW);
+ state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_ALLOW);
assertTrue("Verification should be considered complete now",
state.isVerificationComplete());
@@ -47,23 +49,138 @@ public class PackageVerificationStateTest extends AndroidTestCase {
public void testPackageVerificationState_OnlyRequiredVerifier_DeniedInstall() {
PackageVerificationState state = new PackageVerificationState(null);
- state.setRequiredVerifierUid(REQUIRED_UID);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_REJECT);
+
+ assertTrue("Verification should be considered complete now",
+ state.isVerificationComplete());
+
+ assertFalse("Installation should be marked as denied",
+ state.isInstallAllowed());
+ }
+
+ public void testPackageVerificationState_TwoRequiredVerifiers_AllowedInstall() {
+ PackageVerificationState state = new PackageVerificationState(null);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
+ state.addRequiredVerifierUid(REQUIRED_UID_2);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_ALLOW);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(REQUIRED_UID_2, PackageManager.VERIFICATION_ALLOW);
+
+ assertTrue("Verification should be considered complete now",
+ state.isVerificationComplete());
+
+ assertTrue("Installation should be marked as allowed",
+ state.isInstallAllowed());
+ }
+
+ public void testPackageVerificationState_TwoRequiredVerifiers_DeniedInstall() {
+ PackageVerificationState state = new PackageVerificationState(null);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
+ state.addRequiredVerifierUid(REQUIRED_UID_2);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_REJECT);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(REQUIRED_UID_2, PackageManager.VERIFICATION_REJECT);
+
+ assertTrue("Verification should be considered complete now",
+ state.isVerificationComplete());
+
+ assertFalse("Installation should be marked as denied",
+ state.isInstallAllowed());
+ }
+
+ public void testPackageVerificationState_TwoRequiredVerifiers_FirstDeniedInstall() {
+ PackageVerificationState state = new PackageVerificationState(null);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
+ state.addRequiredVerifierUid(REQUIRED_UID_2);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_REJECT);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(REQUIRED_UID_2, PackageManager.VERIFICATION_ALLOW);
+
+ assertTrue("Verification should be considered complete now",
+ state.isVerificationComplete());
+
+ assertFalse("Installation should be marked as denied",
+ state.isInstallAllowed());
+ }
+
+ public void testPackageVerificationState_TwoRequiredVerifiers_SecondDeniedInstall() {
+ PackageVerificationState state = new PackageVerificationState(null);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
+ state.addRequiredVerifierUid(REQUIRED_UID_2);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_ALLOW);
assertFalse("Verification should not be marked as complete yet",
state.isVerificationComplete());
- state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_REJECT);
+ state.setVerifierResponse(REQUIRED_UID_2, PackageManager.VERIFICATION_REJECT);
assertTrue("Verification should be considered complete now",
state.isVerificationComplete());
- assertFalse("Installation should be marked as allowed",
+ assertFalse("Installation should be marked as denied",
state.isInstallAllowed());
}
public void testPackageVerificationState_RequiredAndOneSufficient_RequiredDeniedInstall() {
PackageVerificationState state = new PackageVerificationState(null);
- state.setRequiredVerifierUid(REQUIRED_UID);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.addSufficientVerifier(SUFFICIENT_UID_1);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(SUFFICIENT_UID_1, PackageManager.VERIFICATION_ALLOW);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_REJECT);
+
+ assertTrue("Verification should be considered complete now",
+ state.isVerificationComplete());
+
+ assertFalse("Installation should be marked as denied",
+ state.isInstallAllowed());
+ }
+
+ public void testPackageVerificationState_RequiredAndOneSufficient_OneRequiredDeniedInstall() {
+ PackageVerificationState state = new PackageVerificationState(null);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
+ state.addRequiredVerifierUid(REQUIRED_UID_2);
assertFalse("Verification should not be marked as complete yet",
state.isVerificationComplete());
@@ -78,18 +195,19 @@ public class PackageVerificationStateTest extends AndroidTestCase {
assertFalse("Verification should not be marked as complete yet",
state.isVerificationComplete());
- state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_REJECT);
+ state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_ALLOW);
+ state.setVerifierResponse(REQUIRED_UID_2, PackageManager.VERIFICATION_REJECT);
assertTrue("Verification should be considered complete now",
state.isVerificationComplete());
- assertFalse("Installation should be marked as allowed",
+ assertFalse("Installation should be marked as denied",
state.isInstallAllowed());
}
public void testPackageVerificationState_RequiredAndOneSufficient_SufficientDeniedInstall() {
PackageVerificationState state = new PackageVerificationState(null);
- state.setRequiredVerifierUid(REQUIRED_UID);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
assertFalse("Verification should not be marked as complete yet",
state.isVerificationComplete());
@@ -104,7 +222,7 @@ public class PackageVerificationStateTest extends AndroidTestCase {
assertFalse("Verification should not be marked as complete yet",
state.isVerificationComplete());
- state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_ALLOW);
+ state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_ALLOW);
assertTrue("Verification should be considered complete now",
state.isVerificationComplete());
@@ -115,7 +233,7 @@ public class PackageVerificationStateTest extends AndroidTestCase {
public void testPackageVerificationState_RequiredAndTwoSufficient_OneSufficientIsEnough() {
PackageVerificationState state = new PackageVerificationState(null);
- state.setRequiredVerifierUid(REQUIRED_UID);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
assertFalse("Verification should not be marked as complete yet",
state.isVerificationComplete());
@@ -131,7 +249,7 @@ public class PackageVerificationStateTest extends AndroidTestCase {
assertFalse("Verification should not be marked as complete yet",
state.isVerificationComplete());
- state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_ALLOW);
+ state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_ALLOW);
assertTrue("Verification should be considered complete now",
state.isVerificationComplete());
@@ -142,7 +260,7 @@ public class PackageVerificationStateTest extends AndroidTestCase {
public void testPackageVerificationState_RequiredAndTwoSufficient_SecondSufficientIsEnough() {
PackageVerificationState state = new PackageVerificationState(null);
- state.setRequiredVerifierUid(REQUIRED_UID);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
assertFalse("Verification should not be marked as complete yet",
state.isVerificationComplete());
@@ -153,7 +271,7 @@ public class PackageVerificationStateTest extends AndroidTestCase {
assertFalse("Verification should not be marked as complete yet",
state.isVerificationComplete());
- state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_ALLOW);
+ state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_ALLOW);
assertFalse("Verification should not be marked as complete yet",
state.isVerificationComplete());
@@ -174,7 +292,7 @@ public class PackageVerificationStateTest extends AndroidTestCase {
public void testPackageVerificationState_RequiredAndTwoSufficient_RequiredOverrides() {
PackageVerificationState state = new PackageVerificationState(null);
- state.setRequiredVerifierUid(REQUIRED_UID);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
assertFalse("Verification should not be marked as complete yet",
state.isVerificationComplete());
@@ -185,7 +303,7 @@ public class PackageVerificationStateTest extends AndroidTestCase {
assertFalse("Verification should not be marked as complete yet",
state.isVerificationComplete());
- state.setVerifierResponse(REQUIRED_UID,
+ state.setVerifierResponse(REQUIRED_UID_1,
PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT);
assertTrue("Verification should be marked as complete immediately",
@@ -213,17 +331,17 @@ public class PackageVerificationStateTest extends AndroidTestCase {
public void testAreAllVerificationsComplete_onlyVerificationPasses() {
PackageVerificationState state = new PackageVerificationState(null);
- state.setRequiredVerifierUid(REQUIRED_UID);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
assertFalse(state.areAllVerificationsComplete());
- state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_ALLOW);
+ state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_ALLOW);
assertFalse(state.areAllVerificationsComplete());
}
public void testAreAllVerificationsComplete_onlyIntegrityCheckPasses() {
PackageVerificationState state = new PackageVerificationState(null);
- state.setRequiredVerifierUid(REQUIRED_UID);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
assertFalse(state.areAllVerificationsComplete());
state.setIntegrityVerificationResult(PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
@@ -233,28 +351,28 @@ public class PackageVerificationStateTest extends AndroidTestCase {
public void testAreAllVerificationsComplete_bothPasses() {
PackageVerificationState state = new PackageVerificationState(null);
- state.setRequiredVerifierUid(REQUIRED_UID);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
assertFalse(state.areAllVerificationsComplete());
state.setIntegrityVerificationResult(PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
- state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_ALLOW);
+ state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_ALLOW);
assertTrue(state.areAllVerificationsComplete());
}
public void testAreAllVerificationsComplete_onlyVerificationFails() {
PackageVerificationState state = new PackageVerificationState(null);
- state.setRequiredVerifierUid(REQUIRED_UID);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
assertFalse(state.areAllVerificationsComplete());
- state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_REJECT);
+ state.setVerifierResponse(REQUIRED_UID_1, PackageManager.VERIFICATION_REJECT);
assertFalse(state.areAllVerificationsComplete());
}
public void testAreAllVerificationsComplete_onlyIntegrityCheckFails() {
PackageVerificationState state = new PackageVerificationState(null);
- state.setRequiredVerifierUid(REQUIRED_UID);
+ state.addRequiredVerifierUid(REQUIRED_UID_1);
assertFalse(state.areAllVerificationsComplete());
state.setIntegrityVerificationResult(PackageManagerInternal.INTEGRITY_VERIFICATION_REJECT);
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index e9171c0c3514..92c7871e611d 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -336,6 +336,59 @@ public class SystemConfigTest {
assertThat(mSysConfig.getAllowedVendorApexes()).isEmpty();
}
+ /**
+ * Tests that readPermissions works correctly for the tag: {@code install-constraints-allowed}.
+ */
+ @Test
+ public void readPermissions_installConstraints_successful() throws IOException {
+ final String contents =
+ "<config>\n"
+ + " <install-constraints-allowed package=\"com.android.apex1\" />\n"
+ + "</config>";
+ final File folder = createTempSubfolder("folder");
+ createTempFile(folder, "install-constraints-allowlist.xml", contents);
+
+ readPermissions(folder, /* Grant all permission flags */ ~0);
+
+ assertThat(mSysConfig.getInstallConstraintsAllowlist())
+ .containsExactly("com.android.apex1");
+ }
+
+ /**
+ * Tests that readPermissions works correctly for the tag: {@code install-constraints-allowed}.
+ */
+ @Test
+ public void readPermissions_installConstraints_noPackage() throws IOException {
+ final String contents =
+ "<config>\n"
+ + " <install-constraints-allowed/>\n"
+ + "</config>";
+ final File folder = createTempSubfolder("folder");
+ createTempFile(folder, "install-constraints-allowlist.xml", contents);
+
+ readPermissions(folder, /* Grant all permission flags */ ~0);
+
+ assertThat(mSysConfig.getInstallConstraintsAllowlist()).isEmpty();
+ }
+
+ /**
+ * Tests that readPermissions works correctly for the tag {@code install-constraints-allowed}
+ * without {@link SystemConfig#ALLOW_VENDOR_APEX}.
+ */
+ @Test
+ public void readPermissions_installConstraints_noAppConfigs() throws IOException {
+ final String contents =
+ "<config>\n"
+ + " <install-constraints-allowed package=\"com.android.apex1\" />\n"
+ + "</config>";
+ final File folder = createTempSubfolder("folder");
+ createTempFile(folder, "install-constraints-allowlist.xml", contents);
+
+ readPermissions(folder, /* Grant all but ALLOW_APP_CONFIGS flag */ ~0x08);
+
+ assertThat(mSysConfig.getInstallConstraintsAllowlist()).isEmpty();
+ }
+
@Test
public void readApexPrivAppPermissions_addAllPermissions()
throws Exception {
diff --git a/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
index 2e85897b6fee..06b4ad975a87 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
@@ -87,6 +87,7 @@ public class AssistDataRequesterTest {
private static final int TEST_UID = 0;
private static final String TEST_PACKAGE = "";
+ private static final String TEST_ATTRIBUTION_TAG = "";
private AssistDataRequester mDataRequester;
private Callbacks mCallbacks;
@@ -148,9 +149,9 @@ public class AssistDataRequesterTest {
boolean assistScreenshotAllowed) throws Exception {
doReturn(currentActivityAssistAllowed).when(mAtm).isAssistDataAllowedOnCurrentActivity();
doReturn(assistStructureAllowed ? MODE_ALLOWED : MODE_ERRORED).when(mAppOpsManager)
- .checkOpNoThrow(eq(OP_ASSIST_STRUCTURE), anyInt(), anyString());
+ .noteOpNoThrow(eq(OP_ASSIST_STRUCTURE), anyInt(), anyString(), any(), any());
doReturn(assistScreenshotAllowed ? MODE_ALLOWED : MODE_ERRORED).when(mAppOpsManager)
- .checkOpNoThrow(eq(OP_ASSIST_SCREENSHOT), anyInt(), anyString());
+ .noteOpNoThrow(eq(OP_ASSIST_SCREENSHOT), anyInt(), anyString(), any(), any());
}
@Test
@@ -160,7 +161,8 @@ public class AssistDataRequesterTest {
CALLER_ASSIST_SCREENSHOT_ALLOWED);
mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
- ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+ ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+ TEST_ATTRIBUTION_TAG);
assertReceivedDataCount(5, 5, 1, 1);
}
@@ -170,7 +172,8 @@ public class AssistDataRequesterTest {
CALLER_ASSIST_SCREENSHOT_ALLOWED);
mDataRequester.requestAssistData(createActivityList(0), FETCH_DATA, FETCH_SCREENSHOTS,
- ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+ ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+ TEST_ATTRIBUTION_TAG);
assertReceivedDataCount(0, 0, 0, 0);
}
@@ -180,7 +183,8 @@ public class AssistDataRequesterTest {
CALLER_ASSIST_SCREENSHOT_ALLOWED);
mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
- ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+ ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+ TEST_ATTRIBUTION_TAG);
assertReceivedDataCount(0, 1, 0, 1);
}
@@ -191,7 +195,8 @@ public class AssistDataRequesterTest {
mCallbacks.mCanHandleReceivedData = false;
mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
- ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+ ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+ TEST_ATTRIBUTION_TAG);
assertEquals(5, mDataRequester.getPendingDataCount());
assertEquals(1, mDataRequester.getPendingScreenshotCount());
mGate.countDown();
@@ -226,7 +231,8 @@ public class AssistDataRequesterTest {
CALLER_ASSIST_SCREENSHOT_ALLOWED);
mDataRequester.requestAssistData(createActivityList(5), !FETCH_DATA, FETCH_SCREENSHOTS,
- ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+ ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+ TEST_ATTRIBUTION_TAG);
assertReceivedDataCount(0, 0, 0, 1);
}
@@ -236,7 +242,8 @@ public class AssistDataRequesterTest {
CALLER_ASSIST_SCREENSHOT_ALLOWED);
mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
- ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+ ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+ TEST_ATTRIBUTION_TAG);
// Expect a single null data when the appops is denied
assertReceivedDataCount(0, 1, 0, 1);
}
@@ -249,7 +256,8 @@ public class AssistDataRequesterTest {
anyBoolean(), anyBoolean());
mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
- ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+ ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+ TEST_ATTRIBUTION_TAG);
// Expect a single null data when requestAssistContextExtras() fails
assertReceivedDataCount(0, 1, 0, 1);
}
@@ -261,7 +269,8 @@ public class AssistDataRequesterTest {
CALLER_ASSIST_SCREENSHOT_ALLOWED);
mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, !FETCH_SCREENSHOTS,
- ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+ ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+ TEST_ATTRIBUTION_TAG);
assertReceivedDataCount(5, 5, 0, 0);
}
@@ -272,7 +281,8 @@ public class AssistDataRequesterTest {
!CALLER_ASSIST_SCREENSHOT_ALLOWED);
mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
- ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+ ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+ TEST_ATTRIBUTION_TAG);
// Expect a single null screenshot when the appops is denied
assertReceivedDataCount(5, 5, 0, 1);
}
@@ -284,7 +294,8 @@ public class AssistDataRequesterTest {
mCallbacks.mCanHandleReceivedData = false;
mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
- ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+ ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+ TEST_ATTRIBUTION_TAG);
mGate.countDown();
waitForIdle(mHandler);
assertThat(mCallbacks.mReceivedData).isEmpty();
@@ -297,7 +308,8 @@ public class AssistDataRequesterTest {
CALLER_ASSIST_SCREENSHOT_ALLOWED);
mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
- !ALLOW_FETCH_DATA, !ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+ !ALLOW_FETCH_DATA, !ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+ TEST_ATTRIBUTION_TAG);
assertReceivedDataCount(0, 1, 0, 1);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/CompatModePackagesTests.java b/services/tests/wmtests/src/com/android/server/wm/CompatModePackagesTests.java
new file mode 100644
index 000000000000..1f7b65e8b701
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/CompatModePackagesTests.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+
+import android.app.GameManagerInternal;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.LocalServices;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for the {@link CompatModePackages} class.
+ *
+ * Build/Install/Run:
+ * atest WmTests:CompatModePackagesTests
+ */
+@SmallTest
+@Presubmit
+public class CompatModePackagesTests extends SystemServiceTestsBase {
+ ActivityTaskManagerService mAtm;
+ GameManagerInternal mGm;
+ static final String TEST_PACKAGE = "compat.mode.packages";
+ static final int TEST_USER_ID = 1;
+
+ @Before
+ public void setUp() {
+ mAtm = mSystemServicesTestRule.getActivityTaskManagerService();
+ mGm = mock(GameManagerInternal.class);
+ }
+
+ @After
+ public void tearDown() {
+ LocalServices.removeServiceForTest(GameManagerInternal.class);
+ }
+
+ @Test
+ public void testGetCompatScale_gameManagerReturnsPositive() {
+ LocalServices.addService(GameManagerInternal.class, mGm);
+ float scale = 0.25f;
+ doReturn(scale).when(mGm).getResolutionScalingFactor(anyString(), anyInt());
+ assertEquals(mAtm.mCompatModePackages.getCompatScale(TEST_PACKAGE, TEST_USER_ID), 1 / scale,
+ 0.01f);
+ }
+
+ @Test
+ public void testGetCompatScale_gameManagerReturnsZero() {
+ LocalServices.addService(GameManagerInternal.class, mGm);
+ float scale = 0f;
+ doReturn(scale).when(mGm).getResolutionScalingFactor(anyString(), anyInt());
+ assertEquals(mAtm.mCompatModePackages.getCompatScale(TEST_PACKAGE, TEST_USER_ID), 1f,
+ 0.01f);
+ }
+
+ @Test
+ public void testGetCompatScale_gameManagerReturnsNegative() {
+ LocalServices.addService(GameManagerInternal.class, mGm);
+ float scale = -1f;
+ doReturn(scale).when(mGm).getResolutionScalingFactor(anyString(), anyInt());
+ assertEquals(mAtm.mCompatModePackages.getCompatScale(TEST_PACKAGE, TEST_USER_ID), 1f,
+ 0.01f);
+ }
+
+ @Test
+ public void testGetCompatScale_noGameManager() {
+ assertEquals(mAtm.mCompatModePackages.getCompatScale(TEST_PACKAGE, TEST_USER_ID), 1f,
+ 0.01f);
+ }
+
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 83f375f85fa3..adf694c2a88d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -1531,10 +1531,10 @@ public class RecentTasksTest extends WindowTestsBase {
public boolean mLastAllowed;
@Override
- void getTasks(int maxNum, List<RunningTaskInfo> list, int flags,
- RootWindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
+ void getTasks(int maxNum, List<RunningTaskInfo> list, int flags, RecentTasks recentTasks,
+ WindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
mLastAllowed = (flags & FLAG_ALLOWED) == FLAG_ALLOWED;
- super.getTasks(maxNum, list, flags, root, callingUid, profileIds);
+ super.getTasks(maxNum, list, flags, recentTasks, root, callingUid, profileIds);
}
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 68079f4a9ac5..9e658e09b8b8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -72,6 +72,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.os.PowerManager;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.util.MergedConfiguration;
@@ -169,7 +170,8 @@ public class RootWindowContainerTests extends WindowTestsBase {
public void testTaskLayerRank() {
final Task rootTask = new TaskBuilder(mSupervisor).build();
final Task task1 = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask).build();
- new ActivityBuilder(mAtm).setTask(task1).build().mVisibleRequested = true;
+ final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task1).build();
+ activity1.mVisibleRequested = true;
mWm.mRoot.rankTaskLayers();
assertEquals(1, task1.mLayerRank);
@@ -177,7 +179,8 @@ public class RootWindowContainerTests extends WindowTestsBase {
assertEquals(Task.LAYER_RANK_INVISIBLE, rootTask.mLayerRank);
final Task task2 = new TaskBuilder(mSupervisor).build();
- new ActivityBuilder(mAtm).setTask(task2).build().mVisibleRequested = true;
+ final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task2).build();
+ activity2.mVisibleRequested = true;
mWm.mRoot.rankTaskLayers();
// Note that ensureActivitiesVisible is disabled in SystemServicesTestRule, so both the
@@ -192,6 +195,17 @@ public class RootWindowContainerTests extends WindowTestsBase {
assertEquals(1, task1.mLayerRank);
assertEquals(2, task2.mLayerRank);
+
+ // The rank should be updated to invisible when device went to sleep.
+ activity1.mVisibleRequested = false;
+ activity2.mVisibleRequested = false;
+ doReturn(true).when(mAtm).isSleepingOrShuttingDownLocked();
+ doReturn(true).when(mRootWindowContainer).putTasksToSleep(anyBoolean(), anyBoolean());
+ mSupervisor.mGoingToSleepWakeLock = mock(PowerManager.WakeLock.class);
+ doReturn(false).when(mSupervisor.mGoingToSleepWakeLock).isHeld();
+ mAtm.mTaskSupervisor.checkReadyForSleepLocked(false /* allowDelay */);
+ assertEquals(Task.LAYER_RANK_INVISIBLE, task1.mLayerRank);
+ assertEquals(Task.LAYER_RANK_INVISIBLE, task2.mLayerRank);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
index 33b236669ec7..b1acae20afb0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
@@ -35,6 +35,8 @@ import android.util.ArraySet;
import androidx.test.filters.MediumTest;
+import com.google.common.truth.Correspondence;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -52,6 +54,9 @@ import java.util.List;
public class RunningTasksTest extends WindowTestsBase {
private static final ArraySet<Integer> PROFILE_IDS = new ArraySet<>();
+ private static final Correspondence<RunningTaskInfo, Integer> TASKINFO_HAS_ID =
+ Correspondence.transforming((RunningTaskInfo t) -> t.taskId, "has id");
+
private RunningTasks mRunningTasks;
@@ -91,8 +96,8 @@ public class RunningTasksTest extends WindowTestsBase {
// collected from all tasks across all the stacks
final int numFetchTasks = 5;
ArrayList<RunningTaskInfo> tasks = new ArrayList<>();
- mRunningTasks.getTasks(5, tasks, FLAG_ALLOWED | FLAG_CROSS_USERS, mRootWindowContainer,
- -1 /* callingUid */, PROFILE_IDS);
+ mRunningTasks.getTasks(5, tasks, FLAG_ALLOWED | FLAG_CROSS_USERS,
+ mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
assertThat(tasks).hasSize(numFetchTasks);
for (int i = 0; i < numFetchTasks; i++) {
assertEquals(numTasks - i - 1, tasks.get(i).id);
@@ -102,7 +107,7 @@ public class RunningTasksTest extends WindowTestsBase {
// and does not crash
tasks.clear();
mRunningTasks.getTasks(100, tasks, FLAG_ALLOWED | FLAG_CROSS_USERS,
- mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
+ mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
assertThat(tasks).hasSize(numTasks);
for (int i = 0; i < numTasks; i++) {
assertEquals(numTasks - i - 1, tasks.get(i).id);
@@ -126,7 +131,7 @@ public class RunningTasksTest extends WindowTestsBase {
final int numFetchTasks = 5;
final ArrayList<RunningTaskInfo> tasks = new ArrayList<>();
mRunningTasks.getTasks(numFetchTasks, tasks, FLAG_ALLOWED | FLAG_CROSS_USERS,
- mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
+ mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
assertThat(tasks).hasSize(numFetchTasks);
for (int i = 0; i < tasks.size(); i++) {
final Bundle extras = tasks.get(i).baseIntent.getExtras();
@@ -151,8 +156,8 @@ public class RunningTasksTest extends WindowTestsBase {
final int numFetchTasks = 5;
final ArrayList<RunningTaskInfo> tasks = new ArrayList<>();
mRunningTasks.getTasks(numFetchTasks, tasks,
- FLAG_ALLOWED | FLAG_CROSS_USERS | FLAG_KEEP_INTENT_EXTRA, mRootWindowContainer,
- -1 /* callingUid */, PROFILE_IDS);
+ FLAG_ALLOWED | FLAG_CROSS_USERS | FLAG_KEEP_INTENT_EXTRA,
+ mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
assertThat(tasks).hasSize(numFetchTasks);
for (int i = 0; i < tasks.size(); i++) {
final Bundle extras = tasks.get(i).baseIntent.getExtras();
@@ -184,8 +189,8 @@ public class RunningTasksTest extends WindowTestsBase {
final int numFetchTasks = 5;
final ArrayList<RunningTaskInfo> fetchTasks = new ArrayList<>();
mRunningTasks.getTasks(numFetchTasks, fetchTasks,
- FLAG_ALLOWED | FLAG_CROSS_USERS | FLAG_KEEP_INTENT_EXTRA, mRootWindowContainer,
- -1 /* callingUid */, PROFILE_IDS);
+ FLAG_ALLOWED | FLAG_CROSS_USERS | FLAG_KEEP_INTENT_EXTRA,
+ mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
assertThat(fetchTasks).hasSize(numFetchTasks);
assertEquals(fetchTasks.get(0).id, focusedTask.mTaskId);
assertEquals(fetchTasks.get(1).id, visibleTask.mTaskId);
@@ -210,4 +215,46 @@ public class RunningTasksTest extends WindowTestsBase {
task.intent = activity.intent;
return task;
}
+
+ @Test
+ public void testMultipleDisplays() {
+ final DisplayContent display0 = new TestDisplayContent.Builder(mAtm, 1000, 2500).build();
+ final DisplayContent display1 = new TestDisplayContent.Builder(mAtm, 1000, 2500).build();
+ final int numTasks = 10;
+ final ArrayList<Task> tasks = new ArrayList<>();
+ for (int i = 0; i < numTasks; i++) {
+ final Task stack = new TaskBuilder(mSupervisor)
+ .setDisplay(i % 2 == 0 ? display0 : display1)
+ .setOnTop(true)
+ .build();
+ final Task task = createTask(stack, ".Task" + i, i, i, null);
+ tasks.add(task);
+ }
+
+ final int numFetchTasks = numTasks;
+ final ArrayList<RunningTaskInfo> fetchTasks = new ArrayList<>();
+
+ mRunningTasks.getTasks(numFetchTasks, fetchTasks,
+ FLAG_ALLOWED | FLAG_CROSS_USERS,
+ mAtm.getRecentTasks(), display0, -1 /* callingUid */, PROFILE_IDS);
+ assertThat(fetchTasks).hasSize(numTasks / 2);
+ assertThat(fetchTasks).comparingElementsUsing(TASKINFO_HAS_ID)
+ .containsExactly(0, 2, 4, 6, 8);
+
+ fetchTasks.clear();
+ mRunningTasks.getTasks(numFetchTasks, fetchTasks,
+ FLAG_ALLOWED | FLAG_CROSS_USERS,
+ mAtm.getRecentTasks(), display1, -1 /* callingUid */, PROFILE_IDS);
+ assertThat(fetchTasks).hasSize(numTasks / 2);
+ assertThat(fetchTasks).comparingElementsUsing(TASKINFO_HAS_ID)
+ .containsExactly(1, 3, 5, 7, 9);
+
+ fetchTasks.clear();
+ mRunningTasks.getTasks(numFetchTasks, fetchTasks,
+ FLAG_ALLOWED | FLAG_CROSS_USERS,
+ mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
+ assertThat(fetchTasks).hasSize(numTasks);
+ assertThat(fetchTasks).comparingElementsUsing(TASKINFO_HAS_ID)
+ .containsExactly(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
+ }
}
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 21839aac4ec5..fde6e3cc0b4f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -102,7 +102,6 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
-import org.mockito.Mockito;
/**
* Tests for Size Compatibility mode.
@@ -2165,6 +2164,40 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
+ public void testLetterboxDetailsForStatusBar_letterboxNotOverlappingStatusBar() {
+ final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2800)
+ .setNotch(100)
+ .build();
+ setUpApp(display);
+ TestWindowState statusBar = addStatusBar(mActivity.mDisplayContent);
+ spyOn(statusBar);
+ doReturn(new Rect(0, 0, statusBar.mRequestedWidth, statusBar.mRequestedHeight))
+ .when(statusBar).getFrame();
+ addWindowToActivity(mActivity); // Add a window to the activity so that we can get an
+ // appearance inside letterboxDetails
+ // Prepare unresizable activity with max aspect ratio
+ prepareUnresizable(mActivity, /* maxAspect */ 1.1f, SCREEN_ORIENTATION_UNSPECIFIED);
+ // Refresh the letterbox
+ mActivity.mRootWindowContainer.performSurfacePlacement();
+
+ Rect mBounds = new Rect(mActivity.getWindowConfiguration().getBounds());
+ assertEquals(mBounds, new Rect(0, 750, 1000, 1950));
+
+ DisplayPolicy displayPolicy = mActivity.getDisplayContent().getDisplayPolicy();
+ LetterboxDetails[] expectedLetterboxDetails = {new LetterboxDetails(
+ mBounds,
+ mActivity.getDisplayContent().getBounds(),
+ mActivity.findMainWindow().mAttrs.insetsFlags.appearance
+ )};
+
+ // Check that letterboxDetails actually gets passed to SysUI
+ StatusBarManagerInternal statusBarManager = displayPolicy.getStatusBarManagerInternal();
+ verify(statusBarManager).onSystemBarAttributesChanged(anyInt(), anyInt(),
+ any(), anyBoolean(), anyInt(),
+ any(InsetsVisibilities.class), isNull(), eq(expectedLetterboxDetails));
+ }
+
+ @Test
public void testSplitScreenLetterboxDetailsForStatusBar_twoLetterboxedApps() {
mAtm.mDevEnableNonResizableMultiWindow = true;
setUpDisplaySizeWithApp(2800, 1000);
@@ -2785,7 +2818,7 @@ public class SizeCompatTests extends WindowTestsBase {
return w;
}
- private static void addStatusBar(DisplayContent displayContent) {
+ private static TestWindowState addStatusBar(DisplayContent displayContent) {
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
doReturn(true).when(displayPolicy).hasStatusBar();
displayPolicy.onConfigurationChanged();
@@ -2806,6 +2839,7 @@ public class SizeCompatTests extends WindowTestsBase {
displayPolicy.addWindowLw(statusBar, attrs);
displayPolicy.layoutWindowLw(statusBar, null, displayContent.mDisplayFrames);
+ return statusBar;
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index eba27553937e..8b3cff8039d4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -75,6 +75,8 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
/**
* Build/Install/Run:
@@ -92,7 +94,6 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
private TaskFragmentOrganizerToken mOrganizerToken;
private ITaskFragmentOrganizer mIOrganizer;
private TaskFragment mTaskFragment;
- private TaskFragmentInfo mTaskFragmentInfo;
private IBinder mFragmentToken;
private WindowContainerTransaction mTransaction;
private WindowContainerToken mFragmentWindowToken;
@@ -100,14 +101,19 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
private IBinder mErrorToken;
private Rect mTaskFragBounds;
+ @Mock
+ private TaskFragmentInfo mTaskFragmentInfo;
+ @Mock
+ private Task mTask;
+
@Before
public void setup() {
+ MockitoAnnotations.initMocks(this);
mWindowOrganizerController = mAtm.mWindowOrganizerController;
mController = mWindowOrganizerController.mTaskFragmentOrganizerController;
mOrganizer = new TaskFragmentOrganizer(Runnable::run);
mOrganizerToken = mOrganizer.getOrganizerToken();
mIOrganizer = ITaskFragmentOrganizer.Stub.asInterface(mOrganizerToken.asBinder());
- mTaskFragmentInfo = mock(TaskFragmentInfo.class);
mFragmentToken = new Binder();
mTaskFragment =
new TaskFragment(mAtm, mFragmentToken, true /* createdByOrganizer */);
@@ -131,6 +137,8 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
@Test
public void testCallTaskFragmentCallbackWithoutRegister_throwsException() {
+ doReturn(mTask).when(mTaskFragment).getTask();
+
assertThrows(IllegalArgumentException.class, () -> mController
.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment));
@@ -140,16 +148,21 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
assertThrows(IllegalArgumentException.class, () -> mController
.onTaskFragmentVanished(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment));
-
- assertThrows(IllegalArgumentException.class, () -> mController
- .onTaskFragmentParentInfoChanged(mTaskFragment.getTaskFragmentOrganizer(),
- mTaskFragment));
}
@Test
public void testOnTaskFragmentAppeared() {
mController.registerOrganizer(mIOrganizer);
+ // No-op when the TaskFragment is not attached.
+ mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+ mController.dispatchPendingEvents();
+
+ verify(mOrganizer, never()).onTaskFragmentAppeared(any());
+
+ // Send callback when the TaskFragment is attached.
+ setupMockParent(mTaskFragment, mTask);
+
mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents();
@@ -159,9 +172,21 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
@Test
public void testOnTaskFragmentInfoChanged() {
mController.registerOrganizer(mIOrganizer);
+ setupMockParent(mTaskFragment, mTask);
+
+ // No-op if onTaskFragmentAppeared is not called yet.
+ mController.onTaskFragmentInfoChanged(mTaskFragment.getTaskFragmentOrganizer(),
+ mTaskFragment);
+ mController.dispatchPendingEvents();
+
+ verify(mOrganizer, never()).onTaskFragmentInfoChanged(any());
+
+ // Call onTaskFragmentAppeared first.
mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents();
+ verify(mOrganizer).onTaskFragmentAppeared(any());
+
// No callback if the info is not changed.
doReturn(true).when(mTaskFragmentInfo).equalsForTaskFragmentOrganizer(any());
doReturn(new Configuration()).when(mTaskFragmentInfo).getConfiguration();
@@ -194,47 +219,74 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
}
@Test
+ public void testOnTaskFragmentVanished_clearUpRemaining() {
+ mController.registerOrganizer(mIOrganizer);
+ setupMockParent(mTaskFragment, mTask);
+
+ // Not trigger onTaskFragmentAppeared.
+ mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+ mController.onTaskFragmentVanished(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+ mController.dispatchPendingEvents();
+
+ verify(mOrganizer, never()).onTaskFragmentAppeared(any());
+ verify(mOrganizer, never()).onTaskFragmentInfoChanged(any());
+ verify(mOrganizer, never()).onTaskFragmentParentInfoChanged(anyInt(), any());
+ verify(mOrganizer).onTaskFragmentVanished(mTaskFragmentInfo);
+
+ // Not trigger onTaskFragmentInfoChanged.
+ // Call onTaskFragmentAppeared before calling onTaskFragmentInfoChanged.
+ mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+ mController.dispatchPendingEvents();
+ clearInvocations(mOrganizer);
+ doReturn(true).when(mTaskFragmentInfo).equalsForTaskFragmentOrganizer(any());
+ mController.onTaskFragmentInfoChanged(mTaskFragment.getTaskFragmentOrganizer(),
+ mTaskFragment);
+ mController.onTaskFragmentVanished(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+ mController.dispatchPendingEvents();
+
+ verify(mOrganizer, never()).onTaskFragmentAppeared(any());
+ verify(mOrganizer, never()).onTaskFragmentInfoChanged(any());
+ verify(mOrganizer, never()).onTaskFragmentParentInfoChanged(anyInt(), any());
+ verify(mOrganizer).onTaskFragmentVanished(mTaskFragmentInfo);
+ }
+
+ @Test
public void testOnTaskFragmentParentInfoChanged() {
mController.registerOrganizer(mIOrganizer);
- final Task parent = mock(Task.class);
- final Configuration parentConfig = new Configuration();
- parentConfig.smallestScreenWidthDp = 10;
- doReturn(parent).when(mTaskFragment).getParent();
- doReturn(parentConfig).when(parent).getConfiguration();
- doReturn(parent).when(parent).asTask();
+ setupMockParent(mTaskFragment, mTask);
+ mTask.getConfiguration().smallestScreenWidthDp = 10;
- mTaskFragment.mTaskFragmentAppearedSent = true;
- mController.onTaskFragmentParentInfoChanged(
+ mController.onTaskFragmentAppeared(
mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents();
- verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mFragmentToken), any());
+ verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mTask.mTaskId), any());
// No extra callback if the info is not changed.
clearInvocations(mOrganizer);
- mController.onTaskFragmentParentInfoChanged(
+ mController.onTaskFragmentInfoChanged(
mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents();
- verify(mOrganizer, never()).onTaskFragmentParentInfoChanged(any(), any());
+ verify(mOrganizer, never()).onTaskFragmentParentInfoChanged(anyInt(), any());
// Trigger callback if the size is changed.
- parentConfig.smallestScreenWidthDp = 100;
- mController.onTaskFragmentParentInfoChanged(
+ mTask.getConfiguration().smallestScreenWidthDp = 100;
+ mController.onTaskFragmentInfoChanged(
mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents();
- verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mFragmentToken), any());
+ verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mTask.mTaskId), any());
// Trigger callback if the windowing mode is changed.
clearInvocations(mOrganizer);
- parentConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED);
- mController.onTaskFragmentParentInfoChanged(
+ mTask.getConfiguration().windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED);
+ mController.onTaskFragmentInfoChanged(
mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents();
- verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mFragmentToken), any());
+ verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mTask.mTaskId), any());
}
@Test
@@ -1089,4 +1141,15 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
.put(mFragmentToken, mTaskFragment);
mTaskFragment.getTask().setWindowingMode(WINDOWING_MODE_PINNED);
}
+
+ /** Setups the mock Task as the parent of the given TaskFragment. */
+ private static void setupMockParent(TaskFragment taskFragment, Task mockParent) {
+ doReturn(mockParent).when(taskFragment).getTask();
+ final Configuration taskConfig = new Configuration();
+ doReturn(taskConfig).when(mockParent).getConfiguration();
+
+ // Task needs to be visible
+ mockParent.lastActiveTime = 100;
+ doReturn(true).when(mockParent).shouldBeVisible(any());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 1096351524d7..88eadfceedb6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -32,6 +32,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_MIN_DIMENSION_VIOLATION;
import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_NEW_TASK_FRAGMENT;
import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_UNTRUSTED_HOST;
@@ -468,6 +469,10 @@ public class TaskFragmentTest extends WindowTestsBase {
newActivity.resultTo = activity;
assertEquals(EMBEDDING_DISALLOWED_NEW_TASK_FRAGMENT,
newTaskFragment.isAllowedToEmbedActivity(newActivity));
+
+ // Allow embedding if the resultTo activity is finishing.
+ activity.finishing = true;
+ assertEquals(EMBEDDING_ALLOWED, newTaskFragment.isAllowedToEmbedActivity(newActivity));
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index f4323db2ceec..6a7e388dc0b8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -846,6 +846,7 @@ public class TaskTests extends WindowTestsBase {
new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, targetClassName);
final Intent intent = new Intent();
+ intent.setPackage(DEFAULT_COMPONENT_PACKAGE_NAME);
intent.setComponent(aliasComponent);
final ActivityInfo info = new ActivityInfo();
info.applicationInfo = new ApplicationInfo();
diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java
index 41f9faef99df..6fc4b6707e9e 100644
--- a/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java
+++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java
@@ -31,15 +31,14 @@ public final class UsbPortHalInstance {
public static UsbPortHal getInstance(UsbPortManager portManager, IndentingPrintWriter pw) {
logAndPrint(Log.DEBUG, null, "Querying USB HAL version");
- if (UsbPortHidl.isServicePresent(null)) {
- logAndPrint(Log.INFO, null, "USB HAL HIDL present");
- return new UsbPortHidl(portManager, pw);
- }
if (UsbPortAidl.isServicePresent(null)) {
logAndPrint(Log.INFO, null, "USB HAL AIDL present");
return new UsbPortAidl(portManager, pw);
}
-
+ if (UsbPortHidl.isServicePresent(null)) {
+ logAndPrint(Log.INFO, null, "USB HAL HIDL present");
+ return new UsbPortHidl(portManager, pw);
+ }
return null;
}
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 7699262f6bce..4b9b4a9781de 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -102,6 +102,7 @@ import com.android.server.UiThread;
import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.permission.LegacyPermissionManagerInternal;
import com.android.server.soundtrigger.SoundTriggerInternal;
+import com.android.server.utils.Slogf;
import com.android.server.utils.TimingsTraceAndSlog;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -134,18 +135,21 @@ public class VoiceInteractionManagerService extends SystemService {
private final RemoteCallbackList<IVoiceInteractionSessionListener>
mVoiceInteractionSessionListeners = new RemoteCallbackList<>();
+ // TODO(b/226201975): remove once RoleService supports pre-created users
+ private final ArrayList<UserHandle> mIgnoredPreCreatedUsers = new ArrayList<>();
+
public VoiceInteractionManagerService(Context context) {
super(context);
mContext = context;
mResolver = context.getContentResolver();
+ mUserManagerInternal = Objects.requireNonNull(
+ LocalServices.getService(UserManagerInternal.class));
mDbHelper = new DatabaseHelper(context);
mServiceStub = new VoiceInteractionManagerServiceStub();
mAmInternal = Objects.requireNonNull(
LocalServices.getService(ActivityManagerInternal.class));
mAtmInternal = Objects.requireNonNull(
LocalServices.getService(ActivityTaskManagerInternal.class));
- mUserManagerInternal = Objects.requireNonNull(
- LocalServices.getService(UserManagerInternal.class));
LegacyPermissionManagerInternal permissionManagerInternal = LocalServices.getService(
LegacyPermissionManagerInternal.class);
@@ -223,12 +227,13 @@ public class VoiceInteractionManagerService extends SystemService {
class LocalService extends VoiceInteractionManagerInternal {
@Override
- public void startLocalVoiceInteraction(IBinder callingActivity, Bundle options) {
+ public void startLocalVoiceInteraction(@NonNull IBinder callingActivity,
+ @Nullable String attributionTag, @NonNull Bundle options) {
if (DEBUG) {
Slog.i(TAG, "startLocalVoiceInteraction " + callingActivity);
}
VoiceInteractionManagerService.this.mServiceStub.startLocalVoiceInteraction(
- callingActivity, options);
+ callingActivity, attributionTag, options);
}
@Override
@@ -300,6 +305,25 @@ public class VoiceInteractionManagerService extends SystemService {
}
return hotwordDetectionConnection.mIdentity;
}
+
+ @Override
+ public void onPreCreatedUserConversion(int userId) {
+ Slogf.d(TAG, "onPreCreatedUserConversion(%d)", userId);
+
+ for (int i = 0; i < mIgnoredPreCreatedUsers.size(); i++) {
+ UserHandle preCreatedUser = mIgnoredPreCreatedUsers.get(i);
+ if (preCreatedUser.getIdentifier() == userId) {
+ Slogf.d(TAG, "Updating role on pre-created user %d", userId);
+ mServiceStub.mRoleObserver.onRoleHoldersChanged(RoleManager.ROLE_ASSISTANT,
+ preCreatedUser);
+ mIgnoredPreCreatedUsers.remove(i);
+ return;
+ }
+ }
+ Slogf.w(TAG, "onPreCreatedUserConversion(%d): not available on "
+ + "mIgnoredPreCreatedUserIds (%s)", userId, mIgnoredPreCreatedUsers);
+ }
+
}
// implementation entry point and binder service
@@ -317,10 +341,12 @@ public class VoiceInteractionManagerService extends SystemService {
private boolean mTemporarilyDisabled;
private final boolean mEnableService;
+ // TODO(b/226201975): remove reference once RoleService supports pre-created users
+ private final RoleObserver mRoleObserver;
VoiceInteractionManagerServiceStub() {
mEnableService = shouldEnableService(mContext);
- new RoleObserver(mContext.getMainExecutor());
+ mRoleObserver = new RoleObserver(mContext.getMainExecutor());
}
void handleUserStop(String packageName, int userHandle) {
@@ -383,14 +409,15 @@ public class VoiceInteractionManagerService extends SystemService {
}
// TODO: VI Make sure the caller is the current user or profile
- void startLocalVoiceInteraction(final IBinder token, Bundle options) {
+ void startLocalVoiceInteraction(@NonNull final IBinder token,
+ @Nullable String attributionTag, @NonNull Bundle options) {
if (mImpl == null) return;
final int callingUid = Binder.getCallingUid();
final long caller = Binder.clearCallingIdentity();
try {
mImpl.showSessionLocked(options,
- VoiceInteractionSession.SHOW_SOURCE_ACTIVITY,
+ VoiceInteractionSession.SHOW_SOURCE_ACTIVITY, attributionTag,
new IVoiceInteractionSessionShowCallback.Stub() {
@Override
public void onFailed() {
@@ -898,13 +925,13 @@ public class VoiceInteractionManagerService extends SystemService {
}
@Override
- public void showSession(Bundle args, int flags) {
+ public void showSession(@NonNull Bundle args, int flags, @Nullable String attributionTag) {
synchronized (this) {
enforceIsCurrentVoiceInteractionService();
final long caller = Binder.clearCallingIdentity();
try {
- mImpl.showSessionLocked(args, flags, null, null);
+ mImpl.showSessionLocked(args, flags, attributionTag, null, null);
} finally {
Binder.restoreCallingIdentity(caller);
}
@@ -929,7 +956,8 @@ public class VoiceInteractionManagerService extends SystemService {
}
@Override
- public boolean showSessionFromSession(IBinder token, Bundle sessionArgs, int flags) {
+ public boolean showSessionFromSession(@NonNull IBinder token, @NonNull Bundle sessionArgs,
+ int flags, @Nullable String attributionTag) {
synchronized (this) {
if (mImpl == null) {
Slog.w(TAG, "showSessionFromSession without running voice interaction service");
@@ -937,7 +965,7 @@ public class VoiceInteractionManagerService extends SystemService {
}
final long caller = Binder.clearCallingIdentity();
try {
- return mImpl.showSessionLocked(sessionArgs, flags, null, null);
+ return mImpl.showSessionLocked(sessionArgs, flags, attributionTag, null, null);
} finally {
Binder.restoreCallingIdentity(caller);
}
@@ -961,8 +989,8 @@ public class VoiceInteractionManagerService extends SystemService {
}
@Override
- public int startVoiceActivity(IBinder token, Intent intent, String resolvedType,
- String callingFeatureId) {
+ public int startVoiceActivity(@NonNull IBinder token, @NonNull Intent intent,
+ @Nullable String resolvedType, @Nullable String attributionTag) {
synchronized (this) {
if (mImpl == null) {
Slog.w(TAG, "startVoiceActivity without running voice interaction service");
@@ -980,8 +1008,8 @@ public class VoiceInteractionManagerService extends SystemService {
} else {
Slog.w(TAG, "Cannot find ActivityInfo in startVoiceActivity.");
}
- return mImpl.startVoiceActivityLocked(
- callingFeatureId, callingPid, callingUid, token, intent, resolvedType);
+ return mImpl.startVoiceActivityLocked(attributionTag, callingPid, callingUid,
+ token, intent, resolvedType);
} finally {
Binder.restoreCallingIdentity(caller);
}
@@ -989,8 +1017,8 @@ public class VoiceInteractionManagerService extends SystemService {
}
@Override
- public int startAssistantActivity(IBinder token, Intent intent, String resolvedType,
- String callingFeatureId) {
+ public int startAssistantActivity(@NonNull IBinder token, @NonNull Intent intent,
+ @Nullable String resolvedType, @Nullable String attributionTag) {
synchronized (this) {
if (mImpl == null) {
Slog.w(TAG, "startAssistantActivity without running voice interaction service");
@@ -1000,7 +1028,7 @@ public class VoiceInteractionManagerService extends SystemService {
final int callingUid = Binder.getCallingUid();
final long caller = Binder.clearCallingIdentity();
try {
- return mImpl.startAssistantActivityLocked(callingFeatureId, callingPid,
+ return mImpl.startAssistantActivityLocked(attributionTag, callingPid,
callingUid, token, intent, resolvedType);
} finally {
Binder.restoreCallingIdentity(caller);
@@ -1669,8 +1697,10 @@ public class VoiceInteractionManagerService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE)
@Override
- public boolean showSessionForActiveService(Bundle args, int sourceFlags,
- IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken) {
+ public boolean showSessionForActiveService(@NonNull Bundle args, int sourceFlags,
+ @Nullable String attributionTag,
+ @Nullable IVoiceInteractionSessionShowCallback showCallback,
+ @Nullable IBinder activityToken) {
if (DEBUG_USER) Slog.d(TAG, "showSessionForActiveService()");
synchronized (this) {
@@ -1691,7 +1721,7 @@ public class VoiceInteractionManagerService extends SystemService {
sourceFlags
| VoiceInteractionSession.SHOW_WITH_ASSIST
| VoiceInteractionSession.SHOW_WITH_SCREENSHOT,
- showCallback, activityToken);
+ attributionTag, showCallback, activityToken);
} finally {
Binder.restoreCallingIdentity(caller);
}
@@ -1882,6 +1912,7 @@ public class VoiceInteractionManagerService extends SystemService {
pw.println(" mTemporarilyDisabled: " + mTemporarilyDisabled);
pw.println(" mCurUser: " + mCurUser);
pw.println(" mCurUserSupported: " + mCurUserSupported);
+ pw.println(" mIgnoredPreCreatedUsers: " + mIgnoredPreCreatedUsers);
dumpSupportedUsers(pw, " ");
mDbHelper.dump(pw);
if (mImpl == null) {
@@ -1995,6 +2026,23 @@ public class VoiceInteractionManagerService extends SystemService {
List<String> roleHolders = mRm.getRoleHoldersAsUser(roleName, user);
+ // TODO(b/226201975): this method is beling called when a pre-created user is added,
+ // at which point it doesn't have any role holders. But it's not called again when
+ // the actual user is added (i.e., when the pre-created user is converted), so we
+ // need to save the user id and call this method again when it's converted
+ // (at onPreCreatedUserConversion()).
+ // Once RoleService properly handles pre-created users, this workaround should be
+ // removed.
+ if (roleHolders.isEmpty()) {
+ UserInfo userInfo = mUserManagerInternal.getUserInfo(user.getIdentifier());
+ if (userInfo != null && userInfo.preCreated) {
+ Slogf.d(TAG, "onRoleHoldersChanged(): ignoring pre-created user %s for now",
+ userInfo.toFullString());
+ mIgnoredPreCreatedUsers.add(user);
+ return;
+ }
+ }
+
int userId = user.getIdentifier();
if (roleHolders.isEmpty()) {
Settings.Secure.putStringForUser(getContext().getContentResolver(),
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index b9793cafb6fe..9f66059b81d4 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -244,8 +244,10 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
/* direct= */ true);
}
- public boolean showSessionLocked(Bundle args, int flags,
- IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken) {
+ public boolean showSessionLocked(@NonNull Bundle args, int flags,
+ @Nullable String attributionTag,
+ @Nullable IVoiceInteractionSessionShowCallback showCallback,
+ @Nullable IBinder activityToken) {
if (mActiveSession == null) {
mActiveSession = new VoiceInteractionSessionConnection(mServiceStub,
mSessionComponentName, mUser, mContext, this,
@@ -269,8 +271,8 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
} else {
visibleActivities = allVisibleActivities;
}
- return mActiveSession.showLocked(args, flags, mDisabledShowContext, showCallback,
- visibleActivities);
+ return mActiveSession.showLocked(args, flags, attributionTag, mDisabledShowContext,
+ showCallback, visibleActivities);
}
public void getActiveServiceSupportedActions(List<String> commands,
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceShellCommand.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceShellCommand.java
index 9bdf4e418a52..80f5dc53c105 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceShellCommand.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceShellCommand.java
@@ -114,8 +114,8 @@ final class VoiceInteractionManagerServiceShellCommand extends ShellCommand {
try {
Bundle args = new Bundle();
- boolean ok = mService.showSessionForActiveService(args, /* sourceFlags= */ 0, callback,
- /* activityToken= */ null);
+ boolean ok = mService.showSessionForActiveService(args, /* sourceFlags= */ 0,
+ /* attributionTag= */ null, callback, /* activityToken= */ null);
if (!ok) {
pw.println("showSessionForActiveService() returned false");
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index 093e97614b5a..63781cca4b96 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -29,6 +29,8 @@ import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_TASK_ID;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.AppOpsManager;
@@ -263,9 +265,9 @@ final class VoiceInteractionSessionConnection implements ServiceConnection,
return flags;
}
- public boolean showLocked(Bundle args, int flags, int disabledContext,
- IVoiceInteractionSessionShowCallback showCallback,
- List<ActivityAssistInfo> topActivities) {
+ public boolean showLocked(@NonNull Bundle args, int flags, @Nullable String attributionTag,
+ int disabledContext, @Nullable IVoiceInteractionSessionShowCallback showCallback,
+ @NonNull List<ActivityAssistInfo> topActivities) {
if (mBound) {
if (!mFullyBound) {
mFullyBound = mContext.bindServiceAsUser(mBindIntent, mFullConnection,
@@ -291,12 +293,15 @@ final class VoiceInteractionSessionConnection implements ServiceConnection,
for (int i = 0; i < topActivitiesCount; i++) {
topActivitiesToken.add(topActivities.get(i).getActivityToken());
}
+
mAssistDataRequester.requestAssistData(topActivitiesToken,
fetchData,
fetchScreenshot,
(disabledContext & VoiceInteractionSession.SHOW_WITH_ASSIST) == 0,
(disabledContext & VoiceInteractionSession.SHOW_WITH_SCREENSHOT) == 0,
- mCallingUid, mSessionComponentName.getPackageName());
+ mCallingUid,
+ mSessionComponentName.getPackageName(),
+ attributionTag);
boolean needDisclosure = mAssistDataRequester.getPendingDataCount() > 0
|| mAssistDataRequester.getPendingScreenshotCount() > 0;
diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java
index 4469ffc14447..7eec86a40c13 100644
--- a/telephony/java/android/telephony/AccessNetworkConstants.java
+++ b/telephony/java/android/telephony/AccessNetworkConstants.java
@@ -115,15 +115,15 @@ public final class AccessNetworkConstants {
/** @hide */
public static @RadioAccessNetworkType int fromString(@NonNull String str) {
switch (str.toUpperCase()) {
- case "GERAN" : return GERAN;
- case "UTRAN" : return UTRAN;
- case "EUTRAN" : return EUTRAN;
- case "CDMA2000" : return CDMA2000;
- case "IWLAN" : return IWLAN;
- case "NGRAN" : return NGRAN;
+ case "UNKNOWN": return UNKNOWN;
+ case "GERAN": return GERAN;
+ case "UTRAN": return UTRAN;
+ case "EUTRAN": return EUTRAN;
+ case "CDMA2000": return CDMA2000;
+ case "IWLAN": return IWLAN;
+ case "NGRAN": return NGRAN;
default:
- Rlog.e(TAG, "Invalid access network type " + str);
- return UNKNOWN;
+ throw new IllegalArgumentException("Invalid access network type " + str);
}
}
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 6deb6f898484..4d18dfe6a62c 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -8596,6 +8596,13 @@ public class CarrierConfigManager {
* IWLAN handover rules that determine whether handover is allowed or disallowed between
* cellular and IWLAN.
*
+ * Rule syntax: "source=[GERAN|UTRAN|EUTRAN|NGRAN|IWLAN|UNKNOWN], target=[GERAN|UTRAN|EUTRAN
+ * |NGRAN|IWLAN], type=[allowed|disallowed], roaming=[true|false], capabilities=[INTERNET|MMS
+ * |FOTA|IMS|CBS|SUPL|EIMS|XCAP|DUN]"
+ *
+ * Note that UNKNOWN can be only specified in the source access network and can be only used
+ * in the disallowed rule.
+ *
* The handover rules will be matched in the order. Here are some sample rules.
* <string-array name="iwlan_handover_rules" num="5">
* <!-- Handover from IWLAN to 2G/3G is not allowed -->
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index a7468a9d268d..789399205f23 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -6641,7 +6641,7 @@ public class TelephonyManager {
* list will be provided. If an error occurs, null will be provided unless the onError
* callback is overridden.
*
- * @param cellInfo a list of {@link CellInfo}, an empty list, or null.
+ * @param cellInfo a list of {@link CellInfo} or an empty list.
*
* {@see android.telephony.TelephonyManager#getAllCellInfo getAllCellInfo()}
*/
diff --git a/test-mock/api/test-current.txt b/test-mock/api/test-current.txt
index 531dd7c008fe..35f076fe3f00 100644
--- a/test-mock/api/test-current.txt
+++ b/test-mock/api/test-current.txt
@@ -6,6 +6,8 @@ package android.test.mock {
}
@Deprecated public class MockPackageManager extends android.content.pm.PackageManager {
+ method public void addCrossProfileIntentFilter(android.content.IntentFilter, int, int, int);
+ method public void clearCrossProfileIntentFilters(int);
method public int getInstallReason(String, android.os.UserHandle);
method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(int, int);
method public String[] getNamesForUids(int[]);
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
index e0ae19383b41..e138d332bb17 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
@@ -108,7 +108,8 @@ abstract class BaseTest @JvmOverloads constructor(
}
/**
- * Checks that the [ComponentMatcher.TASK_BAR] layer is visible during the whole transition
+ * Checks that the [ComponentMatcher.TASK_BAR] window is visible at the start and end of the
+ * transition
*
* Note: Large screen only
*/
@@ -132,7 +133,8 @@ abstract class BaseTest @JvmOverloads constructor(
}
/**
- * Checks that the [ComponentMatcher.STATUS_BAR] layer is visible during the whole transition
+ * Checks that the [ComponentMatcher.STATUS_BAR] layer is visible at the start and end
+ * of the transition
*/
@Presubmit
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index d173b7250548..5e21252f3ebd 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -53,6 +53,16 @@ fun FlickerTestParameter.taskBarWindowIsAlwaysVisible() {
}
/**
+ * Checks that [ComponentMatcher.TASK_BAR] window is visible and above the app windows in
+ * all WM trace entries
+ */
+fun FlickerTestParameter.taskBarWindowIsVisibleAtEnd() {
+ assertWmEnd {
+ this.isAboveAppWindowVisible(ComponentMatcher.TASK_BAR)
+ }
+}
+
+/**
* If [allStates] is true, checks if the stack space of all displays is fully covered
* by any visible layer, during the whole transitions
*
@@ -103,9 +113,25 @@ fun FlickerTestParameter.navBarLayerIsVisibleAtStartAndEnd() {
* trace
*/
fun FlickerTestParameter.taskBarLayerIsVisibleAtStartAndEnd() {
+ this.taskBarLayerIsVisibleAtStart()
+ this.taskBarLayerIsVisibleAtEnd()
+}
+
+/**
+ * Checks that [ComponentMatcher.TASK_BAR] layer is visible at the start of the SF
+ * trace
+ */
+fun FlickerTestParameter.taskBarLayerIsVisibleAtStart() {
assertLayersStart {
this.isVisible(ComponentMatcher.TASK_BAR)
}
+}
+
+/**
+ * Checks that [ComponentMatcher.TASK_BAR] layer is visible at the end of the SF
+ * trace
+ */
+fun FlickerTestParameter.taskBarLayerIsVisibleAtEnd() {
assertLayersEnd {
this.isVisible(ComponentMatcher.TASK_BAR)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/CameraAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/CameraAppHelper.kt
new file mode 100644
index 000000000000..bd0ae4d47383
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/CameraAppHelper.kt
@@ -0,0 +1,54 @@
+/*
+ * 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.wm.flicker.helpers
+
+import android.app.Instrumentation
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
+import android.provider.MediaStore
+import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.IComponentMatcher
+
+class CameraAppHelper @JvmOverloads constructor(
+ instrumentation: Instrumentation,
+ private val pkgManager: PackageManager = instrumentation.context.packageManager
+) : StandardAppHelper(instrumentation, getCameraLauncherName(pkgManager),
+ getCameraComponent(pkgManager)){
+ companion object{
+ private fun getCameraIntent(): Intent {
+ return Intent(MediaStore.ACTION_IMAGE_CAPTURE)
+ }
+
+ private fun getResolveInfo(pkgManager: PackageManager): ResolveInfo {
+ val intent = getCameraIntent()
+ return pkgManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY)
+ ?: error("unable to resolve camera activity")
+ }
+
+ private fun getCameraComponent(pkgManager: PackageManager): IComponentMatcher {
+ val resolveInfo = getResolveInfo(pkgManager)
+ return ComponentMatcher(resolveInfo.activityInfo.packageName,
+ className = resolveInfo.activityInfo.name)
+ }
+
+ private fun getCameraLauncherName(pkgManager: PackageManager): String {
+ val resolveInfo = getResolveInfo(pkgManager)
+ return resolveInfo.activityInfo.name
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt
new file mode 100644
index 000000000000..3ff59e9d5716
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt
@@ -0,0 +1,189 @@
+/*
+ * 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.wm.flicker.launch
+
+import android.platform.test.annotations.FlakyTest
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.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.server.wm.flicker.helpers.CameraAppHelper
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test launching an app after cold opening camera
+ *
+ * To run this test: `atest FlickerTests:OpenAppAfterCameraTest`
+ *
+ * Notes:
+ * Some default assertions are inherited [OpenAppTransition]
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+open class OpenAppAfterCameraTest(
+ testSpec: FlickerTestParameter
+) : OpenAppFromLauncherTransition(testSpec) {
+ private val cameraApp = CameraAppHelper(instrumentation) /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
+ setup {
+ test{
+ tapl.setExpectedRotationCheckEnabled(false)
+ }
+ eachRun {
+ // 1. Open camera - cold -> close it first
+ cameraApp.exit(wmHelper)
+ cameraApp.launchViaIntent(wmHelper)
+ // 2. Press home button (button nav mode) / swipe up to home (gesture nav mode)
+ tapl.goHome()
+ }
+ }
+ teardown {
+ eachRun {
+ testApp.exit(wmHelper)
+ }
+ }
+ transitions {
+ testApp.launchViaIntent(wmHelper)
+ }
+ }
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 206753786)
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun focusChanges() = super.focusChanges()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun appWindowReplacesLauncherAsTopWindow() =
+ super.appWindowReplacesLauncherAsTopWindow()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun appWindowAsTopWindowAtEnd() = super.appWindowAsTopWindowAtEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun appLayerBecomesVisible() = super.appLayerBecomesVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun appWindowBecomesVisible() = super.appWindowBecomesVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun appWindowBecomesTopWindow() = super.appWindowBecomesTopWindow()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun appWindowIsTopWindowAtEnd() = super.appWindowIsTopWindowAtEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun entireScreenCovered() = super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
+
+ /** {@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 visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
+ * repetitions, screen orientation and navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests()
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
index 6e3e74df0193..866e819bf5fc 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
@@ -32,8 +32,12 @@ import com.android.server.wm.flicker.helpers.NotificationAppHelper
import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.taskBarLayerIsVisibleAtEnd
+import com.android.server.wm.flicker.taskBarWindowIsVisibleAtEnd
+import com.android.server.wm.traces.common.ComponentMatcher
import org.junit.Assume
import org.junit.FixMethodOrder
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -231,6 +235,42 @@ open class OpenAppFromNotificationWarm(
super.appWindowBecomesTopWindow()
}
+ /**
+ * Checks that the [ComponentMatcher.TASK_BAR] window is visible at the end of the transition
+ *
+ * Note: Large screen only
+ */
+ @Postsubmit
+ @Test
+ open fun taskBarWindowIsVisibleAtEnd() {
+ Assume.assumeFalse(testSpec.isTablet)
+ testSpec.taskBarWindowIsVisibleAtEnd()
+ }
+
+ /**
+ * Checks that the [ComponentMatcher.TASK_BAR] layer is visible at the end of the transition
+ *
+ * Note: Large screen only
+ */
+ @Postsubmit
+ @Test
+ open fun taskBarLayerIsVisibleAtEnd() {
+ Assume.assumeFalse(testSpec.isTablet)
+ testSpec.taskBarLayerIsVisibleAtEnd()
+ }
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Display is locked at the start")
+ override fun taskBarWindowIsAlwaysVisible() =
+ super.taskBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Display is locked at the start")
+ override fun taskBarLayerIsVisibleAtStartAndEnd() =
+ super.taskBarLayerIsVisibleAtStartAndEnd()
+
companion object {
/**
* Creates the test configurations.
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index cbf920acc8cf..035f911865d0 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -472,6 +472,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, android::IDiagn
manifest_action["compatible-screens"]["screen"];
manifest_action["supports-gl-texture"];
manifest_action["restrict-update"];
+ manifest_action["install-constraints"]["fingerprint-prefix"];
manifest_action["package-verifier"];
manifest_action["meta-data"] = meta_data_action;
manifest_action["uses-split"].Action(RequiredNameIsJavaPackage);
diff --git a/tools/stringslint/stringslint.py b/tools/stringslint/stringslint.py
deleted file mode 100644
index 15088fc81e88..000000000000
--- a/tools/stringslint/stringslint.py
+++ /dev/null
@@ -1,234 +0,0 @@
-#!/usr/bin/env python3
-#-*- coding: utf-8 -*-
-
-# 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.
-
-"""
-Enforces common Android string best-practices. It ignores lint messages from
-a previous strings file, if provided.
-
-Usage: stringslint.py strings.xml
-Usage: stringslint.py strings.xml old_strings.xml
-
-In general:
-* Errors signal issues that must be fixed before submitting, and are only
- used when there are no false-positives.
-* Warnings signal issues that might need to be fixed, but need manual
- inspection due to risk of false-positives.
-* Info signal issues that should be fixed to match best-practices, such
- as providing comments to aid translation.
-"""
-
-import re, sys, codecs
-import lxml.etree as ET
-
-BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
-
-def format(fg=None, bg=None, bright=False, bold=False, dim=False, reset=False):
- # manually derived from http://en.wikipedia.org/wiki/ANSI_escape_code#Codes
- codes = []
- if reset: codes.append("0")
- else:
- if not fg is None: codes.append("3%d" % (fg))
- if not bg is None:
- if not bright: codes.append("4%d" % (bg))
- else: codes.append("10%d" % (bg))
- if bold: codes.append("1")
- elif dim: codes.append("2")
- else: codes.append("22")
- return "\033[%sm" % (";".join(codes))
-
-warnings = None
-
-def warn(tag, msg, actual, expected, color=YELLOW):
- global warnings
- key = "%s:%d" % (tag.attrib["name"], hash(msg))
- value = "%sLine %d: '%s':%s %s" % (format(fg=color, bold=True),
- tag.sourceline,
- tag.attrib["name"],
- format(reset=True),
- msg)
- if not actual is None: value += "\n\tActual: %s%s%s" % (format(dim=True),
- actual,
- format(reset=True))
- if not expected is None: value += "\n\tExample: %s%s%s" % (format(dim=True),
- expected,
- format(reset=True))
- warnings[key] = value
-
-
-def error(tag, msg, actual, expected):
- warn(tag, msg, actual, expected, RED)
-
-def info(tag, msg, actual, expected):
- warn(tag, msg, actual, expected, CYAN)
-
-# Escaping logic borrowed from https://stackoverflow.com/a/24519338
-ESCAPE_SEQUENCE_RE = re.compile(r'''
- ( \\U........ # 8-digit hex escapes
- | \\u.... # 4-digit hex escapes
- | \\x.. # 2-digit hex escapes
- | \\[0-7]{1,3} # Octal escapes
- | \\N\{[^}]+\} # Unicode characters by name
- | \\[\\'"abfnrtv] # Single-character escapes
- )''', re.UNICODE | re.VERBOSE)
-
-def decode_escapes(s):
- def decode_match(match):
- return codecs.decode(match.group(0), 'unicode-escape')
-
- s = re.sub(r"\n\s*", " ", s)
- s = ESCAPE_SEQUENCE_RE.sub(decode_match, s)
- s = re.sub(r"%(\d+\$)?[a-z]", "____", s)
- s = re.sub(r"\^\d+", "____", s)
- s = re.sub(r"<br/?>", "\n", s)
- s = re.sub(r"</?[a-z]+>", "", s)
- return s
-
-def sample_iter(tag):
- if not isinstance(tag, ET._Comment) and re.match("{.*xliff.*}g", tag.tag) and "example" in tag.attrib:
- yield tag.attrib["example"]
- elif tag.text:
- yield decode_escapes(tag.text)
- for e in tag:
- for v in sample_iter(e):
- yield v
- if e.tail:
- yield decode_escapes(e.tail)
-
-def lint(path):
- global warnings
- warnings = {}
-
- with open(path) as f:
- raw = f.read()
- if len(raw.strip()) == 0:
- return warnings
- tree = ET.fromstring(bytes(raw, encoding='utf-8'))
- root = tree #tree.getroot()
-
- last_comment = None
- for child in root:
- # TODO: handle plurals
- if isinstance(child, ET._Comment):
- last_comment = child
- elif child.tag == "string":
- # We always consume comment
- comment = last_comment
- last_comment = None
-
- # Prepare string for analysis
- text = "".join(child.itertext())
- sample = "".join(sample_iter(child)).strip().strip("'\"")
-
- # Validate comment
- if comment is None:
- info(child, "Missing string comment to aid translation",
- None, None)
- continue
- if "do not translate" in comment.text.lower():
- continue
- if "translatable" in child.attrib and child.attrib["translatable"].lower() == "false":
- continue
-
- misspelled_attributes = [
- ("translateable", "translatable"),
- ]
- for misspelling, expected in misspelled_attributes:
- if misspelling in child.attrib:
- error(child, "Misspelled <string> attribute.", misspelling, expected)
-
- limit = re.search("CHAR[ _-]LIMIT=(\d+|NONE|none)", comment.text)
- if limit is None:
- info(child, "Missing CHAR LIMIT to aid translation",
- repr(comment), "<!-- Description of string [CHAR LIMIT=32] -->")
- elif re.match("\d+", limit.group(1)):
- limit = int(limit.group(1))
- if len(sample) > limit:
- warn(child, "Expanded string length is larger than CHAR LIMIT",
- sample, None)
-
- # Look for common mistakes/substitutions
- if "'" in text:
- error(child, "Turned quotation mark glyphs are more polished",
- text, "This doesn\u2019t need to \u2018happen\u2019 today")
- if '"' in text and not text.startswith('"') and text.endswith('"'):
- error(child, "Turned quotation mark glyphs are more polished",
- text, "This needs to \u201chappen\u201d today")
- if "..." in text:
- error(child, "Ellipsis glyph is more polished",
- text, "Loading\u2026")
- if "wi-fi" in text.lower():
- error(child, "Non-breaking glyph is more polished",
- text, "Wi\u2011Fi")
- if "wifi" in text.lower():
- error(child, "Using non-standard spelling",
- text, "Wi\u2011Fi")
- if re.search("\d-\d", text):
- warn(child, "Ranges should use en dash glyph",
- text, "You will find this material in chapters 8\u201312")
- if "--" in text:
- warn(child, "Phrases should use em dash glyph",
- text, "Upon discovering errors\u2014all 124 of them\u2014they recalled.")
- if ". " in text:
- warn(child, "Only use single space between sentences",
- text, "First idea. Second idea.")
- if re.match(r"^[A-Z\s]{5,}$", text):
- warn(child, "Actions should use android:textAllCaps in layout; ignore if acronym",
- text, "Refresh data")
- if " phone " in text and "product" not in child.attrib:
- warn(child, "Strings mentioning phones should have variants for tablets",
- text, None)
-
- # When more than one substitution, require indexes
- if len(re.findall("%[^%]", text)) > 1:
- if len(re.findall("%[^\d]", text)) > 0:
- error(child, "Substitutions must be indexed",
- text, "Add %1$s to %2$s")
-
- # Require xliff substitutions
- for gc in child.iter():
- badsub = False
- if gc.tail and re.search("%[^%]", gc.tail): badsub = True
- if re.match("{.*xliff.*}g", gc.tag):
- if "id" not in gc.attrib:
- error(child, "Substitutions must define id attribute",
- None, "<xliff:g id=\"domain\" example=\"example.com\">%1$s</xliff:g>")
- if "example" not in gc.attrib:
- error(child, "Substitutions must define example attribute",
- None, "<xliff:g id=\"domain\" example=\"example.com\">%1$s</xliff:g>")
- else:
- if gc.text and re.search("%[^%]", gc.text): badsub = True
- if badsub:
- error(child, "Substitutions must be inside xliff tags",
- text, "<xliff:g id=\"domain\" example=\"example.com\">%1$s</xliff:g>")
-
- return warnings
-
-if len(sys.argv) > 2:
- before = lint(sys.argv[2])
-else:
- before = {}
-after = lint(sys.argv[1])
-
-for b in before:
- if b in after:
- del after[b]
-
-if len(after) > 0:
- for a in sorted(after.keys()):
- print(after[a])
- print()
- sys.exit(1)
diff --git a/tools/stringslint/stringslint_sha.sh b/tools/stringslint/stringslint_sha.sh
index bd0569873197..009a1f284bf0 100755
--- a/tools/stringslint/stringslint_sha.sh
+++ b/tools/stringslint/stringslint_sha.sh
@@ -1,5 +1,3 @@
#!/bin/bash
-LOCAL_DIR="$( dirname ${BASH_SOURCE} )"
-git show --name-only --pretty=format: $1 | grep values/strings.xml | while read file; do
- python3 $LOCAL_DIR/stringslint.py <(git show $1:$file) <(git show $1^:$file)
-done
+
+# NOTE: this script has been deprecated and replaced by AyeAye checks directly in Gerrit
diff --git a/tools/xmlpersistence/src/main/kotlin/Generator.kt b/tools/xmlpersistence/src/main/kotlin/Generator.kt
index b2c5f4ac767b..8e62388c860f 100644
--- a/tools/xmlpersistence/src/main/kotlin/Generator.kt
+++ b/tools/xmlpersistence/src/main/kotlin/Generator.kt
@@ -149,6 +149,7 @@ private val ClassFieldInfo.allClassFields: List<ClassFieldInfo>
when (field) {
is ClassFieldInfo -> this += field.allClassFields
is ListFieldInfo -> this += field.element.allClassFields
+ else -> {}
}
}
}