summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/Android.bp38
-rw-r--r--apex/blobstore/framework/java/android/app/blob/BlobHandle.java11
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java5
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java8
-rw-r--r--apex/permission/service/Android.bp43
-rw-r--r--apex/permission/service/api/current.txt45
-rw-r--r--apex/permission/service/api/system-server-current.txt46
-rw-r--r--apex/permission/service/api/system-server-removed.txt1
-rw-r--r--apex/permission/tests/Android.bp2
-rw-r--r--api/test-current.txt1
-rw-r--r--cmds/idmap2/idmap2/CommandUtils.cpp2
-rw-r--r--cmds/idmap2/idmap2/CommandUtils.h3
-rw-r--r--cmds/idmap2/idmap2d/Idmap2Service.cpp16
-rw-r--r--cmds/idmap2/include/idmap2/Idmap.h4
-rw-r--r--cmds/idmap2/libidmap2/Idmap.cpp12
-rw-r--r--cmds/statsd/src/StatsLogProcessor.cpp2
-rw-r--r--cmds/statsd/src/StatsLogProcessor.h1
-rw-r--r--cmds/statsd/src/atoms.proto500
-rw-r--r--cmds/statsd/src/guardrail/StatsdStats.h4
-rw-r--r--cmds/statsd/src/metrics/MetricsManager.cpp8
-rw-r--r--cmds/statsd/src/statsd_config.proto15
-rw-r--r--cmds/statsd/tests/StatsLogProcessor_test.cpp77
-rw-r--r--core/java/android/app/Notification.java1
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java26
-rw-r--r--core/java/android/app/usage/UsageEvents.java1
-rw-r--r--core/java/android/content/pm/LauncherApps.java32
-rw-r--r--core/java/android/content/pm/PackagePartitions.java9
-rw-r--r--core/java/android/hardware/camera2/CameraManager.java19
-rw-r--r--core/java/android/hardware/camera2/impl/CameraDeviceImpl.java194
-rw-r--r--core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java73
-rw-r--r--core/java/android/hardware/camera2/impl/CaptureResultExtras.java28
-rw-r--r--core/java/android/hardware/camera2/impl/RequestLastFrameNumbersHolder.java37
-rw-r--r--core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java5
-rw-r--r--core/java/android/hardware/soundtrigger/ConversionUtil.java18
-rw-r--r--core/java/android/hardware/soundtrigger/SoundTriggerModule.java2
-rw-r--r--core/java/android/net/ConnectivityDiagnosticsManager.java8
-rw-r--r--core/java/android/net/ConnectivityManager.java107
-rw-r--r--core/java/android/net/NetworkAgent.java1
-rw-r--r--core/java/android/net/NetworkAgentConfig.java41
-rw-r--r--core/java/android/os/UserManager.java8
-rw-r--r--core/java/android/os/VibrationEffect.java9
-rwxr-xr-xcore/java/android/provider/Settings.java3
-rw-r--r--core/java/android/service/autofill/augmented/FillCallback.java3
-rw-r--r--core/java/android/service/controls/templates/ControlTemplate.java9
-rw-r--r--core/java/android/service/notification/ZenModeConfig.java43
-rw-r--r--core/java/android/service/notification/ZenPolicy.java35
-rw-r--r--core/java/android/view/InsetsController.java14
-rw-r--r--core/java/android/view/InsetsSource.java9
-rw-r--r--core/java/android/view/InsetsSourceConsumer.java7
-rw-r--r--core/java/android/view/InsetsState.java9
-rw-r--r--core/java/android/view/SurfaceView.java63
-rw-r--r--core/java/android/view/WindowlessWindowManager.java1
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureSession.java8
-rw-r--r--core/java/android/widget/SelectionActionModeHelper.java3
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java18
-rw-r--r--core/java/com/android/internal/app/ChooserListAdapter.java8
-rw-r--r--core/java/com/android/internal/app/PlatLogoActivity.java492
-rw-r--r--core/java/com/android/internal/app/ResolverListAdapter.java3
-rw-r--r--core/java/com/android/internal/app/SimpleIconFactory.java64
-rw-r--r--core/java/com/android/internal/app/chooser/DisplayResolveInfo.java6
-rw-r--r--core/java/com/android/internal/view/IInputMethodManager.aidl2
-rw-r--r--core/java/com/android/internal/widget/ConversationLayout.java17
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java34
-rw-r--r--core/proto/android/service/notification.proto72
-rw-r--r--core/proto/android/stats/connectivity/Android.bp6
-rw-r--r--core/proto/android/stats/connectivity/network_stack.proto154
-rw-r--r--core/res/res/drawable-nodpi/ic_number11.xml12
-rw-r--r--core/res/res/layout-car/car_alert_dialog.xml85
-rw-r--r--core/res/res/layout-car/car_alert_dialog_button_bar.xml64
-rw-r--r--core/res/res/layout-car/car_alert_dialog_title.xml62
-rw-r--r--core/res/res/layout/notification_template_material_conversation.xml4
-rw-r--r--core/res/res/values/config.xml4
-rw-r--r--core/res/res/values/dimens.xml2
-rw-r--r--core/res/res/values/styles_device_defaults.xml7
-rw-r--r--core/res/res/values/styles_material.xml4
-rw-r--r--core/res/res/values/symbols.xml9
-rw-r--r--core/res/res/xml/sms_short_codes.xml4
-rw-r--r--core/tests/coretests/src/android/view/InsetsControllerTest.java3
-rw-r--r--core/tests/coretests/src/android/view/InsetsStateTest.java9
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java12
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java5
-rw-r--r--core/tests/overlaytests/remount/Android.bp3
-rw-r--r--core/tests/overlaytests/remount/src/com/android/overlaytest/remounted/OverlayRemountedTestBase.java1
-rw-r--r--core/tests/overlaytests/remount/src/com/android/overlaytest/remounted/SystemPreparer.java151
-rw-r--r--data/etc/preinstalled-packages-platform.xml6
-rw-r--r--libs/incident/Android.bp12
-rw-r--r--libs/incident/AndroidTest.xml6
-rw-r--r--libs/incident/TEST_MAPPING3
-rw-r--r--location/java/android/location/LocationManager.java20
-rw-r--r--media/java/android/media/IMediaRouterClient.aidl1
-rw-r--r--media/java/android/media/MediaRoute2Info.java51
-rw-r--r--media/java/android/media/MediaRoute2ProviderService.java69
-rw-r--r--media/java/android/media/MediaRouter.java47
-rw-r--r--media/java/android/media/MediaRouter2.java25
-rw-r--r--media/java/android/media/MediaRouter2Manager.java6
-rw-r--r--media/java/android/media/soundtrigger_middleware/RecognitionEvent.aidl4
-rw-r--r--media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java12
-rw-r--r--packages/CarSystemUI/res/values/colors.xml1
-rw-r--r--packages/CarSystemUI/res/values/styles.xml6
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java8
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/notification/BottomNotificationPanelViewMediator.java3
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java29
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/notification/TopNotificationPanelViewMediator.java3
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java15
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java8
-rw-r--r--packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java9
-rw-r--r--packages/OsuLogin/Android.bp1
-rw-r--r--packages/SettingsLib/OWNERS1
-rw-r--r--packages/SettingsLib/res/values-ar/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-de/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-el/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-en-rAU/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-en-rCA/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-en-rGB/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-en-rIN/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-in/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-it/strings.xml12
-rw-r--r--packages/SettingsLib/res/values-pl/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-vi/strings.xml2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java22
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java4
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java4
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java19
-rw-r--r--packages/SystemUI/docs/broadcasts.md2
-rw-r--r--packages/SystemUI/res-product/values-ne/strings.xml2
-rw-r--r--packages/SystemUI/res/layout/bubbles_manage_button_education.xml68
-rw-r--r--packages/SystemUI/res/layout/global_screenshot.xml19
-rw-r--r--packages/SystemUI/res/layout/global_screenshot_static.xml18
-rw-r--r--packages/SystemUI/res/layout/hybrid_conversation_notification.xml12
-rw-r--r--packages/SystemUI/res/layout/media_view.xml4
-rw-r--r--packages/SystemUI/res/layout/notification_conversation_info.xml12
-rw-r--r--packages/SystemUI/res/layout/notification_info.xml14
-rw-r--r--packages/SystemUI/res/layout/partial_conversation_info.xml5
-rw-r--r--packages/SystemUI/res/layout/priority_onboarding_half_shell.xml56
-rw-r--r--packages/SystemUI/res/values-af/strings.xml5
-rw-r--r--packages/SystemUI/res/values-am/strings.xml5
-rw-r--r--packages/SystemUI/res/values-ar/strings.xml3
-rw-r--r--packages/SystemUI/res/values-as/strings.xml5
-rw-r--r--packages/SystemUI/res/values-az/strings.xml5
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/strings.xml7
-rw-r--r--packages/SystemUI/res/values-be/strings.xml7
-rw-r--r--packages/SystemUI/res/values-bg/strings.xml9
-rw-r--r--packages/SystemUI/res/values-bn/strings.xml7
-rw-r--r--packages/SystemUI/res/values-bs/strings.xml5
-rw-r--r--packages/SystemUI/res/values-ca/strings.xml7
-rw-r--r--packages/SystemUI/res/values-cs/strings.xml7
-rw-r--r--packages/SystemUI/res/values-da/strings.xml5
-rw-r--r--packages/SystemUI/res/values-de/strings.xml6
-rw-r--r--packages/SystemUI/res/values-el/strings.xml11
-rw-r--r--packages/SystemUI/res/values-en-rAU/strings.xml7
-rw-r--r--packages/SystemUI/res/values-en-rCA/strings.xml7
-rw-r--r--packages/SystemUI/res/values-en-rGB/strings.xml7
-rw-r--r--packages/SystemUI/res/values-en-rIN/strings.xml7
-rw-r--r--packages/SystemUI/res/values-en-rXC/strings.xml2
-rw-r--r--packages/SystemUI/res/values-es-rUS/strings.xml7
-rw-r--r--packages/SystemUI/res/values-es/strings.xml19
-rw-r--r--packages/SystemUI/res/values-et/strings.xml5
-rw-r--r--packages/SystemUI/res/values-eu/strings.xml9
-rw-r--r--packages/SystemUI/res/values-fa/strings.xml7
-rw-r--r--packages/SystemUI/res/values-fi/strings.xml5
-rw-r--r--packages/SystemUI/res/values-fr-rCA/strings.xml7
-rw-r--r--packages/SystemUI/res/values-fr/strings.xml5
-rw-r--r--packages/SystemUI/res/values-gl/strings.xml5
-rw-r--r--packages/SystemUI/res/values-gu/strings.xml16
-rw-r--r--packages/SystemUI/res/values-hi/strings.xml11
-rw-r--r--packages/SystemUI/res/values-hr/strings.xml7
-rw-r--r--packages/SystemUI/res/values-hu/strings.xml5
-rw-r--r--packages/SystemUI/res/values-hy/strings.xml13
-rw-r--r--packages/SystemUI/res/values-in/strings.xml5
-rw-r--r--packages/SystemUI/res/values-is/strings.xml5
-rw-r--r--packages/SystemUI/res/values-it/strings.xml9
-rw-r--r--packages/SystemUI/res/values-iw/strings.xml7
-rw-r--r--packages/SystemUI/res/values-ja/strings.xml5
-rw-r--r--packages/SystemUI/res/values-ka/strings.xml5
-rw-r--r--packages/SystemUI/res/values-kk/strings.xml7
-rw-r--r--packages/SystemUI/res/values-km/strings.xml7
-rw-r--r--packages/SystemUI/res/values-kn/strings.xml8
-rw-r--r--packages/SystemUI/res/values-ko/strings.xml5
-rw-r--r--packages/SystemUI/res/values-ky/strings.xml21
-rw-r--r--packages/SystemUI/res/values-lo/strings.xml5
-rw-r--r--packages/SystemUI/res/values-lt/strings.xml11
-rw-r--r--packages/SystemUI/res/values-lv/strings.xml5
-rw-r--r--packages/SystemUI/res/values-mk/strings.xml7
-rw-r--r--packages/SystemUI/res/values-ml/strings.xml14
-rw-r--r--packages/SystemUI/res/values-mn/strings.xml5
-rw-r--r--packages/SystemUI/res/values-mr/strings.xml12
-rw-r--r--packages/SystemUI/res/values-ms/strings.xml5
-rw-r--r--packages/SystemUI/res/values-my/strings.xml7
-rw-r--r--packages/SystemUI/res/values-nb/strings.xml5
-rw-r--r--packages/SystemUI/res/values-ne/strings.xml16
-rw-r--r--packages/SystemUI/res/values-nl/strings.xml17
-rw-r--r--packages/SystemUI/res/values-or/strings.xml25
-rw-r--r--packages/SystemUI/res/values-pa/strings.xml8
-rw-r--r--packages/SystemUI/res/values-pl/strings.xml8
-rw-r--r--packages/SystemUI/res/values-pt-rBR/strings.xml7
-rw-r--r--packages/SystemUI/res/values-pt-rPT/strings.xml5
-rw-r--r--packages/SystemUI/res/values-pt/strings.xml7
-rw-r--r--packages/SystemUI/res/values-ro/strings.xml7
-rw-r--r--packages/SystemUI/res/values-ru/strings.xml9
-rw-r--r--packages/SystemUI/res/values-si/strings.xml5
-rw-r--r--packages/SystemUI/res/values-sk/strings.xml5
-rw-r--r--packages/SystemUI/res/values-sl/strings.xml15
-rw-r--r--packages/SystemUI/res/values-sq/strings.xml5
-rw-r--r--packages/SystemUI/res/values-sr/strings.xml7
-rw-r--r--packages/SystemUI/res/values-sv/strings.xml5
-rw-r--r--packages/SystemUI/res/values-sw/strings.xml7
-rw-r--r--packages/SystemUI/res/values-ta/strings.xml9
-rw-r--r--packages/SystemUI/res/values-te/strings.xml7
-rw-r--r--packages/SystemUI/res/values-th/strings.xml7
-rw-r--r--packages/SystemUI/res/values-tl/strings.xml13
-rw-r--r--packages/SystemUI/res/values-tr/strings.xml7
-rw-r--r--packages/SystemUI/res/values-uk/strings.xml5
-rw-r--r--packages/SystemUI/res/values-ur/strings.xml6
-rw-r--r--packages/SystemUI/res/values-uz/strings.xml5
-rw-r--r--packages/SystemUI/res/values-vi/strings.xml7
-rw-r--r--packages/SystemUI/res/values-zh-rCN/strings.xml5
-rw-r--r--packages/SystemUI/res/values-zh-rHK/strings.xml5
-rw-r--r--packages/SystemUI/res/values-zh-rTW/strings.xml7
-rw-r--r--packages/SystemUI/res/values-zu/strings.xml5
-rw-r--r--packages/SystemUI/res/values/colors.xml4
-rw-r--r--packages/SystemUI/res/values/dimens.xml10
-rw-r--r--packages/SystemUI/res/values/styles.xml7
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/BlurUtils.java37
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java16
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/Prefs.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt117
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java47
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java137
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleManageEducationView.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java541
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/animation/AnimatableScaleMatrix.java137
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java54
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/BroadcastDispatcherLog.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt50
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt225
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java64
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java188
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java567
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java54
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java47
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt95
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java78
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java60
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java91
-rw-r--r--packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingSecondaryUserActivity.java70
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt (renamed from packages/SystemUI/src/com/android/systemui/controls/UserAwareController.kt)4
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java24
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt66
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt80
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt179
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java45
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java157
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/animation/PhysicsAnimatorTest.kt1
-rw-r--r--packages/Tethering/Android.bp2
-rw-r--r--packages/Tethering/TEST_MAPPING7
-rw-r--r--packages/Tethering/src/android/net/ip/IpServer.java101
-rw-r--r--packages/Tethering/src/android/net/util/TetheringUtils.java77
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java637
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/Tethering.java9
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java11
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java22
-rw-r--r--packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java176
-rw-r--r--packages/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java246
-rw-r--r--packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt27
-rw-r--r--packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java7
-rw-r--r--services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java7
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java4
-rw-r--r--services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java5
-rw-r--r--services/core/Android.bp2
-rw-r--r--services/core/java/android/content/pm/PackageManagerInternal.java10
-rw-r--r--services/core/java/com/android/server/AppStateTracker.java3
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java2
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java20
-rw-r--r--services/core/java/com/android/server/adb/AdbDebuggingManager.java29
-rw-r--r--services/core/java/com/android/server/adb/AdbService.java7
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java23
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java29
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java10
-rw-r--r--services/core/java/com/android/server/am/UserController.java17
-rwxr-xr-xservices/core/java/com/android/server/audio/AudioService.java9
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java26
-rw-r--r--services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java6
-rw-r--r--services/core/java/com/android/server/location/LocationManagerService.java15
-rw-r--r--services/core/java/com/android/server/location/SettingsHelper.java2
-rw-r--r--services/core/java/com/android/server/locksettings/RebootEscrowManager.java8
-rw-r--r--services/core/java/com/android/server/media/BluetoothRouteProvider.java20
-rw-r--r--services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java48
-rw-r--r--services/core/java/com/android/server/media/MediaRouterService.java16
-rw-r--r--services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java8
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java28
-rw-r--r--services/core/java/com/android/server/notification/SysUiStatsEvent.java5
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java76
-rw-r--r--services/core/java/com/android/server/pm/AppsFilter.java483
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java34
-rw-r--r--services/core/java/com/android/server/pm/PackageSettingBase.java5
-rw-r--r--services/core/java/com/android/server/pm/TEST_MAPPING3
-rw-r--r--services/core/java/com/android/server/pm/UserSystemPackageInstaller.java38
-rw-r--r--services/core/java/com/android/server/power/Notifier.java1
-rw-r--r--services/core/java/com/android/server/slice/SliceClientPermissions.java6
-rw-r--r--services/core/java/com/android/server/slice/SlicePermissionManager.java2
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java23
-rw-r--r--services/core/java/com/android/server/stats/pull/StatsPullAtomService.java27
-rw-r--r--services/core/java/com/android/server/textclassifier/FixedSizeQueue.java103
-rw-r--r--services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java27
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java11
-rw-r--r--services/core/java/com/android/server/wm/ActivityStack.java16
-rw-r--r--services/core/java/com/android/server/wm/ActivityStackSupervisor.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java5
-rw-r--r--services/core/java/com/android/server/wm/AppTransitionController.java25
-rw-r--r--services/core/java/com/android/server/wm/Dimmer.java7
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java6
-rw-r--r--services/core/java/com/android/server/wm/InsetsSourceProvider.java2
-rw-r--r--services/core/java/com/android/server/wm/InsetsStateController.java1
-rw-r--r--services/core/java/com/android/server/wm/RecentTasks.java11
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java6
-rw-r--r--services/core/java/com/android/server/wm/ShellRoot.java2
-rw-r--r--services/core/java/com/android/server/wm/SurfaceAnimator.java11
-rw-r--r--services/core/java/com/android/server/wm/Task.java49
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java8
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java133
-rw-r--r--services/core/java/com/android/server/wm/WindowContainerThumbnail.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java14
-rw-r--r--services/core/jni/com_android_server_tv_TvInputHal.cpp3
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java60
-rw-r--r--services/net/java/android/net/ip/IpClientCallbacks.java23
-rw-r--r--services/net/java/android/net/ip/IpClientUtil.java3
-rw-r--r--services/net/java/android/net/util/DhcpResultsCompatUtil.java54
-rw-r--r--services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java8
-rw-r--r--services/tests/PackageManagerServiceTests/host/Android.bp33
-rw-r--r--services/tests/PackageManagerServiceTests/host/AndroidTest.xml26
-rw-r--r--services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt46
-rw-r--r--services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt98
-rw-r--r--services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/Partition.kt31
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/Android.bp33
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml25
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml21
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml21
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml21
-rw-r--r--services/tests/mockingservicestests/Android.bp2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java17
-rw-r--r--services/tests/servicestests/Android.bp2
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java88
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java53
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java19
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java26
-rw-r--r--services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java62
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java362
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java66
-rw-r--r--services/tests/servicestests/src/com/android/server/textclassifier/FixedSizeQueueTest.java109
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java224
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/WrappedSysUiStatsEvent.java12
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java30
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java198
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java165
-rw-r--r--services/tests/wmtests/AndroidManifest.xml2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java12
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DimmerTests.java14
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java23
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java41
-rw-r--r--telephony/common/com/android/internal/telephony/CarrierAppUtils.java8
-rw-r--r--telephony/java/android/telephony/CellIdentityLte.java2
-rw-r--r--telephony/java/android/telephony/CellIdentityNr.java2
-rw-r--r--tests/net/common/java/android/net/NetworkAgentConfigTest.kt2
-rw-r--r--tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java218
-rw-r--r--tests/utils/hostutils/Android.bp28
-rw-r--r--tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java362
-rw-r--r--wifi/Android.bp12
-rw-r--r--wifi/java/android/net/wifi/SoftApCapability.java8
-rw-r--r--wifi/java/android/net/wifi/SoftApConfiguration.java38
-rw-r--r--wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java10
432 files changed, 9976 insertions, 3823 deletions
diff --git a/apex/Android.bp b/apex/Android.bp
index c1715a002d6d..371bd7fc9479 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -63,9 +63,9 @@ mainline_service_stubs_args =
"--hide-annotation android.annotation.Hide " +
"--hide InternalClasses " // com.android.* classes are okay in this interface
-// Defaults for mainline module provided java_sdk_library instances.
+// Defaults common to all mainline module java_sdk_library instances.
java_defaults {
- name: "framework-module-defaults",
+ name: "framework-module-common-defaults",
// Additional annotations used for compiling both the implementation and the
// stubs libraries.
@@ -88,14 +88,6 @@ java_defaults {
enabled: true,
sdk_version: "module_current",
},
- system: {
- enabled: true,
- sdk_version: "module_current",
- },
- module_lib: {
- enabled: true,
- sdk_version: "module_current",
- },
// Configure framework module specific metalava options.
droiddoc_options: [mainline_stubs_args],
@@ -127,6 +119,32 @@ java_defaults {
sdk_version: "module_current",
}
+// Defaults for mainline module provided java_sdk_library instances.
+java_defaults {
+ name: "framework-module-defaults",
+ defaults: ["framework-module-common-defaults"],
+
+ system: {
+ enabled: true,
+ sdk_version: "module_current",
+ },
+ module_lib: {
+ enabled: true,
+ sdk_version: "module_current",
+ },
+}
+
+// Defaults for mainline module system server provided java_sdk_library instances.
+java_defaults {
+ name: "framework-system-server-module-defaults",
+ defaults: ["framework-module-common-defaults"],
+
+ system_server: {
+ enabled: true,
+ sdk_version: "module_current",
+ },
+}
+
stubs_defaults {
name: "framework-module-stubs-defaults-publicapi",
args: mainline_framework_stubs_args,
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobHandle.java b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
index bcef8ceaa941..ecc78ce7cf34 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
@@ -219,7 +219,7 @@ public final class BlobHandle implements Parcelable {
public void dump(IndentingPrintWriter fout, boolean dumpFull) {
if (dumpFull) {
fout.println("algo: " + algorithm);
- fout.println("digest: " + (dumpFull ? encodeDigest() : safeDigest()));
+ fout.println("digest: " + (dumpFull ? encodeDigest(digest) : safeDigest(digest)));
fout.println("label: " + label);
fout.println("expiryMs: " + expiryTimeMillis);
fout.println("tag: " + tag);
@@ -243,19 +243,20 @@ public final class BlobHandle implements Parcelable {
public String toString() {
return "BlobHandle {"
+ "algo:" + algorithm + ","
- + "digest:" + safeDigest() + ","
+ + "digest:" + safeDigest(digest) + ","
+ "label:" + label + ","
+ "expiryMs:" + expiryTimeMillis + ","
+ "tag:" + tag
+ "}";
}
- private String safeDigest() {
- final String digestStr = encodeDigest();
+ /** @hide */
+ public static String safeDigest(@NonNull byte[] digest) {
+ final String digestStr = encodeDigest(digest);
return digestStr.substring(0, 2) + ".." + digestStr.substring(digestStr.length() - 2);
}
- private String encodeDigest() {
+ private static String encodeDigest(@NonNull byte[] digest) {
return Base64.encodeToString(digest, Base64.NO_WRAP);
}
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
index 0a5ada282388..68c4bb675907 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -478,7 +478,9 @@ public class BlobStoreManagerService extends SystemService {
? Resources.ID_NULL
: getDescriptionResourceId(resourcesGetter.apply(leasee.packageName),
leasee.descriptionResEntryName, leasee.packageName);
- leaseInfos.add(new LeaseInfo(leasee.packageName, leasee.expiryTimeMillis,
+ final long expiryTimeMs = leasee.expiryTimeMillis == 0
+ ? blobHandle.getExpiryTimeMillis() : leasee.expiryTimeMillis;
+ leaseInfos.add(new LeaseInfo(leasee.packageName, expiryTimeMs,
descriptionResId, leasee.description));
});
blobInfos.add(new BlobInfo(blobMetadata.getBlobId(),
@@ -592,6 +594,7 @@ public class BlobStoreManagerService extends SystemService {
} else {
blob.addOrReplaceCommitter(existingCommitter);
}
+ Slog.d(TAG, "Error committing the blob", e);
session.sendCommitCallbackResult(COMMIT_RESULT_ERROR);
}
getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid()))
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
index 2b0458303a23..22d5d11e9ccb 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
@@ -28,7 +28,6 @@ import static android.system.OsConstants.O_RDONLY;
import static android.system.OsConstants.O_RDWR;
import static android.system.OsConstants.SEEK_SET;
-import static com.android.server.blob.BlobStoreConfig.LOGV;
import static com.android.server.blob.BlobStoreConfig.TAG;
import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_SESSION_CREATION_TIME;
import static com.android.server.blob.BlobStoreConfig.hasSessionExpired;
@@ -423,9 +422,10 @@ class BlobStoreSession extends IBlobStoreSession.Stub {
mState = STATE_VERIFIED_VALID;
// Commit callback will be sent once the data is persisted.
} else {
- if (LOGV) {
- Slog.v(TAG, "Digest of the data didn't match the given BlobHandle.digest");
- }
+ Slog.d(TAG, "Digest of the data ("
+ + (mDataDigest == null ? "null" : BlobHandle.safeDigest(mDataDigest))
+ + ") didn't match the given BlobHandle.digest ("
+ + BlobHandle.safeDigest(mBlobHandle.digest) + ")");
mState = STATE_VERIFIED_INVALID;
sendCommitCallbackResult(COMMIT_RESULT_ERROR);
}
diff --git a/apex/permission/service/Android.bp b/apex/permission/service/Android.bp
index 61449763540b..7f3187949712 100644
--- a/apex/permission/service/Android.bp
+++ b/apex/permission/service/Android.bp
@@ -20,14 +20,26 @@ filegroup {
path: "java",
}
-java_library {
+java_sdk_library {
name: "service-permission",
+ defaults: ["framework-system-server-module-defaults"],
+ visibility: [
+ "//frameworks/base/services/core",
+ "//frameworks/base/apex/permission",
+ "//frameworks/base/apex/permission/testing",
+ "//frameworks/base/apex/permission/tests",
+ "//frameworks/base/services/tests/mockingservicestests",
+ ],
+ impl_library_visibility: [
+ "//visibility:override",
+ "//frameworks/base/apex/permission/tests",
+ "//frameworks/base/services/tests/mockingservicestests",
+ "//frameworks/base/services/tests/servicestests",
+ ],
srcs: [
":service-permission-sources",
],
- sdk_version: "module_current",
libs: [
- "framework-annotations-lib",
"framework-permission",
],
apex_available: [
@@ -36,28 +48,3 @@ java_library {
],
installable: true,
}
-
-droidstubs {
- name: "service-permission-stubs-srcs",
- srcs: [ ":service-permission-sources" ],
- defaults: ["service-module-stubs-srcs-defaults"],
- check_api: {
- last_released: {
- api_file: ":service-permission.api.system-server.latest",
- removed_api_file: ":service-permission-removed.api.system-server.latest",
- },
- api_lint: {
- new_since: ":service-permission.api.system-server.latest",
- },
- },
- visibility: ["//visibility:private"],
- dist: { dest: "service-permission.txt" },
-}
-
-java_library {
- name: "service-permission-stubs",
- srcs: [":service-permission-stubs-srcs"],
- defaults: ["service-module-stubs-defaults"],
- visibility: ["//frameworks/base/services/core"],
- dist: { dest: "service-permission.jar" },
-}
diff --git a/apex/permission/service/api/current.txt b/apex/permission/service/api/current.txt
index c76cc3275737..d802177e249b 100644
--- a/apex/permission/service/api/current.txt
+++ b/apex/permission/service/api/current.txt
@@ -1,46 +1 @@
// Signature format: 2.0
-package com.android.permission.persistence {
-
- public interface RuntimePermissionsPersistence {
- method @NonNull public static com.android.permission.persistence.RuntimePermissionsPersistence createInstance();
- method public void deleteForUser(@NonNull android.os.UserHandle);
- method @Nullable public com.android.permission.persistence.RuntimePermissionsState readForUser(@NonNull android.os.UserHandle);
- method public void writeForUser(@NonNull com.android.permission.persistence.RuntimePermissionsState, @NonNull android.os.UserHandle);
- }
-
- public final class RuntimePermissionsState {
- ctor public RuntimePermissionsState(int, @Nullable String, @NonNull java.util.Map<java.lang.String,java.util.List<com.android.permission.persistence.RuntimePermissionsState.PermissionState>>, @NonNull java.util.Map<java.lang.String,java.util.List<com.android.permission.persistence.RuntimePermissionsState.PermissionState>>);
- method @Nullable public String getFingerprint();
- method @NonNull public java.util.Map<java.lang.String,java.util.List<com.android.permission.persistence.RuntimePermissionsState.PermissionState>> getPackagePermissions();
- method @NonNull public java.util.Map<java.lang.String,java.util.List<com.android.permission.persistence.RuntimePermissionsState.PermissionState>> getSharedUserPermissions();
- method public int getVersion();
- field public static final int NO_VERSION = -1; // 0xffffffff
- }
-
- public static final class RuntimePermissionsState.PermissionState {
- ctor public RuntimePermissionsState.PermissionState(@NonNull String, boolean, int);
- method public int getFlags();
- method @NonNull public String getName();
- method public boolean isGranted();
- }
-
-}
-
-package com.android.role.persistence {
-
- public interface RolesPersistence {
- method @NonNull public static com.android.role.persistence.RolesPersistence createInstance();
- method public void deleteForUser(@NonNull android.os.UserHandle);
- method @Nullable public com.android.role.persistence.RolesState readForUser(@NonNull android.os.UserHandle);
- method public void writeForUser(@NonNull com.android.role.persistence.RolesState, @NonNull android.os.UserHandle);
- }
-
- public final class RolesState {
- ctor public RolesState(int, @Nullable String, @NonNull java.util.Map<java.lang.String,java.util.Set<java.lang.String>>);
- method @Nullable public String getPackagesHash();
- method @NonNull public java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getRoles();
- method public int getVersion();
- }
-
-}
-
diff --git a/apex/permission/service/api/system-server-current.txt b/apex/permission/service/api/system-server-current.txt
new file mode 100644
index 000000000000..c76cc3275737
--- /dev/null
+++ b/apex/permission/service/api/system-server-current.txt
@@ -0,0 +1,46 @@
+// Signature format: 2.0
+package com.android.permission.persistence {
+
+ public interface RuntimePermissionsPersistence {
+ method @NonNull public static com.android.permission.persistence.RuntimePermissionsPersistence createInstance();
+ method public void deleteForUser(@NonNull android.os.UserHandle);
+ method @Nullable public com.android.permission.persistence.RuntimePermissionsState readForUser(@NonNull android.os.UserHandle);
+ method public void writeForUser(@NonNull com.android.permission.persistence.RuntimePermissionsState, @NonNull android.os.UserHandle);
+ }
+
+ public final class RuntimePermissionsState {
+ ctor public RuntimePermissionsState(int, @Nullable String, @NonNull java.util.Map<java.lang.String,java.util.List<com.android.permission.persistence.RuntimePermissionsState.PermissionState>>, @NonNull java.util.Map<java.lang.String,java.util.List<com.android.permission.persistence.RuntimePermissionsState.PermissionState>>);
+ method @Nullable public String getFingerprint();
+ method @NonNull public java.util.Map<java.lang.String,java.util.List<com.android.permission.persistence.RuntimePermissionsState.PermissionState>> getPackagePermissions();
+ method @NonNull public java.util.Map<java.lang.String,java.util.List<com.android.permission.persistence.RuntimePermissionsState.PermissionState>> getSharedUserPermissions();
+ method public int getVersion();
+ field public static final int NO_VERSION = -1; // 0xffffffff
+ }
+
+ public static final class RuntimePermissionsState.PermissionState {
+ ctor public RuntimePermissionsState.PermissionState(@NonNull String, boolean, int);
+ method public int getFlags();
+ method @NonNull public String getName();
+ method public boolean isGranted();
+ }
+
+}
+
+package com.android.role.persistence {
+
+ public interface RolesPersistence {
+ method @NonNull public static com.android.role.persistence.RolesPersistence createInstance();
+ method public void deleteForUser(@NonNull android.os.UserHandle);
+ method @Nullable public com.android.role.persistence.RolesState readForUser(@NonNull android.os.UserHandle);
+ method public void writeForUser(@NonNull com.android.role.persistence.RolesState, @NonNull android.os.UserHandle);
+ }
+
+ public final class RolesState {
+ ctor public RolesState(int, @Nullable String, @NonNull java.util.Map<java.lang.String,java.util.Set<java.lang.String>>);
+ method @Nullable public String getPackagesHash();
+ method @NonNull public java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getRoles();
+ method public int getVersion();
+ }
+
+}
+
diff --git a/apex/permission/service/api/system-server-removed.txt b/apex/permission/service/api/system-server-removed.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/apex/permission/service/api/system-server-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/permission/tests/Android.bp b/apex/permission/tests/Android.bp
index a1f7a544434c..271e328c1139 100644
--- a/apex/permission/tests/Android.bp
+++ b/apex/permission/tests/Android.bp
@@ -19,7 +19,7 @@ android_test {
"java/**/*.kt",
],
static_libs: [
- "service-permission",
+ "service-permission.impl",
"androidx.test.rules",
"androidx.test.ext.junit",
"androidx.test.ext.truth",
diff --git a/api/test-current.txt b/api/test-current.txt
index 1142fb631891..795d873ec69c 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3323,6 +3323,7 @@ package android.provider {
field public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
field public static final String NOTIFICATION_BADGING = "notification_badging";
field public static final String POWER_MENU_LOCKED_SHOW_CONTENT = "power_menu_locked_show_content";
+ field public static final String SHOW_IME_WITH_HARD_KEYBOARD = "show_ime_with_hard_keyboard";
field @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static final String SYNC_PARENT_SOUNDS = "sync_parent_sounds";
field public static final String USER_SETUP_COMPLETE = "user_setup_complete";
field public static final String VOICE_INTERACTION_SERVICE = "voice_interaction_service";
diff --git a/cmds/idmap2/idmap2/CommandUtils.cpp b/cmds/idmap2/idmap2/CommandUtils.cpp
index e058cd6e7e70..8f5845bf2e53 100644
--- a/cmds/idmap2/idmap2/CommandUtils.cpp
+++ b/cmds/idmap2/idmap2/CommandUtils.cpp
@@ -29,7 +29,7 @@ using android::idmap2::Result;
using android::idmap2::Unit;
Result<Unit> Verify(const std::string& idmap_path, const std::string& target_path,
- const std::string& overlay_path, uint32_t fulfilled_policies,
+ const std::string& overlay_path, PolicyBitmask fulfilled_policies,
bool enforce_overlayable) {
SYSTRACE << "Verify " << idmap_path;
std::ifstream fin(idmap_path);
diff --git a/cmds/idmap2/idmap2/CommandUtils.h b/cmds/idmap2/idmap2/CommandUtils.h
index 99605de30988..e717e046d15d 100644
--- a/cmds/idmap2/idmap2/CommandUtils.h
+++ b/cmds/idmap2/idmap2/CommandUtils.h
@@ -17,12 +17,13 @@
#ifndef IDMAP2_IDMAP2_COMMAND_UTILS_H_
#define IDMAP2_IDMAP2_COMMAND_UTILS_H_
+#include "idmap2/PolicyUtils.h"
#include "idmap2/Result.h"
android::idmap2::Result<android::idmap2::Unit> Verify(const std::string& idmap_path,
const std::string& target_path,
const std::string& overlay_path,
- uint32_t fulfilled_policies,
+ PolicyBitmask fulfilled_policies,
bool enforce_overlayable);
#endif // IDMAP2_IDMAP2_COMMAND_UTILS_H_
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index 908d96612269..f95b73f17222 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -70,12 +70,12 @@ PolicyBitmask ConvertAidlArgToPolicyBitmask(int32_t arg) {
}
Status GetCrc(const std::string& apk_path, uint32_t* out_crc) {
- const auto overlay_zip = ZipFile::Open(apk_path);
- if (!overlay_zip) {
+ const auto zip = ZipFile::Open(apk_path);
+ if (!zip) {
return error(StringPrintf("failed to open apk %s", apk_path.c_str()));
}
- const auto crc = GetPackageCrc(*overlay_zip);
+ const auto crc = GetPackageCrc(*zip);
if (!crc) {
return error(crc.GetErrorMessage());
}
@@ -121,6 +121,7 @@ Status Idmap2Service::verifyIdmap(const std::string& target_apk_path,
bool* _aidl_return) {
SYSTRACE << "Idmap2Service::verifyIdmap " << overlay_apk_path;
assert(_aidl_return);
+
const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
std::ifstream fin(idmap_path);
const std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(fin);
@@ -156,13 +157,10 @@ Status Idmap2Service::verifyIdmap(const std::string& target_apk_path,
auto up_to_date =
header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(), target_crc, overlay_crc,
- fulfilled_policies, enforce_overlayable);
- if (!up_to_date) {
- *_aidl_return = false;
- return error(up_to_date.GetErrorMessage());
- }
+ ConvertAidlArgToPolicyBitmask(fulfilled_policies), enforce_overlayable);
- return ok();
+ *_aidl_return = static_cast<bool>(up_to_date);
+ return *_aidl_return ? ok() : error(up_to_date.GetErrorMessage());
}
Status Idmap2Service::createIdmap(const std::string& target_apk_path,
diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h
index 8f25b8d6a734..0f05592b70f3 100644
--- a/cmds/idmap2/include/idmap2/Idmap.h
+++ b/cmds/idmap2/include/idmap2/Idmap.h
@@ -141,9 +141,9 @@ class IdmapHeader {
// field *must* be incremented. Because of this, we know that if the idmap
// header is up-to-date the entire file is up-to-date.
Result<Unit> IsUpToDate(const char* target_path, const char* overlay_path,
- uint32_t fulfilled_policies, bool enforce_overlayable) const;
+ PolicyBitmask fulfilled_policies, bool enforce_overlayable) const;
Result<Unit> IsUpToDate(const char* target_path, const char* overlay_path, uint32_t target_crc,
- uint32_t overlay_crc, uint32_t fulfilled_policies,
+ uint32_t overlay_crc, PolicyBitmask fulfilled_policies,
bool enforce_overlayable) const;
void accept(Visitor* v) const;
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
index 0bea21735b24..23c25a7089de 100644
--- a/cmds/idmap2/libidmap2/Idmap.cpp
+++ b/cmds/idmap2/libidmap2/Idmap.cpp
@@ -115,8 +115,7 @@ std::unique_ptr<const IdmapHeader> IdmapHeader::FromBinaryStream(std::istream& s
uint8_t enforce_overlayable;
if (!Read32(stream, &idmap_header->magic_) || !Read32(stream, &idmap_header->version_) ||
!Read32(stream, &idmap_header->target_crc_) || !Read32(stream, &idmap_header->overlay_crc_) ||
- !Read32(stream, &idmap_header->fulfilled_policies_) ||
- !Read8(stream, &enforce_overlayable) ||
+ !Read32(stream, &idmap_header->fulfilled_policies_) || !Read8(stream, &enforce_overlayable) ||
!ReadString256(stream, idmap_header->target_path_) ||
!ReadString256(stream, idmap_header->overlay_path_)) {
return nullptr;
@@ -134,7 +133,8 @@ std::unique_ptr<const IdmapHeader> IdmapHeader::FromBinaryStream(std::istream& s
}
Result<Unit> IdmapHeader::IsUpToDate(const char* target_path, const char* overlay_path,
- uint32_t fulfilled_policies, bool enforce_overlayable) const {
+ PolicyBitmask fulfilled_policies,
+ bool enforce_overlayable) const {
const std::unique_ptr<const ZipFile> target_zip = ZipFile::Open(target_path);
if (!target_zip) {
return Error("failed to open target %s", target_path);
@@ -161,7 +161,8 @@ Result<Unit> IdmapHeader::IsUpToDate(const char* target_path, const char* overla
Result<Unit> IdmapHeader::IsUpToDate(const char* target_path, const char* overlay_path,
uint32_t target_crc, uint32_t overlay_crc,
- uint32_t fulfilled_policies, bool enforce_overlayable) const {
+ PolicyBitmask fulfilled_policies,
+ bool enforce_overlayable) const {
if (magic_ != kIdmapMagic) {
return Error("bad magic: actual 0x%08x, expected 0x%08x", magic_, kIdmapMagic);
}
@@ -187,8 +188,7 @@ Result<Unit> IdmapHeader::IsUpToDate(const char* target_path, const char* overla
if (enforce_overlayable != enforce_overlayable_) {
return Error("bad enforce overlayable: idmap version %s, file system version %s",
- enforce_overlayable ? "true" : "false",
- enforce_overlayable_ ? "true" : "false");
+ enforce_overlayable ? "true" : "false", enforce_overlayable_ ? "true" : "false");
}
if (strcmp(target_path, target_path_) != 0) {
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 095dd1e9be59..e7b32c56551a 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -529,7 +529,9 @@ void StatsLogProcessor::OnConfigUpdatedLocked(
VLOG("StatsdConfig valid");
} else {
// If there is any error in the config, don't use it.
+ // Remove any existing config with the same key.
ALOGE("StatsdConfig NOT valid");
+ mMetricsManagers.erase(key);
}
}
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 7090bd46635d..23f2584655b0 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -284,6 +284,7 @@ private:
FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize);
FRIEND_TEST(StatsLogProcessorTest, TestRateLimitBroadcast);
FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge);
+ FRIEND_TEST(StatsLogProcessorTest, InvalidConfigRemoved);
FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead);
FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot);
FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations);
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 052358bb8397..e998711ef3c0 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -193,7 +193,7 @@ message Atom {
BiometricAcquired biometric_acquired = 87 [(module) = "framework"];
BiometricAuthenticated biometric_authenticated = 88 [(module) = "framework"];
BiometricErrorOccurred biometric_error_occurred = 89 [(module) = "framework"];
- UiEventReported ui_event_reported = 90 [(module) = "framework"];
+ UiEventReported ui_event_reported = 90 [(module) = "framework", (module) = "sysui"];
BatteryHealthSnapshot battery_health_snapshot = 91;
SlowIo slow_io = 92;
BatteryCausedShutdown battery_caused_shutdown = 93;
@@ -419,7 +419,7 @@ message Atom {
DisplayJankReported display_jank_reported = 257;
AppStandbyBucketChanged app_standby_bucket_changed = 258 [(module) = "framework"];
SharesheetStarted sharesheet_started = 259 [(module) = "framework"];
- RankingSelected ranking_selected = 260 [(module) = "framework"];
+ RankingSelected ranking_selected = 260 [(module) = "framework", (module) = "sysui"];
TvSettingsUIInteracted tvsettings_ui_interacted = 261 [(module) = "tv_settings"];
LauncherStaticLayout launcher_snapshot = 262 [(module) = "sysui"];
PackageInstallerV2Reported package_installer_v2_reported = 263 [(module) = "framework"];
@@ -465,6 +465,19 @@ message Atom {
288 [(module) = "car"];
CarUserHalSetUserAssociationResponseReported car_user_hal_set_user_association_response_reported =
289 [(module) = "car"];
+ NetworkIpProvisioningReported network_ip_provisioning_reported =
+ 290 [(module) = "network_stack"];
+ NetworkDhcpRenewReported network_dhcp_renew_reported = 291 [(module) = "network_stack"];
+ NetworkValidationReported network_validation_reported = 292 [(module) = "network_stack"];
+ NetworkStackQuirkReported network_stack_quirk_reported = 293 [(module) = "network_stack"];
+ MediametricsAudioRecordDeviceUsageReported mediametrics_audiorecorddeviceusage_reported =
+ 294;
+ MediametricsAudioThreadDeviceUsageReported mediametrics_audiothreaddeviceusage_reported =
+ 295;
+ MediametricsAudioTrackDeviceUsageReported mediametrics_audiotrackdeviceusage_reported =
+ 296;
+ MediametricsAudioDeviceConnectionReported mediametrics_audiodeviceconnection_reported =
+ 297;
// StatsdStats tracks platform atoms with ids upto 500.
// Update StatsdStats::kMaxPushedAtomId when atom ids here approach that value.
@@ -566,6 +579,7 @@ message Atom {
DataUsageBytesTransfer data_usage_bytes_transfer = 10082 [(module) = "framework"];
BytesTransferByTagAndMetered bytes_transfer_by_tag_and_metered =
10083 [(module) = "framework"];
+ DNDModeProto dnd_mode_rule = 10084 [(module) = "framework"];
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -6083,6 +6097,76 @@ message PackageNotificationChannelPreferences {
}
/**
+ * Atom that represents an item in the list of Do Not Disturb rules, pulled from
+ * NotificationManagerService.java.
+ */
+message DNDModeProto {
+ enum Mode {
+ ROOT_CONFIG = -1; // Used to distinguish the config (one per user) from the rules.
+ ZEN_MODE_OFF = 0;
+ ZEN_MODE_IMPORTANT_INTERRUPTIONS = 1;
+ ZEN_MODE_NO_INTERRUPTIONS = 2;
+ ZEN_MODE_ALARMS = 3;
+ }
+ optional int32 user = 1; // Android user ID (0, 1, 10, ...)
+ optional bool enabled = 2; // true for ROOT_CONFIG if a manualRule is enabled
+ optional bool channels_bypassing = 3; // only valid for ROOT_CONFIG
+ optional Mode zen_mode = 4;
+ // id is one of the system default rule IDs, or empty
+ // May also be "MANUAL_RULE" to indicate app-activation of the manual rule.
+ optional string id = 5;
+ optional int32 uid = 6 [(is_uid) = true]; // currently only SYSTEM_UID or 0 for other
+ optional DNDPolicyProto policy = 7;
+}
+
+/**
+ * Atom that represents a Do Not Disturb policy, an optional detail proto for DNDModeProto.
+ */
+message DNDPolicyProto {
+ enum State {
+ STATE_UNSET = 0;
+ STATE_ALLOW = 1;
+ STATE_DISALLOW = 2;
+ }
+ optional State calls = 1;
+ optional State repeat_callers = 2;
+ optional State messages = 3;
+ optional State conversations = 4;
+ optional State reminders = 5;
+ optional State events = 6;
+ optional State alarms = 7;
+ optional State media = 8;
+ optional State system = 9;
+ optional State fullscreen = 10;
+ optional State lights = 11;
+ optional State peek = 12;
+ optional State status_bar = 13;
+ optional State badge = 14;
+ optional State ambient = 15;
+ optional State notification_list = 16;
+
+ enum PeopleType {
+ PEOPLE_UNSET = 0;
+ PEOPLE_ANYONE = 1;
+ PEOPLE_CONTACTS = 2;
+ PEOPLE_STARRED = 3;
+ PEOPLE_NONE = 4;
+ }
+
+ optional PeopleType allow_calls_from = 17;
+ optional PeopleType allow_messages_from = 18;
+
+ enum ConversationType {
+ CONV_UNSET = 0;
+ CONV_ANYONE = 1;
+ CONV_IMPORTANT = 2;
+ CONV_NONE = 3;
+ }
+
+ optional ConversationType allow_conversations_from = 19;
+}
+
+/**
* Atom that contains a list of a package's channel group preferences, pulled from
* NotificationManagerService.java.
*/
@@ -6333,6 +6417,16 @@ message ContentCaptureServiceEvents {
SET_WHITELIST = 3;
SET_DISABLED = 4;
ON_USER_DATA_REMOVED = 5;
+ ON_DATA_SHARE_REQUEST = 6;
+ ACCEPT_DATA_SHARE_REQUEST = 7;
+ REJECT_DATA_SHARE_REQUEST = 8;
+ DATA_SHARE_WRITE_FINISHED = 9;
+ DATA_SHARE_ERROR_IOEXCEPTION = 10;
+ DATA_SHARE_ERROR_EMPTY_DATA = 11;
+ DATA_SHARE_ERROR_CLIENT_PIPE_FAIL = 12;
+ DATA_SHARE_ERROR_SERVICE_PIPE_FAIL = 13;
+ DATA_SHARE_ERROR_CONCURRENT_REQUEST = 14;
+ DATA_SHARE_ERROR_TIMEOUT_INTERRUPTED = 15;
}
optional Event event = 1;
// component/package of content capture service.
@@ -6741,6 +6835,172 @@ message NetworkDnsEventReported {
}
/**
+ * logs the CapportApiData info
+ * Logged from:
+ * packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+ */
+message CapportApiData {
+ // The TTL of the network connection provided by captive portal
+ optional int32 remaining_ttl_secs = 1;
+
+ // The limit traffic data of the network connection provided by captive portal
+ optional int32 remaining_bytes = 2;
+
+ // Is portal url option included in the DHCP packet (Yes, No)
+ optional bool has_portal_url = 3;
+
+ // Is venue info (e.g. store info, maps, flight status) included (Yes, No)
+ optional bool has_venue_info = 4;
+}
+
+/**
+ * logs a network Probe Event
+ * Logged from:
+ * packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+ */
+message ProbeEvent {
+ // The probe type (http or https, or captive portal API...)
+ optional android.stats.connectivity.ProbeType probe_type = 1;
+
+ // The latency in microseconds of the probe event
+ optional int32 latency_micros = 2;
+
+ // The result of the probe event
+ optional android.stats.connectivity.ProbeResult probe_result = 3;
+
+ // The CaptivePortal API info
+ optional CapportApiData capport_api_data = 4;
+}
+
+/**
+ * log each ProbeEvent in ProbeEvents
+ * Logged from:
+ * packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+ */
+message ProbeEvents {
+ // Record probe event during the validation
+ repeated ProbeEvent probe_event = 1;
+}
+
+/**
+ * The DHCP (Dynamic Host Configuration Protocol) session info
+ * Logged from:
+ * packages/modules/NetworkStack/src/android/net/dhcp/DhcpClient.java
+ */
+message DhcpSession {
+ // The DHCP Feature(s) enabled in this session
+ repeated android.stats.connectivity.DhcpFeature used_features = 1;
+
+ // The discover packet (re)transmit count
+ optional int32 discover_count = 2;
+
+ // The request packet (re)transmit count
+ optional int32 request_count = 3;
+
+ // The IPv4 address conflict count
+ // (only be meaningful when duplicate address detection is enabled)
+ optional int32 conflict_count = 4;
+
+ // The DHCP packet parsing error code in this session
+ // (defined in android.net.metrics.DhcpErrorEvent)
+ repeated android.stats.connectivity.DhcpErrorCode error_code = 5;
+
+ // The result of DHCP hostname transliteration
+ optional android.stats.connectivity.HostnameTransResult ht_result = 6;
+}
+
+/**
+ * Logs Network IP provisioning event
+ * Logged from:
+ * packages/modules/NetworkStack/src/com/android/networkstack/metrics/NetworkIpProvisioningMetrics.java
+ */
+message NetworkIpProvisioningReported {
+ // Transport type (WIFI, CELLULAR, BLUETOOTH, ..)
+ optional android.stats.connectivity.TransportType transport_type = 1;
+
+ // The latency in microseconds of IP Provisioning over IPV4
+ optional int32 ipv4_latency_micros = 2;
+
+ // The latency in microseconds of IP Provisioning over IPV6
+ optional int32 ipv6_latency_micros = 3;
+
+ // The time duration between provisioning start and end (success or failure)
+ optional int64 provisioning_duration_micros = 4;
+
+ // The specific disconnect reason for this IP provisioning
+ optional android.stats.connectivity.DisconnectCode disconnect_code = 5;
+
+ // Log DHCP session info (Only valid for IPv4)
+ optional DhcpSession dhcp_session = 6 [(log_mode) = MODE_BYTES];
+
+ // The random number between 0 ~ 999 for sampling
+ optional int32 random_number = 7;
+}
+
+/**
+ * Logs Network DHCP Renew event
+ * Logged from:
+ * packages/modules/NetworkStack/src/android/net/dhcp/DhcpClient.java
+ */
+message NetworkDhcpRenewReported {
+ // Transport type (WIFI, CELLULAR, BLUETOOTH, ..)
+ optional android.stats.connectivity.TransportType transport_type = 1;
+
+ // The request packet (re)transmit count
+ optional int32 request_count = 2;
+
+ // The latency in microseconds of DHCP Renew
+ optional int32 latency_micros = 3;
+
+ // The DHCP error code is defined in android.net.metrics.DhcpErrorEvent
+ optional android.stats.connectivity.DhcpErrorCode error_code = 4;
+
+ // The result of DHCP renew
+ optional android.stats.connectivity.DhcpRenewResult renew_result = 5;
+
+ // The random number between 0 ~ 999 for sampling
+ optional int32 random_number = 6;
+}
+
+/**
+ * Logs Network Validation event
+ * Logged from:
+ * packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+ */
+message NetworkValidationReported {
+ // Transport type (WIFI, CELLULAR, BLUETOOTH, ..)
+ optional android.stats.connectivity.TransportType transport_type = 1;
+
+ // Record each probe event
+ optional ProbeEvents probe_events = 2 [(log_mode) = MODE_BYTES];
+
+ // The result of the network validation
+ optional android.stats.connectivity.ValidationResult validation_result = 3;
+
+ // The latency in microseconds of network validation
+ optional int32 latency_micros = 4;
+
+ // The validation index (the first validation attempt or second, third...)
+ optional int32 validation_index = 5;
+
+ // The random number between 0 ~ 999 for sampling
+ optional int32 random_number = 6;
+}
+
+/**
+ * Logs NetworkStack Quirk event
+ * Logged from:
+ * packages/modules/NetworkStack/src/com/android/networkstack/
+ */
+message NetworkStackQuirkReported {
+ // Transport type (WIFI, CELLULAR, BLUETOOTH, ..)
+ optional android.stats.connectivity.TransportType transport_type = 1;
+
+ // Record each Quirk event
+ optional android.stats.connectivity.NetworkQuirkEvent event = 2;
+}
+
+/**
* Logs when a data stall event occurs.
*
* Log from:
@@ -9748,7 +10008,7 @@ message GnssStats {
optional int64 time_to_first_fix_reports = 3;
// Total pulled reported time to first fix (in milli-seconds) since boot
- optional int64 time_to_first_fix_milli_s = 4;
+ optional int64 time_to_first_fix_millis = 4;
// Number of position accuracy reports since boot
optional int64 position_accuracy_reports = 5;
@@ -10297,3 +10557,237 @@ message AssistantInvocationReported {
// Whether the Assistant handles were showing at the time of invocation.
optional bool assistant_handles_showing = 6;
}
+
+/**
+ * Logs when an AudioRecord finishes running on an audio device
+ *
+ * Logged from:
+ * frameworks/av/services/mediametrics/AudioAnalytics.cpp
+ */
+message MediametricsAudioRecordDeviceUsageReported {
+ // The devices connected to this AudioRecord.
+ // A string OR of various input device categories, e.g. "DEVICE1|DEVICE2".
+ // See lookup<INPUT_DEVICE>() in frameworks/av/services/mediametrics/AudioTypes.cpp
+ // See audio_device_t in system/media/audio/include/system/audio-base.h
+ optional string devices = 1;
+
+ // The name of the remote device attached to the device, typically available for USB or BT.
+ // This may be empty for a fixed device, or separated by "|" if more than one.
+ optional string device_names = 2;
+
+ // The amount of time spent in the device as measured by the active track in AudioFlinger.
+ optional int64 device_time_nanos = 3;
+
+ // The audio data format used for encoding.
+ // An enumeration from system/media/audio/include/system/audio-base.h audio_format_t
+ optional string encoding = 4;
+
+ // The client-server buffer framecount.
+ // The framecount is generally between 960 - 48000 for PCM encoding.
+ // The framecount represents raw buffer size in bytes for non-PCM encoding.
+ optional int32 frame_count = 5;
+
+ // The number of audio intervals (contiguous, continuous playbacks).
+ optional int32 interval_count = 6;
+
+ // The sample rate of the AudioRecord.
+ // A number generally between 8000-96000 (frames per second).
+ optional int32 sample_rate = 7;
+
+ // The audio input flags used to construct the AudioRecord.
+ // A string OR from system/media/audio/include/system/audio-base.h audio_input_flags_t
+ optional string flags = 8;
+
+ // The santized package name of the audio client associated with the AudioRecord.
+ // See getSanitizedPackageNameAndVersionCode() in
+ // frameworks/av/services/mediametrics/MediaMetricsService.cpp
+ optional string package_name = 9;
+
+ // The selected device id (nonzero if a non-default device is selected)
+ optional int32 selected_device_id = 10;
+
+ // The caller of the AudioRecord.
+ // See lookup<CALLER_NAME>() in frameworks/av/services/mediametrics/AudioTypes.cpp
+ optional string caller = 11;
+
+ // The audio source for AudioRecord.
+ // An enumeration from system/media/audio/include/system/audio-base.h audio_source_t
+ optional string source = 12;
+}
+
+/**
+ * Logs when an AudioThread finishes running on an audio device
+ *
+ * Logged from:
+ * frameworks/av/services/mediametrics/AudioAnalytics.cpp
+ */
+message MediametricsAudioThreadDeviceUsageReported {
+ // The devices connected to this audio thread.
+ // A string OR of various input device categories, e.g. "DEVICE1|DEVICE2".
+ // (for record threads):
+ // See lookup<INPUT_DEVICE> in frameworks/av/services/mediametrics/AudioTypes.cpp
+ // (for playback threads):
+ // See lookup<OUTPUT_DEVICE>() in frameworks/av/services/mediametrics/AudioTypes.cpp
+ // See audio_device_t in system/media/audio/include/system/audio-base.h
+ optional string devices = 1;
+
+ // The name of the remote device attached to the device, typically available for USB or BT.
+ // This may be empty for a fixed device, or separated by "|" if more than one.
+ optional string device_names = 2;
+
+ // The amount of time spent in the device as measured by the active track in AudioFlinger.
+ optional int64 device_time_nanos = 3;
+
+ // The audio data format used for encoding.
+ // An enumeration from system/media/audio/include/system/audio-base.h audio_format_t
+ optional string encoding = 4;
+
+ // The framecount of the buffer delivered to (or from) the HAL.
+ // The framecount is generally ~960 for PCM encoding.
+ // The framecount represents raw buffer size in bytes for non-PCM encoding.
+ optional int32 frame_count = 5;
+
+ // The number of audio intervals (contiguous, continuous playbacks).
+ optional int32 interval_count = 6;
+
+ // The sample rate of the audio thread.
+ // A number generally between 8000-96000 (frames per second).
+ optional int32 sample_rate = 7;
+
+ // The audio flags used to construct the thread
+ // (for record threads):
+ // A string OR from system/media/audio/include/system/audio-base.h audio_input_flags_t
+ // (for playback threads):
+ // A string OR from system/media/audio/include/system/audio-base.h audio_output_flags_t
+ optional string flags = 8;
+
+ // The number of underruns encountered for a playback thread or the
+ // number of overruns encountered for a capture thread.
+ optional int32 xruns = 9;
+
+ // The type of thread
+ // A thread type enumeration from
+ // frameworks/av/mediametrics/services/Translate.h
+ optional string type = 10;
+}
+
+/**
+ * Logs when an AudioTrack finishes running on an audio device
+ *
+ * Logged from:
+ * frameworks/av/services/mediametrics/AudioAnalytics.cpp
+ */
+message MediametricsAudioTrackDeviceUsageReported {
+ // The output devices connected to this AudioTrack.
+ // A string OR of various output device categories, e.g. "DEVICE1|DEVICE2".
+ // See lookup<OUTPUT_DEVICE>() in frameworks/av/services/mediametrics/AudioTypes.cpp
+ // See audio_device_t in system/media/audio/include/system/audio-base.h
+ optional string devices = 1;
+
+ // The name of the remote device attached to the device, typically available for USB or BT.
+ // This may be empty for a fixed device, or separated by "|" if more than one.
+ optional string device_names = 2;
+
+ // The amount of time spent in the device as measured by the active track in AudioFlinger.
+ optional int64 device_time_nanos = 3;
+
+ // The audio data format used for encoding.
+ // An enumeration from system/media/audio/include/system/audio-base.h audio_format_t
+ optional string encoding = 4;
+
+ // The client-server buffer framecount.
+ // The framecount is generally between 960 - 48000 for PCM encoding.
+ // The framecount represents raw buffer size in bytes for non-PCM encoding.
+ // A static track (see traits) may have a very large framecount.
+ optional int32 frame_count = 5;
+
+ // The number of audio intervals (contiguous, continuous playbacks).
+ optional int32 interval_count = 6;
+
+ // The sample rate of the AudioTrack.
+ // A number generally between 8000-96000 (frames per second).
+ optional int32 sample_rate = 7;
+
+ // The audio flags used to construct the AudioTrack.
+ // A string OR from system/media/audio/include/system/audio-base.h audio_output_flags_t
+ optional string flags = 8;
+
+ // The number of underruns encountered.
+ optional int32 xruns = 9;
+
+ // The santized package name of the audio client associated with the AudioTrack.
+ // See getSanitizedPackageNameAndVersionCode() in
+ // frameworks/av/services/mediametrics/MediaMetricsService.cpp
+ optional string package_name = 10;
+
+ // The latency of the last sample in the buffer in milliseconds.
+ optional float device_latency_millis = 11;
+
+ // The startup time in milliseconds from start() to sample played.
+ optional float device_startup_millis = 12;
+
+ // The average volume of the track on the device [ 0.f - 1.f ]
+ optional float device_volume = 13;
+
+ // The selected device id (nonzero if a non-default device is selected)
+ optional int32 selected_device_id = 14;
+
+ // The stream_type category for the AudioTrack.
+ // An enumeration from system/media/audio/include/system/audio-base.h audio_stream_type_t
+ optional string stream_type = 15;
+
+ // The usage for the AudioTrack.
+ // An enumeration from system/media/audio/include/system/audio-base.h audio_usage_t
+ optional string usage = 16;
+
+ // The content type of the AudioTrack.
+ // An enumeration from system/media/audio/include/system/audio-base.h audio_content_type_t
+ optional string content_type = 17;
+
+ // The caller of the AudioTrack.
+ // See lookup<CALLER_NAME>() in frameworks/av/services/mediametrics/AudioTypes.cpp
+ optional string caller = 18;
+
+ // The traits of the AudioTrack.
+ // A string OR of different traits, may be empty string.
+ // Only "static" is supported for R.
+ // See lookup<TRACK_TRAITS>() in frameworks/av/services/mediametrics/AudioTypes.cpp
+ optional string traits = 19;
+}
+
+/**
+ * Logs the status of an audio device connection attempt.
+ *
+ * Logged from:
+ * frameworks/av/services/mediametrics/AudioAnalytics.cpp
+ */
+message MediametricsAudioDeviceConnectionReported {
+ // The input devices represented by this report.
+ // A string OR of various input device categories, e.g. "DEVICE1|DEVICE2".
+ // See lookup<INPUT_DEVICE>() in frameworks/av/services/mediametrics/AudioTypes.cpp
+ // See audio_device_t in system/media/audio/include/system/audio-base.h
+ optional string input_devices = 1;
+
+ // The output devices represented by this report.
+ // A string OR of various output device categories.
+ // See lookup<OUTPUT_DEVICE>() in frameworks/av/services/mediametrics/AudioTypes.cpp
+ // See audio_device_t in system/media/audio/include/system/audio-base.h
+ optional string output_devices = 2;
+
+ // The name of the remote device attached to the device, typically available for USB or BT.
+ // This may be empty for a fixed device, or separated by "|" if more than one.
+ optional string device_names = 3;
+
+ // The result of the audio device connection.
+ // 0 indicates success: connection verified.
+ // 1 indicates unknown: connection not verified or not known if diverted properly.
+ // Other values indicate specific status.
+ // See DeviceConnectionResult in frameworks/av/services/mediametrics/AudioTypes.h
+ optional int32 result = 4;
+
+ // Average milliseconds of time to connect
+ optional float time_to_connect_millis = 5;
+
+ // Number of connections if aggregated statistics, otherwise 1.
+ optional int32 connection_count = 6;
+}
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 8587e1452543..9d46dcea1896 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -101,7 +101,7 @@ public:
// Per atom dimension key size limit
static const std::map<int, std::pair<size_t, size_t>> kAtomDimensionKeySizeLimitMap;
- const static int kMaxConfigCountPerUid = 10;
+ const static int kMaxConfigCountPerUid = 20;
const static int kMaxAlertCountPerConfig = 100;
const static int kMaxConditionCountPerConfig = 300;
const static int kMaxMetricCountPerConfig = 1000;
@@ -660,6 +660,8 @@ private:
FRIEND_TEST(StatsdStatsTest, TestAtomMetricsStats);
FRIEND_TEST(StatsdStatsTest, TestActivationBroadcastGuardrailHit);
FRIEND_TEST(StatsdStatsTest, TestAtomErrorStats);
+
+ FRIEND_TEST(StatsLogProcessorTest, InvalidConfigRemoved);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index e8c575a1adea..7e825efddb75 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -361,8 +361,12 @@ void MetricsManager::onDumpReport(const int64_t dumpTimeStampNs,
protoOutput->end(token);
}
- mLastReportTimeNs = dumpTimeStampNs;
- mLastReportWallClockNs = getWallClockNs();
+ // Do not update the timestamps when data is not cleared to avoid timestamps from being
+ // misaligned.
+ if (erase_data) {
+ mLastReportTimeNs = dumpTimeStampNs;
+ mLastReportWallClockNs = getWallClockNs();
+ }
VLOG("=========================Metric Reports End==========================");
}
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 72decf2c7bd0..acdffd3d4712 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -198,6 +198,9 @@ message EventMetric {
optional int64 condition = 3;
repeated MetricConditionLink links = 4;
+
+ reserved 100;
+ reserved 101;
}
message CountMetric {
@@ -218,6 +221,9 @@ message CountMetric {
repeated MetricStateLink state_link = 9;
optional FieldMatcher dimensions_in_condition = 7 [deprecated = true];
+
+ reserved 100;
+ reserved 101;
}
message DurationMetric {
@@ -245,6 +251,9 @@ message DurationMetric {
optional TimeUnit bucket = 7;
optional FieldMatcher dimensions_in_condition = 8 [deprecated = true];
+
+ reserved 100;
+ reserved 101;
}
message GaugeMetric {
@@ -281,6 +290,9 @@ message GaugeMetric {
optional int32 max_pull_delay_sec = 13 [default = 30];
optional bool split_bucket_for_app_upgrade = 14 [default = true];
+
+ reserved 100;
+ reserved 101;
}
message ValueMetric {
@@ -333,6 +345,9 @@ message ValueMetric {
optional bool split_bucket_for_app_upgrade = 17 [default = true];
optional FieldMatcher dimensions_in_condition = 9 [deprecated = true];
+
+ reserved 100;
+ reserved 101;
}
message Alert {
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 076f32752223..13d977fa2563 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -324,6 +324,41 @@ TEST(StatsLogProcessorTest, TestPullUidProviderSetOnConfigUpdate) {
EXPECT_EQ(pullerManager->mPullUidProviders.find(key), pullerManager->mPullUidProviders.end());
}
+TEST(StatsLogProcessorTest, InvalidConfigRemoved) {
+ // Setup simple config key corresponding to empty config.
+ StatsdStats::getInstance().reset();
+ sp<UidMap> m = new UidMap();
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+ m->updateMap(1, {1, 2}, {1, 2}, {String16("v1"), String16("v2")},
+ {String16("p1"), String16("p2")}, {String16(""), String16("")});
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> subscriberAlarmMonitor;
+ StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
+ [](const ConfigKey& key) { return true; },
+ [](const int&, const vector<int64_t>&) {return true;});
+ ConfigKey key(3, 4);
+ StatsdConfig config = MakeConfig(true);
+ p.OnConfigUpdated(0, key, config);
+ EXPECT_EQ(1, p.mMetricsManagers.size());
+ EXPECT_NE(p.mMetricsManagers.find(key), p.mMetricsManagers.end());
+ // Cannot assert the size of mConfigStats since it is static and does not get cleared on reset.
+ EXPECT_NE(StatsdStats::getInstance().mConfigStats.end(),
+ StatsdStats::getInstance().mConfigStats.find(key));
+ EXPECT_EQ(0, StatsdStats::getInstance().mIceBox.size());
+
+ StatsdConfig invalidConfig = MakeConfig(true);
+ invalidConfig.clear_allowed_log_source();
+ p.OnConfigUpdated(0, key, invalidConfig);
+ EXPECT_EQ(0, p.mMetricsManagers.size());
+ // The current configs should not contain the invalid config.
+ EXPECT_EQ(StatsdStats::getInstance().mConfigStats.end(),
+ StatsdStats::getInstance().mConfigStats.find(key));
+ // Both "config" and "invalidConfig" should be in the icebox.
+ EXPECT_EQ(2, StatsdStats::getInstance().mIceBox.size());
+
+}
+
+
TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) {
int uid = 1111;
@@ -1796,6 +1831,48 @@ TEST(StatsLogProcessorTest_mapIsolatedUidToHostUid, LogIsolatedUidAttributionCha
EXPECT_EQ(field2, actualFieldValues->at(5).mValue.int_value);
}
+TEST(StatsLogProcessorTest, TestDumpReportWithoutErasingDataDoesNotUpdateTimestamp) {
+ int hostUid = 20;
+ int isolatedUid = 30;
+ sp<MockUidMap> mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid});
+ ConfigKey key(3, 4);
+ StatsdConfig config = MakeConfig(false);
+ sp<StatsLogProcessor> processor =
+ CreateStatsLogProcessor(1, 1, config, key, nullptr, 0, mockUidMap);
+ vector<uint8_t> bytes;
+
+ int64_t dumpTime1Ns = 1 * NS_PER_SEC;
+ processor->onDumpReport(key, dumpTime1Ns, false /* include_current_bucket */,
+ true /* erase_data */, ADB_DUMP, FAST, &bytes);
+
+ ConfigMetricsReportList output;
+ output.ParseFromArray(bytes.data(), bytes.size());
+ EXPECT_EQ(output.reports_size(), 1);
+ EXPECT_EQ(output.reports(0).current_report_elapsed_nanos(), dumpTime1Ns);
+
+ int64_t dumpTime2Ns = 5 * NS_PER_SEC;
+ processor->onDumpReport(key, dumpTime2Ns, false /* include_current_bucket */,
+ false /* erase_data */, ADB_DUMP, FAST, &bytes);
+
+ // Check that the dump report without clearing data is successful.
+ output.ParseFromArray(bytes.data(), bytes.size());
+ EXPECT_EQ(output.reports_size(), 1);
+ EXPECT_EQ(output.reports(0).current_report_elapsed_nanos(), dumpTime2Ns);
+ EXPECT_EQ(output.reports(0).last_report_elapsed_nanos(), dumpTime1Ns);
+
+ int64_t dumpTime3Ns = 10 * NS_PER_SEC;
+ processor->onDumpReport(key, dumpTime3Ns, false /* include_current_bucket */,
+ true /* erase_data */, ADB_DUMP, FAST, &bytes);
+
+ // Check that the previous dump report that didn't clear data did not overwrite the first dump's
+ // timestamps.
+ output.ParseFromArray(bytes.data(), bytes.size());
+ EXPECT_EQ(output.reports_size(), 1);
+ EXPECT_EQ(output.reports(0).current_report_elapsed_nanos(), dumpTime3Ns);
+ EXPECT_EQ(output.reports(0).last_report_elapsed_nanos(), dumpTime1Ns);
+
+}
+
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index f881616fd35a..0e3f35e358c0 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -7495,6 +7495,7 @@ public class Notification implements Parcelable
mHistoricMessages = Message.getMessagesFromBundleArray(histMessages);
mIsGroupConversation = extras.getBoolean(EXTRA_IS_GROUP_CONVERSATION);
mUnreadMessageCount = extras.getInt(EXTRA_CONVERSATION_UNREAD_MESSAGE_COUNT);
+ mShortcutIcon = extras.getParcelable(EXTRA_CONVERSATION_ICON);
}
/**
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index c65064324c8c..322cac81d58b 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -4247,6 +4247,12 @@ public class DevicePolicyManager {
* device. After this method is called, the device must be unlocked using strong authentication
* (PIN, pattern, or password). This API is intended for use only by device admins.
* <p>
+ * From version {@link android.os.Build.VERSION_CODES#R} onwards, the caller must either have
+ * the LOCK_DEVICE permission or the device must have the device admin feature; if neither is
+ * true, then the method will return without completing any action. Before version
+ * {@link android.os.Build.VERSION_CODES#R}, the device needed the device admin feature,
+ * regardless of the caller's permissions.
+ * <p>
* The calling device admin must have requested {@link DeviceAdminInfo#USES_POLICY_FORCE_LOCK}
* to be able to call this method; if it has not, a security exception will be thrown.
* <p>
@@ -4274,6 +4280,12 @@ public class DevicePolicyManager {
* device. After this method is called, the device must be unlocked using strong authentication
* (PIN, pattern, or password). This API is intended for use only by device admins.
* <p>
+ * From version {@link android.os.Build.VERSION_CODES#R} onwards, the caller must either have
+ * the LOCK_DEVICE permission or the device must have the device admin feature; if neither is
+ * true, then the method will return without completing any action. Before version
+ * {@link android.os.Build.VERSION_CODES#R}, the device needed the device admin feature,
+ * regardless of the caller's permissions.
+ * <p>
* The calling device admin must have requested {@link DeviceAdminInfo#USES_POLICY_FORCE_LOCK}
* to be able to call this method; if it has not, a security exception will be thrown.
* <p>
@@ -5868,12 +5880,22 @@ public class DevicePolicyManager {
* returned by {@link #getParentProfileInstance(ComponentName)}, where the caller must be
* the profile owner of an organization-owned managed profile.
* <p>
- * If the caller is device owner or called on the parent instance, then the
- * restriction will be applied to all users.
+ * If the caller is device owner, then the restriction will be applied to all users. If
+ * called on the parent instance, then the restriction will be applied on the personal profile.
* <p>
* The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_DISABLE_CAMERA} to be able to call this method; if it has
* not, a security exception will be thrown.
+ * <p>
+ * <b>Note</b>, this policy type is deprecated for legacy device admins since
+ * {@link android.os.Build.VERSION_CODES#Q}. On Android
+ * {@link android.os.Build.VERSION_CODES#Q} devices, legacy device admins targeting SDK
+ * version {@link android.os.Build.VERSION_CODES#P} or below can still call this API to
+ * disable camera, while legacy device admins targeting SDK version
+ * {@link android.os.Build.VERSION_CODES#Q} will receive a SecurityException. Starting
+ * from Android {@link android.os.Build.VERSION_CODES#R}, requests to disable camera from
+ * legacy device admins targeting SDK version {@link android.os.Build.VERSION_CODES#P} or
+ * below will be silently ignored.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param disabled Whether or not the camera should be disabled.
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 0f999ad68a62..3522b1b8aff5 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -133,6 +133,7 @@ public final class UsageEvents implements Parcelable {
/**
* An event type denoting that a component was in the foreground when the stats
* rolled-over. This is effectively treated as a {@link #ACTIVITY_PAUSED}.
+ * This event has a non-null packageName, and a null className.
* {@hide}
*/
public static final int END_OF_DAY = 3;
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index bd1ee27ece9e..1a694b34474a 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -1243,14 +1243,7 @@ public class LauncherApps {
private ParcelFileDescriptor getUriShortcutIconFd(@NonNull String packageName,
@NonNull String shortcutId, int userId) {
- String uri = null;
- try {
- uri = mService.getShortcutIconUri(mContext.getPackageName(), packageName, shortcutId,
- userId);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
-
+ String uri = getShortcutIconUri(packageName, shortcutId, userId);
if (uri == null) {
return null;
}
@@ -1262,6 +1255,18 @@ public class LauncherApps {
}
}
+ private String getShortcutIconUri(@NonNull String packageName,
+ @NonNull String shortcutId, int userId) {
+ String uri = null;
+ try {
+ uri = mService.getShortcutIconUri(mContext.getPackageName(), packageName, shortcutId,
+ userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ return uri;
+ }
+
/**
* Returns the icon for this shortcut, without any badging for the profile.
*
@@ -1357,6 +1362,17 @@ public class LauncherApps {
} catch (IOException ignore) {
}
}
+ } else if (shortcut.hasIconUri()) {
+ String uri = getShortcutIconUri(shortcut.getPackage(), shortcut.getId(),
+ shortcut.getUserId());
+ if (uri == null) {
+ return null;
+ }
+ if (shortcut.hasAdaptiveBitmap()) {
+ return Icon.createWithAdaptiveBitmapContentUri(uri);
+ } else {
+ return Icon.createWithContentUri(uri);
+ }
} else if (shortcut.hasIconResource()) {
return Icon.createWithResource(shortcut.getPackage(), shortcut.getIconResourceId());
} else {
diff --git a/core/java/android/content/pm/PackagePartitions.java b/core/java/android/content/pm/PackagePartitions.java
index 653b9ec9e8f2..98a20f73a120 100644
--- a/core/java/android/content/pm/PackagePartitions.java
+++ b/core/java/android/content/pm/PackagePartitions.java
@@ -183,17 +183,20 @@ public class PackagePartitions {
/** Returns whether the partition contains the specified file in its priv-app folder. */
public boolean containsPrivApp(@NonNull File scanFile) {
- return FileUtils.contains(mPrivAppFolder.getFile(), canonicalize(scanFile));
+ return mPrivAppFolder != null
+ && FileUtils.contains(mPrivAppFolder.getFile(), canonicalize(scanFile));
}
/** Returns whether the partition contains the specified file in its app folder. */
public boolean containsApp(@NonNull File scanFile) {
- return FileUtils.contains(mAppFolder.getFile(), canonicalize(scanFile));
+ return mAppFolder != null
+ && FileUtils.contains(mAppFolder.getFile(), canonicalize(scanFile));
}
/** Returns whether the partition contains the specified file in its overlay folder. */
public boolean containsOverlay(@NonNull File scanFile) {
- return FileUtils.contains(mOverlayFolder.getFile(), canonicalize(scanFile));
+ return mOverlayFolder != null
+ && FileUtils.contains(mOverlayFolder.getFile(), canonicalize(scanFile));
}
}
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 6bbc37a90fae..7f834afd7b30 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -586,13 +586,27 @@ public final class CameraManager {
* priority when accessing the camera, and this method will succeed even if the camera device is
* in use by another camera API client. Any lower-priority application that loses control of the
* camera in this way will receive an
- * {@link android.hardware.camera2.CameraDevice.StateCallback#onDisconnected} callback.</p>
+ * {@link android.hardware.camera2.CameraDevice.StateCallback#onDisconnected} callback.
+ * Opening the same camera ID twice in the same application will similarly cause the
+ * {@link android.hardware.camera2.CameraDevice.StateCallback#onDisconnected} callback
+ * being fired for the {@link CameraDevice} from the first open call and all ongoing tasks
+ * being droppped.</p>
*
* <p>Once the camera is successfully opened, {@link CameraDevice.StateCallback#onOpened} will
* be invoked with the newly opened {@link CameraDevice}. The camera device can then be set up
* for operation by calling {@link CameraDevice#createCaptureSession} and
* {@link CameraDevice#createCaptureRequest}</p>
*
+ * <p>Before API level 30, when the application tries to open multiple {@link CameraDevice} of
+ * different IDs and the device does not support opening such combination, either the
+ * {@link #openCamera} will fail and throw a {@link CameraAccessException} or one or more of
+ * already opened {@link CameraDevice} will be disconnected and receive
+ * {@link android.hardware.camera2.CameraDevice.StateCallback#onDisconnected} callback. Which
+ * behavior will happen depends on the device implementation and can vary on different devices.
+ * Starting in API level 30, if the device does not support the combination of cameras being
+ * opened, it is guaranteed the {@link #openCamera} call will fail and none of existing
+ * {@link CameraDevice} will be disconnected.</p>
+ *
* <!--
* <p>Since the camera device will be opened asynchronously, any asynchronous operations done
* on the returned CameraDevice instance will be queued up until the device startup has
@@ -618,7 +632,8 @@ public final class CameraManager {
* {@code null} to use the current thread's {@link android.os.Looper looper}.
*
* @throws CameraAccessException if the camera is disabled by device policy,
- * has been disconnected, or is being used by a higher-priority camera API client.
+ * has been disconnected, is being used by a higher-priority camera API client, or the device
+ * has reached its maximal resource and cannot open this camera device.
*
* @throws IllegalArgumentException if cameraId or the callback was null,
* or the cameraId does not match any currently or previously available
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 23c86029f3be..6d49add65c5b 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -1072,7 +1072,7 @@ public class CameraDeviceImpl extends CameraDevice
* @param lastFrameNumber last frame number returned from binder.
* @param repeatingRequestTypes the repeating requests' types.
*/
- private void checkEarlyTriggerSequenceCompleteLocked(
+ private void checkEarlyTriggerSequenceComplete(
final int requestId, final long lastFrameNumber,
final int[] repeatingRequestTypes) {
// lastFrameNumber being equal to NO_FRAMES_CAPTURED means that the request
@@ -1212,7 +1212,7 @@ public class CameraDeviceImpl extends CameraDevice
if (repeating) {
if (mRepeatingRequestId != REQUEST_ID_NONE) {
- checkEarlyTriggerSequenceCompleteLocked(mRepeatingRequestId,
+ checkEarlyTriggerSequenceComplete(mRepeatingRequestId,
requestInfo.getLastFrameNumber(),
mRepeatingRequestTypes);
}
@@ -1269,7 +1269,7 @@ public class CameraDeviceImpl extends CameraDevice
return;
}
- checkEarlyTriggerSequenceCompleteLocked(requestId, lastFrameNumber, requestTypes);
+ checkEarlyTriggerSequenceComplete(requestId, lastFrameNumber, requestTypes);
}
}
}
@@ -1302,7 +1302,7 @@ public class CameraDeviceImpl extends CameraDevice
long lastFrameNumber = mRemoteDevice.flush();
if (mRepeatingRequestId != REQUEST_ID_NONE) {
- checkEarlyTriggerSequenceCompleteLocked(mRepeatingRequestId, lastFrameNumber,
+ checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber,
mRepeatingRequestTypes);
mRepeatingRequestId = REQUEST_ID_NONE;
mRepeatingRequestTypes = null;
@@ -1442,135 +1442,78 @@ public class CameraDeviceImpl extends CameraDevice
long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber();
long completedReprocessFrameNumber = mFrameNumberTracker.getCompletedReprocessFrameNumber();
long completedZslStillFrameNumber = mFrameNumberTracker.getCompletedZslStillFrameNumber();
-
+ boolean isReprocess = false;
Iterator<RequestLastFrameNumbersHolder> iter = mRequestLastFrameNumbersList.iterator();
while (iter.hasNext()) {
final RequestLastFrameNumbersHolder requestLastFrameNumbers = iter.next();
+ boolean sequenceCompleted = false;
final int requestId = requestLastFrameNumbers.getRequestId();
final CaptureCallbackHolder holder;
- if (mRemoteDevice == null) {
- Log.w(TAG, "Camera closed while checking sequences");
- return;
- }
- if (!requestLastFrameNumbers.isSequenceCompleted()) {
- long lastRegularFrameNumber =
- requestLastFrameNumbers.getLastRegularFrameNumber();
- long lastReprocessFrameNumber =
- requestLastFrameNumbers.getLastReprocessFrameNumber();
- long lastZslStillFrameNumber =
- requestLastFrameNumbers.getLastZslStillFrameNumber();
- if (lastRegularFrameNumber <= completedFrameNumber
- && lastReprocessFrameNumber <= completedReprocessFrameNumber
- && lastZslStillFrameNumber <= completedZslStillFrameNumber) {
- Log.v(TAG, String.format(
- "Mark requestId %d as completed, because lastRegularFrame %d "
- + "is <= %d, lastReprocessFrame %d is <= %d, "
- + "lastZslStillFrame %d is <= %d", requestId,
- lastRegularFrameNumber, completedFrameNumber,
- lastReprocessFrameNumber, completedReprocessFrameNumber,
- lastZslStillFrameNumber, completedZslStillFrameNumber));
- requestLastFrameNumbers.markSequenceCompleted();
+ synchronized(mInterfaceLock) {
+ if (mRemoteDevice == null) {
+ Log.w(TAG, "Camera closed while checking sequences");
+ return;
}
- // Call onCaptureSequenceCompleted
int index = mCaptureCallbackMap.indexOfKey(requestId);
holder = (index >= 0) ?
mCaptureCallbackMap.valueAt(index) : null;
- if (holder != null && requestLastFrameNumbers.isSequenceCompleted()) {
- Runnable resultDispatch = new Runnable() {
- @Override
- public void run() {
- if (!CameraDeviceImpl.this.isClosed()){
- if (DEBUG) {
- Log.d(TAG, String.format(
- "fire sequence complete for request %d",
- requestId));
- }
-
- holder.getCallback().onCaptureSequenceCompleted(
- CameraDeviceImpl.this,
- requestId,
- requestLastFrameNumbers.getLastFrameNumber());
- }
+ if (holder != null) {
+ long lastRegularFrameNumber =
+ requestLastFrameNumbers.getLastRegularFrameNumber();
+ long lastReprocessFrameNumber =
+ requestLastFrameNumbers.getLastReprocessFrameNumber();
+ long lastZslStillFrameNumber =
+ requestLastFrameNumbers.getLastZslStillFrameNumber();
+ // check if it's okay to remove request from mCaptureCallbackMap
+ if (lastRegularFrameNumber <= completedFrameNumber
+ && lastReprocessFrameNumber <= completedReprocessFrameNumber
+ && lastZslStillFrameNumber <= completedZslStillFrameNumber) {
+ sequenceCompleted = true;
+ mCaptureCallbackMap.removeAt(index);
+ if (DEBUG) {
+ Log.v(TAG, String.format(
+ "Remove holder for requestId %d, because lastRegularFrame %d "
+ + "is <= %d, lastReprocessFrame %d is <= %d, "
+ + "lastZslStillFrame %d is <= %d", requestId,
+ lastRegularFrameNumber, completedFrameNumber,
+ lastReprocessFrameNumber, completedReprocessFrameNumber,
+ lastZslStillFrameNumber, completedZslStillFrameNumber));
}
- };
- final long ident = Binder.clearCallingIdentity();
- try {
- holder.getExecutor().execute(resultDispatch);
- } finally {
- Binder.restoreCallingIdentity(ident);
}
}
}
- if (requestLastFrameNumbers.isSequenceCompleted() &&
- requestLastFrameNumbers.isInflightCompleted()) {
- int index = mCaptureCallbackMap.indexOfKey(requestId);
- if (index >= 0) {
- mCaptureCallbackMap.removeAt(index);
- }
- if (DEBUG) {
- Log.v(TAG, String.format(
- "Remove holder for requestId %d", requestId));
- }
+ // If no callback is registered for this requestId or sequence completed, remove it
+ // from the frame number->request pair because it's not needed anymore.
+ if (holder == null || sequenceCompleted) {
iter.remove();
}
- }
- }
-
- private void removeCompletedCallbackHolderLocked(long lastCompletedRegularFrameNumber,
- long lastCompletedReprocessFrameNumber, long lastCompletedZslStillFrameNumber) {
- if (DEBUG) {
- Log.v(TAG, String.format("remove completed callback holders for "
- + "lastCompletedRegularFrameNumber %d, "
- + "lastCompletedReprocessFrameNumber %d, "
- + "lastCompletedZslStillFrameNumber %d",
- lastCompletedRegularFrameNumber,
- lastCompletedReprocessFrameNumber,
- lastCompletedZslStillFrameNumber));
- }
-
- Iterator<RequestLastFrameNumbersHolder> iter = mRequestLastFrameNumbersList.iterator();
- while (iter.hasNext()) {
- final RequestLastFrameNumbersHolder requestLastFrameNumbers = iter.next();
- final int requestId = requestLastFrameNumbers.getRequestId();
- final CaptureCallbackHolder holder;
- if (mRemoteDevice == null) {
- Log.w(TAG, "Camera closed while removing completed callback holders");
- return;
- }
- long lastRegularFrameNumber =
- requestLastFrameNumbers.getLastRegularFrameNumber();
- long lastReprocessFrameNumber =
- requestLastFrameNumbers.getLastReprocessFrameNumber();
- long lastZslStillFrameNumber =
- requestLastFrameNumbers.getLastZslStillFrameNumber();
-
- if (lastRegularFrameNumber <= lastCompletedRegularFrameNumber
- && lastReprocessFrameNumber <= lastCompletedReprocessFrameNumber
- && lastZslStillFrameNumber <= lastCompletedZslStillFrameNumber) {
+ // Call onCaptureSequenceCompleted
+ if (sequenceCompleted) {
+ Runnable resultDispatch = new Runnable() {
+ @Override
+ public void run() {
+ if (!CameraDeviceImpl.this.isClosed()){
+ if (DEBUG) {
+ Log.d(TAG, String.format(
+ "fire sequence complete for request %d",
+ requestId));
+ }
- if (requestLastFrameNumbers.isSequenceCompleted()) {
- int index = mCaptureCallbackMap.indexOfKey(requestId);
- if (index >= 0) {
- mCaptureCallbackMap.removeAt(index);
- }
- if (DEBUG) {
- Log.v(TAG, String.format(
- "Remove holder for requestId %d, because lastRegularFrame %d "
- + "is <= %d, lastReprocessFrame %d is <= %d, "
- + "lastZslStillFrame %d is <= %d", requestId,
- lastRegularFrameNumber, lastCompletedRegularFrameNumber,
- lastReprocessFrameNumber, lastCompletedReprocessFrameNumber,
- lastZslStillFrameNumber, lastCompletedZslStillFrameNumber));
- }
- iter.remove();
- } else {
- if (DEBUG) {
- Log.v(TAG, "Sequence not yet completed for request id " + requestId);
+ holder.getCallback().onCaptureSequenceCompleted(
+ CameraDeviceImpl.this,
+ requestId,
+ requestLastFrameNumbers.getLastFrameNumber());
+ }
}
- requestLastFrameNumbers.markInflightCompleted();
+ };
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ holder.getExecutor().execute(resultDispatch);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
}
}
@@ -1759,12 +1702,6 @@ public class CameraDeviceImpl extends CameraDevice
return;
}
- // Remove all capture callbacks now that device has gone to IDLE state.
- removeCompletedCallbackHolderLocked(
- Long.MAX_VALUE, /*lastCompletedRegularFrameNumber*/
- Long.MAX_VALUE, /*lastCompletedReprocessFrameNumber*/
- Long.MAX_VALUE /*lastCompletedZslStillFrameNumber*/);
-
if (!CameraDeviceImpl.this.mIdle) {
final long ident = Binder.clearCallingIdentity();
try {
@@ -1810,7 +1747,7 @@ public class CameraDeviceImpl extends CameraDevice
return;
}
- checkEarlyTriggerSequenceCompleteLocked(mRepeatingRequestId, lastFrameNumber,
+ checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber,
mRepeatingRequestTypes);
// Check if there is already a new repeating request
if (mRepeatingRequestId == repeatingRequestId) {
@@ -1829,18 +1766,9 @@ public class CameraDeviceImpl extends CameraDevice
public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) {
int requestId = resultExtras.getRequestId();
final long frameNumber = resultExtras.getFrameNumber();
- final long lastCompletedRegularFrameNumber =
- resultExtras.getLastCompletedRegularFrameNumber();
- final long lastCompletedReprocessFrameNumber =
- resultExtras.getLastCompletedReprocessFrameNumber();
- final long lastCompletedZslFrameNumber =
- resultExtras.getLastCompletedZslFrameNumber();
if (DEBUG) {
- Log.d(TAG, "Capture started for id " + requestId + " frame number " + frameNumber
- + ": completedRegularFrameNumber " + lastCompletedRegularFrameNumber
- + ", completedReprocessFrameNUmber " + lastCompletedReprocessFrameNumber
- + ", completedZslFrameNumber " + lastCompletedZslFrameNumber);
+ Log.d(TAG, "Capture started for id " + requestId + " frame number " + frameNumber);
}
final CaptureCallbackHolder holder;
@@ -1856,12 +1784,6 @@ public class CameraDeviceImpl extends CameraDevice
return;
}
- // Check if it's okay to remove completed callbacks from mCaptureCallbackMap.
- // A callback is completed if the corresponding inflight request has been removed
- // from the inflight queue in cameraservice.
- removeCompletedCallbackHolderLocked(lastCompletedRegularFrameNumber,
- lastCompletedReprocessFrameNumber, lastCompletedZslFrameNumber);
-
// Get the callback for this frame ID, if there is one
holder = CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId);
diff --git a/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java
index 413caf5e22e0..1d9d644c9306 100644
--- a/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java
@@ -182,12 +182,6 @@ public class CameraOfflineSessionImpl extends CameraOfflineSession
return;
}
- // Remove all capture callbacks now that device has gone to IDLE state.
- removeCompletedCallbackHolderLocked(
- Long.MAX_VALUE, /*lastCompletedRegularFrameNumber*/
- Long.MAX_VALUE, /*lastCompletedReprocessFrameNumber*/
- Long.MAX_VALUE /*lastCompletedZslStillFrameNumber*/);
-
Runnable idleDispatch = new Runnable() {
@Override
public void run() {
@@ -210,22 +204,10 @@ public class CameraOfflineSessionImpl extends CameraOfflineSession
public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) {
int requestId = resultExtras.getRequestId();
final long frameNumber = resultExtras.getFrameNumber();
- final long lastCompletedRegularFrameNumber =
- resultExtras.getLastCompletedRegularFrameNumber();
- final long lastCompletedReprocessFrameNumber =
- resultExtras.getLastCompletedReprocessFrameNumber();
- final long lastCompletedZslFrameNumber =
- resultExtras.getLastCompletedZslFrameNumber();
final CaptureCallbackHolder holder;
synchronized(mInterfaceLock) {
- // Check if it's okay to remove completed callbacks from mCaptureCallbackMap.
- // A callback is completed if the corresponding inflight request has been removed
- // from the inflight queue in cameraservice.
- removeCompletedCallbackHolderLocked(lastCompletedRegularFrameNumber,
- lastCompletedReprocessFrameNumber, lastCompletedZslFrameNumber);
-
// Get the callback for this frame ID, if there is one
holder = CameraOfflineSessionImpl.this.mCaptureCallbackMap.get(requestId);
@@ -619,61 +601,6 @@ public class CameraOfflineSessionImpl extends CameraOfflineSession
}
}
- private void removeCompletedCallbackHolderLocked(long lastCompletedRegularFrameNumber,
- long lastCompletedReprocessFrameNumber, long lastCompletedZslStillFrameNumber) {
- if (DEBUG) {
- Log.v(TAG, String.format("remove completed callback holders for "
- + "lastCompletedRegularFrameNumber %d, "
- + "lastCompletedReprocessFrameNumber %d, "
- + "lastCompletedZslStillFrameNumber %d",
- lastCompletedRegularFrameNumber,
- lastCompletedReprocessFrameNumber,
- lastCompletedZslStillFrameNumber));
- }
-
- boolean isReprocess = false;
- Iterator<RequestLastFrameNumbersHolder> iter =
- mOfflineRequestLastFrameNumbersList.iterator();
- while (iter.hasNext()) {
- final RequestLastFrameNumbersHolder requestLastFrameNumbers = iter.next();
- final int requestId = requestLastFrameNumbers.getRequestId();
- final CaptureCallbackHolder holder;
-
- int index = mCaptureCallbackMap.indexOfKey(requestId);
- holder = (index >= 0) ?
- mCaptureCallbackMap.valueAt(index) : null;
- if (holder != null) {
- long lastRegularFrameNumber =
- requestLastFrameNumbers.getLastRegularFrameNumber();
- long lastReprocessFrameNumber =
- requestLastFrameNumbers.getLastReprocessFrameNumber();
- long lastZslStillFrameNumber =
- requestLastFrameNumbers.getLastZslStillFrameNumber();
- if (lastRegularFrameNumber <= lastCompletedRegularFrameNumber
- && lastReprocessFrameNumber <= lastCompletedReprocessFrameNumber
- && lastZslStillFrameNumber <= lastCompletedZslStillFrameNumber) {
- if (requestLastFrameNumbers.isSequenceCompleted()) {
- mCaptureCallbackMap.removeAt(index);
- if (DEBUG) {
- Log.v(TAG, String.format(
- "Remove holder for requestId %d, because lastRegularFrame %d "
- + "is <= %d, lastReprocessFrame %d is <= %d, "
- + "lastZslStillFrame %d is <= %d", requestId,
- lastRegularFrameNumber, lastCompletedRegularFrameNumber,
- lastReprocessFrameNumber, lastCompletedReprocessFrameNumber,
- lastZslStillFrameNumber, lastCompletedZslStillFrameNumber));
- }
-
- iter.remove();
- } else {
- Log.e(TAG, "Sequence not yet completed for request id " + requestId);
- continue;
- }
- }
- }
- }
- }
-
public void notifyFailedSwitch() {
synchronized(mInterfaceLock) {
Runnable switchFailDispatch = new Runnable() {
diff --git a/core/java/android/hardware/camera2/impl/CaptureResultExtras.java b/core/java/android/hardware/camera2/impl/CaptureResultExtras.java
index 5d9da73fd5c0..1ff5bd562f2e 100644
--- a/core/java/android/hardware/camera2/impl/CaptureResultExtras.java
+++ b/core/java/android/hardware/camera2/impl/CaptureResultExtras.java
@@ -30,9 +30,6 @@ public class CaptureResultExtras implements Parcelable {
private int partialResultCount;
private int errorStreamId;
private String errorPhysicalCameraId;
- private long lastCompletedRegularFrameNumber;
- private long lastCompletedReprocessFrameNumber;
- private long lastCompletedZslFrameNumber;
public static final @android.annotation.NonNull Parcelable.Creator<CaptureResultExtras> CREATOR =
new Parcelable.Creator<CaptureResultExtras>() {
@@ -54,9 +51,7 @@ public class CaptureResultExtras implements Parcelable {
public CaptureResultExtras(int requestId, int subsequenceId, int afTriggerId,
int precaptureTriggerId, long frameNumber,
int partialResultCount, int errorStreamId,
- String errorPhysicalCameraId, long lastCompletedRegularFrameNumber,
- long lastCompletedReprocessFrameNumber,
- long lastCompletedZslFrameNumber) {
+ String errorPhysicalCameraId) {
this.requestId = requestId;
this.subsequenceId = subsequenceId;
this.afTriggerId = afTriggerId;
@@ -65,9 +60,6 @@ public class CaptureResultExtras implements Parcelable {
this.partialResultCount = partialResultCount;
this.errorStreamId = errorStreamId;
this.errorPhysicalCameraId = errorPhysicalCameraId;
- this.lastCompletedRegularFrameNumber = lastCompletedRegularFrameNumber;
- this.lastCompletedReprocessFrameNumber = lastCompletedReprocessFrameNumber;
- this.lastCompletedZslFrameNumber = lastCompletedZslFrameNumber;
}
@Override
@@ -90,9 +82,6 @@ public class CaptureResultExtras implements Parcelable {
} else {
dest.writeBoolean(false);
}
- dest.writeLong(lastCompletedRegularFrameNumber);
- dest.writeLong(lastCompletedReprocessFrameNumber);
- dest.writeLong(lastCompletedZslFrameNumber);
}
public void readFromParcel(Parcel in) {
@@ -107,9 +96,6 @@ public class CaptureResultExtras implements Parcelable {
if (errorPhysicalCameraIdPresent) {
errorPhysicalCameraId = in.readString();
}
- lastCompletedRegularFrameNumber = in.readLong();
- lastCompletedReprocessFrameNumber = in.readLong();
- lastCompletedZslFrameNumber = in.readLong();
}
public String getErrorPhysicalCameraId() {
@@ -143,16 +129,4 @@ public class CaptureResultExtras implements Parcelable {
public int getErrorStreamId() {
return errorStreamId;
}
-
- public long getLastCompletedRegularFrameNumber() {
- return lastCompletedRegularFrameNumber;
- }
-
- public long getLastCompletedReprocessFrameNumber() {
- return lastCompletedReprocessFrameNumber;
- }
-
- public long getLastCompletedZslFrameNumber() {
- return lastCompletedZslFrameNumber;
- }
}
diff --git a/core/java/android/hardware/camera2/impl/RequestLastFrameNumbersHolder.java b/core/java/android/hardware/camera2/impl/RequestLastFrameNumbersHolder.java
index 0ee4ebc1aa87..bd1df9e1ac7d 100644
--- a/core/java/android/hardware/camera2/impl/RequestLastFrameNumbersHolder.java
+++ b/core/java/android/hardware/camera2/impl/RequestLastFrameNumbersHolder.java
@@ -38,10 +38,6 @@ public class RequestLastFrameNumbersHolder {
// The last ZSL still capture frame number for this request ID. It's
// CaptureCallback.NO_FRAMES_CAPTURED if the request ID has no zsl request.
private final long mLastZslStillFrameNumber;
- // Whether the sequence is completed. (only consider capture result)
- private boolean mSequenceCompleted;
- // Whether the inflight request is completed. (consider result, buffers, and notifies)
- private boolean mInflightCompleted;
/**
* Create a request-last-frame-numbers holder with a list of requests, request ID, and
@@ -93,8 +89,6 @@ public class RequestLastFrameNumbersHolder {
mLastReprocessFrameNumber = lastReprocessFrameNumber;
mLastZslStillFrameNumber = lastZslStillFrameNumber;
mRequestId = requestInfo.getRequestId();
- mSequenceCompleted = false;
- mInflightCompleted = false;
}
/**
@@ -143,8 +137,6 @@ public class RequestLastFrameNumbersHolder {
mLastZslStillFrameNumber = lastZslStillFrameNumber;
mLastReprocessFrameNumber = CameraCaptureSession.CaptureCallback.NO_FRAMES_CAPTURED;
mRequestId = requestId;
- mSequenceCompleted = false;
- mInflightCompleted = false;
}
/**
@@ -185,34 +177,5 @@ public class RequestLastFrameNumbersHolder {
public int getRequestId() {
return mRequestId;
}
-
- /**
- * Return whether the capture sequence is completed.
- */
- public boolean isSequenceCompleted() {
- return mSequenceCompleted;
- }
-
- /**
- * Mark the capture sequence as completed.
- */
- public void markSequenceCompleted() {
- mSequenceCompleted = true;
- }
-
- /**
- * Return whether the inflight capture is completed.
- */
- public boolean isInflightCompleted() {
- return mInflightCompleted;
- }
-
- /**
- * Mark the inflight capture as completed.
- */
- public void markInflightCompleted() {
- mInflightCompleted = true;
- }
-
}
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index fdd578c419d8..fbc9ac3229c3 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -109,12 +109,11 @@ public class LegacyCameraDevice implements AutoCloseable {
}
if (holder == null) {
return new CaptureResultExtras(ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE,
- ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE, null,
- ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE);
+ ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE, null);
}
return new CaptureResultExtras(holder.getRequestId(), holder.getSubsequeceId(),
/*afTriggerId*/0, /*precaptureTriggerId*/0, holder.getFrameNumber(),
- /*partialResultCount*/1, errorStreamId, null, holder.getFrameNumber(), -1, -1);
+ /*partialResultCount*/1, errorStreamId, null);
}
/**
diff --git a/core/java/android/hardware/soundtrigger/ConversionUtil.java b/core/java/android/hardware/soundtrigger/ConversionUtil.java
index 425218a63cf7..c4d123ca4382 100644
--- a/core/java/android/hardware/soundtrigger/ConversionUtil.java
+++ b/core/java/android/hardware/soundtrigger/ConversionUtil.java
@@ -195,11 +195,14 @@ class ConversionUtil {
public static SoundTrigger.RecognitionEvent aidl2apiRecognitionEvent(
int modelHandle, RecognitionEvent aidlEvent) {
+ // The API recognition event doesn't allow for a null audio format, even though it doesn't
+ // always make sense. We thus replace it with a default.
+ AudioFormat audioFormat = aidl2apiAudioFormatWithDefault(aidlEvent.audioConfig);
return new SoundTrigger.GenericRecognitionEvent(
aidlEvent.status,
modelHandle, aidlEvent.captureAvailable, aidlEvent.captureSession,
aidlEvent.captureDelayMs, aidlEvent.capturePreambleMs, aidlEvent.triggerInData,
- aidl2apiAudioFormat(aidlEvent.audioConfig), aidlEvent.data);
+ audioFormat, aidlEvent.data);
}
public static SoundTrigger.RecognitionEvent aidl2apiPhraseRecognitionEvent(
@@ -210,11 +213,14 @@ class ConversionUtil {
for (int i = 0; i < aidlEvent.phraseExtras.length; ++i) {
apiExtras[i] = aidl2apiPhraseRecognitionExtra(aidlEvent.phraseExtras[i]);
}
+ // The API recognition event doesn't allow for a null audio format, even though it doesn't
+ // always make sense. We thus replace it with a default.
+ AudioFormat audioFormat = aidl2apiAudioFormatWithDefault(aidlEvent.common.audioConfig);
return new SoundTrigger.KeyphraseRecognitionEvent(aidlEvent.common.status, modelHandle,
aidlEvent.common.captureAvailable,
aidlEvent.common.captureSession, aidlEvent.common.captureDelayMs,
aidlEvent.common.capturePreambleMs, aidlEvent.common.triggerInData,
- aidl2apiAudioFormat(aidlEvent.common.audioConfig), aidlEvent.common.data,
+ audioFormat, aidlEvent.common.data,
apiExtras);
}
@@ -226,6 +232,14 @@ class ConversionUtil {
return apiBuilder.build();
}
+ // Same as above, but in case of a null input returns a non-null valid output.
+ public static AudioFormat aidl2apiAudioFormatWithDefault(@Nullable AudioConfig audioConfig) {
+ if (audioConfig != null) {
+ return aidl2apiAudioFormat(audioConfig);
+ }
+ return new AudioFormat.Builder().build();
+ }
+
public static int aidl2apiEncoding(int aidlFormat) {
switch (aidlFormat) {
case android.media.audio.common.AudioFormat.PCM
diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
index c1df5b6d2e7e..a2a15b30d578 100644
--- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
+++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
@@ -303,7 +303,7 @@ public class SoundTriggerModule {
(SoundTrigger.RecognitionEvent) msg.obj);
break;
case EVENT_SERVICE_STATE_CHANGE:
- listener.onServiceStateChange(msg.arg1);
+ listener.onServiceStateChange((int) msg.obj);
break;
case EVENT_SERVICE_DIED:
listener.onServiceDied();
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java
index 275e38c74451..704f31d7f773 100644
--- a/core/java/android/net/ConnectivityDiagnosticsManager.java
+++ b/core/java/android/net/ConnectivityDiagnosticsManager.java
@@ -711,6 +711,13 @@ public class ConnectivityDiagnosticsManager {
* not currently registered. If a ConnectivityDiagnosticsCallback instance is registered with
* multiple NetworkRequests, an IllegalArgumentException will be thrown.
*
+ * <p>To avoid performance issues due to apps leaking callbacks, the system will limit the
+ * number of outstanding requests to 100 per app (identified by their UID), shared with
+ * callbacks in {@link ConnectivityManager}. Registering a callback with this method will count
+ * toward this limit. If this limit is exceeded, an exception will be thrown. To avoid hitting
+ * this issue and to conserve resources, make sure to unregister the callbacks with
+ * {@link #unregisterConnectivityDiagnosticsCallback}.
+ *
* @param request The NetworkRequest that will be used to match with Networks for which
* callbacks will be fired
* @param e The Executor to be used for running the callback method invocations
@@ -718,6 +725,7 @@ public class ConnectivityDiagnosticsManager {
* System
* @throws IllegalArgumentException if the same callback instance is registered with multiple
* NetworkRequests
+ * @throws RuntimeException if the app already has too many callbacks registered.
*/
public void registerConnectivityDiagnosticsCallback(
@NonNull NetworkRequest request,
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 36ffe50ef82d..a29f8782601e 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2246,26 +2246,6 @@ public class ConnectivityManager {
.getPackageNameForUid(context, uid), true /* throwException */);
}
- /** {@hide} */
- public static final void enforceTetherChangePermission(Context context, String callingPkg) {
- Preconditions.checkNotNull(context, "Context cannot be null");
- Preconditions.checkNotNull(callingPkg, "callingPkg cannot be null");
-
- if (context.getResources().getStringArray(
- com.android.internal.R.array.config_mobile_hotspot_provision_app).length == 2) {
- // Have a provisioning app - must only let system apps (which check this app)
- // turn on tethering
- context.enforceCallingOrSelfPermission(
- android.Manifest.permission.TETHER_PRIVILEGED, "ConnectivityService");
- } else {
- int uid = Binder.getCallingUid();
- // If callingPkg's uid is not same as Binder.getCallingUid(),
- // AppOpsService throws SecurityException.
- Settings.checkAndNoteWriteSettingsOperation(context, uid, callingPkg,
- true /* throwException */);
- }
- }
-
/**
* @deprecated - use getSystemService. This is a kludge to support static access in certain
* situations where a Context pointer is unavailable.
@@ -3814,13 +3794,22 @@ public class ConnectivityManager {
* or the ability to modify system settings as determined by
* {@link android.provider.Settings.System#canWrite}.</p>
*
+ * <p>To avoid performance issues due to apps leaking callbacks, the system will limit the
+ * number of outstanding requests to 100 per app (identified by their UID), shared with
+ * all variants of this method, of {@link #registerNetworkCallback} as well as
+ * {@link ConnectivityDiagnosticsManager#registerConnectivityDiagnosticsCallback}.
+ * Requesting a network with this method will count toward this limit. If this limit is
+ * exceeded, an exception will be thrown. To avoid hitting this issue and to conserve resources,
+ * make sure to unregister the callbacks with
+ * {@link #unregisterNetworkCallback(NetworkCallback)}.
+ *
* @param request {@link NetworkRequest} describing this request.
* @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
* the callback must not be shared - it uniquely specifies this request.
* The callback is invoked on the default internal Handler.
* @throws IllegalArgumentException if {@code request} contains invalid network capabilities.
* @throws SecurityException if missing the appropriate permissions.
- * @throws RuntimeException if request limit per UID is exceeded.
+ * @throws RuntimeException if the app already has too many callbacks registered.
*/
public void requestNetwork(@NonNull NetworkRequest request,
@NonNull NetworkCallback networkCallback) {
@@ -3834,8 +3823,8 @@ public class ConnectivityManager {
* but runs all the callbacks on the passed Handler.
*
* <p>This method has the same permission requirements as
- * {@link #requestNetwork(NetworkRequest, NetworkCallback)} and throws the same exceptions in
- * the same conditions.
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback)}, is subject to the same limitations,
+ * and throws the same exceptions in the same conditions.
*
* @param request {@link NetworkRequest} describing this request.
* @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
@@ -3866,8 +3855,8 @@ public class ConnectivityManager {
* for that purpose. Calling this method will attempt to bring up the requested network.
*
* <p>This method has the same permission requirements as
- * {@link #requestNetwork(NetworkRequest, NetworkCallback)} and throws the same exceptions in
- * the same conditions.
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback)}, is subject to the same limitations,
+ * and throws the same exceptions in the same conditions.
*
* @param request {@link NetworkRequest} describing this request.
* @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
@@ -3893,8 +3882,8 @@ public class ConnectivityManager {
* on the passed Handler.
*
* <p>This method has the same permission requirements as
- * {@link #requestNetwork(NetworkRequest, NetworkCallback, int)} and throws the same exceptions
- * in the same conditions.
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback)}, is subject to the same limitations,
+ * and throws the same exceptions in the same conditions.
*
* @param request {@link NetworkRequest} describing this request.
* @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
@@ -3963,6 +3952,15 @@ public class ConnectivityManager {
* is unknown prior to bringing up the network so the framework does not
* know how to go about satisfying a request with these capabilities.
*
+ * <p>To avoid performance issues due to apps leaking callbacks, the system will limit the
+ * number of outstanding requests to 100 per app (identified by their UID), shared with
+ * all variants of this method, of {@link #registerNetworkCallback} as well as
+ * {@link ConnectivityDiagnosticsManager#registerConnectivityDiagnosticsCallback}.
+ * Requesting a network with this method will count toward this limit. If this limit is
+ * exceeded, an exception will be thrown. To avoid hitting this issue and to conserve resources,
+ * make sure to unregister the callbacks with {@link #unregisterNetworkCallback(PendingIntent)}
+ * or {@link #releaseNetworkRequest(PendingIntent)}.
+ *
* <p>This method requires the caller to hold either the
* {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
* or the ability to modify system settings as determined by
@@ -3974,7 +3972,7 @@ public class ConnectivityManager {
* comes from {@link PendingIntent#getBroadcast}. Cannot be null.
* @throws IllegalArgumentException if {@code request} contains invalid network capabilities.
* @throws SecurityException if missing the appropriate permissions.
- * @throws RuntimeException if request limit per UID is exceeded.
+ * @throws RuntimeException if the app already has too many callbacks registered.
*/
public void requestNetwork(@NonNull NetworkRequest request,
@NonNull PendingIntent operation) {
@@ -4030,10 +4028,20 @@ public class ConnectivityManager {
* either the application exits or {@link #unregisterNetworkCallback(NetworkCallback)} is
* called.
*
+ * <p>To avoid performance issues due to apps leaking callbacks, the system will limit the
+ * number of outstanding requests to 100 per app (identified by their UID), shared with
+ * all variants of this method, of {@link #requestNetwork} as well as
+ * {@link ConnectivityDiagnosticsManager#registerConnectivityDiagnosticsCallback}.
+ * Requesting a network with this method will count toward this limit. If this limit is
+ * exceeded, an exception will be thrown. To avoid hitting this issue and to conserve resources,
+ * make sure to unregister the callbacks with
+ * {@link #unregisterNetworkCallback(NetworkCallback)}.
+ *
* @param request {@link NetworkRequest} describing this request.
* @param networkCallback The {@link NetworkCallback} that the system will call as suitable
* networks change state.
* The callback is invoked on the default internal Handler.
+ * @throws RuntimeException if the app already has too many callbacks registered.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
public void registerNetworkCallback(@NonNull NetworkRequest request,
@@ -4047,10 +4055,21 @@ public class ConnectivityManager {
* either the application exits or {@link #unregisterNetworkCallback(NetworkCallback)} is
* called.
*
+ * <p>To avoid performance issues due to apps leaking callbacks, the system will limit the
+ * number of outstanding requests to 100 per app (identified by their UID), shared with
+ * all variants of this method, of {@link #requestNetwork} as well as
+ * {@link ConnectivityDiagnosticsManager#registerConnectivityDiagnosticsCallback}.
+ * Requesting a network with this method will count toward this limit. If this limit is
+ * exceeded, an exception will be thrown. To avoid hitting this issue and to conserve resources,
+ * make sure to unregister the callbacks with
+ * {@link #unregisterNetworkCallback(NetworkCallback)}.
+ *
+ *
* @param request {@link NetworkRequest} describing this request.
* @param networkCallback The {@link NetworkCallback} that the system will call as suitable
* networks change state.
* @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
+ * @throws RuntimeException if the app already has too many callbacks registered.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
public void registerNetworkCallback(@NonNull NetworkRequest request,
@@ -4084,10 +4103,21 @@ public class ConnectivityManager {
* <p>
* The request may be released normally by calling
* {@link #unregisterNetworkCallback(android.app.PendingIntent)}.
+ *
+ * <p>To avoid performance issues due to apps leaking callbacks, the system will limit the
+ * number of outstanding requests to 100 per app (identified by their UID), shared with
+ * all variants of this method, of {@link #requestNetwork} as well as
+ * {@link ConnectivityDiagnosticsManager#registerConnectivityDiagnosticsCallback}.
+ * Requesting a network with this method will count toward this limit. If this limit is
+ * exceeded, an exception will be thrown. To avoid hitting this issue and to conserve resources,
+ * make sure to unregister the callbacks with {@link #unregisterNetworkCallback(PendingIntent)}
+ * or {@link #releaseNetworkRequest(PendingIntent)}.
+ *
* @param request {@link NetworkRequest} describing this request.
* @param operation Action to perform when the network is available (corresponds
* to the {@link NetworkCallback#onAvailable} call. Typically
* comes from {@link PendingIntent#getBroadcast}. Cannot be null.
+ * @throws RuntimeException if the app already has too many callbacks registered.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
public void registerNetworkCallback(@NonNull NetworkRequest request,
@@ -4109,9 +4139,19 @@ public class ConnectivityManager {
* will continue to be called until either the application exits or
* {@link #unregisterNetworkCallback(NetworkCallback)} is called.
*
+ * <p>To avoid performance issues due to apps leaking callbacks, the system will limit the
+ * number of outstanding requests to 100 per app (identified by their UID), shared with
+ * all variants of this method, of {@link #requestNetwork} as well as
+ * {@link ConnectivityDiagnosticsManager#registerConnectivityDiagnosticsCallback}.
+ * Requesting a network with this method will count toward this limit. If this limit is
+ * exceeded, an exception will be thrown. To avoid hitting this issue and to conserve resources,
+ * make sure to unregister the callbacks with
+ * {@link #unregisterNetworkCallback(NetworkCallback)}.
+ *
* @param networkCallback The {@link NetworkCallback} that the system will call as the
* system default network changes.
* The callback is invoked on the default internal Handler.
+ * @throws RuntimeException if the app already has too many callbacks registered.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
public void registerDefaultNetworkCallback(@NonNull NetworkCallback networkCallback) {
@@ -4123,9 +4163,19 @@ public class ConnectivityManager {
* will continue to be called until either the application exits or
* {@link #unregisterNetworkCallback(NetworkCallback)} is called.
*
+ * <p>To avoid performance issues due to apps leaking callbacks, the system will limit the
+ * number of outstanding requests to 100 per app (identified by their UID), shared with
+ * all variants of this method, of {@link #requestNetwork} as well as
+ * {@link ConnectivityDiagnosticsManager#registerConnectivityDiagnosticsCallback}.
+ * Requesting a network with this method will count toward this limit. If this limit is
+ * exceeded, an exception will be thrown. To avoid hitting this issue and to conserve resources,
+ * make sure to unregister the callbacks with
+ * {@link #unregisterNetworkCallback(NetworkCallback)}.
+ *
* @param networkCallback The {@link NetworkCallback} that the system will call as the
* system default network changes.
* @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
+ * @throws RuntimeException if the app already has too many callbacks registered.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
public void registerDefaultNetworkCallback(@NonNull NetworkCallback networkCallback,
@@ -4217,7 +4267,6 @@ public class ConnectivityManager {
* Cannot be null.
*/
public void unregisterNetworkCallback(@NonNull PendingIntent operation) {
- checkPendingIntentNotNull(operation);
releaseNetworkRequest(operation);
}
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 65e772cb5ebb..482d2d2192b8 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -357,6 +357,7 @@ public abstract class NetworkAgent {
final NetworkInfo ni = new NetworkInfo(config.legacyType, config.legacyType,
config.legacyTypeName, "");
ni.setIsAvailable(true);
+ ni.setExtraInfo(config.getLegacyExtraInfo());
return ni;
}
diff --git a/core/java/android/net/NetworkAgentConfig.java b/core/java/android/net/NetworkAgentConfig.java
index fee868a93be4..fe1268d79b89 100644
--- a/core/java/android/net/NetworkAgentConfig.java
+++ b/core/java/android/net/NetworkAgentConfig.java
@@ -185,6 +185,26 @@ public final class NetworkAgentConfig implements Parcelable {
return legacyTypeName;
}
+ /**
+ * The legacy extra info of the agent. The extra info should only be :
+ * <ul>
+ * <li>For cellular agents, the APN name.</li>
+ * <li>For ethernet agents, the interface name.</li>
+ * </ul>
+ * @hide
+ */
+ @NonNull
+ private String mLegacyExtraInfo = "";
+
+ /**
+ * The legacy extra info of the agent.
+ * @hide
+ */
+ @NonNull
+ public String getLegacyExtraInfo() {
+ return mLegacyExtraInfo;
+ }
+
/** @hide */
public NetworkAgentConfig() {
}
@@ -201,6 +221,7 @@ public final class NetworkAgentConfig implements Parcelable {
skip464xlat = nac.skip464xlat;
legacyType = nac.legacyType;
legacyTypeName = nac.legacyTypeName;
+ mLegacyExtraInfo = nac.mLegacyExtraInfo;
}
}
@@ -309,6 +330,18 @@ public final class NetworkAgentConfig implements Parcelable {
}
/**
+ * Sets the legacy extra info of the agent.
+ * @param legacyExtraInfo the legacy extra info.
+ * @return this builder, to facilitate chaining.
+ * @hide
+ */
+ @NonNull
+ public Builder setLegacyExtraInfo(@NonNull String legacyExtraInfo) {
+ mConfig.mLegacyExtraInfo = legacyExtraInfo;
+ return this;
+ }
+
+ /**
* Returns the constructed {@link NetworkAgentConfig} object.
*/
@NonNull
@@ -330,14 +363,15 @@ public final class NetworkAgentConfig implements Parcelable {
&& skip464xlat == that.skip464xlat
&& legacyType == that.legacyType
&& Objects.equals(subscriberId, that.subscriberId)
- && Objects.equals(legacyTypeName, that.legacyTypeName);
+ && Objects.equals(legacyTypeName, that.legacyTypeName)
+ && Objects.equals(mLegacyExtraInfo, that.mLegacyExtraInfo);
}
@Override
public int hashCode() {
return Objects.hash(allowBypass, explicitlySelected, acceptUnvalidated,
acceptPartialConnectivity, provisioningNotificationDisabled, subscriberId,
- skip464xlat, legacyType, legacyTypeName);
+ skip464xlat, legacyType, legacyTypeName, mLegacyExtraInfo);
}
@Override
@@ -353,6 +387,7 @@ public final class NetworkAgentConfig implements Parcelable {
+ ", legacyType = " + legacyType
+ ", hasShownBroken = " + hasShownBroken
+ ", legacyTypeName = '" + legacyTypeName + '\''
+ + ", legacyExtraInfo = '" + mLegacyExtraInfo + '\''
+ "}";
}
@@ -372,6 +407,7 @@ public final class NetworkAgentConfig implements Parcelable {
out.writeInt(skip464xlat ? 1 : 0);
out.writeInt(legacyType);
out.writeString(legacyTypeName);
+ out.writeString(mLegacyExtraInfo);
}
public static final @NonNull Creator<NetworkAgentConfig> CREATOR =
@@ -388,6 +424,7 @@ public final class NetworkAgentConfig implements Parcelable {
networkAgentConfig.skip464xlat = in.readInt() != 0;
networkAgentConfig.legacyType = in.readInt();
networkAgentConfig.legacyTypeName = in.readString();
+ networkAgentConfig.mLegacyExtraInfo = in.readString();
return networkAgentConfig;
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index a8391c2b5461..a415dc57e160 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -4095,14 +4095,14 @@ public class UserManager {
}
/**
- * Returns true if the user switcher should be shown.
- * I.e., returns whether the user switcher is enabled and there is something actionable to show.
+ * Returns true if the user switcher is enabled (regardless of whether there is anything
+ * interesting for it to show).
*
- * @return true if user switcher should be shown.
+ * @return true if user switcher is enabled
* @hide
*/
public boolean isUserSwitcherEnabled() {
- return isUserSwitcherEnabled(false);
+ return isUserSwitcherEnabled(true);
}
/**
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index fd7cdda3808a..06254574401b 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -221,15 +221,16 @@ public abstract class VibrationEffect implements Parcelable {
*
* Waveform vibrations are a potentially repeating series of timing and amplitude pairs. For
* each pair, the value in the amplitude array determines the strength of the vibration and the
- * value in the timing array determines how long it vibrates for. An amplitude of 0 implies no
- * vibration (i.e. off), and any pairs with a timing value of 0 will be ignored.
+ * value in the timing array determines how long it vibrates for, in milliseconds. Amplitude
+ * values must be between 0 and 255, and an amplitude of 0 implies no vibration (i.e. off). Any
+ * pairs with a timing value of 0 will be ignored.
* </p><p>
* To cause the pattern to repeat, pass the index into the timings array at which to start the
* repetition, or -1 to disable repeating.
* </p>
*
- * @param timings The timing values of the timing / amplitude pairs. Timing values of 0
- * will cause the pair to be ignored.
+ * @param timings The timing values, in milliseconds, of the timing / amplitude pairs. Timing
+ * values of 0 will cause the pair to be ignored.
* @param amplitudes The amplitude values of the timing / amplitude pairs. Amplitude values
* must be between 0 and 255, or equal to {@link #DEFAULT_AMPLITUDE}. An
* amplitude value of 0 implies the motor is off.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e10fceaa5bc7..4f0a9728fcf8 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -24,6 +24,7 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UserIdInt;
@@ -6238,6 +6239,8 @@ public final class Settings {
* determines if the IME should be shown when a hard keyboard is attached.
* @hide
*/
+ @TestApi
+ @SuppressLint("NoSettingsProvider")
public static final String SHOW_IME_WITH_HARD_KEYBOARD = "show_ime_with_hard_keyboard";
/**
diff --git a/core/java/android/service/autofill/augmented/FillCallback.java b/core/java/android/service/autofill/augmented/FillCallback.java
index 21738d80f2d3..8ba5c173890c 100644
--- a/core/java/android/service/autofill/augmented/FillCallback.java
+++ b/core/java/android/service/autofill/augmented/FillCallback.java
@@ -62,9 +62,10 @@ public final class FillCallback {
List<Dataset> inlineSuggestions = response.getInlineSuggestions();
Bundle clientState = response.getClientState();
+ // We need to report result regardless of whether inline suggestions are returned or not.
+ mProxy.reportResult(inlineSuggestions, clientState);
if (inlineSuggestions != null && !inlineSuggestions.isEmpty()) {
mProxy.logEvent(AutofillProxy.REPORT_EVENT_INLINE_RESPONSE);
- mProxy.reportResult(inlineSuggestions, clientState);
return;
}
diff --git a/core/java/android/service/controls/templates/ControlTemplate.java b/core/java/android/service/controls/templates/ControlTemplate.java
index 1e16273c455b..e592fad394b8 100644
--- a/core/java/android/service/controls/templates/ControlTemplate.java
+++ b/core/java/android/service/controls/templates/ControlTemplate.java
@@ -214,10 +214,13 @@ public abstract class ControlTemplate {
}
/**
- * Get a singleton {@link ControlTemplate} that has no features.
+ * Get a singleton {@link ControlTemplate}, which supports no direct user input.
*
- * This template has no distinctive field, not even an identifier. Used for a {@link Control}
- * that accepts no type of input, or when there is no known state.
+ * Used by {@link Control.StatelessBuilder} when there is no known state. Can also be used
+ * in {@link Control.StatefulBuilder} for conveying information to a user about the
+ * {@link Control} but direct user interaction is not desired. Since this template has no
+ * corresponding {@link ControlAction}, any user interaction will launch the
+ * {@link Control#getAppIntent()}.
*
* @return a singleton {@link ControlTemplate} to indicate no specific template is used by
* this {@link Control}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 1f6555c85a66..0827fef60252 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -78,6 +78,7 @@ public class ZenModeConfig implements Parcelable {
private static final int DEFAULT_SOURCE = SOURCE_CONTACT;
private static final int DEFAULT_CALLS_SOURCE = SOURCE_STAR;
+ public static final String MANUAL_RULE_ID = "MANUAL_RULE";
public static final String EVENTS_DEFAULT_RULE_ID = "EVENTS_DEFAULT_RULE";
public static final String EVERY_NIGHT_DEFAULT_RULE_ID = "EVERY_NIGHT_DEFAULT_RULE";
public static final List<String> DEFAULT_RULE_IDS = Arrays.asList(EVERY_NIGHT_DEFAULT_RULE_ID,
@@ -959,6 +960,48 @@ public class ZenModeConfig implements Parcelable {
};
/**
+ * Converts a ZenModeConfig to a ZenPolicy
+ */
+ public ZenPolicy toZenPolicy() {
+ ZenPolicy.Builder builder = new ZenPolicy.Builder()
+ .allowCalls(allowCalls
+ ? ZenModeConfig.getZenPolicySenders(allowCallsFrom)
+ : ZenPolicy.PEOPLE_TYPE_NONE)
+ .allowRepeatCallers(allowRepeatCallers)
+ .allowMessages(allowMessages
+ ? ZenModeConfig.getZenPolicySenders(allowMessagesFrom)
+ : ZenPolicy.PEOPLE_TYPE_NONE)
+ .allowReminders(allowReminders)
+ .allowEvents(allowEvents)
+ .allowAlarms(allowAlarms)
+ .allowMedia(allowMedia)
+ .allowSystem(allowSystem)
+ .allowConversations(allowConversations
+ ? ZenModeConfig.getZenPolicySenders(allowConversationsFrom)
+ : ZenPolicy.PEOPLE_TYPE_NONE);
+ if (suppressedVisualEffects == 0) {
+ builder.showAllVisualEffects();
+ } else {
+ // configs don't have an unset state: wither true or false.
+ builder.showFullScreenIntent(
+ (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT) == 0);
+ builder.showLights(
+ (suppressedVisualEffects & SUPPRESSED_EFFECT_LIGHTS) == 0);
+ builder.showPeeking(
+ (suppressedVisualEffects & SUPPRESSED_EFFECT_PEEK) == 0);
+ builder.showStatusBarIcons(
+ (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_STATUS_BAR) == 0);
+ builder.showBadges(
+ (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_BADGE) == 0);
+ builder.showInAmbientDisplay(
+ (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_AMBIENT) == 0);
+ builder.showInNotificationList(
+ (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST) == 0);
+ }
+ return builder.build();
+ }
+
+ /**
* Converts a zenPolicy to a notificationPolicy using this ZenModeConfig's values as its
* defaults for all unset values in zenPolicy
*/
diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java
index 87295e1c95b9..6d0bcffe148e 100644
--- a/core/java/android/service/notification/ZenPolicy.java
+++ b/core/java/android/service/notification/ZenPolicy.java
@@ -24,6 +24,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.util.proto.ProtoOutputStream;
+import java.io.ByteArrayOutputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -1111,6 +1112,40 @@ public final class ZenPolicy implements Parcelable {
}
/**
+ * Converts a policy to a statsd proto.
+ * @hides
+ */
+ public byte[] toProto() {
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ ProtoOutputStream proto = new ProtoOutputStream(bytes);
+
+ proto.write(DNDPolicyProto.CALLS, getPriorityCategoryCalls());
+ proto.write(DNDPolicyProto.REPEAT_CALLERS, getPriorityCategoryRepeatCallers());
+ proto.write(DNDPolicyProto.MESSAGES, getPriorityCategoryMessages());
+ proto.write(DNDPolicyProto.CONVERSATIONS, getPriorityCategoryConversations());
+ proto.write(DNDPolicyProto.REMINDERS, getPriorityCategoryReminders());
+ proto.write(DNDPolicyProto.EVENTS, getPriorityCategoryEvents());
+ proto.write(DNDPolicyProto.ALARMS, getPriorityCategoryAlarms());
+ proto.write(DNDPolicyProto.MEDIA, getPriorityCategoryMedia());
+ proto.write(DNDPolicyProto.SYSTEM, getPriorityCategorySystem());
+
+ proto.write(DNDPolicyProto.FULLSCREEN, getVisualEffectFullScreenIntent());
+ proto.write(DNDPolicyProto.LIGHTS, getVisualEffectLights());
+ proto.write(DNDPolicyProto.PEEK, getVisualEffectPeek());
+ proto.write(DNDPolicyProto.STATUS_BAR, getVisualEffectStatusBar());
+ proto.write(DNDPolicyProto.BADGE, getVisualEffectBadge());
+ proto.write(DNDPolicyProto.AMBIENT, getVisualEffectAmbient());
+ proto.write(DNDPolicyProto.NOTIFICATION_LIST, getVisualEffectNotificationList());
+
+ proto.write(DNDPolicyProto.ALLOW_CALLS_FROM, getPriorityCallSenders());
+ proto.write(DNDPolicyProto.ALLOW_MESSAGES_FROM, getPriorityMessageSenders());
+ proto.write(DNDPolicyProto.ALLOW_CONVERSATIONS_FROM, getPriorityConversationSenders());
+
+ proto.flush();
+ return bytes.toByteArray();
+ }
+
+ /**
* Makes deep copy of this ZenPolicy.
* @hide
*/
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index e4d53c64a063..9a9396c45b66 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -575,21 +575,23 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
@VisibleForTesting
public boolean onStateChanged(InsetsState state) {
- boolean localStateChanged = !mState.equals(state, true /* excludingCaptionInsets */)
+ boolean stateChanged = !mState.equals(state, true /* excludingCaptionInsets */,
+ false /* excludeInvisibleIme */)
|| !captionInsetsUnchanged();
- if (!localStateChanged && mLastDispatchedState.equals(state)) {
+ if (!stateChanged && mLastDispatchedState.equals(state)) {
return false;
}
if (DEBUG) Log.d(TAG, "onStateChanged: " + state);
updateState(state);
+
+ boolean localStateChanged = !mState.equals(mLastDispatchedState,
+ true /* excludingCaptionInsets */, true /* excludeInvisibleIme */);
mLastDispatchedState.set(state, true /* copySources */);
+
applyLocalVisibilityOverride();
if (localStateChanged) {
- if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged");
+ if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged, send state to WM: " + mState);
mHost.notifyInsetsChanged();
- }
- if (!mState.equals(mLastDispatchedState, true /* excludingCaptionInsets */)) {
- if (DEBUG) Log.d(TAG, "onStateChanged, send state to WM: " + mState);
updateRequestedState();
}
return true;
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index b0158467a17b..15b9a9330392 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -191,6 +191,14 @@ public class InsetsSource implements Parcelable {
@Override
public boolean equals(Object o) {
+ return equals(o, false);
+ }
+
+ /**
+ * @param excludeInvisibleImeFrames If {@link InsetsState#ITYPE_IME} frames should be ignored
+ * when IME is not visible.
+ */
+ public boolean equals(Object o, boolean excludeInvisibleImeFrames) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
@@ -198,6 +206,7 @@ public class InsetsSource implements Parcelable {
if (mType != that.mType) return false;
if (mVisible != that.mVisible) return false;
+ if (excludeInvisibleImeFrames && !mVisible && mType == ITYPE_IME) return true;
if (!Objects.equals(mVisibleFrame, that.mVisibleFrame)) return false;
return mFrame.equals(that.mFrame);
}
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index ae70a4971776..3aa246441dbc 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -18,8 +18,8 @@ package android.view;
import static android.view.InsetsController.ANIMATION_TYPE_NONE;
import static android.view.InsetsController.AnimationType;
-import static android.view.InsetsState.getDefaultVisibility;
import static android.view.InsetsController.DEBUG;
+import static android.view.InsetsState.getDefaultVisibility;
import static android.view.InsetsState.toPublicType;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
@@ -221,9 +221,10 @@ public class InsetsSourceConsumer {
final boolean hasControl = mSourceControl != null;
// We still need to let the legacy app know the visibility change even if we don't have the
- // control.
+ // control. If we don't have the source, we don't change the requested visibility for making
+ // the callback behavior compatible.
mController.updateCompatSysUiVisibility(
- mType, hasControl ? mRequestedVisible : isVisible, hasControl);
+ mType, (hasControl || source == null) ? mRequestedVisible : isVisible, hasControl);
// If we don't have control, we are not able to change the visibility.
if (!hasControl) {
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index f0bca260be38..397b04e9c023 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -555,7 +555,7 @@ public class InsetsState implements Parcelable {
@Override
public boolean equals(Object o) {
- return equals(o, false);
+ return equals(o, false, false);
}
/**
@@ -564,10 +564,13 @@ public class InsetsState implements Parcelable {
* excluded.
* @param excludingCaptionInsets {@code true} if we want to compare two InsetsState objects but
* ignore the caption insets source value.
+ * @param excludeInvisibleImeFrames If {@link #ITYPE_IME} frames should be ignored when IME is
+ * not visible.
* @return {@code true} if the two InsetsState objects are equal, {@code false} otherwise.
*/
@VisibleForTesting
- public boolean equals(Object o, boolean excludingCaptionInsets) {
+ public boolean equals(Object o, boolean excludingCaptionInsets,
+ boolean excludeInvisibleImeFrames) {
if (this == o) { return true; }
if (o == null || getClass() != o.getClass()) { return false; }
@@ -598,7 +601,7 @@ public class InsetsState implements Parcelable {
if (otherSource == null) {
return false;
}
- if (!otherSource.equals(source)) {
+ if (!otherSource.equals(source, excludeInvisibleImeFrames)) {
return false;
}
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 57f91ed3c0ae..c098fae11b8c 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -893,12 +893,15 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
return;
}
- ViewRootImpl viewRoot = getViewRootImpl();
- if (viewRoot == null || viewRoot.mSurface == null || !viewRoot.mSurface.isValid()) {
- if (DEBUG) {
- Log.d(TAG, System.identityHashCode(this)
- + " updateSurface: no valid surface");
- }
+ final ViewRootImpl viewRoot = getViewRootImpl();
+
+ if (viewRoot == null) {
+ return;
+ }
+
+ if (viewRoot.mSurface == null || !viewRoot.mSurface.isValid()) {
+ notifySurfaceDestroyed();
+ releaseSurfaces();
return;
}
@@ -1109,28 +1112,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
final boolean surfaceChanged = creating;
if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) {
mSurfaceCreated = false;
- if (mSurface.isValid()) {
- if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
- + "visibleChanged -- surfaceDestroyed");
- callbacks = getSurfaceCallbacks();
- for (SurfaceHolder.Callback c : callbacks) {
- c.surfaceDestroyed(mSurfaceHolder);
- }
- // Since Android N the same surface may be reused and given to us
- // again by the system server at a later point. However
- // as we didn't do this in previous releases, clients weren't
- // necessarily required to clean up properly in
- // surfaceDestroyed. This leads to problems for example when
- // clients don't destroy their EGL context, and try
- // and create a new one on the same surface following reuse.
- // Since there is no valid use of the surface in-between
- // surfaceDestroyed and surfaceCreated, we force a disconnect,
- // so the next connect will always work if we end up reusing
- // the surface.
- if (mSurface.isValid()) {
- mSurface.forceScopedDisconnect();
- }
- }
+ notifySurfaceDestroyed();
}
if (creating) {
@@ -1786,6 +1768,31 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
}
+ private void notifySurfaceDestroyed() {
+ if (mSurface.isValid()) {
+ if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ + "surfaceDestroyed");
+ SurfaceHolder.Callback[] callbacks = getSurfaceCallbacks();
+ for (SurfaceHolder.Callback c : callbacks) {
+ c.surfaceDestroyed(mSurfaceHolder);
+ }
+ // Since Android N the same surface may be reused and given to us
+ // again by the system server at a later point. However
+ // as we didn't do this in previous releases, clients weren't
+ // necessarily required to clean up properly in
+ // surfaceDestroyed. This leads to problems for example when
+ // clients don't destroy their EGL context, and try
+ // and create a new one on the same surface following reuse.
+ // Since there is no valid use of the surface in-between
+ // surfaceDestroyed and surfaceCreated, we force a disconnect,
+ // so the next connect will always work if we end up reusing
+ // the surface.
+ if (mSurface.isValid()) {
+ mSurface.forceScopedDisconnect();
+ }
+ }
+ }
+
/**
* Wrapper of accessibility embedded connection for embedded view hierarchy.
*/
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index a5ec4e900641..d2e506ea550c 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -41,7 +41,6 @@ public class WindowlessWindowManager implements IWindowSession {
private final static String TAG = "WindowlessWindowManager";
private class State {
- //TODO : b/150190730 we should create it when view show and release it when view invisible.
SurfaceControl mSurfaceControl;
WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
int mDisplayId;
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 301ce9f013e4..3f5ef5a2651d 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -39,8 +39,8 @@ import com.android.internal.util.Preconditions;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.security.SecureRandom;
import java.util.ArrayList;
-import java.util.Random;
/**
* Session used when the Android a system-provided content capture service
@@ -50,7 +50,9 @@ public abstract class ContentCaptureSession implements AutoCloseable {
private static final String TAG = ContentCaptureSession.class.getSimpleName();
- private static final Random sIdGenerator = new Random();
+ // TODO(b/158778794): to make the session ids truly globally unique across
+ // processes, we may need to explore other options.
+ private static final SecureRandom ID_GENERATOR = new SecureRandom();
/**
* Initial state, when there is no session.
@@ -622,7 +624,7 @@ public abstract class ContentCaptureSession implements AutoCloseable {
private static int getRandomSessionId() {
int id;
do {
- id = sIdGenerator.nextInt();
+ id = ID_GENERATOR.nextInt();
} while (id == NO_SESSION_ID);
return id;
}
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index e07181abe19c..843700cef55e 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -971,7 +971,8 @@ public final class SelectionActionModeHelper {
return new TextClassifierEvent.LanguageDetectionEvent.Builder(eventType)
.setEventContext(classificationContext)
.setResultId(classification.getId())
- .setEntityTypes(language)
+ // b/158481016: Disable language logging.
+ //.setEntityTypes(language)
.setScores(score)
.setActionIndices(classification.getActions().indexOf(translateAction))
.setModelName(model)
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 2d4d9575584d..0a1e3a0caeff 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -2816,8 +2816,7 @@ public class ChooserActivity extends ResolverActivity implements
}
// no need to query direct share for work profile when its turned off
- UserManager userManager = getSystemService(UserManager.class);
- if (userManager.isQuietModeEnabled(chooserListAdapter.getUserHandle())) {
+ if (isQuietModeEnabled(chooserListAdapter.getUserHandle())) {
getChooserActivityLogger().logSharesheetAppLoadComplete();
return;
}
@@ -2841,6 +2840,12 @@ public class ChooserActivity extends ResolverActivity implements
getChooserActivityLogger().logSharesheetAppLoadComplete();
}
+ @VisibleForTesting
+ protected boolean isQuietModeEnabled(UserHandle userHandle) {
+ UserManager userManager = getSystemService(UserManager.class);
+ return userManager.isQuietModeEnabled(userHandle);
+ }
+
private void setupScrollListener() {
if (mResolverDrawerLayout == null) {
return;
@@ -3095,7 +3100,7 @@ public class ChooserActivity extends ResolverActivity implements
setVerticalScrollEnabled(false);
}
} else if (state == ViewPager.SCROLL_STATE_IDLE) {
- if (mScrollStatus == SCROLL_STATUS_SCROLLING_VERTICAL) {
+ if (mScrollStatus == SCROLL_STATUS_SCROLLING_HORIZONTAL) {
mScrollStatus = SCROLL_STATUS_IDLE;
setVerticalScrollEnabled(true);
}
@@ -3592,10 +3597,9 @@ public class ChooserActivity extends ResolverActivity implements
* Only expand direct share area if there is a minimum number of targets.
*/
private boolean canExpandDirectShare() {
- int orientation = getResources().getConfiguration().orientation;
- return mChooserListAdapter.getNumServiceTargetsForExpand() > getMaxTargetsPerRow()
- && orientation == Configuration.ORIENTATION_PORTRAIT
- && !isInMultiWindowMode();
+ // Do not enable until we have confirmed more apps are using sharing shortcuts
+ // Check git history for enablement logic
+ return false;
}
public ChooserListAdapter getListAdapter() {
diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java
index f4fb993fbb93..d6ff7b13c934 100644
--- a/core/java/com/android/internal/app/ChooserListAdapter.java
+++ b/core/java/com/android/internal/app/ChooserListAdapter.java
@@ -181,6 +181,7 @@ public class ChooserListAdapter extends ResolverListAdapter {
ri.icon = 0;
}
mCallerTargets.add(new DisplayResolveInfo(ii, ri, ii, makePresentationGetter(ri)));
+ if (mCallerTargets.size() == MAX_SUGGESTED_APP_TARGETS) break;
}
}
}
@@ -320,7 +321,7 @@ public class ChooserListAdapter extends ResolverListAdapter {
public int getCallerTargetCount() {
- return Math.min(mCallerTargets.size(), MAX_SUGGESTED_APP_TARGETS);
+ return mCallerTargets.size();
}
/**
@@ -346,8 +347,9 @@ public class ChooserListAdapter extends ResolverListAdapter {
}
int getAlphaTargetCount() {
- int standardCount = mSortedList.size();
- return standardCount > mChooserListCommunicator.getMaxRankedTargets() ? standardCount : 0;
+ int groupedCount = mSortedList.size();
+ int ungroupedCount = mCallerTargets.size() + mDisplayList.size();
+ return ungroupedCount > mChooserListCommunicator.getMaxRankedTargets() ? groupedCount : 0;
}
/**
diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java
index 157e0a74712b..2a7eae626795 100644
--- a/core/java/com/android/internal/app/PlatLogoActivity.java
+++ b/core/java/com/android/internal/app/PlatLogoActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,29 +17,32 @@
package com.android.internal.app;
import android.animation.ObjectAnimator;
-import android.animation.TimeAnimator;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActionBar;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
+import android.content.Context;
import android.content.Intent;
-import android.content.res.ColorStateList;
-import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.ColorFilter;
-import android.graphics.Matrix;
+import android.graphics.LinearGradient;
import android.graphics.Paint;
-import android.graphics.Path;
import android.graphics.PixelFormat;
+import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.provider.Settings;
+import android.util.AttributeSet;
import android.util.Log;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewGroup;
+import android.view.animation.PathInterpolator;
+import android.widget.FrameLayout;
import android.widget.ImageView;
import com.android.internal.R;
@@ -50,23 +53,12 @@ import org.json.JSONObject;
* @hide
*/
public class PlatLogoActivity extends Activity {
- ImageView mZeroView, mOneView;
- BackslashDrawable mBackslash;
- int mClicks;
-
- static final Paint sPaint = new Paint();
- static {
- sPaint.setStyle(Paint.Style.STROKE);
- sPaint.setStrokeWidth(4f);
- sPaint.setStrokeCap(Paint.Cap.SQUARE);
- }
+ private static final boolean WRITE_SETTINGS = true;
+
+ BigDialView mDialView;
@Override
protected void onPause() {
- if (mBackslash != null) {
- mBackslash.stopAnimating();
- }
- mClicks = 0;
super.onPause();
}
@@ -80,101 +72,33 @@ public class PlatLogoActivity extends Activity {
getWindow().setNavigationBarColor(0);
getWindow().setStatusBarColor(0);
- getActionBar().hide();
-
- setContentView(R.layout.platlogo_layout);
-
- mBackslash = new BackslashDrawable((int) (50 * dp));
-
- mOneView = findViewById(R.id.one);
- mOneView.setImageDrawable(new OneDrawable());
- mZeroView = findViewById(R.id.zero);
- mZeroView.setImageDrawable(new ZeroDrawable());
+ final ActionBar ab = getActionBar();
+ if (ab != null) ab.hide();
- final ViewGroup root = (ViewGroup) mOneView.getParent();
- root.setClipChildren(false);
- root.setBackground(mBackslash);
- root.getBackground().setAlpha(0x20);
-
- View.OnTouchListener tl = new View.OnTouchListener() {
- float mOffsetX, mOffsetY;
- long mClickTime;
- ObjectAnimator mRotAnim;
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- measureTouchPressure(event);
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- v.animate().scaleX(1.1f).scaleY(1.1f);
- v.getParent().bringChildToFront(v);
- mOffsetX = event.getRawX() - v.getX();
- mOffsetY = event.getRawY() - v.getY();
- long now = System.currentTimeMillis();
- if (now - mClickTime < 350) {
- mRotAnim = ObjectAnimator.ofFloat(v, View.ROTATION,
- v.getRotation(), v.getRotation() + 3600);
- mRotAnim.setDuration(10000);
- mRotAnim.start();
- mClickTime = 0;
- } else {
- mClickTime = now;
- }
- break;
- case MotionEvent.ACTION_MOVE:
- v.setX(event.getRawX() - mOffsetX);
- v.setY(event.getRawY() - mOffsetY);
- v.performHapticFeedback(HapticFeedbackConstants.TEXT_HANDLE_MOVE);
- break;
- case MotionEvent.ACTION_UP:
- v.performClick();
- // fall through
- case MotionEvent.ACTION_CANCEL:
- v.animate().scaleX(1f).scaleY(1f);
- if (mRotAnim != null) mRotAnim.cancel();
- testOverlap();
- break;
- }
- return true;
- }
- };
-
- findViewById(R.id.one).setOnTouchListener(tl);
- findViewById(R.id.zero).setOnTouchListener(tl);
- findViewById(R.id.text).setOnTouchListener(tl);
- }
-
- private void testOverlap() {
- final float width = mZeroView.getWidth();
- final float targetX = mZeroView.getX() + width * .2f;
- final float targetY = mZeroView.getY() + width * .3f;
- if (Math.hypot(targetX - mOneView.getX(), targetY - mOneView.getY()) < width * .2f
- && Math.abs(mOneView.getRotation() % 360 - 315) < 15) {
- mOneView.animate().x(mZeroView.getX() + width * .2f);
- mOneView.animate().y(mZeroView.getY() + width * .3f);
- mOneView.setRotation(mOneView.getRotation() % 360);
- mOneView.animate().rotation(315);
- mOneView.performHapticFeedback(HapticFeedbackConstants.CONFIRM);
-
- mBackslash.startAnimating();
-
- mClicks++;
- if (mClicks >= 7) {
- launchNextStage();
- }
- } else {
- mBackslash.stopAnimating();
+ mDialView = new BigDialView(this, null);
+ if (Settings.System.getLong(getContentResolver(),
+ "egg_mode" /* Settings.System.EGG_MODE */, 0) == 0) {
+ mDialView.setUnlockTries(3);
}
+
+ final FrameLayout layout = new FrameLayout(this);
+ layout.setBackgroundColor(0xFFFF0000);
+ layout.addView(mDialView, FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT);
+ setContentView(layout);
}
- private void launchNextStage() {
+ private void launchNextStage(boolean locked) {
final ContentResolver cr = getContentResolver();
if (Settings.System.getLong(cr, "egg_mode" /* Settings.System.EGG_MODE */, 0) == 0) {
// For posterity: the moment this user unlocked the easter egg
try {
- Settings.System.putLong(cr,
- "egg_mode", // Settings.System.EGG_MODE,
- System.currentTimeMillis());
+ if (WRITE_SETTINGS) {
+ Settings.System.putLong(cr,
+ "egg_mode", // Settings.System.EGG_MODE,
+ locked ? 0 : System.currentTimeMillis());
+ }
} catch (RuntimeException e) {
Log.e("com.android.internal.app.PlatLogoActivity", "Can't write settings", e);
}
@@ -182,12 +106,12 @@ public class PlatLogoActivity extends Activity {
try {
startActivity(new Intent(Intent.ACTION_MAIN)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ | Intent.FLAG_ACTIVITY_CLEAR_TASK)
.addCategory("com.android.internal.category.PLATLOGO"));
} catch (ActivityNotFoundException ex) {
Log.e("com.android.internal.app.PlatLogoActivity", "No more eggs.");
}
- finish();
+ //finish(); // no longer finish upon unlock; it's fun to frob the dial
}
static final String TOUCH_STATS = "touch.stats";
@@ -223,7 +147,10 @@ public class PlatLogoActivity extends Activity {
if (mPressureMax >= 0) {
touchData.put("min", mPressureMin);
touchData.put("max", mPressureMax);
- Settings.System.putString(getContentResolver(), TOUCH_STATS, touchData.toString());
+ if (WRITE_SETTINGS) {
+ Settings.System.putString(getContentResolver(), TOUCH_STATS,
+ touchData.toString());
+ }
}
} catch (Exception e) {
Log.e("com.android.internal.app.PlatLogoActivity", "Can't write touch settings", e);
@@ -242,149 +169,272 @@ public class PlatLogoActivity extends Activity {
super.onStop();
}
- static class ZeroDrawable extends Drawable {
- int mTintColor;
+ class BigDialView extends ImageView {
+ private static final int COLOR_GREEN = 0xff3ddc84;
+ private static final int COLOR_BLUE = 0xff4285f4;
+ private static final int COLOR_NAVY = 0xff073042;
+ private static final int COLOR_ORANGE = 0xfff86734;
+ private static final int COLOR_CHARTREUSE = 0xffeff7cf;
+ private static final int COLOR_LIGHTBLUE = 0xffd7effe;
- @Override
- public void draw(Canvas canvas) {
- sPaint.setColor(mTintColor | 0xFF000000);
+ private static final int STEPS = 11;
+ private static final float VALUE_CHANGE_MAX = 1f / STEPS;
- canvas.save();
- canvas.scale(canvas.getWidth() / 24f, canvas.getHeight() / 24f);
+ private BigDialDrawable mDialDrawable;
+ private boolean mWasLocked;
- canvas.drawCircle(12f, 12f, 10f, sPaint);
- canvas.restore();
+ BigDialView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ init();
}
- @Override
- public void setAlpha(int alpha) { }
-
- @Override
- public void setColorFilter(ColorFilter colorFilter) { }
-
- @Override
- public void setTintList(ColorStateList tint) {
- mTintColor = tint.getDefaultColor();
+ BigDialView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init();
}
- @Override
- public int getOpacity() {
- return PixelFormat.TRANSLUCENT;
+ BigDialView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init();
}
- }
- static class OneDrawable extends Drawable {
- int mTintColor;
+ private void init() {
+ mDialDrawable = new BigDialDrawable();
+ setImageDrawable(mDialDrawable);
+ }
@Override
- public void draw(Canvas canvas) {
- sPaint.setColor(mTintColor | 0xFF000000);
-
- canvas.save();
- canvas.scale(canvas.getWidth() / 24f, canvas.getHeight() / 24f);
-
- final Path p = new Path();
- p.moveTo(12f, 21.83f);
- p.rLineTo(0f, -19.67f);
- p.rLineTo(-5f, 0f);
- canvas.drawPath(p, sPaint);
- canvas.restore();
+ public void onDraw(Canvas c) {
+ super.onDraw(c);
}
- @Override
- public void setAlpha(int alpha) { }
+ double toPositiveDegrees(double rad) {
+ return (Math.toDegrees(rad) + 360 - 90) % 360;
+ }
@Override
- public void setColorFilter(ColorFilter colorFilter) { }
+ public boolean onTouchEvent(MotionEvent ev) {
+ switch (ev.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ mWasLocked = mDialDrawable.isLocked();
+ // pass through
+ case MotionEvent.ACTION_MOVE:
+ float x = ev.getX();
+ float y = ev.getY();
+ float cx = (getLeft() + getRight()) / 2f;
+ float cy = (getTop() + getBottom()) / 2f;
+ float angle = (float) toPositiveDegrees(Math.atan2(x - cx, y - cy));
+ final int oldLevel = mDialDrawable.getUserLevel();
+ mDialDrawable.touchAngle(angle);
+ final int newLevel = mDialDrawable.getUserLevel();
+ if (oldLevel != newLevel) {
+ performHapticFeedback(newLevel == STEPS
+ ? HapticFeedbackConstants.CONFIRM
+ : HapticFeedbackConstants.CLOCK_TICK);
+ }
+ return true;
+ case MotionEvent.ACTION_UP:
+ if (mWasLocked && !mDialDrawable.isLocked()) {
+ launchNextStage(false);
+ }
+ return true;
+ }
+ return false;
+ }
@Override
- public void setTintList(ColorStateList tint) {
- mTintColor = tint.getDefaultColor();
+ public boolean performClick() {
+ if (mDialDrawable.getUserLevel() < STEPS - 1) {
+ mDialDrawable.setUserLevel(mDialDrawable.getUserLevel() + 1);
+ performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK);
+ }
+ return true;
}
- @Override
- public int getOpacity() {
- return PixelFormat.TRANSLUCENT;
+ void setUnlockTries(int tries) {
+ mDialDrawable.setUnlockTries(tries);
}
- }
- private static class BackslashDrawable extends Drawable implements TimeAnimator.TimeListener {
- Bitmap mTile;
- Paint mPaint = new Paint();
- BitmapShader mShader;
- TimeAnimator mAnimator = new TimeAnimator();
- Matrix mMatrix = new Matrix();
+ private class BigDialDrawable extends Drawable {
+ public final int STEPS = 10;
+ private int mUnlockTries = 0;
+ final Paint mPaint = new Paint();
+ final Drawable mEleven;
+ private boolean mNightMode;
+ private float mValue = 0f;
+ float mElevenAnim = 0f;
+ ObjectAnimator mElevenShowAnimator = ObjectAnimator.ofFloat(this, "elevenAnim", 0f,
+ 1f).setDuration(300);
+ ObjectAnimator mElevenHideAnimator = ObjectAnimator.ofFloat(this, "elevenAnim", 1f,
+ 0f).setDuration(500);
+
+ BigDialDrawable() {
+ mNightMode = getContext().getResources().getConfiguration().isNightModeActive();
+ mEleven = getContext().getDrawable(R.drawable.ic_number11);
+ mElevenShowAnimator.setInterpolator(new PathInterpolator(0.4f, 0f, 0.2f, 1f));
+ mElevenHideAnimator.setInterpolator(new PathInterpolator(0.8f, 0.2f, 0.6f, 1f));
+ }
- public void draw(Canvas canvas) {
- canvas.drawPaint(mPaint);
- }
+ public void setUnlockTries(int count) {
+ if (mUnlockTries != count) {
+ mUnlockTries = count;
+ setValue(getValue());
+ invalidateSelf();
+ }
+ }
- BackslashDrawable(int width) {
- mTile = Bitmap.createBitmap(width, width, Bitmap.Config.ALPHA_8);
- mAnimator.setTimeListener(this);
-
- final Canvas tileCanvas = new Canvas(mTile);
- final float w = tileCanvas.getWidth();
- final float h = tileCanvas.getHeight();
-
- final Path path = new Path();
- path.moveTo(0, 0);
- path.lineTo(w / 2, 0);
- path.lineTo(w, h / 2);
- path.lineTo(w, h);
- path.close();
-
- path.moveTo(0, h / 2);
- path.lineTo(w / 2, h);
- path.lineTo(0, h);
- path.close();
-
- final Paint slashPaint = new Paint();
- slashPaint.setAntiAlias(true);
- slashPaint.setStyle(Paint.Style.FILL);
- slashPaint.setColor(0xFF000000);
- tileCanvas.drawPath(path, slashPaint);
-
- //mPaint.setColor(0xFF0000FF);
- mShader = new BitmapShader(mTile, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
- mPaint.setShader(mShader);
- }
+ boolean isLocked() {
+ return mUnlockTries > 0;
+ }
- public void startAnimating() {
- if (!mAnimator.isStarted()) {
- mAnimator.start();
+ public void setValue(float v) {
+ // until the dial is "unlocked", you can't turn it all the way to 11
+ final float max = isLocked() ? 1f - 1f / STEPS : 1f;
+ mValue = v < 0f ? 0f : v > max ? max : v;
+ invalidateSelf();
}
- }
- public void stopAnimating() {
- if (mAnimator.isStarted()) {
- mAnimator.cancel();
+ public float getValue() {
+ return mValue;
}
- }
- @Override
- public void setAlpha(int alpha) {
- mPaint.setAlpha(alpha);
- }
+ public int getUserLevel() {
+ return Math.round(getValue() * STEPS - 0.25f);
+ }
- @Override
- public void setColorFilter(ColorFilter colorFilter) {
- mPaint.setColorFilter(colorFilter);
- }
+ public void setUserLevel(int i) {
+ setValue(getValue() + ((float) i) / STEPS);
+ }
- @Override
- public int getOpacity() {
- return PixelFormat.TRANSLUCENT;
- }
+ public float getElevenAnim() {
+ return mElevenAnim;
+ }
- @Override
- public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
- if (mShader != null) {
- mMatrix.postTranslate(deltaTime / 4f, 0);
- mShader.setLocalMatrix(mMatrix);
- invalidateSelf();
+ public void setElevenAnim(float f) {
+ if (mElevenAnim != f) {
+ mElevenAnim = f;
+ invalidateSelf();
+ }
+ }
+
+ @Override
+ public void draw(@NonNull Canvas canvas) {
+ final Rect bounds = getBounds();
+ final int w = bounds.width();
+ final int h = bounds.height();
+ final float w2 = w / 2f;
+ final float h2 = h / 2f;
+ final float radius = w / 4f;
+
+ canvas.drawColor(mNightMode ? COLOR_NAVY : COLOR_LIGHTBLUE);
+
+ canvas.save();
+ canvas.rotate(45, w2, h2);
+ canvas.clipRect(w2, h2 - radius, Math.min(w, h), h2 + radius);
+ final int gradientColor = mNightMode ? 0x60000020 : (0x10FFFFFF & COLOR_NAVY);
+ mPaint.setShader(
+ new LinearGradient(w2, h2, Math.min(w, h), h2, gradientColor,
+ 0x00FFFFFF & gradientColor, Shader.TileMode.CLAMP));
+ mPaint.setColor(Color.BLACK);
+ canvas.drawPaint(mPaint);
+ mPaint.setShader(null);
+ canvas.restore();
+
+ mPaint.setStyle(Paint.Style.FILL);
+ mPaint.setColor(COLOR_GREEN);
+
+ canvas.drawCircle(w2, h2, radius, mPaint);
+
+ mPaint.setColor(mNightMode ? COLOR_LIGHTBLUE : COLOR_NAVY);
+ final float cx = w * 0.85f;
+ for (int i = 0; i < STEPS; i++) {
+ final float f = (float) i / STEPS;
+ canvas.save();
+ final float angle = valueToAngle(f);
+ canvas.rotate(-angle, w2, h2);
+ canvas.drawCircle(cx, h2, (i <= getUserLevel()) ? 20 : 5, mPaint);
+ canvas.restore();
+ }
+
+ if (mElevenAnim > 0f) {
+ final int color = COLOR_ORANGE;
+ final int size2 = (int) ((0.5 + 0.5f * mElevenAnim) * w / 14);
+ final float cx11 = cx + size2 / 4f;
+ mEleven.setBounds((int) cx11 - size2, (int) h2 - size2,
+ (int) cx11 + size2, (int) h2 + size2);
+ final int alpha = 0xFFFFFF | ((int) clamp(0xFF * 2 * mElevenAnim, 0, 0xFF)
+ << 24);
+ mEleven.setTint(alpha & color);
+ mEleven.draw(canvas);
+ }
+
+ // don't want to use the rounded value here since the quantization will be visible
+ final float angle = valueToAngle(mValue);
+
+ // it's easier to draw at far-right and rotate backwards
+ canvas.rotate(-angle, w2, h2);
+ mPaint.setColor(Color.WHITE);
+ final float dimple = w2 / 12f;
+ canvas.drawCircle(w - radius - dimple * 2, h2, dimple, mPaint);
+ }
+
+ float clamp(float x, float a, float b) {
+ return x < a ? a : x > b ? b : x;
+ }
+
+ float angleToValue(float a) {
+ return 1f - clamp(a / (360 - 45), 0f, 1f);
+ }
+
+ // rotation: min is at 4:30, max is at 3:00
+ float valueToAngle(float v) {
+ return (1f - v) * (360 - 45);
+ }
+
+ public void touchAngle(float a) {
+ final int oldUserLevel = getUserLevel();
+ final float newValue = angleToValue(a);
+ // this is how we prevent the knob from snapping from max back to min, or from
+ // jumping around wherever the user presses. The new value must be pretty close
+ // to the
+ // previous one.
+ if (Math.abs(newValue - getValue()) < VALUE_CHANGE_MAX) {
+ setValue(newValue);
+
+ if (isLocked() && oldUserLevel != STEPS - 1 && getUserLevel() == STEPS - 1) {
+ mUnlockTries--;
+ }
+
+ if (!isLocked()) {
+ if (getUserLevel() == STEPS && mElevenAnim != 1f
+ && !mElevenShowAnimator.isRunning()) {
+ mElevenHideAnimator.cancel();
+ mElevenShowAnimator.start();
+ } else if (getUserLevel() != STEPS && mElevenAnim == 1f
+ && !mElevenHideAnimator.isRunning()) {
+ mElevenShowAnimator.cancel();
+ mElevenHideAnimator.start();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void setAlpha(int i) {
+ }
+
+ @Override
+ public void setColorFilter(@Nullable ColorFilter colorFilter) {
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
}
}
}
}
+
+
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index b1e8ed1f943e..d63ebda5117e 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -617,7 +617,8 @@ public class ResolverListAdapter extends BaseAdapter {
}
}
- UserHandle getUserHandle() {
+ @VisibleForTesting
+ public UserHandle getUserHandle() {
return mResolverListController.getUserHandle();
}
diff --git a/core/java/com/android/internal/app/SimpleIconFactory.java b/core/java/com/android/internal/app/SimpleIconFactory.java
index ffe2dbe4ccc0..2d91e64b2e67 100644
--- a/core/java/com/android/internal/app/SimpleIconFactory.java
+++ b/core/java/com/android/internal/app/SimpleIconFactory.java
@@ -19,6 +19,7 @@ package com.android.internal.app;
import static android.content.Context.ACTIVITY_SERVICE;
import static android.graphics.Paint.DITHER_FLAG;
import static android.graphics.Paint.FILTER_BITMAP_FLAG;
+import static android.graphics.drawable.AdaptiveIconDrawable.getExtraInsetFraction;
import android.annotation.AttrRes;
import android.annotation.NonNull;
@@ -54,6 +55,7 @@ import com.android.internal.R;
import org.xmlpull.v1.XmlPullParser;
import java.nio.ByteBuffer;
+import java.util.Optional;
/**
@@ -64,6 +66,7 @@ import java.nio.ByteBuffer;
@Deprecated
public class SimpleIconFactory {
+
private static final SynchronizedPool<SimpleIconFactory> sPool =
new SynchronizedPool<>(Runtime.getRuntime().availableProcessors());
@@ -251,7 +254,7 @@ public class SimpleIconFactory {
} else if (w > h && h > 0) {
scale = (float) w / h;
}
- Bitmap bitmap = createIconBitmap(icon, scale);
+ Bitmap bitmap = createIconBitmapNoInsetOrMask(icon, scale);
bitmap = maskBitmapToCircle(bitmap);
icon = new BitmapDrawable(mContext.getResources(), bitmap);
@@ -281,15 +284,19 @@ public class SimpleIconFactory {
final Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
bitmap.getHeight(), Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(output);
- final Paint paint = new Paint();
- paint.setAntiAlias(true);
+ final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG
+ | Paint.FILTER_BITMAP_FLAG);
+
+ // Apply an offset to enable shadow to be drawn
+ final int size = bitmap.getWidth();
+ int offset = Math.max((int) Math.ceil(BLUR_FACTOR * size), 1);
// Draw mask
paint.setColor(0xffffffff);
canvas.drawARGB(0, 0, 0, 0);
canvas.drawCircle(bitmap.getWidth() / 2f,
bitmap.getHeight() / 2f,
- bitmap.getWidth() / 2f - 1 /* -1 to avoid circles with flat sides */,
+ bitmap.getWidth() / 2f - offset,
paint);
// Draw masked bitmap
@@ -306,24 +313,61 @@ public class SimpleIconFactory {
}
private Bitmap createIconBitmap(Drawable icon, float scale) {
- return createIconBitmap(icon, scale, mIconBitmapSize);
+ return createIconBitmap(icon, scale, mIconBitmapSize, true, false);
+ }
+
+ private Bitmap createIconBitmapNoInsetOrMask(Drawable icon, float scale) {
+ return createIconBitmap(icon, scale, mIconBitmapSize, false, true);
}
/**
* @param icon drawable that should be flattened to a bitmap
* @param scale the scale to apply before drawing {@param icon} on the canvas
+ * @param insetAdiForShadow when rendering AdaptiveIconDrawables inset to make room for a shadow
+ * @param ignoreAdiMask when rendering AdaptiveIconDrawables ignore the current system mask
*/
- private Bitmap createIconBitmap(Drawable icon, float scale, int size) {
+ private Bitmap createIconBitmap(Drawable icon, float scale, int size, boolean insetAdiForShadow,
+ boolean ignoreAdiMask) {
Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
mCanvas.setBitmap(bitmap);
mOldBounds.set(icon.getBounds());
if (icon instanceof AdaptiveIconDrawable) {
- int offset = Math.max((int) Math.ceil(BLUR_FACTOR * size),
- Math.round(size * (1 - scale) / 2));
- icon.setBounds(offset, offset, size - offset, size - offset);
- icon.draw(mCanvas);
+ final AdaptiveIconDrawable adi = (AdaptiveIconDrawable) icon;
+
+ // By default assumes the output bitmap will have a shadow directly applied and makes
+ // room for it by insetting. If there are intermediate steps before applying the shadow
+ // insetting is disableable.
+ int offset = Math.round(size * (1 - scale) / 2);
+ if (insetAdiForShadow) {
+ offset = Math.max((int) Math.ceil(BLUR_FACTOR * size), offset);
+ }
+ Rect bounds = new Rect(offset, offset, size - offset, size - offset);
+
+ // AdaptiveIconDrawables are by default masked by the user's icon shape selection.
+ // If further masking is to be done, directly render to avoid the system masking.
+ if (ignoreAdiMask) {
+ final int cX = bounds.width() / 2;
+ final int cY = bounds.height() / 2;
+ final float portScale = 1f / (1 + 2 * getExtraInsetFraction());
+ final int insetWidth = (int) (bounds.width() / (portScale * 2));
+ final int insetHeight = (int) (bounds.height() / (portScale * 2));
+
+ Rect childRect = new Rect(cX - insetWidth, cY - insetHeight, cX + insetWidth,
+ cY + insetHeight);
+ Optional.ofNullable(adi.getBackground()).ifPresent(drawable -> {
+ drawable.setBounds(childRect);
+ drawable.draw(mCanvas);
+ });
+ Optional.ofNullable(adi.getForeground()).ifPresent(drawable -> {
+ drawable.setBounds(childRect);
+ drawable.draw(mCanvas);
+ });
+ } else {
+ adi.setBounds(bounds);
+ adi.draw(mCanvas);
+ }
} else {
if (icon instanceof BitmapDrawable) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
diff --git a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
index 86a9af3db196..fe0e7d012262 100644
--- a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
+++ b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
@@ -40,8 +40,10 @@ import java.util.List;
* resolve it to an activity.
*/
public class DisplayResolveInfo implements TargetInfo {
- // Temporary flag for new chooser delegate behavior.
- private static final boolean ENABLE_CHOOSER_DELEGATE = true;
+ // Temporary flag for new chooser delegate behavior. There are occassional token
+ // permission errors from bouncing through the delegate. Watch out before reenabling:
+ // b/157272342 is one example but this issue has been reported many times
+ private static final boolean ENABLE_CHOOSER_DELEGATE = false;
private final ResolveInfo mResolveInfo;
private CharSequence mDisplayLabel;
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 3f03f2a3e754..d22f94213338 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -71,4 +71,6 @@ interface IInputMethodManager {
void reportActivityView(in IInputMethodClient parentClient, int childDisplayId,
in float[] matrixValues);
+
+ void removeImeSurface();
}
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 7c7d9312e2db..b64923fb5bf8 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -72,6 +72,7 @@ import com.android.internal.util.ContrastColorUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
+import java.util.Objects;
import java.util.function.Consumer;
import java.util.regex.Pattern;
@@ -761,9 +762,13 @@ public class ConversationLayout extends FrameLayout
// group
: mExpandedGroupMessagePadding;
+ int iconPadding = mIsOneToOne || mIsCollapsed
+ ? mConversationIconTopPadding
+ : mConversationIconTopPaddingExpandedGroup;
+
mConversationIconContainer.setPaddingRelative(
mConversationIconContainer.getPaddingStart(),
- mConversationIconTopPadding,
+ iconPadding,
mConversationIconContainer.getPaddingEnd(),
mConversationIconContainer.getPaddingBottom());
@@ -780,8 +785,8 @@ public class ConversationLayout extends FrameLayout
}
@RemotableViewMethod
- public void setShortcutIcon(Icon conversationIcon) {
- mConversationIcon = conversationIcon;
+ public void setShortcutIcon(Icon shortcutIcon) {
+ mShortcutIcon = shortcutIcon;
}
/**
@@ -791,7 +796,8 @@ public class ConversationLayout extends FrameLayout
*/
@RemotableViewMethod
public void setConversationTitle(CharSequence conversationTitle) {
- mConversationTitle = conversationTitle;
+ // Remove formatting from the title.
+ mConversationTitle = conversationTitle != null ? conversationTitle.toString() : null;
}
public CharSequence getConversationTitle() {
@@ -1048,6 +1054,9 @@ public class ConversationLayout extends FrameLayout
groups.add(currentGroup);
if (sender == null) {
sender = mUser;
+ } else {
+ // Remove all formatting from the sender name
+ sender = sender.toBuilder().setName(Objects.toString(sender.getName())).build();
}
senders.add(sender);
currentSenderKey = key;
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 03a7b3da6251..93690cdfc811 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1430,6 +1430,32 @@ public class LockPatternUtils {
== StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
}
+ private static class WrappedCallback extends ICheckCredentialProgressCallback.Stub {
+
+ private Handler mHandler;
+ private CheckCredentialProgressCallback mCallback;
+
+ WrappedCallback(Handler handler, CheckCredentialProgressCallback callback) {
+ mHandler = handler;
+ mCallback = callback;
+ }
+
+ @Override
+ public void onCredentialVerified() throws RemoteException {
+ if (mHandler == null) {
+ Log.e(TAG, "Handler is null during callback");
+ }
+ // Kill reference immediately to allow early GC at client side independent of
+ // when system_server decides to lose its reference to the
+ // ICheckCredentialProgressCallback binder object.
+ mHandler.post(() -> {
+ mCallback.onEarlyMatched();
+ mCallback = null;
+ });
+ mHandler = null;
+ }
+ }
+
private ICheckCredentialProgressCallback wrapCallback(
final CheckCredentialProgressCallback callback) {
if (callback == null) {
@@ -1439,13 +1465,7 @@ public class LockPatternUtils {
throw new IllegalStateException("Must construct LockPatternUtils on a looper thread"
+ " to use progress callbacks.");
}
- return new ICheckCredentialProgressCallback.Stub() {
-
- @Override
- public void onCredentialVerified() throws RemoteException {
- mHandler.post(callback::onEarlyMatched);
- }
- };
+ return new WrappedCallback(mHandler, callback);
}
}
diff --git a/core/proto/android/service/notification.proto b/core/proto/android/service/notification.proto
index ecb4193a2c6c..8e4006aa6861 100644
--- a/core/proto/android/service/notification.proto
+++ b/core/proto/android/service/notification.proto
@@ -274,4 +274,74 @@ message PackageRemoteViewInfoProto {
// Next Tag: 2
message NotificationRemoteViewsProto {
repeated PackageRemoteViewInfoProto package_remote_view_info = 1;
-} \ No newline at end of file
+}
+
+/**
+ * Atom that represents an item in the list of Do Not Disturb rules, pulled from
+ * NotificationManagerService.java.
+ */
+message DNDModeProto {
+ enum Mode {
+ ROOT_CONFIG = -1; // Used to distinguish the config (one per user) from the rules.
+ ZEN_MODE_OFF = 0;
+ ZEN_MODE_IMPORTANT_INTERRUPTIONS = 1;
+ ZEN_MODE_NO_INTERRUPTIONS = 2;
+ ZEN_MODE_ALARMS = 3;
+ }
+ optional int32 user = 1; // Android user ID (0, 1, 10, ...)
+ optional bool enabled = 2; // true for ROOT_CONFIG if a manualRule is enabled
+ optional bool channels_bypassing = 3; // only valid for ROOT_CONFIG
+ optional Mode zen_mode = 4;
+ // id is one of the system default rule IDs, or empty
+ // May also be "MANUAL_RULE" to indicate app-activation of the manual rule.
+ optional string id = 5;
+ optional int32 uid = 6; // currently only SYSTEM_UID or 0 for other
+ optional DNDPolicyProto policy = 7;
+}
+
+/**
+ * Atom that represents a Do Not Disturb policy, an optional detail proto for DNDModeProto.
+ */
+message DNDPolicyProto {
+ enum State {
+ STATE_UNSET = 0;
+ STATE_ALLOW = 1;
+ STATE_DISALLOW = 2;
+ }
+ optional State calls = 1;
+ optional State repeat_callers = 2;
+ optional State messages = 3;
+ optional State conversations = 4;
+ optional State reminders = 5;
+ optional State events = 6;
+ optional State alarms = 7;
+ optional State media = 8;
+ optional State system = 9;
+ optional State fullscreen = 10;
+ optional State lights = 11;
+ optional State peek = 12;
+ optional State status_bar = 13;
+ optional State badge = 14;
+ optional State ambient = 15;
+ optional State notification_list = 16;
+
+ enum PeopleType {
+ PEOPLE_UNSET = 0;
+ PEOPLE_ANYONE = 1;
+ PEOPLE_CONTACTS = 2;
+ PEOPLE_STARRED = 3;
+ PEOPLE_NONE = 4;
+ }
+
+ optional PeopleType allow_calls_from = 17;
+ optional PeopleType allow_messages_from = 18;
+
+ enum ConversationType {
+ CONV_UNSET = 0;
+ CONV_ANYONE = 1;
+ CONV_IMPORTANT = 2;
+ CONV_NONE = 3;
+ }
+
+ optional ConversationType allow_conversations_from = 19;
+}
diff --git a/core/proto/android/stats/connectivity/Android.bp b/core/proto/android/stats/connectivity/Android.bp
index 5d642d3845fe..9cd233e1ba85 100644
--- a/core/proto/android/stats/connectivity/Android.bp
+++ b/core/proto/android/stats/connectivity/Android.bp
@@ -13,12 +13,12 @@
// limitations under the License.
java_library_static {
- name: "networkstackprotosnano",
+ name: "networkstackprotos",
proto: {
- type: "nano",
+ type: "lite",
},
srcs: [
"network_stack.proto",
],
- sdk_version: "system_current",
+ sdk_version: "system_29",
}
diff --git a/core/proto/android/stats/connectivity/network_stack.proto b/core/proto/android/stats/connectivity/network_stack.proto
index 7d9aa1c6eb23..e9726d7ce195 100644
--- a/core/proto/android/stats/connectivity/network_stack.proto
+++ b/core/proto/android/stats/connectivity/network_stack.proto
@@ -20,6 +20,160 @@ package android.stats.connectivity;
option java_multiple_files = true;
option java_outer_classname = "NetworkStackProto";
+enum DhcpRenewResult {
+ RR_UNKNOWN = 0;
+ RR_SUCCESS = 1;
+ RR_ERROR_NAK = 2;
+ RR_ERROR_IP_MISMATCH = 3;
+ RR_ERROR_IP_EXPIRE = 4;
+}
+
+enum DisconnectCode {
+ DC_NONE = 0;
+ DC_NORMAL_TERMINATION = 1;
+ DC_PROVISIONING_FAIL = 2;
+ DC_ERROR_STARTING_IPV4 = 4;
+ DC_ERROR_STARTING_IPV6 = 5;
+ DC_ERROR_STARTING_IPREACHABILITYMONITOR = 6;
+ DC_INVALID_PROVISIONING = 7;
+ DC_INTERFACE_NOT_FOUND = 8;
+ DC_PROVISIONING_TIMEOUT = 9;
+}
+
+enum TransportType {
+ TT_UNKNOWN = 0;
+ // Indicates this network uses a Cellular transport
+ TT_CELLULAR = 1;
+ // Indicates this network uses a Wi-Fi transport
+ TT_WIFI = 2;
+ // Indicates this network uses a Bluetooth transport
+ TT_BLUETOOTH = 3;
+ // Indicates this network uses an Ethernet transport
+ TT_ETHERNET = 4;
+ // Indicates this network uses a Wi-Fi Aware transport
+ TT_WIFI_AWARE = 5;
+ // Indicates this network uses a LoWPAN transport
+ TT_LOWPAN = 6;
+ // Indicates this network uses a Cellular+VPN transport
+ TT_CELLULAR_VPN = 7;
+ // Indicates this network uses a Wi-Fi+VPN transport
+ TT_WIFI_VPN = 8;
+ // Indicates this network uses a Bluetooth+VPN transport
+ TT_BLUETOOTH_VPN = 9;
+ // Indicates this network uses an Ethernet+VPN transport
+ TT_ETHERNET_VPN = 10;
+ // Indicates this network uses a Wi-Fi+Cellular+VPN transport
+ TT_WIFI_CELLULAR_VPN = 11;
+ // Indicates this network uses for test only
+ TT_TEST = 12;
+}
+
+enum DhcpFeature {
+ DF_UNKNOWN = 0;
+ // DHCP INIT-REBOOT state
+ DF_INITREBOOT = 1;
+ // DHCP rapid commit option
+ DF_RAPIDCOMMIT = 2;
+ // Duplicate address detection
+ DF_DAD = 3;
+ // Fast initial Link setup
+ DF_FILS = 4;
+}
+
+enum HostnameTransResult {
+ HTR_UNKNOWN = 0;
+ HTR_SUCCESS = 1;
+ HTR_FAILURE = 2;
+ HTR_DISABLE = 3;
+}
+
+enum ProbeResult {
+ PR_UNKNOWN = 0;
+ PR_SUCCESS = 1;
+ PR_FAILURE = 2;
+ PR_PORTAL = 3;
+ // DNS query for the probe host returned a private IP address
+ PR_PRIVATE_IP_DNS = 4;
+}
+
+enum ValidationResult {
+ VR_UNKNOWN = 0;
+ VR_SUCCESS = 1;
+ VR_FAILURE = 2;
+ VR_PORTAL = 3;
+ VR_PARTIAL = 4;
+}
+
+enum ProbeType {
+ PT_UNKNOWN = 0;
+ PT_DNS = 1;
+ PT_HTTP = 2;
+ PT_HTTPS = 3;
+ PT_PAC = 4;
+ PT_FALLBACK = 5;
+ PT_PRIVDNS = 6;
+ PT_CAPPORT_API = 7;
+}
+
+// The Dhcp error code is defined in android.net.metrics.DhcpErrorEvent
+enum DhcpErrorCode {
+ ET_UNKNOWN = 0;
+ ET_L2_ERROR = 1;
+ ET_L3_ERROR = 2;
+ ET_L4_ERROR = 3;
+ ET_DHCP_ERROR = 4;
+ ET_MISC_ERROR = 5;
+ /* Reserve for error type
+ // ET_L2_ERROR_TYPE = ET_L2_ERROR << 8;
+ ET_L2_ERROR_TYPE = 256;
+ // ET_L3_ERROR_TYPE = ET_L3_ERROR << 8;
+ ET_L3_ERROR_TYPE = 512;
+ // ET_L4_ERROR_TYPE = ET_L4_ERROR << 8;
+ ET_L4_ERROR_TYPE = 768;
+ // ET_DHCP_ERROR_TYPE = ET_DHCP_ERROR << 8;
+ ET_DHCP_ERROR_TYPE = 1024;
+ // ET_MISC_ERROR_TYPE = ET_MISC_ERROR << 8;
+ ET_MISC_ERROR_TYPE = 1280;
+ */
+ // ET_L2_TOO_SHORT = (ET_L2_ERROR_TYPE | 0x1) << 16;
+ ET_L2_TOO_SHORT = 16842752;
+ // ET_L2_WRONG_ETH_TYPE = (ET_L2_ERROR_TYPE | 0x2) << 16;
+ ET_L2_WRONG_ETH_TYPE = 16908288;
+ // ET_L3_TOO_SHORT = (ET_L3_ERROR_TYPE | 0x1) << 16;
+ ET_L3_TOO_SHORT = 33619968;
+ // ET_L3_NOT_IPV4 = (ET_L3_ERROR_TYPE | 0x2) << 16;
+ ET_L3_NOT_IPV4 = 33685504;
+ // ET_L3_INVALID_IP = (ET_L3_ERROR_TYPE | 0x3) << 16;
+ ET_L3_INVALID_IP = 33751040;
+ // ET_L4_NOT_UDP = (ET_L4_ERROR_TYPE | 0x1) << 16;
+ ET_L4_NOT_UDP = 50397184;
+ // ET_L4_WRONG_PORT = (ET_L4_ERROR_TYPE | 0x2) << 16;
+ ET_L4_WRONG_PORT = 50462720;
+ // ET_BOOTP_TOO_SHORT = (ET_DHCP_ERROR_TYPE | 0x1) << 16;
+ ET_BOOTP_TOO_SHORT = 67174400;
+ // ET_DHCP_BAD_MAGIC_COOKIE = (ET_DHCP_ERROR_TYPE | 0x2) << 16;
+ ET_DHCP_BAD_MAGIC_COOKIE = 67239936;
+ // ET_DHCP_INVALID_OPTION_LENGTH = (ET_DHCP_ERROR_TYPE | 0x3) << 16;
+ ET_DHCP_INVALID_OPTION_LENGTH = 67305472;
+ // ET_DHCP_NO_MSG_TYPE = (ET_DHCP_ERROR_TYPE | 0x4) << 16;
+ ET_DHCP_NO_MSG_TYPE = 67371008;
+ // ET_DHCP_UNKNOWN_MSG_TYPE = (ET_DHCP_ERROR_TYPE | 0x5) << 16;
+ ET_DHCP_UNKNOWN_MSG_TYPE = 67436544;
+ // ET_DHCP_NO_COOKIE = (ET_DHCP_ERROR_TYPE | 0x6) << 16;
+ ET_DHCP_NO_COOKIE = 67502080;
+ // ET_BUFFER_UNDERFLOW = (ET_MISC_ERROR_TYPE | 0x1) << 16;
+ ET_BUFFER_UNDERFLOW = 83951616;
+ // ET_RECEIVE_ERROR = (ET_MISC_ERROR_TYPE | 0x2) << 16;
+ ET_RECEIVE_ERROR = 84017152;
+ // ET_PARSING_ERROR = (ET_MISC_ERROR_TYPE | 0x3) << 16;
+ ET_PARSING_ERROR = 84082688;
+}
+
+enum NetworkQuirkEvent {
+ QE_UNKNOWN = 0;
+ QE_IPV6_PROVISIONING_ROUTER_LOST = 1;
+}
+
message NetworkStackEventData {
}
diff --git a/core/res/res/drawable-nodpi/ic_number11.xml b/core/res/res/drawable-nodpi/ic_number11.xml
new file mode 100644
index 000000000000..daad61148c80
--- /dev/null
+++ b/core/res/res/drawable-nodpi/ic_number11.xml
@@ -0,0 +1,12 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M5.14,5H1.59a0.88,0.88 0,0 1,-0.88 -0.89V0.88A0.87,0.87 0,0 1,1.59 0H9.36a0.87,0.87 0,0 1,0.88 0.88V23.12a0.88,0.88 0,0 1,-0.88 0.88H6a0.88,0.88 0,0 1,-0.88 -0.88Z"
+ android:fillColor="#000000"/>
+ <path
+ android:pathData="M18.19,5H14.64a0.89,0.89 0,0 1,-0.88 -0.89V0.88A0.88,0.88 0,0 1,14.64 0h7.78a0.87,0.87 0,0 1,0.87 0.88V23.12a0.88,0.88 0,0 1,-0.87 0.88H19.08a0.89,0.89 0,0 1,-0.89 -0.88Z"
+ android:fillColor="#000000"/>
+</vector>
diff --git a/core/res/res/layout-car/car_alert_dialog.xml b/core/res/res/layout-car/car_alert_dialog.xml
new file mode 100644
index 000000000000..569e5948e2e0
--- /dev/null
+++ b/core/res/res/layout-car/car_alert_dialog.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<com.android.internal.widget.AlertDialogLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/parentPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="start|top"
+ android:orientation="vertical">
+
+ <include layout="@layout/car_alert_dialog_title" />
+
+ <FrameLayout
+ android:id="@+id/contentPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="48dp">
+
+ <ScrollView
+ android:id="@+id/scrollView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <Space
+ android:id="@+id/textSpacerNoTitle"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/dialog_no_title_padding_top" />
+
+ <TextView
+ android:id="@+id/message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/text_view_start_margin"
+ android:layout_marginEnd="@dimen/text_view_end_margin"
+ style="@style/CarBody2"/>
+
+ <!-- we don't need this spacer, but the id needs to be here for compatibility -->
+ <Space
+ android:id="@+id/textSpacerNoButtons"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="0dp" />
+ </LinearLayout>
+ </ScrollView>
+ </FrameLayout>
+
+ <FrameLayout
+ android:id="@+id/customPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="48dp">
+
+ <FrameLayout
+ android:id="@+id/custom"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </FrameLayout>
+
+ <include
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ layout="@layout/car_alert_dialog_button_bar" />
+</com.android.internal.widget.AlertDialogLayout>
diff --git a/core/res/res/layout-car/car_alert_dialog_button_bar.xml b/core/res/res/layout-car/car_alert_dialog_button_bar.xml
new file mode 100644
index 000000000000..277b0dcca657
--- /dev/null
+++ b/core/res/res/layout-car/car_alert_dialog_button_bar.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/buttonPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scrollbarAlwaysDrawVerticalTrack="true"
+ android:scrollIndicators="top|bottom"
+ android:fillViewport="true"
+ style="?attr/buttonBarStyle">
+ <com.android.internal.widget.ButtonBarLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="@dimen/button_bar_layout_start_padding"
+ android:paddingEnd="@dimen/button_bar_layout_end_padding"
+ android:paddingTop="@dimen/button_bar_layout_top_padding"
+ android:layoutDirection="locale"
+ android:orientation="horizontal"
+ android:gravity="left|center_vertical">
+
+ <Button
+ android:id="@+id/button3"
+ style="@style/CarAction1"
+ android:background="@drawable/car_dialog_button_background"
+ android:layout_marginRight="@dimen/button_end_margin"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/button_layout_height" />
+
+ <Button
+ android:id="@+id/button2"
+ style="@style/CarAction1"
+ android:background="@drawable/car_dialog_button_background"
+ android:layout_marginRight="@dimen/button_end_margin"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/button_layout_height" />
+
+ <Button
+ android:id="@+id/button1"
+ style="@style/CarAction1"
+ android:background="@drawable/car_dialog_button_background"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/button_layout_height" />
+ <Space
+ android:id="@+id/spacer"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:visibility="invisible" />
+ </com.android.internal.widget.ButtonBarLayout>
+</ScrollView>
diff --git a/core/res/res/layout-car/car_alert_dialog_title.xml b/core/res/res/layout-car/car_alert_dialog_title.xml
new file mode 100644
index 000000000000..ba735a649dc2
--- /dev/null
+++ b/core/res/res/layout-car/car_alert_dialog_title.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/topPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:orientation="vertical">
+
+ <!-- If the client uses a customTitle, it will be added here. -->
+
+ <RelativeLayout
+ android:id="@+id/title_template"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/car_card_header_height"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="@dimen/image_size"
+ android:layout_height="@dimen/image_size"
+ android:layout_marginStart="@dimen/image_margin_start"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:scaleType="fitCenter"
+ android:src="@null" />
+
+ <com.android.internal.widget.DialogTitle
+ android:id="@+id/alertTitle"
+ android:maxLines="1"
+ android:ellipsize="none"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_toEndOf="@+id/icon"
+ android:textAlignment="viewStart"
+ android:layout_centerVertical="true"
+ android:layout_marginStart="@dimen/text_view_start_margin"
+ android:layout_marginEnd="@dimen/text_view_end_margin"
+ style="?attr/windowTitleStyle" />
+ </RelativeLayout>
+
+ <Space
+ android:id="@+id/titleDividerNoCustom"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="0dp" />
+</LinearLayout>
diff --git a/core/res/res/layout/notification_template_material_conversation.xml b/core/res/res/layout/notification_template_material_conversation.xml
index d3857941969b..139185f98b69 100644
--- a/core/res/res/layout/notification_template_material_conversation.xml
+++ b/core/res/res/layout/notification_template_material_conversation.xml
@@ -148,7 +148,6 @@
android:layout_weight="1"
/>
-
<TextView
android:id="@+id/app_name_divider"
android:layout_width="wrap_content"
@@ -174,6 +173,7 @@
android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin"
android:paddingTop="1sp"
android:singleLine="true"
+ android:visibility="gone"
/>
<TextView
@@ -192,7 +192,7 @@
<DateTimeView
android:id="@+id/time"
- android:textAppearance="@style/TextAppearance.Material.Notification.Time"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b79c9e804f89..0e11d49c93e5 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4313,10 +4313,6 @@
notifications until they target R -->
<string-array name="config_notificationMsgPkgsAllowedAsConvos" translatable="false"/>
- <!-- Contains a blacklist of apps that should not get pre-installed carrier app permission
- grants, even if the UICC claims that the app should be privileged. See b/138150105 -->
- <string-array name="config_restrictedPreinstalledCarrierApps" translatable="false"/>
-
<!-- Sharesheet: define a max number of targets per application for new shortcuts-based direct share introduced in Q -->
<integer name="config_maxShortcutTargetsPerApp">3</integer>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 4ee919289f28..ebaf85c64a14 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -734,7 +734,7 @@
<dimen name="conversation_icon_container_top_padding">12dp</dimen>
<!-- The top padding of the conversation icon container when the avatar is small-->
- <dimen name="conversation_icon_container_top_padding_small_avatar">17.5dp</dimen>
+ <dimen name="conversation_icon_container_top_padding_small_avatar">9dp</dimen>
<!-- The padding of the conversation header when expanded. This is calculated from the expand button size + notification_content_margin_end -->
<dimen name="conversation_header_expanded_padding_end">38dp</dimen>
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index e9ac679ec39c..ef019ba92769 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -296,9 +296,12 @@ easier.
<style name="TextAppearance.DeviceDefault.Notification.Info" parent="TextAppearance.Material.Notification.Info">
<item name="fontFamily">@string/config_bodyFontFamily</item>
</style>
+ <style name="TextAppearance.DeviceDefault.Notification.Time" parent="TextAppearance.Material.Notification.Time">
+ <item name="fontFamily">@string/config_bodyFontFamily</item>
+ </style>
<style name="TextAppearance.DeviceDefault.Notification.Conversation.AppName"
- parent="@*android:style/TextAppearance.DeviceDefault.Notification.Title">
- <item name="android:textSize">16sp</item>
+ parent="TextAppearance.Material.Notification.Conversation.AppName">
+ <item name="fontFamily">@string/config_headlineFontFamilyMedium</item>
</style>
<style name="TextAppearance.DeviceDefault.Widget" parent="TextAppearance.Material.Widget">
<item name="fontFamily">@string/config_bodyFontFamily</item>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 2415837cf826..67536fde9b0b 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -491,6 +491,10 @@ please see styles_device_defaults.xml.
<item name="textColor">#66000000</item>
</style>
+ <style name="TextAppearance.Material.Notification.Conversation.AppName" parent="TextAppearance.Material.Notification.Title">
+ <item name="android:textSize">16sp</item>
+ </style>
+
<style name="TextAppearance.Material.ListItem" parent="TextAppearance.Material.Subhead" />
<style name="TextAppearance.Material.ListItemSecondary" parent="TextAppearance.Material.Body1" />
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 23ae1e7d271e..8e1dc8950155 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3827,10 +3827,10 @@
<java-symbol type="string" name="config_defaultSupervisionProfileOwnerComponent" />
<java-symbol type="bool" name="config_inflateSignalStrength" />
- <java-symbol type="array" name="config_restrictedPreinstalledCarrierApps" />
<java-symbol type="drawable" name="android_logotype" />
<java-symbol type="layout" name="platlogo_layout" />
+ <java-symbol type="drawable" name="ic_number11" />
<java-symbol type="integer" name="config_notificationWarnRemoteViewSizeBytes" />
<java-symbol type="integer" name="config_notificationStripRemoteViewSizeBytes" />
@@ -3956,6 +3956,13 @@
<java-symbol type="id" name="conversation_unread_count" />
<java-symbol type="string" name="unread_convo_overflow" />
<java-symbol type="style" name="TextAppearance.DeviceDefault.Notification.Conversation.AppName" />
+ <java-symbol type="drawable" name="conversation_badge_background" />
+ <java-symbol type="drawable" name="conversation_badge_ring" />
+ <java-symbol type="color" name="conversation_important_highlight" />
+ <java-symbol type="dimen" name="importance_ring_stroke_width" />
+ <java-symbol type="dimen" name="importance_ring_anim_max_stroke_width" />
+ <java-symbol type="dimen" name="importance_ring_size" />
+ <java-symbol type="dimen" name="conversation_icon_size_badged" />
<!-- Intent resolver and share sheet -->
<java-symbol type="string" name="resolver_personal_tab" />
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 70917e76f8b4..d5733e34a8ef 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -39,7 +39,7 @@
<!-- Albania: 5 digits, known short codes listed -->
<shortcode country="al" pattern="\\d{5}" premium="15191|55[56]00" />
- <!-- Argentia: 5 digits, known short codes listed -->
+ <!-- Argentina: 5 digits, known short codes listed -->
<shortcode country="ar" pattern="\\d{5}" free="11711|28291" />
<!-- Armenia: 3-4 digits, emergency numbers 10[123] -->
@@ -70,7 +70,7 @@
<shortcode country="by" pattern="\\d{4}" premium="3336|4161|444[4689]|501[34]|7781" />
<!-- Canada: 5-6 digits -->
- <shortcode country="ca" pattern="\\d{5,6}" premium="60999|88188|43030" standard="244444" />
+ <shortcode country="ca" pattern="\\d{5,6}" premium="60999|88188|43030" standard="244444" free="455677" />
<!-- Switzerland: 3-5 digits: http://www.swisscom.ch/fxres/kmu/thirdpartybusiness_code_of_conduct_en.pdf -->
<shortcode country="ch" pattern="[2-9]\\d{2,4}" premium="543|83111|30118" free="98765|30075|30047" />
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index d4c256972b28..964ae21d6086 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -678,7 +678,8 @@ public class InsetsControllerTest {
final InsetsState currentState = new InsetsState(mController.getState());
// The caption bar source should be synced with the info in mAttachInfo.
assertEquals(captionFrame, currentState.peekSource(ITYPE_CAPTION_BAR).getFrame());
- assertTrue(currentState.equals(state, true /* excludingCaptionInsets*/));
+ assertTrue(currentState.equals(state, true /* excludingCaptionInsets*/,
+ true /* excludeInvisibleIme */));
mController.setCaptionInsetsHeight(0);
mController.onStateChanged(state);
// The caption bar source should not be there at all, because we don't add empty
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index cd93eeb857f1..7115acfedcf6 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -276,6 +276,15 @@ public class InsetsStateTest {
}
@Test
+ public void testEquals_excludeInvisibleIme() {
+ mState.getSource(ITYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(ITYPE_IME).setVisible(false);
+ mState2.getSource(ITYPE_IME).setFrame(new Rect(0, 0, 100, 200));
+ mState2.getSource(ITYPE_IME).setVisible(false);
+ assertTrue(mState2.equals(mState, true, true /* excludeInvisibleIme */));
+ }
+
+ @Test
public void testParcelUnparcel() {
mState.getSource(ITYPE_IME).setFrame(new Rect(0, 0, 100, 100));
mState.getSource(ITYPE_IME).setVisibleFrame(new Rect(0, 0, 50, 10));
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index 547176855f32..49de7c80057f 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -21,6 +21,7 @@ import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.swipeUp;
import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.hasSibling;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
@@ -299,9 +300,8 @@ public class ChooserActivityTest {
public void fourOptionsStackedIntoOneTarget() throws InterruptedException {
Intent sendIntent = createSendTextIntent();
- // create 12 unique app targets to ensure the app ranking row can be filled, otherwise
- // targets will not stack
- List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(12);
+ // create just enough targets to ensure the a-z list should be shown
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(1);
// next create 4 targets in a single app that should be stacked into a single target
String packageName = "xxx.yyy";
@@ -328,8 +328,8 @@ public class ChooserActivityTest {
.launchActivity(Intent.createChooser(sendIntent, null));
waitForIdle();
- // expect 12 unique targets + 1 group + 4 ranked app targets
- assertThat(activity.getAdapter().getCount(), is(17));
+ // expect 1 unique targets + 1 group + 4 ranked app targets
+ assertThat(activity.getAdapter().getCount(), is(6));
ResolveInfo[] chosen = new ResolveInfo[1];
sOverrides.onSafelyStartCallback = targetInfo -> {
@@ -337,7 +337,7 @@ public class ChooserActivityTest {
return true;
};
- onView(withText(appName)).perform(click());
+ onView(allOf(withText(appName), hasSibling(withText("")))).perform(click());
waitForIdle();
// clicking will launch a dialog to choose the activity within the app
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
index 44a52639bd35..0f6b51f82116 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -222,6 +222,11 @@ public class ChooserWrapperActivity extends ChooserActivity {
super.queryTargetServices(adapter);
}
+ @Override
+ protected boolean isQuietModeEnabled(UserHandle userHandle) {
+ return sOverrides.isQuietModeEnabled;
+ }
+
/**
* We cannot directly mock the activity created since instrumentation creates it.
* <p>
diff --git a/core/tests/overlaytests/remount/Android.bp b/core/tests/overlaytests/remount/Android.bp
index 4e79a4574af4..939334c94312 100644
--- a/core/tests/overlaytests/remount/Android.bp
+++ b/core/tests/overlaytests/remount/Android.bp
@@ -19,6 +19,9 @@ java_test_host {
"tradefed",
"junit",
],
+ static_libs: [
+ "frameworks-base-hostutils",
+ ],
test_suites: ["general-tests"],
java_resources: [
":com.android.overlaytest.overlaid",
diff --git a/core/tests/overlaytests/remount/src/com/android/overlaytest/remounted/OverlayRemountedTestBase.java b/core/tests/overlaytests/remount/src/com/android/overlaytest/remounted/OverlayRemountedTestBase.java
index 14b5bf6eacba..1a39e20f9a2b 100644
--- a/core/tests/overlaytests/remount/src/com/android/overlaytest/remounted/OverlayRemountedTestBase.java
+++ b/core/tests/overlaytests/remount/src/com/android/overlaytest/remounted/OverlayRemountedTestBase.java
@@ -18,6 +18,7 @@ package com.android.overlaytest.remounted;
import static org.junit.Assert.fail;
+import com.android.internal.util.test.SystemPreparer;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
diff --git a/core/tests/overlaytests/remount/src/com/android/overlaytest/remounted/SystemPreparer.java b/core/tests/overlaytests/remount/src/com/android/overlaytest/remounted/SystemPreparer.java
deleted file mode 100644
index bb72d0ee1d03..000000000000
--- a/core/tests/overlaytests/remount/src/com/android/overlaytest/remounted/SystemPreparer.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.overlaytest.remounted;
-
-import static org.junit.Assert.assertTrue;
-
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-
-import org.junit.Assert;
-import org.junit.rules.ExternalResource;
-import org.junit.rules.TemporaryFolder;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-
-class SystemPreparer extends ExternalResource {
- private static final long OVERLAY_ENABLE_TIMEOUT_MS = 30000;
-
- // The paths of the files pushed onto the device through this rule.
- private ArrayList<String> mPushedFiles = new ArrayList<>();
-
- // The package names of packages installed through this rule.
- private ArrayList<String> mInstalledPackages = new ArrayList<>();
-
- private final TemporaryFolder mHostTempFolder;
- private final DeviceProvider mDeviceProvider;
-
- SystemPreparer(TemporaryFolder hostTempFolder, DeviceProvider deviceProvider) {
- mHostTempFolder = hostTempFolder;
- mDeviceProvider = deviceProvider;
- }
-
- /** Copies a file within the host test jar to a path on device. */
- SystemPreparer pushResourceFile(String resourcePath,
- String outputPath) throws DeviceNotAvailableException, IOException {
- final ITestDevice device = mDeviceProvider.getDevice();
- remount();
- assertTrue(device.pushFile(copyResourceToTemp(resourcePath), outputPath));
- mPushedFiles.add(outputPath);
- return this;
- }
-
- /** Installs an APK within the host test jar onto the device. */
- SystemPreparer installResourceApk(String resourcePath, String packageName)
- throws DeviceNotAvailableException, IOException {
- final ITestDevice device = mDeviceProvider.getDevice();
- final File tmpFile = copyResourceToTemp(resourcePath);
- final String result = device.installPackage(tmpFile, true /* reinstall */);
- Assert.assertNull(result);
- mInstalledPackages.add(packageName);
- return this;
- }
-
- /** Sets the enable state of an overlay package. */
- SystemPreparer setOverlayEnabled(String packageName, boolean enabled)
- throws DeviceNotAvailableException {
- final ITestDevice device = mDeviceProvider.getDevice();
- final String enable = enabled ? "enable" : "disable";
-
- // Wait for the overlay to change its enabled state.
- final long endMillis = System.currentTimeMillis() + OVERLAY_ENABLE_TIMEOUT_MS;
- String result;
- while (System.currentTimeMillis() <= endMillis) {
- device.executeShellCommand(String.format("cmd overlay %s %s", enable, packageName));
- result = device.executeShellCommand("cmd overlay dump isenabled "
- + packageName);
- if (((enabled) ? "true\n" : "false\n").equals(result)) {
- return this;
- }
-
- try {
- Thread.sleep(200);
- } catch (InterruptedException ignore) {
- }
- }
-
- throw new IllegalStateException(String.format("Failed to %s overlay %s:\n%s", enable,
- packageName, device.executeShellCommand("cmd overlay list")));
- }
-
- /** Restarts the device and waits until after boot is completed. */
- SystemPreparer reboot() throws DeviceNotAvailableException {
- final ITestDevice device = mDeviceProvider.getDevice();
- device.reboot();
- return this;
- }
-
- SystemPreparer remount() throws DeviceNotAvailableException {
- mDeviceProvider.getDevice().executeAdbCommand("remount");
- return this;
- }
-
- /** Copies a file within the host test jar to a temporary file on the host machine. */
- private File copyResourceToTemp(String resourcePath) throws IOException {
- final File tempFile = mHostTempFolder.newFile(resourcePath);
- final ClassLoader classLoader = getClass().getClassLoader();
- try (InputStream assetIs = classLoader.getResource(resourcePath).openStream();
- FileOutputStream assetOs = new FileOutputStream(tempFile)) {
- if (assetIs == null) {
- throw new IllegalStateException("Failed to find resource " + resourcePath);
- }
-
- int b;
- while ((b = assetIs.read()) >= 0) {
- assetOs.write(b);
- }
- }
-
- return tempFile;
- }
-
- /** Removes installed packages and files that were pushed to the device. */
- @Override
- protected void after() {
- final ITestDevice device = mDeviceProvider.getDevice();
- try {
- remount();
- for (final String file : mPushedFiles) {
- device.deleteFile(file);
- }
- for (final String packageName : mInstalledPackages) {
- device.uninstallPackage(packageName);
- }
- device.reboot();
- } catch (DeviceNotAvailableException e) {
- Assert.fail(e.toString());
- }
- }
-
- interface DeviceProvider {
- ITestDevice getDevice();
- }
-}
diff --git a/data/etc/preinstalled-packages-platform.xml b/data/etc/preinstalled-packages-platform.xml
index 17e1f2e0c229..625558406b9c 100644
--- a/data/etc/preinstalled-packages-platform.xml
+++ b/data/etc/preinstalled-packages-platform.xml
@@ -17,7 +17,9 @@
<!--
This XML file declares which system packages should be initially installed for new users based on
their user type. All system packages on the device should ideally have an entry in an xml file
-(keyed by its manifest name).
+(keyed by its manifest name), except auto-generated rro packages. Auto-generated RRO packages
+(package name ends with ".auto_generated_rro_product__" or ".auto_generated_rro_vendor__")
+will be installed for new users according to corresponding overlay target packages.
Base user-types (every user will be at least one of these types) are:
SYSTEM (user 0)
@@ -51,7 +53,7 @@ The following three examples should cover most normal cases:
2. For a system package to be pre-installed on all human users (e.g. a web browser), i.e. to be
-installed on any user of type type FULL or PROFILE (since this covers all human users):
+installed on any user of type FULL or PROFILE (since this covers all human users):
<install-in-user-type package="com.android.example">
<install-in user-type="FULL" />
diff --git a/libs/incident/Android.bp b/libs/incident/Android.bp
index af6411011411..d291ec001daf 100644
--- a/libs/incident/Android.bp
+++ b/libs/incident/Android.bp
@@ -95,7 +95,17 @@ cc_test {
name: "libincident_test",
test_config: "AndroidTest.xml",
defaults: ["libincidentpriv_defaults"],
- test_suites: ["device-tests"],
+ test_suites: ["device-tests", "mts"],
+ compile_multilib: "both",
+ multilib: {
+ lib64: {
+ suffix: "64",
+ },
+ lib32: {
+ suffix: "32",
+ },
+ },
+ require_root: true,
include_dirs: [
"frameworks/base/libs/incident/include",
diff --git a/libs/incident/AndroidTest.xml b/libs/incident/AndroidTest.xml
index 7c0b04471d13..b6b3f8596826 100644
--- a/libs/incident/AndroidTest.xml
+++ b/libs/incident/AndroidTest.xml
@@ -16,13 +16,17 @@
<configuration description="Config for libincident_test">
<option name="test-suite-tag" value="device-tests" />
<option name="config-descriptor:metadata" key="component" value="misc" />
- <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
<option name="push" value="libincident_test->/data/local/tmp/libincident_test" />
+ <option name="append-bitness" value="true" />
</target_preparer>
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="libincident_test" />
</test>
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+ <option name="mainline-module-package-name" value="com.google.android.os.statsd" />
+ </object>
</configuration>
diff --git a/libs/incident/TEST_MAPPING b/libs/incident/TEST_MAPPING
index 59ebe7664b5f..25e000092ecf 100644
--- a/libs/incident/TEST_MAPPING
+++ b/libs/incident/TEST_MAPPING
@@ -2,9 +2,6 @@
"presubmit": [
{
"name": "libincident_test"
- },
- {
- "name": "GtsLibIncidentTests"
}
]
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index a112bdd0ce03..7d15bbd46697 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -2583,9 +2583,15 @@ public class LocationManager {
}
public void cancel() {
+ remove();
+ }
+
+ private Consumer<Location> remove() {
+ Consumer<Location> consumer;
ICancellationSignal cancellationSignal;
synchronized (this) {
mExecutor = null;
+ consumer = mConsumer;
mConsumer = null;
if (mAlarmManager != null) {
@@ -2605,6 +2611,8 @@ public class LocationManager {
// ignore
}
}
+
+ return consumer;
}
public void fail() {
@@ -2663,16 +2671,10 @@ public class LocationManager {
}
private void acceptResult(Location location) {
- Consumer<Location> consumer;
- synchronized (this) {
- if (mConsumer == null) {
- return;
- }
- consumer = mConsumer;
- cancel();
+ Consumer<Location> consumer = remove();
+ if (consumer != null) {
+ consumer.accept(location);
}
-
- consumer.accept(location);
}
}
diff --git a/media/java/android/media/IMediaRouterClient.aidl b/media/java/android/media/IMediaRouterClient.aidl
index 240ae796f957..53122bb990d6 100644
--- a/media/java/android/media/IMediaRouterClient.aidl
+++ b/media/java/android/media/IMediaRouterClient.aidl
@@ -23,4 +23,5 @@ oneway interface IMediaRouterClient {
void onStateChanged();
void onRestoreRoute();
void onSelectedRouteChanged(String routeId);
+ void onGlobalA2dpChanged(boolean a2dpOn);
}
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index e5ad569bb24f..54c0bc94c2d0 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -317,9 +317,10 @@ public final class MediaRoute2Info implements Parcelable {
@ConnectionState
final int mConnectionState;
final String mClientPackageName;
- final int mVolume;
- final int mVolumeMax;
final int mVolumeHandling;
+ final int mVolumeMax;
+ final int mVolume;
+ final String mAddress;
final Bundle mExtras;
final String mProviderId;
@@ -336,6 +337,7 @@ public final class MediaRoute2Info implements Parcelable {
mVolumeHandling = builder.mVolumeHandling;
mVolumeMax = builder.mVolumeMax;
mVolume = builder.mVolume;
+ mAddress = builder.mAddress;
mExtras = builder.mExtras;
mProviderId = builder.mProviderId;
}
@@ -353,6 +355,7 @@ public final class MediaRoute2Info implements Parcelable {
mVolumeHandling = in.readInt();
mVolumeMax = in.readInt();
mVolume = in.readInt();
+ mAddress = in.readString();
mExtras = in.readBundle();
mProviderId = in.readString();
}
@@ -483,6 +486,15 @@ public final class MediaRoute2Info implements Parcelable {
return mVolume;
}
+ /**
+ * Gets the hardware address of the route if available.
+ * @hide
+ */
+ @Nullable
+ public String getAddress() {
+ return mAddress;
+ }
+
@Nullable
public Bundle getExtras() {
return mExtras == null ? null : new Bundle(mExtras);
@@ -564,6 +576,7 @@ public final class MediaRoute2Info implements Parcelable {
&& (mVolumeHandling == other.mVolumeHandling)
&& (mVolumeMax == other.mVolumeMax)
&& (mVolume == other.mVolume)
+ && Objects.equals(mAddress, other.mAddress)
&& Objects.equals(mProviderId, other.mProviderId);
}
@@ -572,7 +585,7 @@ public final class MediaRoute2Info implements Parcelable {
// Note: mExtras is not included.
return Objects.hash(mId, mName, mFeatures, mType, mIsSystem, mIconUri, mDescription,
mConnectionState, mClientPackageName, mVolumeHandling, mVolumeMax, mVolume,
- mProviderId);
+ mAddress, mProviderId);
}
@Override
@@ -614,6 +627,7 @@ public final class MediaRoute2Info implements Parcelable {
dest.writeInt(mVolumeHandling);
dest.writeInt(mVolumeMax);
dest.writeInt(mVolume);
+ dest.writeString(mAddress);
dest.writeBundle(mExtras);
dest.writeString(mProviderId);
}
@@ -637,6 +651,7 @@ public final class MediaRoute2Info implements Parcelable {
int mVolumeHandling = PLAYBACK_VOLUME_FIXED;
int mVolumeMax;
int mVolume;
+ String mAddress;
Bundle mExtras;
String mProviderId;
@@ -669,24 +684,7 @@ public final class MediaRoute2Info implements Parcelable {
* @param routeInfo the existing instance to copy data from.
*/
public Builder(@NonNull MediaRoute2Info routeInfo) {
- Objects.requireNonNull(routeInfo, "routeInfo must not be null");
-
- mId = routeInfo.mId;
- mName = routeInfo.mName;
- mFeatures = new ArrayList<>(routeInfo.mFeatures);
- mType = routeInfo.mType;
- mIsSystem = routeInfo.mIsSystem;
- mIconUri = routeInfo.mIconUri;
- mDescription = routeInfo.mDescription;
- mConnectionState = routeInfo.mConnectionState;
- mClientPackageName = routeInfo.mClientPackageName;
- mVolumeHandling = routeInfo.mVolumeHandling;
- mVolumeMax = routeInfo.mVolumeMax;
- mVolume = routeInfo.mVolume;
- if (routeInfo.mExtras != null) {
- mExtras = new Bundle(routeInfo.mExtras);
- }
- mProviderId = routeInfo.mProviderId;
+ this(routeInfo.mId, routeInfo);
}
/**
@@ -715,6 +713,7 @@ public final class MediaRoute2Info implements Parcelable {
mVolumeHandling = routeInfo.mVolumeHandling;
mVolumeMax = routeInfo.mVolumeMax;
mVolume = routeInfo.mVolume;
+ mAddress = routeInfo.mAddress;
if (routeInfo.mExtras != null) {
mExtras = new Bundle(routeInfo.mExtras);
}
@@ -865,6 +864,16 @@ public final class MediaRoute2Info implements Parcelable {
}
/**
+ * Sets the hardware address of the route.
+ * @hide
+ */
+ @NonNull
+ public Builder setAddress(String address) {
+ mAddress = address;
+ return this;
+ }
+
+ /**
* Sets a bundle of extras for the route.
* <p>
* Note: The extras will not affect the result of {@link MediaRoute2Info#equals(Object)}.
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index 05c6e3ad9392..e199cf49db9a 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -40,8 +40,10 @@ import com.android.internal.annotations.GuardedBy;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Deque;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -132,15 +134,21 @@ public abstract class MediaRoute2ProviderService extends Service {
@Retention(RetentionPolicy.SOURCE)
public @interface Reason {}
+ private static final int MAX_REQUEST_IDS_SIZE = 500;
+
private final Handler mHandler;
private final Object mSessionLock = new Object();
+ private final Object mRequestIdsLock = new Object();
private final AtomicBoolean mStatePublishScheduled = new AtomicBoolean(false);
private MediaRoute2ProviderServiceStub mStub;
private IMediaRoute2ProviderServiceCallback mRemoteCallback;
private volatile MediaRoute2ProviderInfo mProviderInfo;
+ @GuardedBy("mRequestIdsLock")
+ private final Deque<Long> mRequestIds = new ArrayDeque<>(MAX_REQUEST_IDS_SIZE);
+
@GuardedBy("mSessionLock")
- private ArrayMap<String, RoutingSessionInfo> mSessionInfo = new ArrayMap<>();
+ private final ArrayMap<String, RoutingSessionInfo> mSessionInfo = new ArrayMap<>();
public MediaRoute2ProviderService() {
mHandler = new Handler(Looper.getMainLooper());
@@ -230,10 +238,15 @@ public abstract class MediaRoute2ProviderService extends Service {
@NonNull RoutingSessionInfo sessionInfo) {
Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
+ if (requestId != REQUEST_ID_NONE && !removeRequestId(requestId)) {
+ Log.w(TAG, "notifySessionCreated: The requestId doesn't exist. requestId=" + requestId);
+ return;
+ }
+
String sessionId = sessionInfo.getId();
synchronized (mSessionLock) {
if (mSessionInfo.containsKey(sessionId)) {
- Log.w(TAG, "Ignoring duplicate session id.");
+ Log.w(TAG, "notifySessionCreated: Ignoring duplicate session id.");
return;
}
mSessionInfo.put(sessionInfo.getId(), sessionInfo);
@@ -261,7 +274,7 @@ public abstract class MediaRoute2ProviderService extends Service {
if (mSessionInfo.containsKey(sessionId)) {
mSessionInfo.put(sessionId, sessionInfo);
} else {
- Log.w(TAG, "Ignoring unknown session info.");
+ Log.w(TAG, "notifySessionUpdated: Ignoring unknown session info.");
return;
}
@@ -291,7 +304,7 @@ public abstract class MediaRoute2ProviderService extends Service {
sessionInfo = mSessionInfo.remove(sessionId);
if (sessionInfo == null) {
- Log.w(TAG, "Ignoring unknown session info.");
+ Log.w(TAG, "notifySessionReleased: Ignoring unknown session info.");
return;
}
@@ -301,7 +314,7 @@ public abstract class MediaRoute2ProviderService extends Service {
try {
mRemoteCallback.notifySessionReleased(sessionInfo);
} catch (RemoteException ex) {
- Log.w(TAG, "Failed to notify session info changed.");
+ Log.w(TAG, "Failed to notify session released.", ex);
}
}
}
@@ -322,6 +335,13 @@ public abstract class MediaRoute2ProviderService extends Service {
if (mRemoteCallback == null) {
return;
}
+
+ if (!removeRequestId(requestId)) {
+ Log.w(TAG, "notifyRequestFailed: The requestId doesn't exist. requestId="
+ + requestId);
+ return;
+ }
+
try {
mRemoteCallback.notifyRequestFailed(requestId, reason);
} catch (RemoteException ex) {
@@ -465,7 +485,37 @@ public abstract class MediaRoute2ProviderService extends Service {
try {
mRemoteCallback.updateState(mProviderInfo);
} catch (RemoteException ex) {
- Log.w(TAG, "Failed to send onProviderInfoUpdated");
+ Log.w(TAG, "Failed to publish provider state.", ex);
+ }
+ }
+
+ /**
+ * Adds a requestId in the request ID list whose max size is {@link #MAX_REQUEST_IDS_SIZE}.
+ * When the max size is reached, the first element is removed (FIFO).
+ */
+ private void addRequestId(long requestId) {
+ synchronized (mRequestIdsLock) {
+ if (mRequestIds.size() >= MAX_REQUEST_IDS_SIZE) {
+ mRequestIds.removeFirst();
+ }
+ mRequestIds.addLast(requestId);
+ }
+ }
+
+ /**
+ * Removes the given {@code requestId} from received request ID list.
+ * <p>
+ * Returns whether the list contains the {@code requestId}. These are the cases when the list
+ * doesn't contain the given {@code requestId}:
+ * <ul>
+ * <li>This service has never received a request with the requestId. </li>
+ * <li>{@link #notifyRequestFailed} or {@link #notifySessionCreated} already has been called
+ * for the requestId. </li>
+ * </ul>
+ */
+ private boolean removeRequestId(long requestId) {
+ synchronized (mRequestIdsLock) {
+ return mRequestIds.removeFirstOccurrence(requestId);
}
}
@@ -529,6 +579,7 @@ public abstract class MediaRoute2ProviderService extends Service {
if (!checkRouteIdIsValid(routeId, "setRouteVolume")) {
return;
}
+ addRequestId(requestId);
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSetRouteVolume,
MediaRoute2ProviderService.this, requestId, routeId, volume));
}
@@ -542,6 +593,7 @@ public abstract class MediaRoute2ProviderService extends Service {
if (!checkRouteIdIsValid(routeId, "requestCreateSession")) {
return;
}
+ addRequestId(requestId);
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onCreateSession,
MediaRoute2ProviderService.this, requestId, packageName, routeId,
requestCreateSession));
@@ -556,6 +608,7 @@ public abstract class MediaRoute2ProviderService extends Service {
|| !checkRouteIdIsValid(routeId, "selectRoute")) {
return;
}
+ addRequestId(requestId);
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSelectRoute,
MediaRoute2ProviderService.this, requestId, sessionId, routeId));
}
@@ -569,6 +622,7 @@ public abstract class MediaRoute2ProviderService extends Service {
|| !checkRouteIdIsValid(routeId, "deselectRoute")) {
return;
}
+ addRequestId(requestId);
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onDeselectRoute,
MediaRoute2ProviderService.this, requestId, sessionId, routeId));
}
@@ -582,6 +636,7 @@ public abstract class MediaRoute2ProviderService extends Service {
|| !checkRouteIdIsValid(routeId, "transferToRoute")) {
return;
}
+ addRequestId(requestId);
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onTransferToRoute,
MediaRoute2ProviderService.this, requestId, sessionId, routeId));
}
@@ -594,6 +649,7 @@ public abstract class MediaRoute2ProviderService extends Service {
if (!checkSessionIdIsValid(sessionId, "setSessionVolume")) {
return;
}
+ addRequestId(requestId);
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSetSessionVolume,
MediaRoute2ProviderService.this, requestId, sessionId, volume));
}
@@ -606,6 +662,7 @@ public abstract class MediaRoute2ProviderService extends Service {
if (!checkSessionIdIsValid(sessionId, "releaseSession")) {
return;
}
+ addRequestId(requestId);
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onReleaseSession,
MediaRoute2ProviderService.this, requestId, sessionId));
}
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 12fc3a60cf28..7ae2949e6074 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -621,33 +621,27 @@ public class MediaRouter {
final class Client extends IMediaRouterClient.Stub {
@Override
public void onStateChanged() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- if (Client.this == mClient) {
- updateClientState();
- }
+ mHandler.post(() -> {
+ if (Client.this == mClient) {
+ updateClientState();
}
});
}
@Override
public void onRestoreRoute() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- // Skip restoring route if the selected route is not a system audio route,
- // MediaRouter is initializing, or mClient was changed.
- if (Client.this != mClient || mSelectedRoute == null
- || (mSelectedRoute != mDefaultAudioVideo
- && mSelectedRoute != mBluetoothA2dpRoute)) {
- return;
- }
- if (DEBUG) {
- Log.d(TAG, "onRestoreRoute() : route=" + mSelectedRoute);
- }
- mSelectedRoute.select();
+ mHandler.post(() -> {
+ // Skip restoring route if the selected route is not a system audio route,
+ // MediaRouter is initializing, or mClient was changed.
+ if (Client.this != mClient || mSelectedRoute == null
+ || (mSelectedRoute != mDefaultAudioVideo
+ && mSelectedRoute != mBluetoothA2dpRoute)) {
+ return;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "onRestoreRoute() : route=" + mSelectedRoute);
}
+ mSelectedRoute.select();
});
}
@@ -659,6 +653,19 @@ public class MediaRouter {
}
});
}
+
+ // Called when the selection of a connected device (phone speaker or BT devices)
+ // is changed.
+ @Override
+ public void onGlobalA2dpChanged(boolean a2dpOn) {
+ mHandler.post(() -> {
+ if (mSelectedRoute == mDefaultAudioVideo && a2dpOn) {
+ setSelectedRoute(mBluetoothA2dpRoute, false);
+ } else if (mSelectedRoute == mBluetoothA2dpRoute && !a2dpOn) {
+ setSelectedRoute(mDefaultAudioVideo, false);
+ }
+ });
+ }
}
}
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index e767c68f13ac..0f538a57a5ee 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -196,7 +196,7 @@ public final class MediaRouter2 {
try {
mMediaRouterService.setDiscoveryRequestWithRouter2(mStub, mDiscoveryPreference);
} catch (RemoteException ex) {
- Log.e(TAG, "registerRouteCallback: Unable to set discovery request.");
+ Log.e(TAG, "registerRouteCallback: Unable to set discovery request.", ex);
}
}
}
@@ -214,7 +214,7 @@ public final class MediaRouter2 {
if (!mRouteCallbackRecords.remove(
new RouteCallbackRecord(null, routeCallback, null))) {
- Log.w(TAG, "Ignoring unknown callback");
+ Log.w(TAG, "unregisterRouteCallback: Ignoring unknown callback");
return;
}
@@ -227,7 +227,7 @@ public final class MediaRouter2 {
mMediaRouterService.setDiscoveryRequestWithRouter2(
mStub, mDiscoveryPreference);
} catch (RemoteException ex) {
- Log.e(TAG, "unregisterRouteCallback: Unable to set discovery request.");
+ Log.e(TAG, "unregisterRouteCallback: Unable to set discovery request.", ex);
}
}
if (mRouteCallbackRecords.isEmpty() && mNonSystemRoutingControllers.isEmpty()) {
@@ -500,7 +500,7 @@ public final class MediaRouter2 {
try {
mMediaRouterService.setRouteVolumeWithRouter2(stub, route, volume);
} catch (RemoteException ex) {
- Log.e(TAG, "Unable to send control request.", ex);
+ Log.e(TAG, "Unable to set route volume.", ex);
}
}
}
@@ -788,7 +788,8 @@ public final class MediaRouter2 {
mMediaRouterService.notifySessionHintsForCreatingSession(
stub, uniqueRequestId, route, controllerHints);
} catch (RemoteException ex) {
- Log.e(TAG, "getSessionHintsOnHandler: Unable to request.", ex);
+ Log.e(TAG, "onGetControllerHintsForCreatingSessionOnHandler: Unable to notify "
+ + " session hints for creating session.", ex);
}
}
}
@@ -1120,7 +1121,7 @@ public final class MediaRouter2 {
Objects.requireNonNull(route, "route must not be null");
synchronized (mControllerLock) {
if (mIsReleased) {
- Log.w(TAG, "selectRoute() called on released controller. Ignoring.");
+ Log.w(TAG, "selectRoute: Called on released controller. Ignoring.");
return;
}
}
@@ -1169,7 +1170,7 @@ public final class MediaRouter2 {
Objects.requireNonNull(route, "route must not be null");
synchronized (mControllerLock) {
if (mIsReleased) {
- Log.w(TAG, "deselectRoute() called on released controller. Ignoring.");
+ Log.w(TAG, "deselectRoute: called on released controller. Ignoring.");
return;
}
}
@@ -1216,7 +1217,7 @@ public final class MediaRouter2 {
Objects.requireNonNull(route, "route must not be null");
synchronized (mControllerLock) {
if (mIsReleased) {
- Log.w(TAG, "transferToRoute() called on released controller. Ignoring.");
+ Log.w(TAG, "transferToRoute: Called on released controller. Ignoring.");
return;
}
@@ -1254,17 +1255,17 @@ public final class MediaRouter2 {
*/
public void setVolume(int volume) {
if (getVolumeHandling() == MediaRoute2Info.PLAYBACK_VOLUME_FIXED) {
- Log.w(TAG, "setVolume: the routing session has fixed volume. Ignoring.");
+ Log.w(TAG, "setVolume: The routing session has fixed volume. Ignoring.");
return;
}
if (volume < 0 || volume > getVolumeMax()) {
- Log.w(TAG, "setVolume: the target volume is out of range. Ignoring");
+ Log.w(TAG, "setVolume: The target volume is out of range. Ignoring");
return;
}
synchronized (mControllerLock) {
if (mIsReleased) {
- Log.w(TAG, "setVolume is called on released controller. Ignoring.");
+ Log.w(TAG, "setVolume: Called on released controller. Ignoring.");
return;
}
}
@@ -1298,7 +1299,7 @@ public final class MediaRouter2 {
boolean releaseInternal(boolean shouldReleaseSession, boolean shouldNotifyStop) {
synchronized (mControllerLock) {
if (mIsReleased) {
- Log.w(TAG, "releaseInternal() called on released controller. Ignoring.");
+ Log.w(TAG, "releaseInternal: Called on released controller. Ignoring.");
return false;
}
mIsReleased = true;
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 1c2d581b733d..a18cfccb6cb2 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -121,7 +121,7 @@ public final class MediaRouter2Manager {
CallbackRecord callbackRecord = new CallbackRecord(executor, callback);
if (!mCallbackRecords.addIfAbsent(callbackRecord)) {
- Log.w(TAG, "Ignoring to add the same callback twice.");
+ Log.w(TAG, "Ignoring to register the same callback twice.");
return;
}
}
@@ -384,7 +384,7 @@ public final class MediaRouter2Manager {
int requestId = mNextRequestId.getAndIncrement();
mMediaRouterService.setRouteVolumeWithManager(client, requestId, route, volume);
} catch (RemoteException ex) {
- Log.e(TAG, "Unable to send control request.", ex);
+ Log.e(TAG, "Unable to set route volume.", ex);
}
}
}
@@ -414,7 +414,7 @@ public final class MediaRouter2Manager {
mMediaRouterService.setSessionVolumeWithManager(
client, requestId, sessionInfo.getId(), volume);
} catch (RemoteException ex) {
- Log.e(TAG, "Unable to send control request.", ex);
+ Log.e(TAG, "Unable to set session volume.", ex);
}
}
}
diff --git a/media/java/android/media/soundtrigger_middleware/RecognitionEvent.aidl b/media/java/android/media/soundtrigger_middleware/RecognitionEvent.aidl
index de4d060ce484..a237ec1aa3b3 100644
--- a/media/java/android/media/soundtrigger_middleware/RecognitionEvent.aidl
+++ b/media/java/android/media/soundtrigger_middleware/RecognitionEvent.aidl
@@ -43,9 +43,9 @@ parcelable RecognitionEvent {
boolean triggerInData;
/**
* Audio format of either the trigger in event data or to use for capture of the rest of the
- * utterance.
+ * utterance. May be null when no audio is available for this event type.
*/
- AudioConfig audioConfig;
+ @nullable AudioConfig audioConfig;
/** Additional data. */
byte[] data;
}
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
index 957558107de9..638a842e4e5f 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
@@ -593,11 +593,16 @@ public class MediaRouter2ManagerTest {
final int failureReason = REASON_REJECTED;
final CountDownLatch onRequestFailedLatch = new CountDownLatch(1);
+ final CountDownLatch onRequestFailedSecondCallLatch = new CountDownLatch(1);
addManagerCallback(new MediaRouter2Manager.Callback() {
@Override
public void onRequestFailed(int reason) {
if (reason == failureReason) {
- onRequestFailedLatch.countDown();
+ if (onRequestFailedLatch.getCount() > 0) {
+ onRequestFailedLatch.countDown();
+ } else {
+ onRequestFailedSecondCallLatch.countDown();
+ }
}
}
});
@@ -609,6 +614,11 @@ public class MediaRouter2ManagerTest {
final long validRequestId = requestIds.get(0);
instance.notifyRequestFailed(validRequestId, failureReason);
assertTrue(onRequestFailedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+ // Test calling notifyRequestFailed() multiple times with the same valid requestId.
+ // onRequestFailed() shouldn't be called since the requestId has been already handled.
+ instance.notifyRequestFailed(validRequestId, failureReason);
+ assertFalse(onRequestFailedSecondCallLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
@Test
diff --git a/packages/CarSystemUI/res/values/colors.xml b/packages/CarSystemUI/res/values/colors.xml
index d20ab49a22e6..ab9426593535 100644
--- a/packages/CarSystemUI/res/values/colors.xml
+++ b/packages/CarSystemUI/res/values/colors.xml
@@ -15,7 +15,6 @@
~ limitations under the License
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <color name="nav_bar_ripple_background_color">#40ffffff</color>
<!-- colors for user switcher -->
<color name="car_user_switcher_background_color">#000000</color>
<color name="car_user_switcher_name_text_color">@*android:color/car_body1_light</color>
diff --git a/packages/CarSystemUI/res/values/styles.xml b/packages/CarSystemUI/res/values/styles.xml
index 7fc69e6d5d8f..e76373d4a4f7 100644
--- a/packages/CarSystemUI/res/values/styles.xml
+++ b/packages/CarSystemUI/res/values/styles.xml
@@ -37,13 +37,9 @@
<item name="android:textColor">@*android:color/car_grey_50</item>
</style>
- <style name="CarNavigationBarButtonTheme">
- <item name="android:colorControlHighlight">@color/nav_bar_ripple_background_color</item>
- </style>
-
<style name="NavigationBarButton">
<item name="android:layout_height">96dp</item>
<item name="android:layout_width">96dp</item>
- <item name="android:background">@*android:drawable/item_background_material</item>
+ <item name="android:background">?android:attr/selectableItemBackground</item>
</style>
</resources> \ No newline at end of file
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
index 2dad5f872e73..69766cc6c0d0 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
@@ -103,6 +103,7 @@ public class CarKeyguardViewController extends OverlayViewController implements
private KeyguardBouncer mBouncer;
private OnKeyguardCancelClickedListener mKeyguardCancelClickedListener;
private boolean mShowing;
+ private boolean mIsOccluded;
@Inject
public CarKeyguardViewController(
@@ -220,6 +221,7 @@ public class CarKeyguardViewController extends OverlayViewController implements
@Override
public void setOccluded(boolean occluded, boolean animate) {
+ mIsOccluded = occluded;
getOverlayViewGlobalStateController().setOccluded(occluded);
if (!occluded) {
reset(/* hideBouncerWhenShowing= */ false);
@@ -244,6 +246,12 @@ public class CarKeyguardViewController extends OverlayViewController implements
@Override
public void dismissAndCollapse() {
+ // If dismissing and collapsing Keyguard is requested (e.g. by a Keyguard-dismissing
+ // Activity) while Keyguard is occluded, unocclude Keyguard so the user can authenticate to
+ // dismiss Keyguard.
+ if (mIsOccluded) {
+ setOccluded(/* occluded= */ false, /* animate= */ false);
+ }
if (!mBouncer.isSecure()) {
hide(/* startTime= */ 0, /* fadeoutDuration= */ 0);
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/BottomNotificationPanelViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/BottomNotificationPanelViewMediator.java
index 6d140cae5442..7d353f5acd9a 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/BottomNotificationPanelViewMediator.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/BottomNotificationPanelViewMediator.java
@@ -16,6 +16,7 @@
package com.android.systemui.car.notification;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.car.CarDeviceProvisionedController;
import com.android.systemui.car.navigationbar.CarNavigationBarController;
import com.android.systemui.car.window.OverlayPanelViewController;
@@ -37,6 +38,7 @@ public class BottomNotificationPanelViewMediator extends NotificationPanelViewMe
NotificationPanelViewController notificationPanelViewController,
PowerManagerHelper powerManagerHelper,
+ BroadcastDispatcher broadcastDispatcher,
CarDeviceProvisionedController carDeviceProvisionedController,
ConfigurationController configurationController
@@ -44,6 +46,7 @@ public class BottomNotificationPanelViewMediator extends NotificationPanelViewMe
super(carNavigationBarController,
notificationPanelViewController,
powerManagerHelper,
+ broadcastDispatcher,
carDeviceProvisionedController,
configurationController);
notificationPanelViewController.setOverlayDirection(
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java
index 41349b284147..0c185bae8199 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java
@@ -17,10 +17,17 @@
package com.android.systemui.car.notification;
import android.car.hardware.power.CarPowerManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.res.Configuration;
+import android.os.UserHandle;
+import android.util.Log;
import androidx.annotation.CallSuper;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.car.CarDeviceProvisionedController;
import com.android.systemui.car.navigationbar.CarNavigationBarController;
import com.android.systemui.car.window.OverlayViewMediator;
@@ -37,18 +44,36 @@ import javax.inject.Singleton;
public class NotificationPanelViewMediator implements OverlayViewMediator,
ConfigurationController.ConfigurationListener {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "NotificationPanelVM";
+
private final CarNavigationBarController mCarNavigationBarController;
private final NotificationPanelViewController mNotificationPanelViewController;
private final PowerManagerHelper mPowerManagerHelper;
+ private final BroadcastDispatcher mBroadcastDispatcher;
private final CarDeviceProvisionedController mCarDeviceProvisionedController;
private final ConfigurationController mConfigurationController;
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DEBUG) Log.v(TAG, "onReceive: " + intent);
+ String action = intent.getAction();
+ if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
+ if (mNotificationPanelViewController.isPanelExpanded()) {
+ mNotificationPanelViewController.toggle();
+ }
+ }
+ }
+ };
+
@Inject
public NotificationPanelViewMediator(
CarNavigationBarController carNavigationBarController,
NotificationPanelViewController notificationPanelViewController,
PowerManagerHelper powerManagerHelper,
+ BroadcastDispatcher broadcastDispatcher,
CarDeviceProvisionedController carDeviceProvisionedController,
ConfigurationController configurationController
@@ -56,6 +81,7 @@ public class NotificationPanelViewMediator implements OverlayViewMediator,
mCarNavigationBarController = carNavigationBarController;
mNotificationPanelViewController = notificationPanelViewController;
mPowerManagerHelper = powerManagerHelper;
+ mBroadcastDispatcher = broadcastDispatcher;
mCarDeviceProvisionedController = carDeviceProvisionedController;
mConfigurationController = configurationController;
}
@@ -84,6 +110,9 @@ public class NotificationPanelViewMediator implements OverlayViewMediator,
return mNotificationPanelViewController.isPanelExpanded();
}
});
+
+ mBroadcastDispatcher.registerReceiver(mBroadcastReceiver,
+ new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), null, UserHandle.ALL);
}
@Override
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/TopNotificationPanelViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/TopNotificationPanelViewMediator.java
index 8d3eb4c2bbee..89c9931ac76e 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/TopNotificationPanelViewMediator.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/TopNotificationPanelViewMediator.java
@@ -16,6 +16,7 @@
package com.android.systemui.car.notification;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.car.CarDeviceProvisionedController;
import com.android.systemui.car.navigationbar.CarNavigationBarController;
import com.android.systemui.car.window.OverlayPanelViewController;
@@ -37,6 +38,7 @@ public class TopNotificationPanelViewMediator extends NotificationPanelViewMedia
NotificationPanelViewController notificationPanelViewController,
PowerManagerHelper powerManagerHelper,
+ BroadcastDispatcher broadcastDispatcher,
CarDeviceProvisionedController carDeviceProvisionedController,
ConfigurationController configurationController
@@ -44,6 +46,7 @@ public class TopNotificationPanelViewMediator extends NotificationPanelViewMedia
super(carNavigationBarController,
notificationPanelViewController,
powerManagerHelper,
+ broadcastDispatcher,
carDeviceProvisionedController,
configurationController);
notificationPanelViewController.setOverlayDirection(
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
index 775ef8152ca2..8aa7b6389d85 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
@@ -23,9 +23,12 @@ import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.os.Handler;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.util.Log;
+import android.view.IWindowManager;
import android.widget.ImageView;
import android.widget.TextView;
@@ -44,13 +47,14 @@ import javax.inject.Singleton;
*/
@Singleton
public class UserSwitchTransitionViewController extends OverlayViewController {
- private static final String TAG = "UserSwitchTransitionViewController";
+ private static final String TAG = "UserSwitchTransition";
private static final String ENABLE_DEVELOPER_MESSAGE_TRUE = "true";
private final Context mContext;
private final Handler mHandler;
private final Resources mResources;
private final UserManager mUserManager;
+ private final IWindowManager mWindowManagerService;
@GuardedBy("this")
private boolean mShowing;
@@ -62,6 +66,7 @@ public class UserSwitchTransitionViewController extends OverlayViewController {
@Main Handler handler,
@Main Resources resources,
UserManager userManager,
+ IWindowManager windowManagerService,
OverlayViewGlobalStateController overlayViewGlobalStateController) {
super(R.id.user_switching_dialog_stub, overlayViewGlobalStateController);
@@ -70,6 +75,7 @@ public class UserSwitchTransitionViewController extends OverlayViewController {
mHandler = handler;
mResources = resources;
mUserManager = userManager;
+ mWindowManagerService = windowManagerService;
}
/**
@@ -81,6 +87,13 @@ public class UserSwitchTransitionViewController extends OverlayViewController {
if (mPreviousUserId == newUserId || mShowing) return;
mShowing = true;
mHandler.post(() -> {
+ try {
+ mWindowManagerService.setSwitchingUser(true);
+ mWindowManagerService.lockNow(null);
+ } catch (RemoteException e) {
+ Log.e(TAG, "unable to notify window manager service regarding user switch");
+ }
+
start();
populateDialog(mPreviousUserId, newUserId);
// next time a new user is selected, this current new user will be the previous user.
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java
index eab381c92d98..65c556269f13 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java
@@ -28,6 +28,7 @@ import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableResources;
+import android.view.IWindowManager;
import android.view.LayoutInflater;
import android.view.ViewGroup;
@@ -52,6 +53,8 @@ public class UserSwitchTransitionViewControllerTest extends SysuiTestCase {
private TestableResources mTestableResources;
@Mock
private OverlayViewGlobalStateController mOverlayViewGlobalStateController;
+ @Mock
+ private IWindowManager mWindowManagerService;
@Before
public void setUp() {
@@ -62,6 +65,7 @@ public class UserSwitchTransitionViewControllerTest extends SysuiTestCase {
Handler.getMain(),
mTestableResources.getResources(),
(UserManager) mContext.getSystemService(Context.USER_SERVICE),
+ mWindowManagerService,
mOverlayViewGlobalStateController
);
@@ -125,8 +129,10 @@ public class UserSwitchTransitionViewControllerTest extends SysuiTestCase {
TestableUserSwitchTransitionViewController(Context context, Handler handler,
Resources resources, UserManager userManager,
+ IWindowManager windowManagerService,
OverlayViewGlobalStateController overlayViewGlobalStateController) {
- super(context, handler, resources, userManager, overlayViewGlobalStateController);
+ super(context, handler, resources, userManager, windowManagerService,
+ overlayViewGlobalStateController);
mHandler = handler;
}
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 8f919c3d86ca..f42bf1982b36 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -95,7 +95,8 @@ public class ExternalStorageProvider extends FileSystemProvider {
public String docId;
public File visiblePath;
public File path;
- public boolean reportAvailableBytes = true;
+ // TODO (b/157033915): Make getFreeBytes() faster
+ public boolean reportAvailableBytes = false;
}
private static final String ROOT_ID_PRIMARY_EMULATED =
@@ -520,9 +521,11 @@ public class ExternalStorageProvider extends FileSystemProvider {
final RootInfo root = resolvedDocId.first;
File child = resolvedDocId.second;
+ final File rootFile = root.visiblePath != null ? root.visiblePath
+ : root.path;
final File parent = TextUtils.isEmpty(parentDocId)
- ? root.path
- : getFileForDocId(parentDocId);
+ ? rootFile
+ : getFileForDocId(parentDocId);
return new Path(parentDocId == null ? root.rootId : null, findDocumentPath(parent, child));
}
diff --git a/packages/OsuLogin/Android.bp b/packages/OsuLogin/Android.bp
index d7e36b164811..445c81b89273 100644
--- a/packages/OsuLogin/Android.bp
+++ b/packages/OsuLogin/Android.bp
@@ -1,5 +1,6 @@
android_app {
name: "OsuLogin",
+ defaults: ["wifi-module-sdk-version-defaults"],
static_libs: ["androidx.legacy_legacy-support-v4"],
resource_dirs: ["res"],
srcs: ["src/**/*.java"],
diff --git a/packages/SettingsLib/OWNERS b/packages/SettingsLib/OWNERS
index a28ba8584054..d7bd6a49d75b 100644
--- a/packages/SettingsLib/OWNERS
+++ b/packages/SettingsLib/OWNERS
@@ -3,6 +3,7 @@ dsandler@android.com
edgarwang@google.com
emilychuang@google.com
evanlaird@google.com
+juliacr@google.com
leifhendrik@google.com
rafftsai@google.com
tmfang@google.com
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 2b51b54a2fe3..e17c0f015ff8 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -57,7 +57,7 @@
<string name="osu_sign_up_complete" msgid="7640183358878916847">"اكتمل الاشتراك. جارٍ الاتصال…"</string>
<string name="speed_label_very_slow" msgid="8526005255731597666">"بطيئة جدًا"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"بطيئة"</string>
- <string name="speed_label_okay" msgid="1253594383880810424">"حسنًا"</string>
+ <string name="speed_label_okay" msgid="1253594383880810424">"جيدة"</string>
<string name="speed_label_medium" msgid="9078405312828606976">"متوسطة"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"سريعة"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"سريعة جدًا"</string>
@@ -557,5 +557,5 @@
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"غير مفعّل"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"مفعّل"</string>
<string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"يجب إعادة تشغيل جهازك ليتم تطبيق هذا التغيير. يمكنك إعادة التشغيل الآن أو إلغاء التغيير."</string>
- <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"سمّاعة رأس سلكية"</string>
+ <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"سمّاعة سلكية"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index fe2a7751bead..6b0ae2e24bb6 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -57,7 +57,7 @@
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Anmeldung abgeschlossen. Verbindung wird hergestellt…"</string>
<string name="speed_label_very_slow" msgid="8526005255731597666">"Sehr langsam"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Langsam"</string>
- <string name="speed_label_okay" msgid="1253594383880810424">"Ok"</string>
+ <string name="speed_label_okay" msgid="1253594383880810424">"Mittel"</string>
<string name="speed_label_medium" msgid="9078405312828606976">"Mittel"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Schnell"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Sehr schnell"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 2644cb987e3a..4d7c88287f72 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -465,7 +465,7 @@
<item msgid="8894873528875953317">"50%"</item>
<item msgid="7529124349186240216">"100%"</item>
</string-array>
- <string name="charge_length_format" msgid="6941645744588690932">"Πριν από <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="charge_length_format" msgid="6941645744588690932">"Πριν <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="4310625772926171089">"Απομένουν <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="screen_zoom_summary_small" msgid="6050633151263074260">"Μικρά"</string>
<string name="screen_zoom_summary_default" msgid="1888865694033865408">"Προεπιλογή"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 0ef8e0677d6d..0def17e1c21a 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -553,5 +553,5 @@
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Disabled"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
<string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string>
- <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Wired headphone"</string>
+ <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Wired headphones"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index 0ef8e0677d6d..0def17e1c21a 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -553,5 +553,5 @@
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Disabled"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
<string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string>
- <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Wired headphone"</string>
+ <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Wired headphones"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 0ef8e0677d6d..0def17e1c21a 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -553,5 +553,5 @@
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Disabled"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
<string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string>
- <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Wired headphone"</string>
+ <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Wired headphones"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 0ef8e0677d6d..0def17e1c21a 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -553,5 +553,5 @@
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Disabled"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
<string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string>
- <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Wired headphone"</string>
+ <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Wired headphones"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 9735c163857d..42ccd5310913 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -347,8 +347,8 @@
<string name="disable_overlays_summary" msgid="1954852414363338166">"Selalu gunakan GPU untuk pengomposisian layar"</string>
<string name="simulate_color_space" msgid="1206503300335835151">"Simulasikan ruang warna"</string>
<string name="enable_opengl_traces_title" msgid="4638773318659125196">"Aktifkan jejak OpenGL"</string>
- <string name="usb_audio_disable_routing" msgid="3367656923544254975">"Perutean audio USB nonaktif"</string>
- <string name="usb_audio_disable_routing_summary" msgid="8768242894849534699">"Perutean otomatis ke periferal audio USB nonaktif"</string>
+ <string name="usb_audio_disable_routing" msgid="3367656923544254975">"Pemilihan rute audio USB nonaktif"</string>
+ <string name="usb_audio_disable_routing_summary" msgid="8768242894849534699">"Pemilihan rute otomatis ke periferal audio USB nonaktif"</string>
<string name="debug_layout" msgid="1659216803043339741">"Tampilkan batas tata letak"</string>
<string name="debug_layout_summary" msgid="8825829038287321978">"Tampilkan batas klip, margin, dll."</string>
<string name="force_rtl_layout_all_locales" msgid="8690762598501599796">"Paksa arah tata letak RTL"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index efc186aa24bb..24711ff92d05 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -64,10 +64,10 @@
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Scaduta"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Disconnesso"</string>
- <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Disconnessione..."</string>
- <string name="bluetooth_connecting" msgid="5871702668260192755">"Connessione..."</string>
+ <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Disconnessione…"</string>
+ <string name="bluetooth_connecting" msgid="5871702668260192755">"Connessione…"</string>
<string name="bluetooth_connected" msgid="8065345572198502293">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connesso"</string>
- <string name="bluetooth_pairing" msgid="4269046942588193600">"Accoppiamento..."</string>
+ <string name="bluetooth_pairing" msgid="4269046942588193600">"Accoppiamento…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connesso (telefono escluso)"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connesso (contenuti multimediali esclusi)"</string>
<string name="bluetooth_connected_no_map" msgid="3381860077002724689">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connesso (nessun accesso ai messaggi)"</string>
@@ -406,11 +406,11 @@
<string name="select_webview_provider_dialog_title" msgid="2444261109877277714">"Imposta l\'implementazione di WebView"</string>
<string name="select_webview_provider_toast_text" msgid="8512254949169359848">"La selezione non è più valida. Riprova."</string>
<string name="convert_to_file_encryption" msgid="2828976934129751818">"Converti in crittografia basata su file"</string>
- <string name="convert_to_file_encryption_enabled" msgid="840757431284311754">"Converti..."</string>
+ <string name="convert_to_file_encryption_enabled" msgid="840757431284311754">"Converti…"</string>
<string name="convert_to_file_encryption_done" msgid="8965831011811180627">"Crittografia su base file già eseguita"</string>
<string name="title_convert_fbe" msgid="5780013350366495149">"Conversione in crittografia basata su file"</string>
- <string name="convert_to_fbe_warning" msgid="34294381569282109">"Converti la partizione di dati in crittografia basata su file.\n Attenzione. Questa operazione eliminerà tutti i tuoi dati.\n Questa funzione è in versione alpha, pertanto potrebbe non funzionare correttamente.\n Premi \"Cancella e converti...\" per continuare."</string>
- <string name="button_convert_fbe" msgid="1159861795137727671">"Cancella e converti..."</string>
+ <string name="convert_to_fbe_warning" msgid="34294381569282109">"Converti la partizione di dati in crittografia basata su file.\n Attenzione. Questa operazione eliminerà tutti i tuoi dati.\n Questa funzione è in versione alpha, pertanto potrebbe non funzionare correttamente.\n Premi \"Cancella e converti…\" per continuare."</string>
+ <string name="button_convert_fbe" msgid="1159861795137727671">"Cancella e converti…"</string>
<string name="picture_color_mode" msgid="1013807330552931903">"Modalità colori immagini"</string>
<string name="picture_color_mode_desc" msgid="151780973768136200">"Use sRGB"</string>
<string name="daltonizer_mode_disabled" msgid="403424372812399228">"Disattivato"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index fd5dfa5c9e8b..1120c504717f 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -514,7 +514,7 @@
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem z połączeniem. Wyłącz i ponownie włącz urządzenie"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Przewodowe urządzenie audio"</string>
<string name="help_label" msgid="3528360748637781274">"Pomoc i opinie"</string>
- <string name="storage_category" msgid="2287342585424631813">"Pamięć"</string>
+ <string name="storage_category" msgid="2287342585424631813">"Pamięć wewnętrzna"</string>
<string name="shared_data_title" msgid="1017034836800864953">"Udostępniane dane"</string>
<string name="shared_data_summary" msgid="5516326713822885652">"Wyświetl i zmień udostępniane dane"</string>
<string name="shared_data_no_blobs_text" msgid="3108114670341737434">"Brak udostępnionych danych w przypadku tego użytkownika."</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index b501c656a680..7e1b476b7a0c 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -195,7 +195,7 @@
</string-array>
<string name="choose_profile" msgid="343803890897657450">"Chọn hồ sơ"</string>
<string name="category_personal" msgid="6236798763159385225">"Cá nhân"</string>
- <string name="category_work" msgid="4014193632325996115">"Cơ quan"</string>
+ <string name="category_work" msgid="4014193632325996115">"Công việc"</string>
<string name="development_settings_title" msgid="140296922921597393">"Tùy chọn cho nhà phát triển"</string>
<string name="development_settings_enable" msgid="4285094651288242183">"Bật tùy chọn nhà phát triển"</string>
<string name="development_settings_summary" msgid="8718917813868735095">"Đặt tùy chọn cho phát triển ứng dụng"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index b83a9c4835e0..6c7e03f104dd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -56,7 +56,7 @@ import java.util.concurrent.Executors;
public class InfoMediaManager extends MediaManager {
private static final String TAG = "InfoMediaManager";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);;
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@VisibleForTesting
final RouterManagerCallback mMediaRouterCallback = new RouterManagerCallback();
@VisibleForTesting
@@ -430,7 +430,7 @@ public class InfoMediaManager extends MediaManager {
case TYPE_HEARING_AID:
case TYPE_BLUETOOTH_A2DP:
final BluetoothDevice device =
- BluetoothAdapter.getDefaultAdapter().getRemoteDevice(route.getOriginalId());
+ BluetoothAdapter.getDefaultAdapter().getRemoteDevice(route.getAddress());
final CachedBluetoothDevice cachedDevice =
mBluetoothManager.getCachedDeviceManager().findDevice(device);
if (cachedDevice != null) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index f381cde31f83..9d06c8467e41 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -20,7 +20,6 @@ import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR;
import android.app.Notification;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.media.RoutingSessionInfo;
import android.text.TextUtils;
@@ -409,7 +408,8 @@ public class LocalMediaManager implements BluetoothCallback {
synchronized (mMediaDevicesLock) {
for (MediaDevice device : mMediaDevices) {
if (device instanceof BluetoothMediaDevice) {
- if (isActiveDevice(((BluetoothMediaDevice) device).getCachedDevice())) {
+ if (isActiveDevice(((BluetoothMediaDevice) device).getCachedDevice())
+ && device.isConnected()) {
return device;
}
} else if (device instanceof PhoneMediaDevice) {
@@ -422,8 +422,22 @@ public class LocalMediaManager implements BluetoothCallback {
}
private boolean isActiveDevice(CachedBluetoothDevice device) {
- return device.isActiveDevice(BluetoothProfile.A2DP)
- || device.isActiveDevice(BluetoothProfile.HEARING_AID);
+ boolean isActiveDeviceA2dp = false;
+ boolean isActiveDeviceHearingAid = false;
+ final A2dpProfile a2dpProfile = mLocalBluetoothManager.getProfileManager().getA2dpProfile();
+ if (a2dpProfile != null) {
+ isActiveDeviceA2dp = device.getDevice().equals(a2dpProfile.getActiveDevice());
+ }
+ if (!isActiveDeviceA2dp) {
+ final HearingAidProfile hearingAidProfile = mLocalBluetoothManager.getProfileManager()
+ .getHearingAidProfile();
+ if (hearingAidProfile != null) {
+ isActiveDeviceHearingAid =
+ hearingAidProfile.getActiveDevices().contains(device.getDevice());
+ }
+ }
+
+ return isActiveDeviceA2dp || isActiveDeviceHearingAid;
}
private Collection<DeviceCallback> getCallbacks() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java b/packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java
index d5f1ece5f83f..549bc8a455cf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java
@@ -86,7 +86,7 @@ public class ConversationIconFactory extends BaseIconFactory {
/**
* Returns the conversation info drawable
*/
- private Drawable getBaseIconDrawable(ShortcutInfo shortcutInfo) {
+ public Drawable getBaseIconDrawable(ShortcutInfo shortcutInfo) {
return mLauncherApps.getShortcutIconDrawable(shortcutInfo, mFillResIconDpi);
}
@@ -94,7 +94,7 @@ public class ConversationIconFactory extends BaseIconFactory {
* Get the {@link Drawable} that represents the app icon, badged with the work profile icon
* if appropriate.
*/
- private Drawable getAppBadge(String packageName, int userId) {
+ public Drawable getAppBadge(String packageName, int userId) {
Drawable badge = null;
try {
final ApplicationInfo appInfo = mPackageManager.getApplicationInfoAsUser(
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 248eb5b96b92..94d95f06050d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -681,7 +681,7 @@ public class InfoMediaManagerTest {
assertThat(mInfoMediaManager.mMediaDevices.get(0) instanceof PhoneMediaDevice).isTrue();
when(route2Info.getType()).thenReturn(TYPE_BLUETOOTH_A2DP);
- when(route2Info.getOriginalId()).thenReturn("00:00:00:00:00:00");
+ when(route2Info.getAddress()).thenReturn("00:00:00:00:00:00");
when(mLocalBluetoothManager.getCachedDeviceManager())
.thenReturn(cachedBluetoothDeviceManager);
when(cachedBluetoothDeviceManager.findDevice(any(BluetoothDevice.class)))
@@ -703,7 +703,7 @@ public class InfoMediaManagerTest {
mock(CachedBluetoothDeviceManager.class);
when(route2Info.getType()).thenReturn(TYPE_BLUETOOTH_A2DP);
- when(route2Info.getOriginalId()).thenReturn("00:00:00:00:00:00");
+ when(route2Info.getAddress()).thenReturn("00:00:00:00:00:00");
when(mLocalBluetoothManager.getCachedDeviceManager())
.thenReturn(cachedBluetoothDeviceManager);
when(cachedBluetoothDeviceManager.findDevice(any(BluetoothDevice.class)))
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index 368245f2002d..a654fd47ea12 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -578,6 +578,9 @@ public class LocalMediaManagerTest {
when(cachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(cachedDevice.isConnected()).thenReturn(false);
when(cachedDevice.getConnectableProfiles()).thenReturn(profiles);
+ when(cachedDevice.getDevice()).thenReturn(bluetoothDevice);
+ when(mA2dpProfile.getActiveDevice()).thenReturn(bluetoothDevice);
+ when(mHapProfile.getActiveDevices()).thenReturn(new ArrayList<>());
when(device1.getId()).thenReturn(TEST_DEVICE_ID_1);
when(device2.getId()).thenReturn(TEST_DEVICE_ID_2);
@@ -734,11 +737,19 @@ public class LocalMediaManagerTest {
final PhoneMediaDevice phoneDevice = mock(PhoneMediaDevice.class);
final CachedBluetoothDevice cachedDevice1 = mock(CachedBluetoothDevice.class);
final CachedBluetoothDevice cachedDevice2 = mock(CachedBluetoothDevice.class);
+ final BluetoothDevice bluetoothDevice1 = mock(BluetoothDevice.class);
+ final BluetoothDevice bluetoothDevice2 = mock(BluetoothDevice.class);
+ when(mA2dpProfile.getActiveDevice()).thenReturn(bluetoothDevice2);
+ when(mHapProfile.getActiveDevices()).thenReturn(new ArrayList<>());
when(device1.getCachedDevice()).thenReturn(cachedDevice1);
when(device2.getCachedDevice()).thenReturn(cachedDevice2);
+ when(cachedDevice1.getDevice()).thenReturn(bluetoothDevice1);
+ when(cachedDevice2.getDevice()).thenReturn(bluetoothDevice2);
when(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).thenReturn(false);
when(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).thenReturn(true);
+ when(device1.isConnected()).thenReturn(true);
+ when(device2.isConnected()).thenReturn(true);
mLocalMediaManager.mMediaDevices.add(device1);
mLocalMediaManager.mMediaDevices.add(phoneDevice);
@@ -754,11 +765,19 @@ public class LocalMediaManagerTest {
final PhoneMediaDevice phoneDevice = mock(PhoneMediaDevice.class);
final CachedBluetoothDevice cachedDevice1 = mock(CachedBluetoothDevice.class);
final CachedBluetoothDevice cachedDevice2 = mock(CachedBluetoothDevice.class);
+ final BluetoothDevice bluetoothDevice1 = mock(BluetoothDevice.class);
+ final BluetoothDevice bluetoothDevice2 = mock(BluetoothDevice.class);
+ when(mA2dpProfile.getActiveDevice()).thenReturn(null);
+ when(mHapProfile.getActiveDevices()).thenReturn(new ArrayList<>());
when(device1.getCachedDevice()).thenReturn(cachedDevice1);
when(device2.getCachedDevice()).thenReturn(cachedDevice2);
+ when(cachedDevice1.getDevice()).thenReturn(bluetoothDevice1);
+ when(cachedDevice2.getDevice()).thenReturn(bluetoothDevice2);
when(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).thenReturn(false);
when(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).thenReturn(false);
+ when(device1.isConnected()).thenReturn(true);
+ when(device2.isConnected()).thenReturn(true);
mLocalMediaManager.mMediaDevices.add(device1);
mLocalMediaManager.mMediaDevices.add(phoneDevice);
diff --git a/packages/SystemUI/docs/broadcasts.md b/packages/SystemUI/docs/broadcasts.md
index 6c8488b332c2..8ec20f5689ff 100644
--- a/packages/SystemUI/docs/broadcasts.md
+++ b/packages/SystemUI/docs/broadcasts.md
@@ -62,7 +62,7 @@ Acquire the dispatcher by using `@Inject` to obtain a `BroadcastDispatcher`. The
* @param executor An executor to dispatch [BroadcastReceiver.onReceive]. Pass null to use an
* executor in the main thread (default).
* @param user A user handle to determine which broadcast should be dispatched to this receiver.
- * By default, it is the current user.
+ * By default, it is the user of the context (system user in SystemUI).
* @throws IllegalArgumentException if the filter has other constraints that are not actions or
* categories or the filter has no actions.
*/
diff --git a/packages/SystemUI/res-product/values-ne/strings.xml b/packages/SystemUI/res-product/values-ne/strings.xml
index 81a063c721cf..148cb51fd902 100644
--- a/packages/SystemUI/res-product/values-ne/strings.xml
+++ b/packages/SystemUI/res-product/values-ne/strings.xml
@@ -21,7 +21,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="dock_alignment_slow_charging" product="default" msgid="6997633396534416792">"अझ छिटो चार्ज गर्न फोनलाई फेरि मिलाउनुहोस्"</string>
<string name="dock_alignment_not_charging" product="default" msgid="3980752926226749808">"तारविनै चार्ज गर्न फोनलाई फेरि मिलाउनुहोस्"</string>
- <string name="inattentive_sleep_warning_message" product="tv" msgid="6844464574089665063">"Android TV यन्त्र चाँडै निष्क्रिय हुने छ; सक्रिय राख्न कुनै बटन थिच्नुहोस्।"</string>
+ <string name="inattentive_sleep_warning_message" product="tv" msgid="6844464574089665063">"Android टिभी यन्त्र चाँडै निष्क्रिय हुने छ; सक्रिय राख्न कुनै बटन थिच्नुहोस्।"</string>
<string name="inattentive_sleep_warning_message" product="default" msgid="5693904520452332224">"यो यन्त्र चाँडै निष्क्रिय हुने छ; सक्रिय राख्न थिच्नुहोस्।"</string>
<string name="keyguard_missing_sim_message" product="tablet" msgid="5018086454277963787">"ट्याब्लेटमा SIM कार्ड छैन।"</string>
<string name="keyguard_missing_sim_message" product="default" msgid="7053347843877341391">"फोनमा SIM कार्ड छैन।"</string>
diff --git a/packages/SystemUI/res/layout/bubbles_manage_button_education.xml b/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
index 0cabc3245152..7313d54c599a 100644
--- a/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
+++ b/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
@@ -17,30 +17,72 @@
<com.android.systemui.bubbles.BubbleManageEducationView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent">
-
+ android:layout_height="match_parent"
+ >
<LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:id="@+id/manage_education_view"
+ android:clickable="true"
+ android:paddingTop="28dp"
+ android:paddingBottom="16dp"
+ android:paddingStart="@dimen/bubble_expanded_view_padding"
+ android:paddingEnd="48dp"
+ android:layout_marginEnd="24dp"
android:orientation="vertical"
- android:layout_height="wrap_content"
- android:layout_width="@dimen/bubbles_manage_education_width">
+ android:background="@drawable/bubble_stack_user_education_bg"
+ android:alpha="0.9"
+ >
+
+ <TextView
+ android:id="@+id/user_education_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingStart="16dp"
+ android:paddingBottom="16dp"
+ android:fontFamily="@*android:string/config_bodyFontFamilyMedium"
+ android:maxLines="1"
+ android:text="@string/bubbles_user_education_manage_title"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Headline"/>
<TextView
android:id="@+id/user_education_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:padding="8dp"
+ android:paddingStart="16dp"
+ android:paddingBottom="24dp"
android:text="@string/bubbles_user_education_manage"
android:fontFamily="@*android:string/config_bodyFontFamily"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"
- android:background="@drawable/bubble_manage_user_education_bg"
- />
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"/>
+
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:orientation="horizontal" >
- <View
- android:id="@+id/user_education_pointer"
- android:layout_width="@dimen/bubble_pointer_width"
- android:layout_height="@dimen/bubble_pointer_height"
- />
+ <com.android.systemui.statusbar.AlphaOptimizedButton
+ style="@android:style/Widget.Material.Button.Borderless"
+ android:id="@+id/manage"
+ android:layout_gravity="start"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:focusable="true"
+ android:clickable="false"
+ android:text="@string/manage_bubbles_text"
+ android:textColor="?attr/wallpaperTextColor"
+ android:alpha="0.89"
+ />
+ <com.android.systemui.statusbar.AlphaOptimizedButton
+ style="@android:style/Widget.Material.Button.Borderless"
+ android:id="@+id/got_it"
+ android:layout_gravity="start"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:focusable="true"
+ android:text="@string/bubbles_user_education_got_it"
+ android:textColor="?attr/wallpaperTextColor"
+ />
+ </LinearLayout>
</LinearLayout>
</com.android.systemui.bubbles.BubbleManageEducationView> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml
index fce4610b934b..ef7325ea8f38 100644
--- a/packages/SystemUI/res/layout/global_screenshot.xml
+++ b/packages/SystemUI/res/layout/global_screenshot.xml
@@ -30,11 +30,10 @@
android:id="@+id/global_screenshot_animated_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center"
+ android:layout_gravity="top|start"
android:visibility="gone"
android:elevation="@dimen/screenshot_preview_elevation"
- android:background="@drawable/screenshot_rounded_corners"
- android:adjustViewBounds="true"/>
+ android:background="@drawable/screenshot_rounded_corners" />
<ImageView
android:id="@+id/global_screenshot_flash"
android:layout_width="match_parent"
@@ -49,4 +48,18 @@
android:visibility="gone"
android:pointerIcon="crosshair"/>
<include layout="@layout/global_screenshot_static"/>
+ <FrameLayout
+ android:id="@+id/global_screenshot_dismiss_button"
+ android:layout_width="@dimen/screenshot_dismiss_button_tappable_size"
+ android:layout_height="@dimen/screenshot_dismiss_button_tappable_size"
+ android:elevation="7dp"
+ android:visibility="gone"
+ android:contentDescription="@string/screenshot_dismiss_ui_description">
+ <ImageView
+ android:id="@+id/global_screenshot_dismiss_image"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_margin="@dimen/screenshot_dismiss_button_margin"
+ android:src="@drawable/screenshot_cancel"/>
+ </FrameLayout>
</FrameLayout>
diff --git a/packages/SystemUI/res/layout/global_screenshot_static.xml b/packages/SystemUI/res/layout/global_screenshot_static.xml
index da5277ce3876..9ec2f20597e7 100644
--- a/packages/SystemUI/res/layout/global_screenshot_static.xml
+++ b/packages/SystemUI/res/layout/global_screenshot_static.xml
@@ -54,22 +54,4 @@
android:layout_height="wrap_content"/>
</HorizontalScrollView>
<include layout="@layout/global_screenshot_preview"/>
- <FrameLayout
- android:id="@+id/global_screenshot_dismiss_button"
- android:layout_width="@dimen/screenshot_dismiss_button_tappable_size"
- android:layout_height="@dimen/screenshot_dismiss_button_tappable_size"
- android:elevation="7dp"
- android:visibility="gone"
- android:contentDescription="@string/screenshot_dismiss_ui_description"
- app:layout_constraintStart_toEndOf="@+id/global_screenshot_preview"
- app:layout_constraintEnd_toEndOf="@+id/global_screenshot_preview"
- app:layout_constraintTop_toTopOf="@+id/global_screenshot_preview"
- app:layout_constraintBottom_toTopOf="@+id/global_screenshot_preview">
- <ImageView
- android:id="@+id/global_screenshot_dismiss_image"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_margin="@dimen/screenshot_dismiss_button_margin"
- android:src="@drawable/screenshot_cancel"/>
- </FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/hybrid_conversation_notification.xml b/packages/SystemUI/res/layout/hybrid_conversation_notification.xml
index 21a671cf9d5a..214c44a41c5c 100644
--- a/packages/SystemUI/res/layout/hybrid_conversation_notification.xml
+++ b/packages/SystemUI/res/layout/hybrid_conversation_notification.xml
@@ -20,24 +20,26 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical|start"
+ android:paddingTop="1dp"
+ android:paddingBottom="1dp"
android:paddingEnd="12dp">
<FrameLayout
android:layout_width="@*android:dimen/conversation_content_start"
- android:layout_height="36dp"
+ android:layout_height="25dp"
>
<ImageView
android:id="@*android:id/conversation_icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
+ android:layout_width="20dp"
+ android:layout_height="20dp"
android:layout_gravity="center"
/>
<ViewStub
android:id="@*android:id/conversation_face_pile"
android:layout="@*android:layout/conversation_face_pile_layout"
- android:layout_width="36dp"
- android:layout_height="36dp"
+ android:layout_width="25dp"
+ android:layout_height="25dp"
android:layout_gravity="center"
/>
</FrameLayout>
diff --git a/packages/SystemUI/res/layout/media_view.xml b/packages/SystemUI/res/layout/media_view.xml
index d721818d6e54..a722fbfce19a 100644
--- a/packages/SystemUI/res/layout/media_view.xml
+++ b/packages/SystemUI/res/layout/media_view.xml
@@ -39,7 +39,7 @@
android:layout_alignParentLeft="true"
android:fontFamily="@*android:string/config_bodyFontFamily"
android:textColor="@color/media_primary_text"
- android:gravity="left"
+ android:gravity="start"
android:textSize="14sp" />
<TextView
@@ -49,7 +49,7 @@
android:layout_alignParentRight="true"
android:fontFamily="@*android:string/config_bodyFontFamily"
android:textColor="@color/media_primary_text"
- android:gravity="right"
+ android:gravity="end"
android:textSize="14sp" />
</FrameLayout>
diff --git a/packages/SystemUI/res/layout/notification_conversation_info.xml b/packages/SystemUI/res/layout/notification_conversation_info.xml
index 49d525fc189a..2c4b937ce95b 100644
--- a/packages/SystemUI/res/layout/notification_conversation_info.xml
+++ b/packages/SystemUI/res/layout/notification_conversation_info.xml
@@ -34,7 +34,7 @@
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:clipChildren="false"
- android:paddingTop="8dp"
+ android:paddingTop="11dp"
android:clipToPadding="true">
<ImageView
android:id="@+id/conversation_icon"
@@ -85,20 +85,20 @@
style="@style/TextAppearance.NotificationImportanceChannel"/>
</LinearLayout>
<TextView
- android:id="@+id/pkg_name"
+ android:id="@+id/group_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- style="@style/TextAppearance.NotificationImportanceChannelGroup"
android:ellipsize="end"
android:textDirection="locale"
- android:maxLines="1"/>
+ style="@style/TextAppearance.NotificationImportanceChannelGroup"/>
<TextView
- android:id="@+id/group_name"
+ android:id="@+id/pkg_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ style="@style/TextAppearance.NotificationImportanceApp"
android:ellipsize="end"
android:textDirection="locale"
- style="@style/TextAppearance.NotificationImportanceChannelGroup"/>
+ android:maxLines="1"/>
<TextView
android:id="@+id/delegate_name"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index 870deacd87ae..1c7c22653f9f 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -59,20 +59,20 @@
android:textDirection="locale"
style="@style/TextAppearance.NotificationImportanceChannel"/>
<TextView
- android:id="@+id/pkg_name"
+ android:id="@+id/group_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- style="@style/TextAppearance.NotificationImportanceChannelGroup"
- android:ellipsize="end"
android:textDirection="locale"
- android:maxLines="1"/>
+ android:ellipsize="end"
+ style="@style/TextAppearance.NotificationImportanceChannelGroup"/>
<TextView
- android:id="@+id/group_name"
+ android:id="@+id/pkg_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:textDirection="locale"
+ style="@style/TextAppearance.NotificationImportanceApp"
android:ellipsize="end"
- style="@style/TextAppearance.NotificationImportanceChannelGroup"/>
+ android:textDirection="locale"
+ android:maxLines="1"/>
<TextView
android:id="@+id/delegate_name"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/partial_conversation_info.xml b/packages/SystemUI/res/layout/partial_conversation_info.xml
index ecc311893497..07951705664a 100644
--- a/packages/SystemUI/res/layout/partial_conversation_info.xml
+++ b/packages/SystemUI/res/layout/partial_conversation_info.xml
@@ -113,18 +113,21 @@
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false"
+ android:layout_centerVertical="true"
android:orientation="horizontal">
<ImageView
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
android:layout_width="wrap_content"
android:contentDescription="@null"
android:src="@drawable/ic_info"
+ android:layout_centerVertical="true"
android:tint="?android:attr/textColorPrimary"
android:layout_marginEnd="8dp"/>
<TextView
android:id="@+id/non_configurable_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
style="@style/TextAppearance.NotificationImportanceChannelGroup" />
</LinearLayout>
</com.android.systemui.statusbar.notification.row.ButtonLinearLayout>
diff --git a/packages/SystemUI/res/layout/priority_onboarding_half_shell.xml b/packages/SystemUI/res/layout/priority_onboarding_half_shell.xml
index bf2eac3c8ff3..3f0e514a9af2 100644
--- a/packages/SystemUI/res/layout/priority_onboarding_half_shell.xml
+++ b/packages/SystemUI/res/layout/priority_onboarding_half_shell.xml
@@ -38,11 +38,61 @@
android:background="@drawable/rounded_bg_full"
>
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:padding="12dp"
+ android:layout_gravity="center_horizontal"
+ >
+
+ <!-- Big icon: 52x52, 12dp padding left + top, 16dp padding right -->
<ImageView
android:id="@+id/conversation_icon"
- android:layout_width="@dimen/notification_guts_conversation_icon_size"
- android:layout_height="@dimen/notification_guts_conversation_icon_size"
- android:layout_gravity="center_horizontal" />
+ android:layout_width="@*android:dimen/conversation_avatar_size"
+ android:layout_height="@*android:dimen/conversation_avatar_size"
+ android:scaleType="centerCrop"
+ android:importantForAccessibility="no"
+ />
+
+ <FrameLayout
+ android:id="@+id/conversation_icon_badge"
+ android:layout_width="@*android:dimen/conversation_icon_size_badged"
+ android:layout_height="@*android:dimen/conversation_icon_size_badged"
+ android:layout_marginLeft="@*android:dimen/conversation_badge_side_margin"
+ android:layout_marginTop="@*android:dimen/conversation_badge_side_margin"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ >
+ <ImageView
+ android:id="@+id/conversation_icon_badge_bg"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:src="@*android:drawable/conversation_badge_background"
+ android:forceHasOverlappingRendering="false"
+ />
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_margin="4dp"
+ android:layout_gravity="center"
+ android:forceHasOverlappingRendering="false"
+ />
+ <ImageView
+ android:id="@+id/conversation_icon_badge_ring"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:src="@*android:drawable/conversation_badge_ring"
+ android:forceHasOverlappingRendering="false"
+ android:clipToPadding="false"
+ android:scaleType="center"
+ />
+ </FrameLayout>
+ </FrameLayout>
<TextView
android:id="@+id/title"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 88102b2adc94..48b25b0773e4 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Vee alles uit"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Bestuur"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Geskiedenis"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Inkomend"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Nuut"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Stil"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Kennisgewings"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Gesprekke"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Beweeg na regs bo"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Beweeg na links onder"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Beweeg na regs onder"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Maak borrel toe"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Moenie dat gesprek \'n borrel word nie"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Klets met borrels"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Nuwe gesprekke verskyn as swerwende ikone, of borrels Tik op borrel om dit oop te maak. Sleep om dit te skuif."</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 66c0131d6671..9a75c1c0879c 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"ሁሉንም አጽዳ"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"ያቀናብሩ"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ታሪክ"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"ገቢ"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"አዲስ"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"ጸጥ ያለ"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"ማሳወቂያዎች"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"ውይይቶች"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"ወደ ላይኛው ቀኝ አንቀሳቅስ"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"የግርጌውን ግራ አንቀሳቅስ"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"ታችኛውን ቀኝ ያንቀሳቅሱ"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"አረፋን አሰናብት"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"ውይይቶችን በአረፋ አታሳይ"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"አረፋዎችን በመጠቀም ይወያዩ"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"አዲስ ውይይቶች እንደ ተንሳፋፊ አዶዎች ወይም አረፋዎች ሆነው ይታያሉ። አረፋን ለመክፈት መታ ያድርጉ። ለመውሰድ ይጎትቱት።"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index ebf4bc80b689..fd475b9ee4de 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -522,7 +522,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"محو الكل"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"إدارة"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"السجلّ"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"الإشعارات الواردة"</string>
+ <!-- no translation found for notification_section_header_incoming (850925217908095197) -->
+ <skip />
<string name="notification_section_header_gentle" msgid="6804099527336337197">"صامت"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"الإشعارات"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"المحادثات"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index c3d1e78f74e5..ca21ff674595 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"সকলো মচক"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"পৰিচালনা"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ইতিহাস"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"অন্তৰ্গামী"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"নতুন"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"নীৰৱ"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"জাননীসমূহ"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"বাৰ্তালাপ"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"শীৰ্ষৰ সোঁফালে নিয়ক"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"বুটামটো বাওঁফালে নিয়ক"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"তলৰ সোঁফালে নিয়ক"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"বাবল অগ্ৰাহ্য কৰক"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"বাৰ্তালাপ বাবল নকৰিব"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Bubbles ব্যৱহাৰ কৰি চাট কৰক"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"নতুন বাৰ্তালাপ উপঙি থকা চিহ্নসমূহ অথবা bubbles হিচাপে প্ৰদর্শিত হয়। Bubbles খুলিবলৈ টিপক। এইটো স্থানান্তৰ কৰিবলৈ টানি নিয়ক।"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 3919fe314361..3a7c98ca34cf 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Hamısını silin"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"İdarə edin"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Tarixçə"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Gələn"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Yeni"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Səssiz"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Bildirişlər"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Söhbətlər"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Yuxarıya sağa köçürün"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Aşağıya sola köçürün"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Aşağıya sağa köçürün"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Qabarcığı ləğv edin"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Söhbətdən gələn bildirişi göstərməyin"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Yumrucuqlardan istifadə edərək söhbət edin"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Yeni söhbətlər üzən nişanlar və ya yumrucuqlar kimi görünür. Yumrucuğu açmaq üçün toxunun. Hərəkət etdirmək üçün sürüşdürün."</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index f2a6d65bd051..cdface92a34c 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -513,7 +513,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Obriši sve"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Upravljajte"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Istorija"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Dolazno"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Novo"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Nečujno"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Obaveštenja"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Konverzacije"</string>
@@ -598,7 +598,7 @@
<string name="screen_pinning_title" msgid="9058007390337841305">"Aplikacija je zakačena"</string>
<string name="screen_pinning_description" msgid="8699395373875667743">"Na ovaj način se ovo stalno prikazuje dok ga ne otkačite. Dodirnite i zadržite Nazad i Pregled da biste ga otkačili."</string>
<string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"Na ovaj način se ovo stalno prikazuje dok ga ne otkačite. Dodirnite i zadržite Nazad i Početna da biste ga otkačili."</string>
- <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Na ovaj način se stalno prikazuje dok ga ne otkačite. Prevucite nagore i zadržite da biste ga otkačili."</string>
+ <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Stalno će se prikazivati dok je ne otkačite. Prevucite nagore i zadržite da biste je otkačili."</string>
<string name="screen_pinning_description_accessible" msgid="7386449191953535332">"Na ovaj način se ovo stalno prikazuje dok ga ne otkačite. Dodirnite i zadržite Pregled da biste ga otkačili."</string>
<string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"Na ovaj način se ovo stalno prikazuje dok ga ne otkačite. Dodirnite i zadržite Početna da biste ga otkačili."</string>
<string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"Mogu da budu dostupni lični podaci (kao što su kontakti i sadržaj imejlova)."</string>
@@ -1007,8 +1007,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Premesti gore desno"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Premesti dole levo"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Premesti dole desno"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Odbacivanje oblačića"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Ne koristi oblačiće za konverzaciju"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Ćaskajte u oblačićima"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Nove konverzacije se prikazuju kao plutajuće ikone ili oblačići. Dodirnite da biste otvorili oblačić. Prevucite da biste ga premestili."</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index d34f089ec9aa..32ec1841ea3e 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -516,7 +516,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Ачысціць усё"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Кіраваць"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Гісторыя"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Уваходныя"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Новае"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Без гуку"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Апавяшчэнні"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Размовы"</string>
@@ -601,7 +601,7 @@
<string name="screen_pinning_title" msgid="9058007390337841305">"Праграма замацавана"</string>
<string name="screen_pinning_description" msgid="8699395373875667743">"Будзе паказвацца, пакуль не адмацуеце. Каб адмацаваць, краніце і ўтрымлівайце кнопкі \"Назад\" і \"Агляд\"."</string>
<string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"Будзе паказвацца, пакуль не адмацуеце. Каб адмацаваць, націсніце і ўтрымлівайце кнопкі \"Назад\" і \"Галоўны экран\"."</string>
- <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Будзе паказвацца, пакуль не адмацуеце Каб адмацаваць, прагартайце ўверх, утрымліваючы палец на экране"</string>
+ <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Будзе паказвацца, пакуль не адмацуеце. Каб адмацаваць, прагартайце ўверх, утрымліваючы палец на экране."</string>
<string name="screen_pinning_description_accessible" msgid="7386449191953535332">"Будзе паказвацца, пакуль не адмацуеце. Каб адмацаваць, краніце і ўтрымлівайце кнопку \"Агляд\"."</string>
<string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"Будзе паказвацца, пакуль не адмацуеце. Каб адмацаваць, націсніце і ўтрымлівайце кнопку \"Галоўны экран\"."</string>
<string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"Могуць быць даступныя асабістыя даныя (напрыклад, кантакты і змесціва электроннай пошты)."</string>
@@ -1012,8 +1012,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Перамясціце правей і вышэй"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Перамясціць лявей і ніжэй"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Перамясціць правей і ніжэй"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Адхіліць апавяшчэнне"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Не паказваць размову ў выглядзе ўсплывальных апавяшчэнняў"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Усплывальныя апавяшчэнні"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Новыя размовы будуць паказвацца як рухомыя значкі ці ўсплывальныя апавяшчэнні. Націсніце, каб адкрыць усплывальнае апавяшчэнне. Перацягніце яго, каб перамясціць."</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 2388cf6167c0..a2a2671b3f70 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -92,7 +92,7 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Записът на екрана се обработва"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Текущо известие за сесия за записване на екрана"</string>
<string name="screenrecord_start_label" msgid="1750350278888217473">"Да се стартира ли записът?"</string>
- <string name="screenrecord_description" msgid="1123231719680353736">"По време на записване системата Android може да прихване поверителна информация, която е показана на екрана или възпроизвеждана на устройството ви. Това включва пароли, данни за плащане, снимки, съобщения и аудио."</string>
+ <string name="screenrecord_description" msgid="1123231719680353736">"По време на записване системата Android може да запише и поверителна информация, която е показана на екрана или възпроизвеждана на устройството ви. Това включва пароли, данни за плащане, снимки, съобщения и аудио."</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Записване на звук"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Аудио от устройството"</string>
<string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Звук от устройството ви, като например музика, обаждания и мелодии"</string>
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Изчистване на всички"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Управление"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"История"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Входящи"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Нови"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Беззвучни"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Известия"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Разговори"</string>
@@ -1002,10 +1002,9 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Преместване горе вдясно"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Преместване долу вляво"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Преместване долу вдясно"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Балонче: Отхвърляне"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Без балончета за разговора"</string>
- <string name="bubbles_user_education_title" msgid="5547017089271445797">"Разговаряне чрез балончета"</string>
+ <string name="bubbles_user_education_title" msgid="5547017089271445797">"Чат с балончета"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Новите разговори се показват като плаващи икони, или балончета. Докоснете балонче, за да го отворите, или го плъзнете, за да го преместите."</string>
<string name="bubbles_user_education_manage_title" msgid="2848511858160342320">"Управление на балончетата по всяко време"</string>
<string name="bubbles_user_education_manage" msgid="1391639189507036423">"Докоснете „Управление“, за да изключите балончетата от това приложение"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index d5e827f6b618..aad8eec3e35e 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"সবকিছু সাফ করুন"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"পরিচালনা করুন"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ইতিহাস"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"ইনকামিং"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"নতুন"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"আওয়াজ করবে না"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"বিজ্ঞপ্তি"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"কথোপকথন"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"উপরে ডানদিকে সরান"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"নিচে বাঁদিকে সরান"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"নিচে ডান দিকে সরান"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"বাবল খারিজ করুন"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"কথোপকথন বাবল হিসেবে দেখাবে না"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"বাবল ব্যবহার করে চ্যাট করুন"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"নতুন কথোপকথন ভেসে থাকা আইকন বা বাবল হিসেবে দেখানো হয়। বাবল খুলতে ট্যাপ করুন। সেটি সরাতে ধরে টেনে আনুন।"</string>
@@ -1068,7 +1067,7 @@
<string name="controls_error_retryable" msgid="864025882878378470">"সমস্যা, আবার চেষ্টা করা হচ্ছে…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"খুঁজে পাওয়া যায়নি"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"কন্ট্রোল উপলভ্য নেই"</string>
- <string name="controls_error_removed_message" msgid="2885911717034750542">"<xliff:g id="DEVICE">%1$s</xliff:g> ডিভাইস অ্যাক্সেস করা যায়নি। <xliff:g id="APPLICATION">%2$s</xliff:g> অ্যাপ চেক করে দেখুন যাতে এটি নিশ্চিত করে নিতে পারেন যে কন্ট্রোল এখনও উপলভ্য আছে এবং অ্যাপ সেটিংসে কোনও পরিবর্তন করা হয়নি।"</string>
+ <string name="controls_error_removed_message" msgid="2885911717034750542">"<xliff:g id="DEVICE">%1$s</xliff:g> ডিভাইস অ্যাক্সেস করা যায়নি। কন্ট্রোল এখনও উপলভ্য আছে কিনা ও অ্যাপ সেটিংসে কোনও পরিবর্তন করা হয়েছে কিনা তা ভাল করে দেখে নিতে <xliff:g id="APPLICATION">%2$s</xliff:g> অ্যাপ চেক করুন।"</string>
<string name="controls_open_app" msgid="483650971094300141">"অ্যাপ খুলুন"</string>
<string name="controls_error_generic" msgid="352500456918362905">"স্ট্যাটাস লোড করা যাচ্ছে না"</string>
<string name="controls_error_failed" msgid="960228639198558525">"সমস্যা হয়েছে, আবার চেষ্টা করুন"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 37397f959aea..6cdc1d79b00d 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -513,7 +513,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Očisti sve"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Upravljajte"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historija"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Dolazno"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Novo"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Nečujno"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Obavještenja"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Razgovori"</string>
@@ -1009,8 +1009,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Pomjerite gore desno"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Pomjeri dolje lijevo"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Pomjerite dolje desno"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Odbaci oblačić"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Nemoj prikazivati razgovor u oblačićima"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Chatajte koristeći oblačiće"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Novi razgovori se prikazuju kao plutajuće ikone ili oblačići. Dodirnite da otvorite oblačić. Prevucite da ga premjestite."</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 2fdddf710e13..5cc0853a6e18 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Esborra-ho tot"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Gestiona"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historial"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Entrants"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Novetats"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Silenci"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificacions"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Converses"</string>
@@ -1002,10 +1002,9 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Mou a dalt a la dreta"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Mou a baix a l\'esquerra"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Mou a baix a la dreta"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Ignora la bombolla"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"No mostris la conversa com a bombolla"</string>
- <string name="bubbles_user_education_title" msgid="5547017089271445797">"Xateja utilitzant les bombolles"</string>
+ <string name="bubbles_user_education_title" msgid="5547017089271445797">"Xateja amb bombolles"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Les converses noves es mostren com a icones flotants o bombolles. Toca per obrir una bombolla. Arrossega-la per moure-la."</string>
<string name="bubbles_user_education_manage_title" msgid="2848511858160342320">"Controla les bombolles en qualsevol moment"</string>
<string name="bubbles_user_education_manage" msgid="1391639189507036423">"Toca Gestiona per desactivar les bombolles d\'aquesta aplicació"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 18012b5e4280..a071793af881 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -516,7 +516,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Smazat vše"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Spravovat"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historie"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Příchozí"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Nové"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Tiché"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Oznámení"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Konverzace"</string>
@@ -601,7 +601,7 @@
<string name="screen_pinning_title" msgid="9058007390337841305">"Aplikace je připnuta"</string>
<string name="screen_pinning_description" msgid="8699395373875667743">"Obsah bude připnut v zobrazení, dokud jej neuvolníte. Uvolníte jej stisknutím a podržením tlačítek Zpět a Přehled."</string>
<string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"Obsah bude připnut v zobrazení, dokud ho neuvolníte. Uvolníte ho podržením tlačítek Zpět a Plocha."</string>
- <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Obsah bude připnut v zobrazení, dokud ho neuvolníte. Uvolnit ho můžete přejetím nahoru a podržením."</string>
+ <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Díky připnutí bude vidět, dokud ji neodepnete. Odepnout ji můžete přejetím nahoru a podržením."</string>
<string name="screen_pinning_description_accessible" msgid="7386449191953535332">"Obsah bude připnut v zobrazení, dokud jej neuvolníte. Uvolníte jej stisknutím a podržením tlačítka Přehled."</string>
<string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"Obsah bude připnut v zobrazení, dokud ho neuvolníte. Uvolníte ho podržením tlačítka Plocha."</string>
<string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"Může mít přístup k soukromým datům (například kontaktům a obsahu e-mailů)"</string>
@@ -1012,8 +1012,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Přesunout vpravo nahoru"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Přesunout vlevo dolů"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Přesunout vpravo dolů"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Zavřít bublinu"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Nezobrazovat konverzaci v bublinách"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Chatujte pomocí bublin"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Nové konverzace se zobrazují jako plovoucí ikony, neboli bubliny. Klepnutím bublinu otevřete. Přetažením ji posunete."</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 3f71e3d49137..22a0e05e2ea5 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Ryd alle"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Administrer"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historik"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Indgående"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Nye"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Lydløs"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifikationer"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Samtaler"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Flyt op til højre"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Flyt ned til venstre"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Flyt ned til højre"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Afvis bobbel"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Vis ikke samtaler i bobler"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Chat ved hjælp af bobler"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Nye samtaler vises som svævende ikoner eller bobler. Tryk for at åbne boblen. Træk for at flytte den."</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 075a496c244b..3397cd615138 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -510,7 +510,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Alle löschen"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Verwalten"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Verlauf"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Neue Benachrichtigungen"</string>
+ <!-- no translation found for notification_section_header_incoming (850925217908095197) -->
+ <skip />
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Lautlos"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Benachrichtigungen"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Unterhaltungen"</string>
@@ -1002,8 +1003,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Nach rechts oben verschieben"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Nach unten links verschieben"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Nach unten rechts verschieben"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Bubble schließen"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Unterhaltung nicht als Bubble anzeigen"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Bubbles zum Chatten verwenden"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Neue Unterhaltungen erscheinen als unverankerte Symbole, \"Bubbles\" genannt. Wenn du die Bubble öffnen möchtest, tippe sie an. Wenn du sie verschieben möchtest, zieh an ihr."</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index c8db318dfb65..f24f7457b0c6 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -256,7 +256,7 @@
<!-- no translation found for accessibility_work_mode (1280025758672376313) -->
<skip />
<string name="accessibility_notification_dismissed" msgid="4411652015138892952">"Η ειδοποίηση έχει απορριφθεί."</string>
- <string name="accessibility_bubble_dismissed" msgid="270358867566720729">"Η φούσκα παραβλέφθηκε."</string>
+ <string name="accessibility_bubble_dismissed" msgid="270358867566720729">"Το συννεφάκι παραβλέφθηκε."</string>
<string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Πλαίσιο σκίασης ειδοποιήσεων."</string>
<string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Γρήγορες ρυθμίσεις."</string>
<string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Οθόνη κλειδώματος"</string>
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Διαγραφή όλων"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Διαχείριση"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Ιστορικό"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Εισερχόμενες"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Νέα"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Σίγαση"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Ειδοποιήσεις"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Συζητήσεις"</string>
@@ -710,7 +710,7 @@
<string name="inline_keep_showing_app" msgid="4393429060390649757">"Να συνεχίσουν να εμφανίζονται ειδοποιήσεις από αυτήν την εφαρμογή;"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Σίγαση"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Προεπιλογή"</string>
- <string name="notification_bubble_title" msgid="8330481035191903164">"Φούσκα"</string>
+ <string name="notification_bubble_title" msgid="8330481035191903164">"Συννεφάκι"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Χωρίς ήχο ή δόνηση"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Χωρίς ήχο ή δόνηση και εμφανίζεται χαμηλά στην ενότητα συζητήσεων"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Ενδέχεται να κουδουνίζει ή να δονείται βάσει των ρυθμίσεων του τηλεφώνου"</string>
@@ -1002,11 +1002,10 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Μετακίνηση επάνω δεξιά"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Μετακίνηση κάτω αριστερά"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Μετακίνηση κάτω δεξιά"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Παράβλ. για συννεφ."</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Να μην γίνει προβολή της συζήτησης σε συννεφάκια."</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Συζητήστε χρησιμοποιώντας συννεφάκια."</string>
- <string name="bubbles_user_education_description" msgid="1160281719576715211">"Οι νέες συζητήσεις εμφανίζονται ως κινούμενα εικονίδια ή φούσκες. Πατήστε για να ανοίξετε τη φούσκα. Σύρετε για να το μετακινήσετε."</string>
+ <string name="bubbles_user_education_description" msgid="1160281719576715211">"Οι νέες συζητήσεις εμφανίζονται ως κινούμενα εικονίδια ή συννεφάκια. Πατήστε για να ανοίξετε το συννεφάκι. Σύρετε για να το μετακινήσετε."</string>
<string name="bubbles_user_education_manage_title" msgid="2848511858160342320">"Ελέγξτε τα συννεφάκια ανά πάσα στιγμή."</string>
<string name="bubbles_user_education_manage" msgid="1391639189507036423">"Πατήστε Διαχείριση για να απενεργοποιήσετε τα συννεφάκια από αυτήν την εφαρμογή."</string>
<string name="bubbles_user_education_got_it" msgid="8282812431953161143">"Το κατάλαβα."</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 708f2f4f017a..171b6f61cff4 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -92,7 +92,7 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
<string name="screenrecord_start_label" msgid="1750350278888217473">"Start recording?"</string>
- <string name="screenrecord_description" msgid="1123231719680353736">"While recording, Android system can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string>
+ <string name="screenrecord_description" msgid="1123231719680353736">"While recording, Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Record audio"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Device audio"</string>
<string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sound from your device, like music, calls and ringtones"</string>
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Clear all"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Manage"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"History"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Incoming"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"New"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Silent"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversations"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Move top right"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Move bottom left"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Move bottom right"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Dismiss bubble"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Don’t bubble conversation"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Chat using bubbles"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"New conversations appear as floating icons, or bubbles. Tap to open bubble. Drag to move it."</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 313c604953ea..5fb6236d6dcd 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -92,7 +92,7 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
<string name="screenrecord_start_label" msgid="1750350278888217473">"Start recording?"</string>
- <string name="screenrecord_description" msgid="1123231719680353736">"While recording, Android system can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string>
+ <string name="screenrecord_description" msgid="1123231719680353736">"While recording, Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Record audio"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Device audio"</string>
<string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sound from your device, like music, calls and ringtones"</string>
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Clear all"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Manage"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"History"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Incoming"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"New"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Silent"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversations"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Move top right"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Move bottom left"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Move bottom right"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Dismiss bubble"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Don’t bubble conversation"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Chat using bubbles"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"New conversations appear as floating icons, or bubbles. Tap to open bubble. Drag to move it."</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 708f2f4f017a..171b6f61cff4 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -92,7 +92,7 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
<string name="screenrecord_start_label" msgid="1750350278888217473">"Start recording?"</string>
- <string name="screenrecord_description" msgid="1123231719680353736">"While recording, Android system can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string>
+ <string name="screenrecord_description" msgid="1123231719680353736">"While recording, Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Record audio"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Device audio"</string>
<string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sound from your device, like music, calls and ringtones"</string>
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Clear all"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Manage"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"History"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Incoming"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"New"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Silent"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversations"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Move top right"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Move bottom left"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Move bottom right"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Dismiss bubble"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Don’t bubble conversation"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Chat using bubbles"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"New conversations appear as floating icons, or bubbles. Tap to open bubble. Drag to move it."</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 708f2f4f017a..171b6f61cff4 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -92,7 +92,7 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
<string name="screenrecord_start_label" msgid="1750350278888217473">"Start recording?"</string>
- <string name="screenrecord_description" msgid="1123231719680353736">"While recording, Android system can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string>
+ <string name="screenrecord_description" msgid="1123231719680353736">"While recording, Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Record audio"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Device audio"</string>
<string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sound from your device, like music, calls and ringtones"</string>
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Clear all"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Manage"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"History"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Incoming"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"New"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Silent"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversations"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Move top right"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Move bottom left"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Move bottom right"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Dismiss bubble"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Don’t bubble conversation"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Chat using bubbles"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"New conversations appear as floating icons, or bubbles. Tap to open bubble. Drag to move it."</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 5d2e3eb16780..f37742d62062 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‏‏‎‏‎‏‎‏‎‏‏‏‎‏‎‎‎‏‎‎‎‎‎‏‎‏‎‏‏‏‏‎‏‏‎‎‏‎‎‏‏‎‎‏‎‎‎‏‏‏‏‏‎‎‏‎‎Clear all‎‏‎‎‏‎"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‎‏‏‏‎‏‎‏‏‏‎‎‏‏‏‏‏‎‏‎‏‏‎‎‏‎‎‎‏‎‎‎‎‏‏‎‎‏‏‎‏‏‏‏‏‎‏‏‏‏‏‎‎‎Manage‎‏‎‎‏‎"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎‎‏‏‎‎‏‎‏‎‏‎‏‏‎‏‎‎‎‎‎‏‏‏‎‏‏‏‎‎‏‏‎‏‏‏‎‏‏‏‏‏‎‏‏‎‏‏‏‏‏‏‏‎‎‏‏‎‎History‎‏‎‎‏‎"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‏‏‏‏‏‎‎‏‎‏‏‏‎‏‎‏‏‏‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‏‏‏‎‏‎‎‎‎‎‎‎‏‏‏‎Incoming‎‏‎‎‏‎"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‏‏‎‎‏‏‏‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‏‏‏‏‏‏‎‏‎‏‎‏‎‏‏‏‏‎‎‎‏‏‎‏‏‏‎‏‎New‎‏‎‎‏‎"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‏‎‏‏‎‏‎‎‎‎‎‏‎‎‎‏‏‏‏‎‎‎‎‏‏‎‏‏‏‏‎‏‎‎‏‎‎‏‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‏‎Silent‎‏‎‎‏‎"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‏‏‎‏‎‎‎‏‎‏‎‎‎‏‎‎‎‏‏‏‏‏‎‏‏‎‏‏‏‏‏‏‎‎‏‏‎‏‏‎‏‏‏‏‎‏‏‏‎‏‎‎‏‏‎Notifications‎‏‎‎‏‎"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‏‏‎‎‏‏‏‏‎‏‏‏‏‏‎‎‏‎‏‏‎‎‎‏‎‎‎‏‏‎‎‎‏‎‎‏‎‎‎‎‎‏‏‎‎‎‎‏‎‏‏‏‏‎‏‎Conversations‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index db5b68c0dd15..b4bba8f3145b 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -146,7 +146,7 @@
<string name="biometric_dialog_face_icon_description_authenticating" msgid="3401633342366146535">"Buscando tu rostro"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Se autenticó el rostro"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmado"</string>
- <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Presiona Confirmar para completarla"</string>
+ <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Presiona Confirmar para completar"</string>
<string name="biometric_dialog_authenticated" msgid="7337147327545272484">"Autenticado"</string>
<string name="biometric_dialog_use_pin" msgid="8385294115283000709">"Usar PIN"</string>
<string name="biometric_dialog_use_pattern" msgid="2315593393167211194">"Usar patrón"</string>
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Borrar todo"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Administrar"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historial"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Entrante"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Nuevo"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Silenciadas"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificaciones"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversaciones"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Ubicar arriba a la derecha"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Ubicar abajo a la izquierda"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Ubicar abajo a la derecha"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Descartar burbuja"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"No mostrar la conversación en burbujas"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Chat con burbujas"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Las conversaciones nuevas aparecen como elementos flotantes o burbujas. Presiona para abrir la burbuja. Arrástrala para moverla."</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 1d8549a78efa..6ffed58c3df8 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Borrar todo"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Gestionar"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historial"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Entrantes"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Nuevas"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Silenciadas"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificaciones"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversaciones"</string>
@@ -593,16 +593,16 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"desactivar"</string>
<string name="accessibility_output_chooser" msgid="7807898688967194183">"Cambiar dispositivo de salida"</string>
<string name="screen_pinning_title" msgid="9058007390337841305">"La aplicación está fijada"</string>
- <string name="screen_pinning_description" msgid="8699395373875667743">"La pantalla se mantiene visible hasta que dejas de fijarla. Para ello, mantén pulsados los botones Atrás y Aplicaciones recientes."</string>
- <string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"La pantalla se mantiene visible hasta que dejas de fijarla. Para ello, mantén pulsados los botones Atrás e Inicio."</string>
- <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Se mantiene visible hasta que dejas de fijarla. Para ello, desliza el dedo hacia arriba y mantén pulsado."</string>
- <string name="screen_pinning_description_accessible" msgid="7386449191953535332">"La pantalla se mantiene visible hasta que dejas de fijarla. Para ello, mantén pulsado el botón Aplicaciones recientes."</string>
- <string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"La pantalla se mantiene visible hasta que dejas de fijarla. Para ello, mantén pulsado el botón Inicio."</string>
- <string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"Es posible que se revelen datos personales, como contactos o el contenido de correos."</string>
+ <string name="screen_pinning_description" msgid="8699395373875667743">"La pantalla se mantendrá visible hasta que dejes de fijarla. Para dejar de fijarla, mantén pulsados los botones Atrás y Aplicaciones recientes."</string>
+ <string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"La pantalla se mantendrá visible hasta que dejes de fijarla. Para dejar de fijarla, mantén pulsados los botones Atrás e Inicio."</string>
+ <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"La pantalla se mantendrá visible hasta que dejes de fijarla. Para dejar de fijarla, desliza el dedo hacia arriba y mantenlo pulsado."</string>
+ <string name="screen_pinning_description_accessible" msgid="7386449191953535332">"La pantalla se mantendrá visible hasta que dejes de fijarla. Para dejar de fijarla, mantén pulsado el botón Aplicaciones recientes."</string>
+ <string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"La pantalla se mantendrá visible hasta que dejes de fijarla. Para dejar de fijarla, mantén pulsado el botón Inicio."</string>
+ <string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"Es posible que se pueda acceder a datos personales, como contactos o el contenido de correos."</string>
<string name="screen_pinning_can_open_other_apps" msgid="7529756813231421455">"Se pueden abrir otras aplicaciones desde aplicaciones fijadas."</string>
<string name="screen_pinning_toast" msgid="8177286912533744328">"Para dejar de fijar esta aplicación, mantén pulsados los botones Atrás y Aplicaciones recientes"</string>
<string name="screen_pinning_toast_recents_invisible" msgid="6850978077443052594">"Para dejar de fijar esta aplicación, mantén pulsados los botones Atrás e Inicio"</string>
- <string name="screen_pinning_toast_gesture_nav" msgid="170699893395336705">"Para dejar de fijar esta aplicación, desliza el dedo hacia arriba y mantenlo pulsado"</string>
+ <string name="screen_pinning_toast_gesture_nav" msgid="170699893395336705">"Para dejar de fijar esta aplicación, desliza el dedo hacia arriba y no lo separes de la pantalla."</string>
<string name="screen_pinning_positive" msgid="3285785989665266984">"Entendido"</string>
<string name="screen_pinning_negative" msgid="6882816864569211666">"No, gracias"</string>
<string name="screen_pinning_start" msgid="7483998671383371313">"Aplicación fijada"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Mover arriba a la derecha"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Mover abajo a la izquierda."</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Mover abajo a la derecha"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Cerrar burbuja"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"No mostrar conversación en burbujas"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Chatea con burbujas"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Las conversaciones nuevas aparecen como iconos flotantes llamadas \"burbujas\". Toca para abrir la burbuja. Arrastra para moverla."</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index ede01b7f191c..258bf5a581d0 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Tühjenda kõik"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Haldamine"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Ajalugu"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Sissetulevad"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Uued"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Hääletu"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Märguanded"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Vestlused"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Teisalda üles paremale"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Teisalda alla vasakule"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Teisalda alla paremale"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Sule mull"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Ära kuva vestlust mullina"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Vestelge mullide abil"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Uued vestlused kuvatakse hõljuvate ikoonidena ehk mullidena. Puudutage mulli avamiseks. Lohistage mulli, et seda liigutada."</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 641d031ef4aa..8e244a2a37ae 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -92,7 +92,7 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Pantaila-grabaketa prozesatzen"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Pantailaren grabaketa-saioaren jakinarazpen jarraitua"</string>
<string name="screenrecord_start_label" msgid="1750350278888217473">"Grabatzen hasi nahi duzu?"</string>
- <string name="screenrecord_description" msgid="1123231719680353736">"Pantaila grabatzen duzun bitartean, Android sistemak detektatu egin dezake pantailan agertzen den edo gailuak erreproduzitzen duen kontuzko informazioa; besteak beste, pasahitzak, ordainketen informazioa, argazkiak, mezuak eta audioak."</string>
+ <string name="screenrecord_description" msgid="1123231719680353736">"Pantaila grabatzen duzun bitartean, Android sistemak detektatu egin dezake pantailan agertzen den edo gailuak erreproduzitzen duen kontuzko informazioa; besteak beste, pasahitzak, ordainketa-informazioa, argazkiak, mezuak eta audioa."</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Grabatu audioa"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Gailuaren audioa"</string>
<string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Gailuko soinuak; adibidez, musika, deiak eta tonuak"</string>
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Garbitu guztiak"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Kudeatu"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historia"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Jasotako azkenak"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Berria"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Isila"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Jakinarazpenak"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Elkarrizketak"</string>
@@ -624,7 +624,7 @@
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Dardara"</string>
<string name="volume_ringer_status_silent" msgid="3691324657849880883">"Ez jo tonua"</string>
<string name="qs_status_phone_vibrate" msgid="7055409506885541979">"Telefonoaren dardara aktibatuta dago"</string>
- <string name="qs_status_phone_muted" msgid="3763664791309544103">"Telefonoaren tonu-jotzailea desaktibatuta dago"</string>
+ <string name="qs_status_phone_muted" msgid="3763664791309544103">"Tonu-jotzailea desaktibatuta"</string>
<string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Sakatu audioa aktibatzeko."</string>
<string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Sakatu dardara ezartzeko. Baliteke erabilerraztasun-eginbideen audioa desaktibatzea."</string>
<string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Sakatu audioa desaktibatzeko. Baliteke erabilerraztasun-eginbideen audioa desaktibatzea."</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Eraman goialdera, eskuinetara"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Eraman behealdera, ezkerretara"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Eraman behealdera, eskuinetara"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Baztertu burbuila"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Ez erakutsi elkarrizketak burbuila gisa"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Txateatu burbuilen bidez"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Elkarrizketa berriak ikono gainerakor edo burbuila gisa agertzen dira. Sakatu burbuila irekitzeko. Arrasta ezazu mugitzeko."</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index eec54deb2b8e..4ebfa797edf4 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"پاک کردن همه موارد"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"مدیریت"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"سابقه"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"ورودی"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"جدید"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"بی‌صدا"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"اعلان‌ها"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"مکالمه‌ها"</string>
@@ -598,7 +598,7 @@
<string name="screen_pinning_description_gestural" msgid="7246323931831232068">"تا برداشتن پین، در نما نگه‌داشته می‌شود. برای برداشتن پین، از پایین صفحه تند به‌طرف بالا بکشید و نگه‌دارید."</string>
<string name="screen_pinning_description_accessible" msgid="7386449191953535332">"تا زمانی که پین را بردارید، در نما نگه‌داشته می‌شود. برای برداشتن پین، «نمای کلی» را لمس کنید و نگه‌دارید."</string>
<string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"تا برداشتن پین، در نما نگه‌داشته می‌شود. برای برداشتن پین، «صفحه اصلی» را لمس کنید و نگه‌دارید."</string>
- <string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"ممکن است داده‌های شخصی (مانند مخاطبین و محتوای ایمیل) دردسترس باشند."</string>
+ <string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"ممکن است داده‌های شخصی (مانند مخاطبین و محتوای ایمیل) در دسترس باشد."</string>
<string name="screen_pinning_can_open_other_apps" msgid="7529756813231421455">"برنامه پین‌شده ممکن است برنامه‌های دیگر را باز کند."</string>
<string name="screen_pinning_toast" msgid="8177286912533744328">"برای برداشتن پین این برنامه، دکمه‌های «برگشت» و «نمای کلی» را لمس کنید و نگه‌دارید"</string>
<string name="screen_pinning_toast_recents_invisible" msgid="6850978077443052594">"برای برداشتن پین این برنامه، دکمه‌های «برگشت» و «صفحه اصلی» را لمس کنید و نگه دارید"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"انتقال به بالا سمت چپ"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"انتقال به پایین سمت راست"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"انتقال به پایین سمت چپ"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"رد کردن حبابک"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"مکالمه در حباب نشان داده نشود"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"گپ بااستفاده از ابزارک اعلان"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"مکالمه‌های جدید به‌صورت نمادهای شناور یا ابزارک اعلان نشان داده شوند. برای باز کردن ابزارک اعلان ضربه بزنید. برای جابه‌جایی، آن را بکشید."</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index ee7ed70718a1..05a88f4cc6ee 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Poista kaikki"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Muuta asetuksia"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historia"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Saapuvat"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Uudet"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Äänetön"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Ilmoitukset"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Keskustelut"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Siirrä oikeaan yläreunaan"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Siirrä vasempaan alareunaan"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Siirrä oikeaan alareunaan"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Ohita kupla"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Älä näytä kuplia keskusteluista"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Chattaile kuplien avulla"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Uudet keskustelut näkyvät kelluvina kuvakkeina tai kuplina. Avaa kupla napauttamalla. Siirrä sitä vetämällä."</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index c71ab7ee4894..e7ef0824539f 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Tout effacer"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Gérer"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historique"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Entrantes"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Nouvelles"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Mode silencieux"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversations"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Déplacer dans coin sup. droit"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Déplacer dans coin inf. gauche"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Déplacer dans coin inf. droit"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Ignorer la bulle"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Ne pas afficher les conversations dans des bulles"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Clavarder en utilisant des bulles"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Les nouvelles conversations s\'affichent sous forme d\'icônes flottantes (de bulles). Touchez une bulle pour l\'ouvrir. Faites-la glisser pour la déplacer."</string>
@@ -1069,7 +1068,7 @@
<string name="controls_error_removed" msgid="6675638069846014366">"Introuvable"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"La commande n\'est pas accessible"</string>
<string name="controls_error_removed_message" msgid="2885911717034750542">"Impossible d\'accéder à <xliff:g id="DEVICE">%1$s</xliff:g>. Vérifiez l\'application <xliff:g id="APPLICATION">%2$s</xliff:g> pour vous assurer que la commande est toujours offerte et que les paramètres de l\'application n\'ont pas changé."</string>
- <string name="controls_open_app" msgid="483650971094300141">"Ouvrir"</string>
+ <string name="controls_open_app" msgid="483650971094300141">"Ouvrir l\'application"</string>
<string name="controls_error_generic" msgid="352500456918362905">"Impossible de charger l\'état"</string>
<string name="controls_error_failed" msgid="960228639198558525">"Erreur. Veuillez réessayer."</string>
<string name="controls_in_progress" msgid="4421080500238215939">"En cours"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 4c69c865d1b5..c7d88720a9df 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Tout effacer"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Gérer"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historique"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Notifications entrantes"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Nouvelles notifications"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Silencieuses"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifications"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversations"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Déplacer en haut à droite"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Déplacer en bas à gauche"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Déplacer en bas à droite"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Fermer la bulle"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Ne pas afficher la conversations dans des bulles"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Chatter en utilisant des bulles"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Les nouvelles conversations s\'affichent sous forme d\'icônes flottantes ou bulles. Appuyez sur la bulle pour l\'ouvrir. Faites-la glisser pour la déplacer."</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index d80bd95abf65..93524f8492d8 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Eliminar todas"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Xestionar"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historial"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Entrantes"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Notificacións novas"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Silencio"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificacións"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversas"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Mover á parte superior dereita"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Mover á parte infer. esquerda"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Mover á parte inferior dereita"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Ignorar burbulla"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Non mostrar a conversa como burbulla"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Chatear usando burbullas"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"As conversas novas aparecen como iconas flotantes ou burbullas. Toca para abrir a burbulla e arrastra para movela."</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index c641208055c1..c4b8ae2ea421 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -510,7 +510,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"બધુ સાફ કરો"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"મેનેજ કરો"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ઇતિહાસ"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"ઇનકમિંગ"</string>
+ <!-- no translation found for notification_section_header_incoming (850925217908095197) -->
+ <skip />
<string name="notification_section_header_gentle" msgid="6804099527336337197">"સાઇલન્ટ"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"નોટિફિકેશન"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"વાતચીત"</string>
@@ -593,11 +594,11 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"બંધ કરો"</string>
<string name="accessibility_output_chooser" msgid="7807898688967194183">"આઉટપુટ ઉપકરણ સ્વિચ કરો"</string>
<string name="screen_pinning_title" msgid="9058007390337841305">"ઍપને પિન કરેલી છે"</string>
- <string name="screen_pinning_description" msgid="8699395373875667743">"તમે જ્યાં સુધી અનપિન કરશો નહીં ત્યાં સુધી આ તેને દૃશ્યક્ષમ રાખે છે. અનપિન કરવા માટે પાછળ અને ઝલકને સ્પર્શ કરી રાખો."</string>
- <string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"તમે જ્યાં સુધી અનપિન કરશો નહીં ત્યાં સુધી આ તેને દૃશ્યક્ષમ રાખે છે. અનપિન કરવા માટે પાછળ અને હોમને સ્પર્શ કરી રાખો."</string>
- <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"તમે જ્યાં સુધી અનપિન નહીં કરો ત્યાં સુધી આ તેને દૃશ્યક્ષમ રાખે છે. ઉપરની તરફ સ્વાઇપ કરો અને અનપિન કરવા માટે દબાવી રાખો."</string>
- <string name="screen_pinning_description_accessible" msgid="7386449191953535332">"તમે જ્યાં સુધી અનપિન કરશો નહીં ત્યાં સુધી આ તેને દૃશ્યક્ષમ રાખે છે. અનપિન કરવા માટે ઝલકને સ્પર્શ કરી રાખો."</string>
- <string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"તમે જ્યાં સુધી અનપિન કરશો નહીં ત્યાં સુધી આ તેને દૃશ્યક્ષમ રાખે છે. અનપિન કરવા માટે હોમને સ્પર્શ કરી રાખો."</string>
+ <string name="screen_pinning_description" msgid="8699395373875667743">"તમે જ્યાં સુધી અનપિન કરશો નહીં ત્યાં સુધી આ તેને વ્યૂમાં રાખે છે. અનપિન કરવા માટે પાછળ અને ઓવરવ્યૂને સ્પર્શ કરી રાખો."</string>
+ <string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"તમે જ્યાં સુધી અનપિન કરશો નહીં ત્યાં સુધી આ તેને વ્યૂમાં રાખે છે. અનપિન કરવા માટે પાછળ અને હોમને સ્પર્શ કરી રાખો."</string>
+ <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"તમે જ્યાં સુધી અનપિન નહીં કરો ત્યાં સુધી આ તેને વ્યૂમાં રાખે છે. ઉપરની તરફ સ્વાઇપ કરો અને અનપિન કરવા માટે દબાવી રાખો."</string>
+ <string name="screen_pinning_description_accessible" msgid="7386449191953535332">"તમે જ્યાં સુધી અનપિન કરશો નહીં ત્યાં સુધી આ તેને વ્યૂમાં રાખે છે. અનપિન કરવા માટે ઓવરવ્યૂને સ્પર્શ કરી રાખો."</string>
+ <string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"તમે જ્યાં સુધી અનપિન કરશો નહીં ત્યાં સુધી આ તેને વ્યૂમાં રાખે છે. અનપિન કરવા માટે હોમને સ્પર્શ કરી રાખો."</string>
<string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"વ્યક્તિગત ડેટા ઍક્સેસ કરી શકાતો હોઈ શકે (જેમ કે સંપર્કો અને ઇમેઇલનું કન્ટેન્ટ)."</string>
<string name="screen_pinning_can_open_other_apps" msgid="7529756813231421455">"પિન કરેલી ઍપ અન્ય ઍપને ખોલી શકે છે."</string>
<string name="screen_pinning_toast" msgid="8177286912533744328">"આ ઍપને અનપિન કરવા માટે, પાછળ અને ઓવરવ્યૂ બટનને સ્પર્શ કરી રાખો"</string>
@@ -1002,8 +1003,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"ઉપર જમણે ખસેડો"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"નીચે ડાબે ખસેડો"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"નીચે જમણે ખસેડો"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"બબલને છોડી દો"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"વાતચીતને બબલ કરશો નહીં"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"બબલનો ઉપયોગ કરીને ચેટ કરો"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"નવી વાતચીત ફ્લોટિંગ આઇકન અથવા બબલ જેવી દેખાશે. બબલને ખોલવા માટે ટૅપ કરો. તેને ખસેડવા માટે ખેંચો."</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 428840742b45..22f908011455 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -512,7 +512,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"सभी को हटाएं"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"प्रबंधित करें"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"इतिहास"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"हाल ही में मिली सूचनाएं"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"नई सूचनाएं"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"बिना आवाज़ किए मिलने वाली सूचनाएं"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"वाइब्रेशन या आवाज़ के साथ मिलने वाली सूचनाएं"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"बातचीत"</string>
@@ -1004,8 +1004,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"सबसे ऊपर दाईं ओर ले जाएं"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"बाईं ओर सबसे नीचे ले जाएं"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"सबसे नीचे दाईं ओर ले जाएं"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"बबल खारिज करें"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"बातचीत को बबल न करें"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"बबल्स का इस्तेमाल करके चैट करें"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"नई बातचीत फ़्लोटिंग आइकॉन या बबल्स की तरह दिखेंगी. बबल को खोलने के लिए टैप करें. इसे एक जगह से दूसरी जगह ले जाने के लिए खींचें और छोड़ें."</string>
@@ -1017,7 +1016,7 @@
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"सिस्टम नेविगेशन अपडेट करने के लिए \'सेटिंग\' में जाएं"</string>
<string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"स्टैंडबाई"</string>
<string name="priority_onboarding_title" msgid="2893070698479227616">"बातचीत को अहम बातचीत के तौर पर सेट किया गया है"</string>
- <string name="priority_onboarding_behavior" msgid="5342816047020432929">"अहम बातचीत:"</string>
+ <string name="priority_onboarding_behavior" msgid="5342816047020432929">"अहम बातचीत यहां दिखेगी:"</string>
<string name="priority_onboarding_show_at_top_text" msgid="1678400241025513541">"बातचीत सेक्शन में सबसे ऊपर दिखाएं"</string>
<string name="priority_onboarding_show_avatar_text" msgid="5756291381124091508">"लॉक स्क्रीन पर प्रोफ़ाइल फ़ोटो दिखाएं"</string>
<string name="priority_onboarding_appear_as_bubble_text" msgid="4227039772250263122">"खास बातचीत फ़्लोटिंग बबल की तरह ऐप्लिकेशन के ऊपर दिखेंगी"</string>
@@ -1068,9 +1067,9 @@
<string name="controls_media_resume" msgid="1933520684481586053">"फिर से शुरू करें"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"काम नहीं कर रहा, ऐप जांचें"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"कोई गड़बड़ी हुई, फिर से कोशिश की जा रही है…"</string>
- <string name="controls_error_removed" msgid="6675638069846014366">"कंट्रोल नहीं मिला"</string>
+ <string name="controls_error_removed" msgid="6675638069846014366">"कंट्रोल मौजूद नहीं है"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"कंट्रोल मौजूद नहीं है"</string>
- <string name="controls_error_removed_message" msgid="2885911717034750542">"<xliff:g id="DEVICE">%1$s</xliff:g> ऐक्सेस नहीं किया जा सका. <xliff:g id="APPLICATION">%2$s</xliff:g> ऐप्लिकेशन देखें, ताकि यह पक्का किया जा सके कि कंट्रोल अब भी मौजूद है और सेटिंग में कोई बदलाव नहीं हुआ है."</string>
+ <string name="controls_error_removed_message" msgid="2885911717034750542">"<xliff:g id="DEVICE">%1$s</xliff:g> को ऐक्सेस नहीं किया जा सका. <xliff:g id="APPLICATION">%2$s</xliff:g> ऐप्लिकेशन देखें, ताकि यह पक्का किया जा सके कि कंट्रोल अब भी मौजूद है और सेटिंग में कोई बदलाव नहीं हुआ है."</string>
<string name="controls_open_app" msgid="483650971094300141">"ऐप्लिकेशन खोलें"</string>
<string name="controls_error_generic" msgid="352500456918362905">"स्थिति लोड नहीं की जा सकती"</string>
<string name="controls_error_failed" msgid="960228639198558525">"गड़बड़ी हुई, फिर से कोशिश करें"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index b8b784c36735..b0e6b51e8f6a 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -513,7 +513,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Izbriši sve"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Upravljajte"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Povijest"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Dolazno"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Novo"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Bešumno"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Obavijesti"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Razgovori"</string>
@@ -1007,10 +1007,9 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Premjesti u gornji desni kut"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Premjesti u donji lijevi kut"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Premjestite u donji desni kut"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Odbaci oblačić"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Zaustavi razgovor u oblačićima"</string>
- <string name="bubbles_user_education_title" msgid="5547017089271445797">"Chatanje pomoću oblačića"</string>
+ <string name="bubbles_user_education_title" msgid="5547017089271445797">"Oblačići u chatu"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Novi razgovori pojavljuju se kao pomične ikone ili oblačići. Dodirnite za otvaranje oblačića. Povucite da biste ga premjestili."</string>
<string name="bubbles_user_education_manage_title" msgid="2848511858160342320">"Upravljanje oblačićima u svakom trenutku"</string>
<string name="bubbles_user_education_manage" msgid="1391639189507036423">"Dodirnite Upravljanje da biste isključili oblačiće iz ove aplikacije"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 551fcd8b2975..0dc7256f985d 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Az összes törlése"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Kezelés"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Előzmények"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Bejövő"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Új"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Néma"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Értesítések"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Beszélgetések"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Áthelyezés fel és jobbra"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Áthelyezés le és balra"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Áthelyezés le és jobbra"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Buborék elvetése"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Ne jelenjen meg a beszélgetés buborékban"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Buborékokat használó csevegés"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Az új beszélgetések lebegő ikonként, vagyis buborékként jelennek meg. A buborék megnyitásához koppintson rá. Áthelyezéshez húzza a kívánt helyre."</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 730f1155502b..a337cc22390b 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -92,7 +92,7 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Էկրանի տեսագրության մշակում"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Էկրանի տեսագրման աշխատաշրջանի ընթացիկ ծանուցում"</string>
<string name="screenrecord_start_label" msgid="1750350278888217473">"Սկսե՞լ տեսագրումը"</string>
- <string name="screenrecord_description" msgid="1123231719680353736">"Տեսագրման ընթացքում Android-ի համակարգը կարող է գրանցել անձնական տեղեկություններ, որոնք տեսանելի են էկրանին կամ նվագարկվում են ձեր սարքում։ Սա ներառում է այնպիսի տեղեկություններ, ինչպիսիք են, օրինակ, գաղտնաբառերը, վճարային տվյալները, լուսանկարները, հաղորդագրությունները և նվագարկվող աուդիո ֆայլերը։"</string>
+ <string name="screenrecord_description" msgid="1123231719680353736">"Տեսագրման ընթացքում Android համակարգը կարող է գրանցել անձնական տեղեկություններ, որոնք տեսանելի են էկրանին կամ նվագարկվում են ձեր սարքում։ Սա ներառում է այնպիսի տեղեկություններ, ինչպիսիք են, օրինակ, գաղտնաբառերը, վճարային տվյալները, լուսանկարները, հաղորդագրությունները և նվագարկվող աուդիո ֆայլերը։"</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Ձայնագրել"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Սարքի ձայները"</string>
<string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Ձեր սարքի ձայները, օրինակ՝ երաժշտությունը, զանգերն ու զանգերանգները"</string>
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Մաքրել բոլորը"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Կառավարել"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Պատմություն"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Մուտքային"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Նոր"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Անձայն"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Ծանուցումներ"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Խոսակցություններ"</string>
@@ -720,7 +720,7 @@
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Կարգավորումներ"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Կարևոր"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը զրույցի գործառույթներ չի աջակցում"</string>
- <string name="bubble_overflow_empty_title" msgid="3120029421991510842">"Վերջին ամպիկներ չկան"</string>
+ <string name="bubble_overflow_empty_title" msgid="3120029421991510842">"Ամպիկներ չկան"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"Այստեղ կցուցադրվեն վերջերս օգտագործված և փակված ամպիկները, որոնք կկարողանաք հեշտությամբ վերաբացել"</string>
<string name="notification_unblockable_desc" msgid="2073030886006190804">"Այս ծանուցումները չեն կարող փոփոխվել:"</string>
<string name="notification_multichannel_desc" msgid="7414593090056236179">"Ծանուցումների տվյալ խումբը հնարավոր չէ կարգավորել այստեղ"</string>
@@ -1002,11 +1002,10 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Տեղափոխել վերև՝ աջ"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Տեղափոխել ներքև՝ ձախ"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Տեղափոխել ներքև՝ աջ"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Փակել ամպիկը"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Զրույցը չցուցադրել ամպիկի տեսքով"</string>
- <string name="bubbles_user_education_title" msgid="5547017089271445797">"Զրուցել ամպիկների միջոցով"</string>
- <string name="bubbles_user_education_description" msgid="1160281719576715211">"Նոր խոսակցությունները կհայտնվեն լողացող պատկերակների կամ ամպիկների տեսքով։ Հպեք՝ ամպիկը բացելու համար։ Քաշեք՝ այն տեղափոխելու համար։"</string>
+ <string name="bubbles_user_education_title" msgid="5547017089271445797">"Զրույցի ամպիկներ"</string>
+ <string name="bubbles_user_education_description" msgid="1160281719576715211">"Նոր զրույցները կհայտնվեն լողացող պատկերակների կամ ամպիկների տեսքով։ Հպեք՝ ամպիկը բացելու համար։ Քաշեք՝ այն տեղափոխելու համար։"</string>
<string name="bubbles_user_education_manage_title" msgid="2848511858160342320">"Ամպիկների կարգավորումներ"</string>
<string name="bubbles_user_education_manage" msgid="1391639189507036423">"Հպեք «Կառավարել» կոճակին՝ այս հավելվածի ամպիկներն անջատելու համար։"</string>
<string name="bubbles_user_education_got_it" msgid="8282812431953161143">"Եղավ"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 3614cb4d88c0..dd6c363abb4c 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Hapus semua"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Kelola"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Histori"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Masuk"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Baru"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Senyap"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifikasi"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Percakapan"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Pindahkan ke kanan atas"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Pindahkan ke kiri bawah"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Pindahkan ke kanan bawah"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Tutup balon"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Jangan gunakan percakapan balon"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Chat dalam tampilan balon"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Percakapan baru muncul sebagai ikon mengambang, atau balon. Ketuk untuk membuka balon. Tarik untuk memindahkannya."</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 224164c012e5..a3119ce21498 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Hreinsa allt"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Stjórna"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Ferill"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Mótteknar"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Nýtt"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Hljóðlaust"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Tilkynningar"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Samtöl"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Færa efst til hægri"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Færa neðst til vinstri"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Færðu neðst til hægri"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Loka blöðru"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Ekki setja samtal í blöðru"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Spjalla með blöðrum"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Ný samtöl birtast sem fljótandi tákn eða blöðrur. Ýttu til að opna blöðru. Dragðu hana til að færa."</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index f9fcda4b3eca..21434bac9807 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -78,8 +78,8 @@
<string name="compat_mode_off" msgid="7682459748279487945">"Estendi per riemp. schermo"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"è stata inviata un\'immagine"</string>
- <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Salvataggio screenshot..."</string>
- <string name="screenshot_saving_title" msgid="2298349784913287333">"Salvataggio screenshot..."</string>
+ <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Salvataggio screenshot…"</string>
+ <string name="screenshot_saving_title" msgid="2298349784913287333">"Salvataggio screenshot…"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Screenshot salvato"</string>
<string name="screenshot_saved_text" msgid="7778833104901642442">"Tocca per visualizzare lo screenshot"</string>
<string name="screenshot_failed_title" msgid="3259148215671936891">"Impossibile salvare lo screenshot"</string>
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Cancella tutto"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Gestisci"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Cronologia"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"In arrivo"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Nuove"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Silenziose"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notifiche"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversazioni"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Sposta in alto a destra"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Sposta in basso a sinistra"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Sposta in basso a destra"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Ignora bolle"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Non utilizzare bolle per la conversazione"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Chatta utilizzando le bolle"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Le nuove conversazioni vengono visualizzate come icone mobili o bolle. Tocca per aprire la bolla. Trascinala per spostarla."</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 172919c497e3..24dae2745b8c 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -516,7 +516,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"ניקוי הכל"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"ניהול"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"היסטוריה"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"התקבלו"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"התראות חדשות"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"שקט"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"התראות"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"שיחות"</string>
@@ -1012,10 +1012,9 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"העברה לפינה הימנית העליונה"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"העברה לפינה השמאלית התחתונה"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"העברה לפינה הימנית התחתונה"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"סגירת בועה"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"אין להציג בועות לשיחה"</string>
- <string name="bubbles_user_education_title" msgid="5547017089271445797">"לדבר בצ\'אט באמצעות בועות"</string>
+ <string name="bubbles_user_education_title" msgid="5547017089271445797">"לדבר בבועות"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"שיחות חדשות מופיעות כסמלים צפים, או בועות. יש להקיש כדי לפתוח בועה. יש לגרור כדי להזיז אותה."</string>
<string name="bubbles_user_education_manage_title" msgid="2848511858160342320">"שליטה בבועות, בכל זמן"</string>
<string name="bubbles_user_education_manage" msgid="1391639189507036423">"יש להקיש על \'ניהול\' כדי להשבית את הבועות מהאפליקציה הזו"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index a2bb39cd9e6b..0f248a1332b2 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"すべて消去"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"管理"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"履歴"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"新着"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"新着"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"サイレント"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"通知"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"会話"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"右上に移動"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"左下に移動"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"右下に移動"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"ふきだしを閉じる"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"会話をバブルで表示しない"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"チャットでバブルを使う"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"新しい会話はフローティング アイコン(バブル)として表示されます。タップするとバブルが開きます。ドラッグしてバブルを移動できます。"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 37b5ec98797e..aeee416750ed 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"ყველას გასუფთავება"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"მართვა"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ისტორია"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"შემომავალი"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"ახალი"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"ჩუმი"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"შეტყობინებები"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"საუბრები"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"გადაანაცვლეთ ზევით და მარჯვნივ"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"ქვევით და მარცხნივ გადატანა"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"გადაანაცვ. ქვემოთ და მარჯვნივ"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"ბუშტის დახურვა"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"აიკრძალოს საუბრის ბუშტები"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"ჩეთი ბუშტების გამოყენებით"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"ახალი საუბრები გამოჩნდება როგორც მოტივტივე ხატულები ან ბუშტები. შეეხეთ ბუშტის გასახსნელად. გადაიტანეთ ჩავლებით."</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 73f0e6c7af7d..356526ce9a1d 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -101,7 +101,7 @@
<string name="screenrecord_start" msgid="330991441575775004">"Бастау"</string>
<string name="screenrecord_ongoing_screen_only" msgid="4459670242451527727">"Экрандағы бейне жазылуда."</string>
<string name="screenrecord_ongoing_screen_and_audio" msgid="5351133763125180920">"Экрандағы бейне және аудио жазылуда."</string>
- <string name="screenrecord_taps_label" msgid="1595690528298857649">"Экранды түртуді көрсету"</string>
+ <string name="screenrecord_taps_label" msgid="1595690528298857649">"Экранды түрткенде көрсету"</string>
<string name="screenrecord_stop_text" msgid="6549288689506057686">"Тоқтату үшін түртіңіз"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Тоқтату"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Тоқтата тұру"</string>
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Барлығын тазалау"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Басқару"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Тарих"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Кіріс"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Жаңа"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Үнсіз"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Хабарландырулар"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Әңгімелер"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Жоғары оң жаққа жылжыту"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Төменгі сол жаққа жылжыту"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Төменгі оң жаққа жылжыту"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Қалқымалы анықтаманы жабу"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Әңгіменің қалқыма хабары көрсетілмесін"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Қалқыма хабарлар арқылы сөйлесу"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Жаңа әңгімелер қалқыма белгішелер немесе хабарлар түрінде көрсетіледі. Қалқыма хабарды ашу үшін түртіңіз. Жылжыту үшін сүйреңіз."</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 47fd544912ea..b0f90e2b2869 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"សម្អាត​ទាំងអស់"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"គ្រប់គ្រង"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ប្រវត្តិ"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"មក​ដល់"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"ថ្មី"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"ស្ងាត់"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"ការជូនដំណឹង"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"ការសន្ទនា"</string>
@@ -595,7 +595,7 @@
<string name="screen_pinning_title" msgid="9058007390337841305">"កម្មវិធី​ត្រូវបានខ្ទាស់"</string>
<string name="screen_pinning_description" msgid="8699395373875667743">"វា​នឹង​នៅតែ​បង្ហាញ រហូត​ទាល់​តែ​អ្នក​ដក​ការដៅ។ សូម​សង្កត់​ប៊ូតុង​ថយ​ក្រោយ និង​ប៊ូតុង​ទិដ្ឋភាពរួម​ឲ្យ​ជាប់ ដើម្បី​ដក​ការ​ដៅ។"</string>
<string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"វា​នឹង​នៅតែ​បង្ហាញ រហូត​ទាល់​តែ​អ្នក​ដក​ការដៅ។ សូម​ចុចប៊ូតុង​ថយក្រោយ និងប៊ូតុង​ទំព័រដើម​ឱ្យ​ជាប់ ដើម្បី​ដក​ការ​ដៅ។"</string>
- <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"វា​នឹង​នៅតែ​បង្ហាញ រហូតទាល់​តែអ្នក​ដកការដៅ។ អូសឡើងលើ​ឱ្យជាប់ ដើម្បី​ដក​ការដៅ។"</string>
+ <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"វា​នឹង​នៅតែ​បង្ហាញ រហូតទាល់​តែអ្នក​ដកខ្ទាស់ចេញ។ អូសឡើងលើ​ឱ្យជាប់ ដើម្បី​ដក​ខ្ទាស់។"</string>
<string name="screen_pinning_description_accessible" msgid="7386449191953535332">"វា​នឹង​នៅតែ​បង្ហាញ រហូត​ទាល់​តែ​អ្នក​ដក​ការ​ដៅ។ សូម​សង្កត់​ប៊ូតុង​ទិដ្ឋភាពរួម​​ឲ្យ​ជាប់ ដើម្បី​ដក​ការ​ដៅ។"</string>
<string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"វា​នឹង​នៅតែ​បង្ហាញ រហូត​ទាល់​តែ​អ្នក​ដក​ការដៅ។ សូម​ចុច​ប៊ូតុង​ទំព័រដើម​ឱ្យ​ជាប់ ដើម្បី​ដក​ការ​ដៅ។"</string>
<string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"អាចចូលប្រើ​ទិន្នន័យផ្ទាល់ខ្លួន​បាន (ដូចជា ទំនាក់ទំនង និងខ្លឹមសារ​អ៊ីមែលជាដើម)។"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"ផ្លាស់ទីទៅផ្នែកខាងលើខាងស្ដាំ"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"ផ្លាស់ទីទៅផ្នែកខាងក្រោមខាងឆ្វេង​"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"ផ្លាស់ទីទៅផ្នែកខាងក្រោម​ខាងស្ដាំ"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"ច្រានចោល​ពពុះ"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"កុំបង្ហាញ​ការសន្ទនា​ជាពពុះ"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"ជជែក​ដោយប្រើ​ពពុះ"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"ការសន្ទនាថ្មីៗ​បង្ហាញជា​​ពពុះ ឬរូបអណ្ដែត។ ចុច ដើម្បីបើក​ពពុះ។ អូស ដើម្បី​ផ្លាស់ទី​ពពុះនេះ។"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index afa60524fca6..ed5342554913 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -510,7 +510,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"ಎಲ್ಲವನ್ನೂ ತೆರವುಗೊಳಿಸಿ"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"ನಿರ್ವಹಿಸಿ"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ಇತಿಹಾಸ"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"ಒಳಬರುವ"</string>
+ <!-- no translation found for notification_section_header_incoming (850925217908095197) -->
+ <skip />
<string name="notification_section_header_gentle" msgid="6804099527336337197">"ನಿಶ್ಶಬ್ದ"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"ಅಧಿಸೂಚನೆಗಳು"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"ಸಂಭಾಷಣೆಗಳು"</string>
@@ -1002,8 +1003,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"ಬಲ ಮೇಲ್ಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"ಸ್ಕ್ರೀನ್‌ನ ಎಡ ಕೆಳಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"ಕೆಳಗಿನ ಬಲಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"ಬಬಲ್ ವಜಾಗೊಳಿಸಿ"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"ಸಂಭಾಷಣೆಯನ್ನು ಬಬಲ್ ಮಾಡಬೇಡಿ"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"ಬಬಲ್ಸ್ ಬಳಸಿ ಚಾಟ್ ಮಾಡಿ"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"ಹೊಸ ಸಂಭಾಷಣೆಗಳು ತೇಲುವ ಐಕಾನ್‌ಗಳು ಅಥವಾ ಬಬಲ್ಸ್ ಆಗಿ ಗೋಚರಿಸುತ್ತವೆ. ಬಬಲ್ ತೆರೆಯಲು ಟ್ಯಾಪ್ ಮಾಡಿ. ಅದನ್ನು ಡ್ರ್ಯಾಗ್ ಮಾಡಲು ಎಳೆಯಿರಿ."</string>
@@ -1068,7 +1068,7 @@
<string name="controls_error_retryable" msgid="864025882878378470">"ದೋಷ, ಮರುಪ್ರಯತ್ನಿಸಲಾಗುತ್ತಿದೆ…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"ಕಂಡುಬಂದಿಲ್ಲ"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"ನಿಯಂತ್ರಣ ಲಭ್ಯವಿಲ್ಲ"</string>
- <string name="controls_error_removed_message" msgid="2885911717034750542">"<xliff:g id="DEVICE">%1$s</xliff:g> ಪ್ರವೇಶಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ನಿಯಂತ್ರಣ ಈಗಲೂ ಲಭ್ಯವಿದೆಯೇ ಮತ್ತು <xliff:g id="APPLICATION">%2$s</xliff:g> ಆ್ಯಪ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ಬದಲಾಯಿಸಲಾಗಿಲ್ಲ ಎಂಬುದನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಲು ಆ್ಯಪ್ ಅನ್ನು ಪರಿಶೀಲಿಸಿ."</string>
+ <string name="controls_error_removed_message" msgid="2885911717034750542">"<xliff:g id="DEVICE">%1$s</xliff:g> ಪ್ರವೇಶಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ. ನಿಯಂತ್ರಣ ಈಗಲೂ ಲಭ್ಯವಿದೆ ಮತ್ತು ಆ್ಯಪ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ಬದಲಾಯಿಸಲಾಗಿಲ್ಲ ಎಂಬುದನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಲು <xliff:g id="APPLICATION">%2$s</xliff:g> ಆ್ಯಪ್ ಅನ್ನು ಪರಿಶೀಲಿಸಿ."</string>
<string name="controls_open_app" msgid="483650971094300141">"ಆ್ಯಪ್ ತೆರೆಯಿರಿ"</string>
<string name="controls_error_generic" msgid="352500456918362905">"ಸ್ಥಿತಿ ಲೋಡ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
<string name="controls_error_failed" msgid="960228639198558525">"ದೋಷ, ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index a75c230cd681..a27f4f723a0c 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"모두 지우기"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"관리"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"기록"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"최근 알림"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"새 알림"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"무음"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"알림"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"대화"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"오른쪽 상단으로 이동"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"왼쪽 하단으로 이동"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"오른쪽 하단으로 이동"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"대화창 닫기"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"대화를 대화창으로 표시하지 않음"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"대화창으로 채팅하기"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"새로운 대화가 플로팅 아이콘인 대화창으로 표시됩니다. 대화창을 열려면 탭하세요. 드래그하여 이동할 수 있습니다."</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index a0f532acf9da..9b8fa78213bc 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -95,7 +95,7 @@
<string name="screenrecord_description" msgid="1123231719680353736">"Жаздыруу учурунда Android тутуму экраныңызда көрүнүп турган жана түзмөктө ойноп жаткан бардык купуя маалыматты жаздырып алат. Буга сырсөздөр, төлөм маалыматы, сүрөттөр, билдирүүлөр жана аудио файлдар кирет."</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Аудио жаздыруу"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Түзмөктүн аудиосу"</string>
- <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Музыка, чалуулар жана рингтондор сыяктуу түзмөгүңүздөгү добуштар"</string>
+ <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Музыка, чалуулар жана шыңгырлар сыяктуу түзмөгүңүздөгү добуштар"</string>
<string name="screenrecord_mic_label" msgid="2111264835791332350">"Микрофон"</string>
<string name="screenrecord_device_audio_and_mic_label" msgid="1831323771978646841">"Түзмөктүн аудиосу жана микрофон"</string>
<string name="screenrecord_start" msgid="330991441575775004">"Баштадык"</string>
@@ -506,11 +506,11 @@
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Бул функцияны аткарган кызматка экраныңыздагы бардык маалымат же түзмөктө ойнотулуп жаткан нерсе, сырсөздөр, төлөмдөрдүн чоо-жайы, сүрөттөр, билдирүүлөр жана аудио файлдар жеткиликтүү болот."</string>
<string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Жаздырып же тышкы экранга чыгарып баштайсызбы?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> менен жаздырылып же тышкы экранга чыгарылып башталсынбы?"</string>
- <string name="media_projection_remember_text" msgid="6896767327140422951">"Экинчи көрсөтүлбөсүн"</string>
+ <string name="media_projection_remember_text" msgid="6896767327140422951">"Экинчи көрүнбөсүн"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Баарын тазалап салуу"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Башкаруу"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Таржымал"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Кирүүчү"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Жаңы"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Үнсүз"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Билдирмелер"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Жазышуулар"</string>
@@ -714,14 +714,14 @@
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Үнү чыкпайт жана дирилдебейт"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Үнү чыгып же дирилдебейт жана жазышуу бөлүмүнүн ылдый жагында көрүнөт"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Телефондун жөндөөлөрүнө жараша шыңгырап же дирилдеши мүмкүн"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Телефондун жөндөөлөрүнө жараша шыңгырап же дирилдеши мүмкүн. <xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосундагы жазышуулар демейки жөндөө боюнча калкып чыкма билдирмелер болуп көрүнөт."</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Телефондун жөндөөлөрүнө жараша шыңгырап же дирилдеши мүмкүн. <xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосундагы жазышуулар демейки жөндөө боюнча калкып чыкма билдирмелер түрүндө көрүнөт."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Калкыма ыкчам баскыч менен көңүлүңүздү бул мазмунга буруп турат."</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Жазышуу бөлүмүнүн жогорку жагында калкып чыкма билдирме түрүндө көрүнүп, профиль сүрөтү кулпуланган экрандан чагылдырылат"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Жөндөөлөр"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Маанилүүлүгү"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> жазышуу функцияларын колдоого албайт"</string>
- <string name="bubble_overflow_empty_title" msgid="3120029421991510842">"Акыркы калкып чыкма билдирмелер жок"</string>
- <string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"Кайра жөнөтүлгөн жана жабылган калкып чыкма билдирмелер ушул жерде көрүнөт"</string>
+ <string name="bubble_overflow_empty_title" msgid="3120029421991510842">"Азырынча эч нерсе жок"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"Акыркы жана жабылган калкып чыкма билдирмелер ушул жерде көрүнөт"</string>
<string name="notification_unblockable_desc" msgid="2073030886006190804">"Бул билдирмелерди өзгөртүүгө болбойт."</string>
<string name="notification_multichannel_desc" msgid="7414593090056236179">"Бул билдирмелердин тобун бул жерде конфигурациялоого болбойт"</string>
<string name="notification_delegate_header" msgid="1264510071031479920">"Прокси билдирмеси"</string>
@@ -864,12 +864,12 @@
<string-array name="clock_options">
<item msgid="3986445361435142273">"Сааттар, мүнөттөр жана секунддар"</item>
<item msgid="1271006222031257266">"Сааттар жана мүнөттөр (демейки шартта)"</item>
- <item msgid="6135970080453877218">"Бул сүрөтчө көрсөтүлбөсүн"</item>
+ <item msgid="6135970080453877218">"Бул сүрөтчө көрүнбөсүн"</item>
</string-array>
<string-array name="battery_options">
<item msgid="7714004721411852551">"Ар дайым пайызы көрсөтүлсүн"</item>
<item msgid="3805744470661798712">"Кубаттоо учурунда пайызы көрсөтүлсүн (демейки)"</item>
- <item msgid="8619482474544321778">"Бул сүрөтчө көрсөтүлбөсүн"</item>
+ <item msgid="8619482474544321778">"Бул сүрөтчө көрүнбөсүн"</item>
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Анча маанилүү эмес билдирменин сүрөтчөлөрүн көрсөтүү"</string>
<string name="other" msgid="429768510980739978">"Башка"</string>
@@ -1002,11 +1002,10 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Жогорку оң жакка жылдырыңыз"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Төмөнкү сол жакка жылдыруу"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Төмөнкү оң жакка жылдырыңыз"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Калкып чыкма билдирмени жабуу"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Жазышуу калкып чыкма билдирмеде көрүнбөсүн"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Калкып чыкма билдирмелер аркылуу маектешүү"</string>
- <string name="bubbles_user_education_description" msgid="1160281719576715211">"Жаңы жазышуулар калкыма сүрөтчөлөр же калкып чыкма билдирмелер болуп көрүнөт. Калкып чыкма билдирмелерди ачуу үчүн таптап коюңуз. Жылдыруу үчүн сүйрөңүз."</string>
+ <string name="bubbles_user_education_description" msgid="1160281719576715211">"Жаңы жазышуулар калкыма сүрөтчөлөр же калкып чыкма билдирмелер түрүндө көрүнөт. Калкып чыкма билдирмелерди ачуу үчүн таптап коюңуз. Жылдыруу үчүн сүйрөңүз."</string>
<string name="bubbles_user_education_manage_title" msgid="2848511858160342320">"Калкып чыкма билдирмелерди каалаган убакта көзөмөлдөңүз"</string>
<string name="bubbles_user_education_manage" msgid="1391639189507036423">"Бул колдонмодогу калкып чыкма билдирмелерди өчүрүү үчүн \"Башкарууну\" басыңыз"</string>
<string name="bubbles_user_education_got_it" msgid="8282812431953161143">"Түшүндүм"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 829d39aac864..47353fd85e03 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"ລຶບລ້າງທັງໝົດ"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"ຈັດການ"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ປະຫວັດ"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"ຂາເຂົ້າ"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"ໃໝ່"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"ປິດສຽງ"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"ການແຈ້ງເຕືອນ"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"ການສົນທະນາ"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"ຍ້າຍຂວາເທິງ"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"ຍ້າຍຊ້າຍລຸ່ມ"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"ຍ້າຍຂວາລຸ່ມ"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"ປິດ bubble ໄວ້"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"ຢ່າໃຊ້ຟອງໃນການສົນທະນາ"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"ສົນທະນາໂດຍໃຊ້ຟອງ"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"ການສົນທະນາໃໝ່ຈະປາກົດເປັນໄອຄອນ ຫຼື ຟອງແບບລອຍ. ແຕະເພື່ອເປີດຟອງ. ລາກເພື່ອຍ້າຍມັນ."</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index de793947a037..59cab5795f13 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -516,7 +516,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Viską išvalyti"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Tvarkyti"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Istorija"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Gaunami"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Nauja"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Tylūs"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Pranešimai"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Pokalbiai"</string>
@@ -724,10 +724,10 @@
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Naudojant slankųjį spartųjį klavišą lengviau sutelkti dėmesį į šį turinį."</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Rodoma pokalbių skilties viršuje, rodoma kaip slankusis burbulas, pateikiama profilio nuotrauka užrakinimo ekrane"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Nustatymai"</string>
- <string name="notification_priority_title" msgid="2079708866333537093">"Prioritetas"</string>
+ <string name="notification_priority_title" msgid="2079708866333537093">"Prioritetiniai"</string>
<string name="no_shortcut" msgid="8257177117568230126">"Programa „<xliff:g id="APP_NAME">%1$s</xliff:g>“ nepalaiko pokalbių funkcijų"</string>
<string name="bubble_overflow_empty_title" msgid="3120029421991510842">"Nėra naujausių burbulų"</string>
- <string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"Naujausi ir atsisakyti burbulus bus rodomi čia"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"Naujausi ir atsisakyti burbulai bus rodomi čia"</string>
<string name="notification_unblockable_desc" msgid="2073030886006190804">"Šių pranešimų keisti negalima."</string>
<string name="notification_multichannel_desc" msgid="7414593090056236179">"Šios grupės pranešimai čia nekonfigūruojami"</string>
<string name="notification_delegate_header" msgid="1264510071031479920">"Per tarpinį serverį gautas pranešimas"</string>
@@ -1012,11 +1012,10 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Perkelti į viršų dešinėje"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Perkelti į apačią kairėje"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Perkelti į apačią dešinėje"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Atsisakyti burbulo"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Nerodyti pokalbio burbule"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Pokalbis naudojant burbulus"</string>
- <string name="bubbles_user_education_description" msgid="1160281719576715211">"Nauji pokalbiai rodomi kaip slankiosios piktogramos arba burbulus. Palieskite, kad atidarytumėte burbulą. Vilkite, kad perkeltumėte."</string>
+ <string name="bubbles_user_education_description" msgid="1160281719576715211">"Nauji pokalbiai rodomi kaip slankiosios piktogramos arba burbulai. Palieskite, kad atidarytumėte burbulą. Vilkite, kad perkeltumėte."</string>
<string name="bubbles_user_education_manage_title" msgid="2848511858160342320">"Bet kada valdyti burbulus"</string>
<string name="bubbles_user_education_manage" msgid="1391639189507036423">"Palieskite „Tvarkyti“, kad išjungtumėte burbulus šioje programoje"</string>
<string name="bubbles_user_education_got_it" msgid="8282812431953161143">"Supratau"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index ba6b8954f655..bc157026e745 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -513,7 +513,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Dzēst visu"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Pārvaldīt"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Vēsture"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Ienākošie"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Jauni"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Klusums"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Paziņojumi"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Sarunas"</string>
@@ -1007,8 +1007,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Pārvietot augšpusē pa labi"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Pārvietot apakšpusē pa kreisi"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Pārvietot apakšpusē pa labi"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Nerādīt burbuli"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Nerādīt sarunu burbuļos"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Tērzēšana, izmantojot burbuļus"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Jaunas sarunas tiek rādītas kā peldošas ikonas vai burbuļi. Pieskarieties, lai atvērtu burbuli. Velciet, lai to pārvietotu."</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 522cebd8ea6c..0b629a329336 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -479,7 +479,7 @@
<string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Да се отстрани гостинот?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Сите апликации и податоци во сесијата ќе се избришат."</string>
<string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Отстрани"</string>
- <string name="guest_wipe_session_title" msgid="7147965814683990944">"Добредојде назад, гостине!"</string>
+ <string name="guest_wipe_session_title" msgid="7147965814683990944">"Добре дојде пак, гостине!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Дали сакате да продолжите со сесијата?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Почни одново"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Да, продолжи"</string>
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Избриши сѐ"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Управувајте"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Историја"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Дојдовни"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Нов"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Безгласно"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Известувања"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Разговори"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Премести горе десно"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Премести долу лево"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Премести долу десно"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Отфрли балонче"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Не прикажувај го разговорот во балончиња"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Разговор во балончиња"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Новите разговори ќе се појавуваат како лебдечки икони или балончиња. Допрете за отворање на балончето. Повлечете за да го преместите."</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 03e826f54e45..b24518daa31b 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -510,7 +510,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"എല്ലാം മായ്‌ക്കുക"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"മാനേജ് ചെയ്യുക"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ചരിത്രം"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"ഇൻകമിംഗ്"</string>
+ <!-- no translation found for notification_section_header_incoming (850925217908095197) -->
+ <skip />
<string name="notification_section_header_gentle" msgid="6804099527336337197">"നിശബ്‌ദം"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"അറിയിപ്പുകൾ"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"സംഭാഷണങ്ങൾ"</string>
@@ -1006,7 +1007,7 @@
<skip />
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"സംഭാഷണം ബബിൾ ചെയ്യരുത്"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"ബബിളുകൾ ഉപയോഗിച്ച് ചാറ്റ് ചെയ്യുക"</string>
- <string name="bubbles_user_education_description" msgid="1160281719576715211">"പുതിയ സംഭാഷണങ്ങൾ ഫ്ലോട്ടിംഗ് ഐക്കണുകളോ ബബിളുകളോ ആയി ദൃശ്യമാവുന്നു. ബബിൾ തുറക്കാൻ ടാപ്പ് ചെയ്യു. ഇത് നീക്കാൻ വലിച്ചിടുക."</string>
+ <string name="bubbles_user_education_description" msgid="1160281719576715211">"പുതിയ സംഭാഷണങ്ങൾ ഫ്ലോട്ടിംഗ് ഐക്കണുകളോ ബബിളുകളോ ആയി ദൃശ്യമാവുന്നു. ബബിൾ തുറക്കാൻ ടാപ്പ് ചെയ്യൂ. ഇത് നീക്കാൻ വലിച്ചിടുക."</string>
<string name="bubbles_user_education_manage_title" msgid="2848511858160342320">"ബബിളുകൾ ഏതുസമയത്തും നിയന്ത്രിക്കുക"</string>
<string name="bubbles_user_education_manage" msgid="1391639189507036423">"ഈ ആപ്പിൽ നിന്നുള്ള ബബിളുകൾ ഓഫാക്കാൻ മാനേജ് ചെയ്യുക ടാപ്പ് ചെയ്യുക"</string>
<string name="bubbles_user_education_got_it" msgid="8282812431953161143">"ലഭിച്ചു"</string>
@@ -1014,17 +1015,14 @@
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"സിസ്‌റ്റം നാവിഗേഷൻ അപ്‌ഡേറ്റ് ചെയ്‌തു. മാറ്റങ്ങൾ വരുത്താൻ ക്രമീകരണത്തിലേക്ക് പോവുക."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"സിസ്‌റ്റം നാവിഗേഷൻ അപ്‌ഡേറ്റ് ചെയ്യാൻ ക്രമീകരണത്തിലേക്ക് പോവുക"</string>
<string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"സ്‌റ്റാൻഡ്‌ബൈ"</string>
- <!-- no translation found for priority_onboarding_title (2893070698479227616) -->
- <skip />
- <!-- no translation found for priority_onboarding_behavior (5342816047020432929) -->
- <skip />
+ <string name="priority_onboarding_title" msgid="2893070698479227616">"സംഭാഷണം മുൻഗണനയുള്ളതായി സജ്ജീകരിച്ചു"</string>
+ <string name="priority_onboarding_behavior" msgid="5342816047020432929">"മുൻഗണനാ സംഭാഷണങ്ങൾ ഇനിപ്പറയുന്നവ ചെയ്യും:"</string>
<string name="priority_onboarding_show_at_top_text" msgid="1678400241025513541">"സംഭാഷണ വിഭാഗത്തിന്റെ മുകളിൽ കാണിക്കുക"</string>
<string name="priority_onboarding_show_avatar_text" msgid="5756291381124091508">"ലോക്ക് സ്ക്രീനിൽ പ്രൊഫൈൽ ചിത്രം കാണിക്കുക"</string>
<string name="priority_onboarding_appear_as_bubble_text" msgid="4227039772250263122">"ആപ്പുകളുടെ മുകളിൽ ഫ്ലോട്ടിംഗ് ബബിൾ ആയി ദൃശ്യമാകും"</string>
<string name="priority_onboarding_ignores_dnd_text" msgid="2918952762719600529">"\'ശല്യപ്പെടുത്തരുത്\' തടസ്സപ്പെടുത്തുക"</string>
<string name="priority_onboarding_done_button_title" msgid="4569550984286506007">"മനസ്സിലായി"</string>
- <!-- no translation found for priority_onboarding_settings_button_title (6663601574303585927) -->
- <skip />
+ <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"ക്രമീകരണം"</string>
<string name="magnification_overlay_title" msgid="6584179429612427958">"മാഗ്നിഫിക്കേഷൻ ഓവർലേ വിൻഡോ"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"മാഗ്നിഫിക്കേഷൻ വിൻഡോ"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"മാഗ്നിഫിക്കേഷൻ വിൻഡോ നിയന്ത്രണങ്ങൾ"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 9a15e98fa763..4ab58f089e24 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Бүгдийг арилгах"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Удирдах"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Түүх"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Ирж буй"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Шинэ"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Чимээгүй"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Мэдэгдлүүд"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Харилцан яриа"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Баруун дээш зөөх"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Зүүн доош зөөх"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Баруун доош зөөх"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Бөмбөлгийг хаах"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Харилцан яриаг бүү бөмбөлөг болго"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Бөмбөлөг ашиглан чатлаарай"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Шинэ харилцан яриа нь хөвөгч дүрс тэмдэг эсвэл бөмбөлөг хэлбэрээр харагддаг. Бөмбөлгийг нээхийн тулд товшино уу. Түүнийг зөөхийн тулд чирнэ үү."</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index cbc50b90e300..9587e71e6136 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -510,7 +510,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"सर्व साफ करा"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"व्यवस्थापित करा"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"इतिहास"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"आलेल्या"</string>
+ <!-- no translation found for notification_section_header_incoming (850925217908095197) -->
+ <skip />
<string name="notification_section_header_gentle" msgid="6804099527336337197">"सायलंट"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"सूचना"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"संभाषणे"</string>
@@ -1014,17 +1015,14 @@
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"सिस्टम नेव्हिगेशन अपडेट केले. बदल करण्यासाठी, सेटिंग्जवर जा."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"सिस्टम नेव्हिगेशन अपडेट करण्यासाठी सेटिंग्जवर जा"</string>
<string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"स्टँडबाय"</string>
- <!-- no translation found for priority_onboarding_title (2893070698479227616) -->
- <skip />
- <!-- no translation found for priority_onboarding_behavior (5342816047020432929) -->
- <skip />
+ <string name="priority_onboarding_title" msgid="2893070698479227616">"संभाषणाला प्राधान्य म्हणून सेट केले आहे"</string>
+ <string name="priority_onboarding_behavior" msgid="5342816047020432929">"प्राधान्य दिलेली संभाषणे:"</string>
<string name="priority_onboarding_show_at_top_text" msgid="1678400241025513541">"संभाषण विभागाच्या सर्वात वरती दाखवा"</string>
<string name="priority_onboarding_show_avatar_text" msgid="5756291381124091508">"लॉक स्‍क्रीनवर प्रोफाइल फोटो दाखवा"</string>
<string name="priority_onboarding_appear_as_bubble_text" msgid="4227039772250263122">"ॲप्सच्या सर्वात वरती फ्लोटिंग बबल म्हणून दिसतील"</string>
<string name="priority_onboarding_ignores_dnd_text" msgid="2918952762719600529">"व्यत्यय आणू नका मध्ये अडथळा आणतील"</string>
<string name="priority_onboarding_done_button_title" msgid="4569550984286506007">"समजले"</string>
- <!-- no translation found for priority_onboarding_settings_button_title (6663601574303585927) -->
- <skip />
+ <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"सेटिंग्ज"</string>
<string name="magnification_overlay_title" msgid="6584179429612427958">"मॅग्निफिकेशन ओव्हरले विंडो"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"मॅग्निफिकेशन विंडो"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"मॅग्निफिकेशन विंडो नियंत्रणे"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index f89d5a0a4d4a..be17eade57a1 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Kosongkan semua"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Urus"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Sejarah"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Masuk"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Baharu"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Senyap"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Pemberitahuan"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Perbualan"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Alihkan ke atas sebelah kanan"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Alihkan ke bawah sebelah kiri"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Alihkan ke bawah sebelah kanan"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Ketepikan gelembung"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Jangan jadikan perbualan dalam bentuk gelembung"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Bersembang menggunakan gelembung"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Perbualan baharu muncul sebagai ikon terapung atau gelembung. Ketik untuk membuka gelembung. Seret untuk mengalihkan gelembung tersebut."</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index dc70e80a7125..8c06f6768590 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -101,7 +101,7 @@
<string name="screenrecord_start" msgid="330991441575775004">"စတင်ရန်"</string>
<string name="screenrecord_ongoing_screen_only" msgid="4459670242451527727">"ဖန်သားပြင်ကို ရိုက်ကူးနေသည်"</string>
<string name="screenrecord_ongoing_screen_and_audio" msgid="5351133763125180920">"အသံနှင့် ဖန်သားပြင်ကို ရိုက်ကူးနေသည်"</string>
- <string name="screenrecord_taps_label" msgid="1595690528298857649">"မျက်နှာပြင်ပေါ်တွင် ထိချက်များ ပြသည်"</string>
+ <string name="screenrecord_taps_label" msgid="1595690528298857649">"မျက်နှာပြင်ပေါ်တွင် ထိချက်များ ပြရန်"</string>
<string name="screenrecord_stop_text" msgid="6549288689506057686">"ရပ်ရန် တို့ပါ"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"ရပ်ရန်"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"ခဏရပ်ရန်"</string>
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"အားလုံး ဖယ်ရှားရန်"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"စီမံရန်"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"မှတ်တမ်း"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"အဝင်"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"အသစ်"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"အသံတိတ်ခြင်း"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"အကြောင်းကြားချက်များ"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"စကားဝိုင်းများ"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"ညာဘက်ထိပ်သို့ ရွှေ့ပါ"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"ဘယ်အောက်ခြေသို့ ရွှေ့ရန်"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"ညာအောက်ခြေသို့ ရွှေ့ပါ"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"ပူဖောင်းကွက် ပယ်ရန်"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"စကားဝိုင်းကို ပူဖောင်းကွက် မပြုလုပ်ပါနှင့်"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"ပူဖောင်းကွက် သုံး၍ ချတ်လုပ်ခြင်း"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"စကားဝိုင်းအသစ်များကို မျောနေသည့် သင်္ကေတများ သို့မဟုတ် ပူဖောင်းကွက်များအဖြစ် မြင်ရပါမည်။ ပူဖောင်းကွက်ကိုဖွင့်ရန် တို့ပါ။ ရွှေ့ရန် ၎င်းကို ဖိဆွဲပါ။"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 53806a7e4daa..aee2dd288262 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Fjern alt"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Administrer"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Logg"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Innkommende"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Ny"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Lydløs"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Varsler"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Samtaler"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Flytt til øverst til høyre"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Flytt til nederst til venstre"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Flytt til nederst til høyre"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Lukk boblen"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Ikke vis samtaler i bobler"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Chat med bobler"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Nye samtaler vises som flytende ikoner eller bobler. Trykk for å åpne bobler. Dra for å flytte dem."</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 19cf233fa092..812477ad33d2 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -510,7 +510,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"सबै हटाउनुहोस्"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"व्यवस्थित गर्नुहोस्"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"इतिहास"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"हालसालै प्राप्त भएका सूचनाहरू"</string>
+ <!-- no translation found for notification_section_header_incoming (850925217908095197) -->
+ <skip />
<string name="notification_section_header_gentle" msgid="6804099527336337197">"मौन"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"सूचनाहरू"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"वार्तालापहरू"</string>
@@ -1014,17 +1015,14 @@
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"प्रणालीको नेभिगेसन अद्यावधिक गरियो। परिवर्तन गर्न सेटिङमा जानुहोस्।"</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"प्रणालीको नेभिगेसन अद्यावधिक गर्न सेटिङमा जानुहोस्"</string>
<string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"स्ट्यान्डबाई"</string>
- <!-- no translation found for priority_onboarding_title (2893070698479227616) -->
- <skip />
- <!-- no translation found for priority_onboarding_behavior (5342816047020432929) -->
- <skip />
+ <string name="priority_onboarding_title" msgid="2893070698479227616">"वार्तालापको प्राथमिकता निर्धारण गरी महत्त्वपूर्ण बनाइयो"</string>
+ <string name="priority_onboarding_behavior" msgid="5342816047020432929">"महत्वपूर्ण वार्तालापहरू यहाँ देखिने छन्:"</string>
<string name="priority_onboarding_show_at_top_text" msgid="1678400241025513541">"वार्तालाप खण्डको सिरानमा देखाइयोस्"</string>
<string name="priority_onboarding_show_avatar_text" msgid="5756291381124091508">"लक स्क्रिनमा प्रोफाइल तस्बिर देखाइयोस्"</string>
<string name="priority_onboarding_appear_as_bubble_text" msgid="4227039772250263122">"एपहरूमाथि तैरिने बबलका रूपमा देखाइयोस्"</string>
<string name="priority_onboarding_ignores_dnd_text" msgid="2918952762719600529">"बाधा नपुऱ्याउनुहोस् मोडलाई बेवास्ता गरियोस्"</string>
<string name="priority_onboarding_done_button_title" msgid="4569550984286506007">"बुझेँ"</string>
- <!-- no translation found for priority_onboarding_settings_button_title (6663601574303585927) -->
- <skip />
+ <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"सेटिङ"</string>
<string name="magnification_overlay_title" msgid="6584179429612427958">"म्याग्निफिकेसन ओभरले विन्डो"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"म्याग्निफिकेसन विन्डो"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"म्याग्निफिकेसन विन्डोका नियन्त्रणहरू"</string>
@@ -1070,8 +1068,8 @@
<string name="controls_error_timeout" msgid="794197289772728958">"निष्क्रिय छ, एप जाँच गर्नु…"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"त्रुटि भयो, फेरि प्रयास गर्दै…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"फेला परेन"</string>
- <string name="controls_error_removed_title" msgid="1207794911208047818">"नियन्त्रण सुविधा उपलब्ध छैन"</string>
- <string name="controls_error_removed_message" msgid="2885911717034750542">"<xliff:g id="DEVICE">%1$s</xliff:g> माथि पहुँच राख्न सकिएन। यो नियन्त्रण सुविधा अझै पनि उपलब्ध छ र <xliff:g id="APPLICATION">%2$s</xliff:g> एपका सेटिङ परिवर्तन गरिएका छैनन् भन्ने कुरा सुनिश्चित गर्न यो एप जाँच्नुहोस्।"</string>
+ <string name="controls_error_removed_title" msgid="1207794911208047818">"नियन्त्रण उपलब्ध छैन"</string>
+ <string name="controls_error_removed_message" msgid="2885911717034750542">"<xliff:g id="DEVICE">%1$s</xliff:g> प्रयोग गर्न सकिएन। यो नियन्त्रण सुविधा अझै पनि उपलब्ध छ र <xliff:g id="APPLICATION">%2$s</xliff:g> एपका सेटिङ परिवर्तन गरिएका छैनन् भन्ने कुरा सुनिश्चित गर्न यो एप जाँच्नुहोस्।"</string>
<string name="controls_open_app" msgid="483650971094300141">"एप खोल्नुहोस्"</string>
<string name="controls_error_generic" msgid="352500456918362905">"वस्तुस्थिति लोड गर्न सकिएन"</string>
<string name="controls_error_failed" msgid="960228639198558525">"त्रुटि भयो, फेरि प्रयास गर्नु…"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 21e66ab67cd8..4e4cc773d69f 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -259,10 +259,10 @@
<string name="accessibility_bubble_dismissed" msgid="270358867566720729">"Bubbel gesloten."</string>
<string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Meldingenpaneel."</string>
<string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Snelle instellingen."</string>
- <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Vergrendelingsscherm."</string>
+ <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Vergrendelscherm."</string>
<string name="accessibility_desc_settings" msgid="6728577365389151969">"Instellingen"</string>
<string name="accessibility_desc_recent_apps" msgid="1748675199348914194">"Overzicht."</string>
- <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Vergrendelingsscherm voor werk"</string>
+ <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Vergrendelscherm voor werk"</string>
<string name="accessibility_desc_close" msgid="8293708213442107755">"Sluiten"</string>
<string name="accessibility_quick_settings_wifi" msgid="167707325133803052">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
<string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wifi uitgeschakeld."</string>
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Alles wissen"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Beheren"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Geschiedenis"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Inkomend"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Nieuw"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Stil"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Meldingen"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Gesprekken"</string>
@@ -687,7 +687,7 @@
<string name="tuner_full_importance_settings" msgid="1388025816553459059">"Beheeropties voor meldingen met betrekking tot stroomverbruik"</string>
<string name="tuner_full_importance_settings_on" msgid="917981436602311547">"Aan"</string>
<string name="tuner_full_importance_settings_off" msgid="5580102038749680829">"Uit"</string>
- <string name="power_notification_controls_description" msgid="1334963837572708952">"Met beheeropties voor meldingen met betrekking tot stroomverbruik kun je een belangrijkheidsniveau van 0 tot 5 instellen voor de meldingen van een app. \n\n"<b>"Niveau 5"</b>" \n- Boven aan de lijst met meldingen weergeven \n- Onderbreking op volledig scherm toestaan \n- Altijd korte weergave \n\n"<b>"Niveau 4"</b>" \n- Geen onderbreking op volledig scherm \n- Altijd korte weergave \n\n"<b>"Niveau 3"</b>" \n- Geen onderbreking op volledig scherm \n- Nooit korte weergave \n\n"<b>"Niveau 2"</b>" \n- Geen onderbreking op volledig scherm \n- Nooit korte weergave \n- Nooit geluid laten horen of trillen \n\n"<b>"Niveau 1"</b>" \n- Geen onderbreking op volledig scherm \n- Nooit korte weergave \n- Nooit geluid laten horen of trillen \n- Verbergen op vergrendelingsscherm en statusbalk \n- Onder aan de lijst met meldingen weergeven \n\n"<b>"Niveau 0"</b>" \n- Alle meldingen van de app blokkeren"</string>
+ <string name="power_notification_controls_description" msgid="1334963837572708952">"Met beheeropties voor meldingen met betrekking tot stroomverbruik kun je een belangrijkheidsniveau van 0 tot 5 instellen voor de meldingen van een app. \n\n"<b>"Niveau 5"</b>" \n- Boven aan de lijst met meldingen weergeven \n- Onderbreking op volledig scherm toestaan \n- Altijd korte weergave \n\n"<b>"Niveau 4"</b>" \n- Geen onderbreking op volledig scherm \n- Altijd korte weergave \n\n"<b>"Niveau 3"</b>" \n- Geen onderbreking op volledig scherm \n- Nooit korte weergave \n\n"<b>"Niveau 2"</b>" \n- Geen onderbreking op volledig scherm \n- Nooit korte weergave \n- Nooit geluid laten horen of trillen \n\n"<b>"Niveau 1"</b>" \n- Geen onderbreking op volledig scherm \n- Nooit korte weergave \n- Nooit geluid laten horen of trillen \n- Verbergen op vergrendelscherm en statusbalk \n- Onder aan de lijst met meldingen weergeven \n\n"<b>"Niveau 0"</b>" \n- Alle meldingen van de app blokkeren"</string>
<string name="notification_header_default_channel" msgid="225454696914642444">"Meldingen"</string>
<string name="notification_channel_disabled" msgid="928065923928416337">"Meldingen worden niet meer weergegeven"</string>
<string name="notification_channel_minimized" msgid="6892672757877552959">"Deze meldingen worden geminimaliseerd"</string>
@@ -716,7 +716,7 @@
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Kan overgaan of trillen op basis van de telefooninstellingen"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Kan overgaan of trillen op basis van de telefooninstellingen. Gesprekken uit <xliff:g id="APP_NAME">%1$s</xliff:g> worden standaard als bubbels weergegeven."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Trekt de aandacht met een zwevende snelkoppeling naar deze content."</string>
- <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Wordt bovenaan het gedeelte met gesprekken weergegeven, verschijnt als zwevende bubbel, geeft de profielfoto weer op het vergrendelingsscherm"</string>
+ <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Wordt bovenaan het gedeelte met gesprekken weergegeven, verschijnt als zwevende bubbel, geeft de profielfoto weer op het vergrendelscherm"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Instellingen"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Prioriteit"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ondersteunt geen gespreksfuncties"</string>
@@ -907,7 +907,7 @@
<string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"<xliff:g id="ID_1">%s</xliff:g>-instellingen openen."</string>
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Volgorde van instellingen bewerken."</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Pagina <xliff:g id="ID_1">%1$d</xliff:g> van <xliff:g id="ID_2">%2$d</xliff:g>"</string>
- <string name="tuner_lock_screen" msgid="2267383813241144544">"Vergrendelingsscherm"</string>
+ <string name="tuner_lock_screen" msgid="2267383813241144544">"Vergrendelscherm"</string>
<string name="pip_phone_expand" msgid="1424988917240616212">"Uitvouwen"</string>
<string name="pip_phone_minimize" msgid="9057117033655996059">"Minimaliseren"</string>
<string name="pip_phone_close" msgid="8801864042095341824">"Sluiten"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Naar rechtsboven verplaatsen"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Naar linksonder verplaatsen"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Naar rechtsonder verplaatsen"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Bubbel sluiten"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Gesprekken niet in bubbels weergeven"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Chatten met bubbels"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Nieuwe gesprekken worden weergegeven als zwevende iconen of \'bubbels\'. Tik om een bubbel te openen. Sleep om de bubbel te verplaatsen."</string>
@@ -1017,7 +1016,7 @@
<string name="priority_onboarding_title" msgid="2893070698479227616">"Gesprek ingesteld als prioriteit"</string>
<string name="priority_onboarding_behavior" msgid="5342816047020432929">"Bij prioriteitsgesprekken gebeurt het volgende:"</string>
<string name="priority_onboarding_show_at_top_text" msgid="1678400241025513541">"Worden bovenaan het gespreksgedeelte weergegeven"</string>
- <string name="priority_onboarding_show_avatar_text" msgid="5756291381124091508">"Tonen profielafbeelding op vergrendelingsscherm"</string>
+ <string name="priority_onboarding_show_avatar_text" msgid="5756291381124091508">"Tonen profielafbeelding op vergrendelscherm"</string>
<string name="priority_onboarding_appear_as_bubble_text" msgid="4227039772250263122">"Worden als zwevende ballon weergegeven vóór apps"</string>
<string name="priority_onboarding_ignores_dnd_text" msgid="2918952762719600529">"Onderbreken \'Niet storen\'"</string>
<string name="priority_onboarding_done_button_title" msgid="4569550984286506007">"OK"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index f575f9868427..13bc85ae689d 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -273,7 +273,7 @@
<string name="accessibility_quick_settings_airplane_on" msgid="8106176561295294255">"ଏୟାର୍‌ପ୍ଲେନ୍‌ ମୋଡ୍‌ ଅନ୍ ଅଛି।"</string>
<string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"ଏୟାର୍‌ପ୍ଲେନ୍‌ ମୋଡ୍‌କୁ ବନ୍ଦ କରାଯାଇଛି।"</string>
<string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"ଏୟାର୍‌ପ୍ଲେନ୍‌ ମୋଡ୍‌କୁ ଚାଲୁ କରାଯାଇଛି।"</string>
- <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"ସମ୍ପୂର୍ଣ୍ଣ ନିରବ"</string>
+ <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"ସମ୍ପୂର୍ଣ୍ଣ ନୀରବତା"</string>
<string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"କେବଳ ଆଲାର୍ମ"</string>
<string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ।"</string>
<string name="accessibility_quick_settings_dnd_changed_off" msgid="1457150026842505799">"\"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\"କୁ ବନ୍ଦ କରାଯାଇଛି।"</string>
@@ -345,7 +345,7 @@
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ"</string>
<string name="quick_settings_dnd_priority_label" msgid="6251076422352664571">"କେବଳ ପ୍ରାଥମିକତା"</string>
<string name="quick_settings_dnd_alarms_label" msgid="1241780970469630835">"କେବଳ ଆଲାର୍ମ"</string>
- <string name="quick_settings_dnd_none_label" msgid="8420869988472836354">"ସମ୍ପୂର୍ଣ୍ଣ ନୀରବ"</string>
+ <string name="quick_settings_dnd_none_label" msgid="8420869988472836354">"ସମ୍ପୂର୍ଣ୍ଣ ନୀରବତା"</string>
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"ବ୍ଲୁଟୁଥ"</string>
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="6595808498429809855">"ବ୍ଲୁଟୂଥ୍‍‌ (<xliff:g id="NUMBER">%d</xliff:g>ଟି ଡିଭାଇସ୍‌)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="6375098046500790870">"ବ୍ଲୁଟୂଥ୍‍‌ ଅଫ୍"</string>
@@ -460,7 +460,7 @@
<string name="voice_hint" msgid="7476017460191291417">"ଭଏସ୍‍ ସହାୟକ ପାଇଁ ଆଇକନରୁ ସ୍ୱାଇପ୍‍ କରନ୍ତୁ"</string>
<string name="camera_hint" msgid="4519495795000658637">"କ୍ୟାମେରା ପାଇଁ ଆଇକନରୁ ସ୍ୱାଇପ୍‍ କରନ୍ତୁ"</string>
<string name="interruption_level_none_with_warning" msgid="8394434073508145437">"ସମ୍ପୂର୍ଣ୍ଣ ନୀରବ। ଏହାଦ୍ୱାରା ସ୍କ୍ରୀନ୍‍ ରିଡର୍‍ ମଧ୍ୟ ନୀରବ ହୋଇଯିବ।"</string>
- <string name="interruption_level_none" msgid="219484038314193379">"ସମ୍ପୂର୍ଣ୍ଣ ନୀରବ"</string>
+ <string name="interruption_level_none" msgid="219484038314193379">"ସମ୍ପୂର୍ଣ୍ଣ ନୀରବତା"</string>
<string name="interruption_level_priority" msgid="661294280016622209">"କେବଳ ପ୍ରାଥମିକତା"</string>
<string name="interruption_level_alarms" msgid="2457850481335846959">"କେବଳ ଆଲାର୍ମ"</string>
<string name="interruption_level_none_twoline" msgid="8579382742855486372">"ସମ୍ପୂର୍ଣ୍ଣ\nନୀରବ"</string>
@@ -510,7 +510,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"ସମସ୍ତ ଖାଲି କରନ୍ତୁ"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"ପରିଚାଳନା କରନ୍ତୁ"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ଇତିହାସ"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"ଇନକମିଂ"</string>
+ <!-- no translation found for notification_section_header_incoming (850925217908095197) -->
+ <skip />
<string name="notification_section_header_gentle" msgid="6804099527336337197">"ନୀରବ"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"ବାର୍ତ୍ତାଳାପଗୁଡ଼ିକ"</string>
@@ -1002,8 +1003,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"ଉପର-ଡାହାଣକୁ ନିଅନ୍ତୁ"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"ତଳ ବାମକୁ ନିଅନ୍ତୁ"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"ତଳ ଡାହାଣକୁ ନିଅନ୍ତୁ"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"ବବଲ୍ ଖାରଜ କରନ୍ତୁ"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"ବାର୍ତ୍ତାଳାପକୁ ବବଲ୍ କରନ୍ତୁ ନାହିଁ"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"ବବଲଗୁଡ଼ିକୁ ବ୍ୟବହାର କରି ଚାଟ୍ କରନ୍ତୁ"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"ନୂଆ ବାର୍ତ୍ତାଳାପଗୁଡ଼ିକ ଫ୍ଲୋଟିଂ ଆଇକନ୍ କିମ୍ବା ବବଲ୍ ଭାବେ ଦେଖାଯିବ। ବବଲ୍ ଖୋଲିବାକୁ ଟାପ୍ କରନ୍ତୁ। ଏହାକୁ ମୁଭ୍ କରିବାକୁ ଟାଣନ୍ତୁ।"</string>
@@ -1014,17 +1014,14 @@
<string name="notification_content_system_nav_changed" msgid="5077913144844684544">"ସିଷ୍ଟମ୍ ନାଭିଗେସନ୍ ଅପ୍‌ଡେଟ୍ ହୋଇଛି। ପରିବର୍ତ୍ତନ କରିବା ପାଇଁ, ସେଟିଂସ୍‌କୁ ଯାଆନ୍ତୁ।"</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"ସିଷ୍ଟମ୍ ନାଭିଗେସନ୍ ଅପ୍‌ଡେଟ୍ କରିବା ପାଇଁ ସେଟିଂସ୍‍କୁ ଯାଆନ୍ତୁ"</string>
<string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"ଷ୍ଟାଣ୍ଡବାଏ"</string>
- <!-- no translation found for priority_onboarding_title (2893070698479227616) -->
- <skip />
- <!-- no translation found for priority_onboarding_behavior (5342816047020432929) -->
- <skip />
+ <string name="priority_onboarding_title" msgid="2893070698479227616">"ବାର୍ତ୍ତାଳାପ ପ୍ରାଥମିକରେ ସେଟ୍ କରାଯାଇଛି"</string>
+ <string name="priority_onboarding_behavior" msgid="5342816047020432929">"ପ୍ରାଥମିକ ବାର୍ତ୍ତାଳାପଗୁଡ଼ିକ ଏଠାରେ ଦେଖାଯିବ:"</string>
<string name="priority_onboarding_show_at_top_text" msgid="1678400241025513541">"ବାର୍ତ୍ତାଳାପ ବିଭାଗର ଶୀର୍ଷରେ ଦେଖାନ୍ତୁ"</string>
<string name="priority_onboarding_show_avatar_text" msgid="5756291381124091508">"ଲକ୍ ସ୍କ୍ରିନରେ ପ୍ରୋଫାଇଲ୍ ଛବି ଦେଖାନ୍ତୁ"</string>
<string name="priority_onboarding_appear_as_bubble_text" msgid="4227039772250263122">"ଆପଗୁଡ଼ିକ ଉପରେ ଫ୍ଲୋଟିଂ ବବଲ୍ ପରି ଦେଖାଯିବ"</string>
<string name="priority_onboarding_ignores_dnd_text" msgid="2918952762719600529">"\'ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\' ମୋଡରେ ବାଧା"</string>
<string name="priority_onboarding_done_button_title" msgid="4569550984286506007">"ବୁଝିଗଲି"</string>
- <!-- no translation found for priority_onboarding_settings_button_title (6663601574303585927) -->
- <skip />
+ <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"ସେଟିଂସ୍"</string>
<string name="magnification_overlay_title" msgid="6584179429612427958">"ମ୍ୟାଗ୍ନିଫିକେସନ୍ ଓଭର୍‌ଲେ ୱିଣ୍ଡୋ"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"ମ୍ୟାଗ୍ନିଫିକେସନ୍ ୱିଣ୍ଡୋ"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"ମ୍ୟାଗ୍ନିଫିକେସନ୍ ୱିଣ୍ଡୋ ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ"</string>
@@ -1069,9 +1066,9 @@
<string name="controls_media_resume" msgid="1933520684481586053">"ପୁଣି ଆରମ୍ଭ କରନ୍ତୁ"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"ନିଷ୍କ୍ରିୟ ଅଛି, ଆପ ଯାଞ୍ଚ କରନ୍ତୁ"</string>
<string name="controls_error_retryable" msgid="864025882878378470">"ତ୍ରୁଟି, ପୁଣି ଚେଷ୍ଟା କରୁଛି…"</string>
- <string name="controls_error_removed" msgid="6675638069846014366">"ମିଳୁ ନାହିଁ"</string>
+ <string name="controls_error_removed" msgid="6675638069846014366">"ମିଳିଲା ନାହିଁ"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"ନିୟନ୍ତ୍ରଣ ଉପଲବ୍ଧ ନାହିଁ"</string>
- <string name="controls_error_removed_message" msgid="2885911717034750542">"<xliff:g id="DEVICE">%1$s</xliff:g>କୁ ଆକ୍ସେସ୍ କରିହେଲା ନାହିଁ। ନିୟନ୍ତ୍ରଣ ଏବେ ବି ଉପଲବ୍ଧ ଅଛି ଏବଂ ଆପ୍ ସେଟିଂସ୍ ବଦଳାଯାଇ ନାହିଁ ବୋଲି ସୁନିଶ୍ଚିତ କରିବାକୁ <xliff:g id="APPLICATION">%2$s</xliff:g> ଆପ୍ ଯାଞ୍ଚ କରନ୍ତୁ।"</string>
+ <string name="controls_error_removed_message" msgid="2885911717034750542">"<xliff:g id="DEVICE">%1$s</xliff:g>କୁ ଆକ୍ସେସ୍ କରିହେଲା ନାହିଁ। ନିୟନ୍ତ୍ରଣ ଏବେ ବି ଉପଲବ୍ଧ ଅଛି ଏବଂ ଆପ୍ ସେଟିଂସ୍ ବଦଳାଯାଇ ନାହିଁ ବୋଲି ସୁନିଶ୍ଚିତ କରିବାକୁ <xliff:g id="APPLICATION">%2$s</xliff:g> ଆପକୁ ଯାଞ୍ଚ କରନ୍ତୁ।"</string>
<string name="controls_open_app" msgid="483650971094300141">"ଆପ୍ ଖୋଲନ୍ତୁ"</string>
<string name="controls_error_generic" msgid="352500456918362905">"ସ୍ଥିତି ଲୋଡ୍ କରାଯାଇପାରିବ ନାହିଁ"</string>
<string name="controls_error_failed" msgid="960228639198558525">"ତ୍ରୁଟି ହୋଇଛି, ପୁଣି ଚେଷ୍ଟା କର"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 3389a18c8f9c..fe4dfb2fba54 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -510,7 +510,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"ਸਭ ਕਲੀਅਰ ਕਰੋ"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ਇਤਿਹਾਸ"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"ਇਨਕਮਿੰਗ"</string>
+ <!-- no translation found for notification_section_header_incoming (850925217908095197) -->
+ <skip />
<string name="notification_section_header_gentle" msgid="6804099527336337197">"ਸ਼ਾਂਤ"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"ਸੂਚਨਾਵਾਂ"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"ਗੱਲਾਂਬਾਤਾਂ"</string>
@@ -1002,8 +1003,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"ਉੱਪਰ ਵੱਲ ਸੱਜੇ ਲਿਜਾਓ"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"ਹੇਠਾਂ ਵੱਲ ਖੱਬੇ ਲਿਜਾਓ"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"ਹੇਠਾਂ ਵੱਲ ਸੱਜੇ ਲਿਜਾਓ"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"ਬਬਲ ਨੂੰ ਖਾਰਜ ਕਰੋ"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"ਗੱਲਬਾਤ \'ਤੇ ਬਬਲ ਨਾ ਲਾਓ"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"ਬਬਲ ਵਰਤਦੇ ਹੋਏ ਚੈਟ ਕਰੋ"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"ਨਵੀਆਂ ਗੱਲਾਂਬਾਤਾਂ ਫਲੋਟਿੰਗ ਪ੍ਰਤੀਕਾਂ ਜਾਂ ਬਬਲ ਦੇ ਰੂਪ ਵਿੱਚ ਦਿਸਦੀਆਂ ਹਨ। ਬਬਲ ਨੂੰ ਖੋਲ੍ਹਣ ਲਈ ਟੈਪ ਕਰੋ। ਇਸਨੂੰ ਲਿਜਾਣ ਲਈ ਘਸੀਟੋ।"</string>
@@ -1068,7 +1068,7 @@
<string name="controls_error_retryable" msgid="864025882878378470">"ਗੜਬੜ, ਮੁੜ ਕੋਸ਼ਿਸ਼ ਹੋ ਰਹੀ ਹੈ…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"ਨਹੀਂ ਮਿਲਿਆ"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"ਕੰਟਰੋਲ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
- <string name="controls_error_removed_message" msgid="2885911717034750542">"<xliff:g id="DEVICE">%1$s</xliff:g> ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕੀ। ਇਹ ਪੱਕਾ ਕਰਨ ਲਈ <xliff:g id="APPLICATION">%2$s</xliff:g> ਐਪ ਦੀ ਜਾਂਚ ਕਰੋ ਕਿ ਕੰਟਰੋਲ ਹਾਲੇ ਵੀ ਉਪਲਬਧ ਹੈ ਅਤੇ ਐਪ ਸੈਟਿੰਗ ਬਦਲੀ ਨਹੀਂ ਹੈ।"</string>
+ <string name="controls_error_removed_message" msgid="2885911717034750542">"<xliff:g id="DEVICE">%1$s</xliff:g> ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕੀ। ਇਹ ਪੱਕਾ ਕਰਨ ਲਈ <xliff:g id="APPLICATION">%2$s</xliff:g> ਐਪ ਦੀ ਜਾਂਚ ਕਰੋ ਕਿ ਕੰਟਰੋਲ ਹਾਲੇ ਉਪਲਬਧ ਹੈ ਅਤੇ ਐਪ ਸੈਟਿੰਗਾਂ ਨਹੀਂ ਬਦਲੀਆਂ ਹਨ।"</string>
<string name="controls_open_app" msgid="483650971094300141">"ਐਪ ਖੋਲ੍ਹੋ"</string>
<string name="controls_error_generic" msgid="352500456918362905">"ਸਥਿਤੀ ਲੋਡ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ"</string>
<string name="controls_error_failed" msgid="960228639198558525">"ਗੜਬੜ, ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index e85b76f228ac..b2aaa05724e1 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -168,7 +168,7 @@
<string name="biometric_dialog_last_password_attempt_before_wipe_profile" msgid="8538032972389729253">"Jeśli następnym razem podasz nieprawidłowe hasło, profil służbowy oraz powiązane z nim dane zostaną usunięte."</string>
<string name="biometric_dialog_failed_attempts_now_wiping_device" msgid="6585503524026243042">"Zbyt wiele nieudanych prób. Dane na urządzeniu zostaną usunięte."</string>
<string name="biometric_dialog_failed_attempts_now_wiping_user" msgid="7015008539146949115">"Zbyt wiele nieudanych prób. Użytkownik zostanie usunięty."</string>
- <string name="biometric_dialog_failed_attempts_now_wiping_profile" msgid="5239378521440749682">"Zbyt wiele nieudanych prób. Profil do pracy i powiązane z nim dane zostaną usunięte."</string>
+ <string name="biometric_dialog_failed_attempts_now_wiping_profile" msgid="5239378521440749682">"Zbyt wiele nieudanych prób. Profil służbowy i powiązane z nim dane zostaną usunięte."</string>
<string name="biometric_dialog_now_wiping_dialog_dismiss" msgid="7189432882125106154">"Zamknij"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"Dotknij czytnika linii papilarnych"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="4465698996175640549">"Ikona odcisku palca"</string>
@@ -516,7 +516,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Usuń wszystkie"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Zarządzaj"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historia"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Przychodzące"</string>
+ <!-- no translation found for notification_section_header_incoming (850925217908095197) -->
+ <skip />
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Ciche"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Powiadomienia"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Rozmowy"</string>
@@ -1012,8 +1013,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Przenieś w prawy górny róg"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Przenieś w lewy dolny róg"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Przenieś w prawy dolny róg"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Zamknij dymek"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Nie wyświetlaj rozmowy jako dymku"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Czatuj, korzystając z dymków"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Nowe rozmowy będą wyświetlane jako pływające ikony lub dymki. Kliknij, by otworzyć dymek. Przeciągnij, by go przenieść."</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 2259c305022e..9b3976eaffb8 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Limpar tudo"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Gerenciar"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Histórico"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Recebidas"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Novas"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Silenciosas"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificações"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversas"</string>
@@ -595,7 +595,7 @@
<string name="screen_pinning_title" msgid="9058007390337841305">"O app está fixado"</string>
<string name="screen_pinning_description" msgid="8699395373875667743">"Ela é mantida à vista até que seja liberada. Toque em Voltar e em Visão geral e mantenha essas opções pressionadas para liberar."</string>
<string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"Ela é mantida à vista até que seja liberada. Toque em Voltar e em Início e mantenha essas opções pressionadas para liberar."</string>
- <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Ela é mantida à vista até que seja liberada. Deslize para cima e a mantenha pressionada para liberar."</string>
+ <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Ele é mantido à vista até que seja liberado. Deslize para cima e mantenha pressionado para liberar."</string>
<string name="screen_pinning_description_accessible" msgid="7386449191953535332">"Ela é mantida à vista até que seja liberada. Toque em Visão geral e mantenha essa opção pressionada para liberar."</string>
<string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"Ela é mantida à vista até que seja liberada. Toque em Início e mantenha essa opção pressionada para liberar."</string>
<string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"Dados pessoais podem ficar acessíveis (como contatos e conteúdo de e-mail)."</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Mover para canto superior direito"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Mover para canto inferior esquerdo"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Mover para canto inferior direito"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Dispensar balão"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Não criar balões de conversa"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Converse usando balões"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Novas conversas aparecerão como ícones flutuantes, ou balões. Toque para abrir o balão. Arraste para movê-lo."</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index d90b8b2eeb13..24e7912be621 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Limpar tudo"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Gerir"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Histórico"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"A receber"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Nova"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Silencioso"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificações"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversas"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Mover parte superior direita"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Mover p/ parte infer. esquerda"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Mover parte inferior direita"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Ignorar balão"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Não apresentar a conversa em balões"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Converse no chat através de balões"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"As novas conversas aparecem como ícones flutuantes ou balões. Toque para abrir o balão. Arraste para o mover."</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 2259c305022e..9b3976eaffb8 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Limpar tudo"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Gerenciar"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Histórico"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Recebidas"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Novas"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Silenciosas"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificações"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversas"</string>
@@ -595,7 +595,7 @@
<string name="screen_pinning_title" msgid="9058007390337841305">"O app está fixado"</string>
<string name="screen_pinning_description" msgid="8699395373875667743">"Ela é mantida à vista até que seja liberada. Toque em Voltar e em Visão geral e mantenha essas opções pressionadas para liberar."</string>
<string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"Ela é mantida à vista até que seja liberada. Toque em Voltar e em Início e mantenha essas opções pressionadas para liberar."</string>
- <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Ela é mantida à vista até que seja liberada. Deslize para cima e a mantenha pressionada para liberar."</string>
+ <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Ele é mantido à vista até que seja liberado. Deslize para cima e mantenha pressionado para liberar."</string>
<string name="screen_pinning_description_accessible" msgid="7386449191953535332">"Ela é mantida à vista até que seja liberada. Toque em Visão geral e mantenha essa opção pressionada para liberar."</string>
<string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"Ela é mantida à vista até que seja liberada. Toque em Início e mantenha essa opção pressionada para liberar."</string>
<string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"Dados pessoais podem ficar acessíveis (como contatos e conteúdo de e-mail)."</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Mover para canto superior direito"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Mover para canto inferior esquerdo"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Mover para canto inferior direito"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Dispensar balão"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Não criar balões de conversa"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Converse usando balões"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Novas conversas aparecerão como ícones flutuantes, ou balões. Toque para abrir o balão. Arraste para movê-lo."</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index c90c04c666b1..6b18dc41932f 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -513,7 +513,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Ștergeți toate notificările"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Gestionați"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Istoric"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Primite"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Noi"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Silențioase"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificări"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversații"</string>
@@ -1007,10 +1007,9 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Mutați în dreapta sus"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Mutați în stânga jos"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Mutați în dreapta jos"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Închideți balonul"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Nu afișați conversația în balon"</string>
- <string name="bubbles_user_education_title" msgid="5547017089271445797">"Discutați pe chat folosind baloanele"</string>
+ <string name="bubbles_user_education_title" msgid="5547017089271445797">"Chat cu baloane"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Conversațiile noi apar ca pictograme flotante sau baloane. Atingeți pentru a deschide balonul. Trageți pentru a-l muta."</string>
<string name="bubbles_user_education_manage_title" msgid="2848511858160342320">"Controlați oricând baloanele"</string>
<string name="bubbles_user_education_manage" msgid="1391639189507036423">"Atingeți Gestionați pentru a dezactiva baloanele din această aplicație"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 35f4b745830a..916b2c7923e7 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -516,7 +516,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Очистить все"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Настроить"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"История"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Входящие"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Новое"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Без звука"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Уведомления"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Разговоры"</string>
@@ -727,7 +727,7 @@
<string name="notification_priority_title" msgid="2079708866333537093">"Приоритет"</string>
<string name="no_shortcut" msgid="8257177117568230126">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" не поддерживает функции разговоров."</string>
<string name="bubble_overflow_empty_title" msgid="3120029421991510842">"Нет недавних всплывающих чатов"</string>
- <string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"Здесь будут появляться недавние и закрытые всплывающие чаты."</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"Здесь будут появляться недавние и скрытые всплывающие чаты."</string>
<string name="notification_unblockable_desc" msgid="2073030886006190804">"Эти уведомления нельзя изменить."</string>
<string name="notification_multichannel_desc" msgid="7414593090056236179">"Эту группу уведомлений нельзя настроить здесь."</string>
<string name="notification_delegate_header" msgid="1264510071031479920">"Уведомление отправлено через прокси-сервер."</string>
@@ -1012,10 +1012,9 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Перенести в правый верхний угол"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Перенести в левый нижний угол"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Перенести в правый нижний угол"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Закрыть всплывающий чат"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Не показывать всплывающие чаты для разговоров"</string>
- <string name="bubbles_user_education_title" msgid="5547017089271445797">"Всплывающие чаты для разговоров"</string>
+ <string name="bubbles_user_education_title" msgid="5547017089271445797">"Всплывающие чаты"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Новые разговоры будут появляться в виде плавающих значков, или всплывающих чатов. Чтобы открыть чат, нажмите на него, а чтобы переместить – перетащите."</string>
<string name="bubbles_user_education_manage_title" msgid="2848511858160342320">"Настройки всплывающих чатов"</string>
<string name="bubbles_user_education_manage" msgid="1391639189507036423">"Чтобы отключить всплывающие чаты от приложения, нажмите \"Настроить\"."</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index be88cbafe59b..b9b95ac45f81 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"සියල්ල හිස් කරන්න"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"කළමනාකරණය කරන්න"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ඉතිහාසය"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"එන"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"නව"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"නිහඬ"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"දැනුම් දීම්"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"සංවාද"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"ඉහළ දකුණට ගෙන යන්න"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"පහළ වමට ගෙන යන්න"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"පහළ දකුණට ගෙන යන්න"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"බුබුලු ඉවත ලන්න"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"සංවාදය බුබුලු නොදමන්න"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"බුබුලු භාවිතයෙන් කතාබහ කරන්න"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"නව සංවාද පාවෙන අයිකන හෝ බුබුලු ලෙස දිස් වේ. බුබුල විවෘත කිරීමට තට්ටු කරන්න. එය ගෙන යාමට අදින්න."</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index e0ba13dbfd98..684e29645978 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -516,7 +516,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Vymazať všetko"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Spravovať"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"História"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Prichádzajúce"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Nové"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Ticho"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Upozornenia"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Konverzácie"</string>
@@ -1012,8 +1012,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Presunúť doprava nahor"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Presunúť doľava nadol"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Presunúť doprava nadol"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Zavrieť bublinu"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Nezobrazovať konverzáciu ako bublinu"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Čet pomocou bublín"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Nové konverzácie sa zobrazujú ako plávajúce ikony či bubliny. Bublinu otvoríte klepnutím. Premiestnite ju presunutím."</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 7066e8dc1612..326ae97cdec3 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -516,7 +516,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Izbriši vse"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Upravljanje"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Zgodovina"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Dohodno"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Novo"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Tiho"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Obvestila"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Pogovori"</string>
@@ -599,11 +599,11 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"onemogoči"</string>
<string name="accessibility_output_chooser" msgid="7807898688967194183">"Izbira druge izhodne naprave"</string>
<string name="screen_pinning_title" msgid="9058007390337841305">"Aplikacija je pripeta"</string>
- <string name="screen_pinning_description" msgid="8699395373875667743">"S tem ostane zaslon viden, dokler ga ne odpnete. Če ga želite odpeti, hkrati pridržite gumba za nazaj in pregled."</string>
- <string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"S tem ostane zaslon viden, dokler ga ne odpnete. Če ga želite odpeti, hkrati pridržite gumba za nazaj in za začetni zaslon."</string>
- <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"S tem ostane zaslon viden, dokler ga ne odpnete. Če ga želite odpeti, povlecite navzgor in pridržite."</string>
- <string name="screen_pinning_description_accessible" msgid="7386449191953535332">"S tem ostane zaslon viden, dokler ga ne odpnete. Če ga želite odpeti, pridržite gumb za pregled."</string>
- <string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"S tem ostane zaslon viden, dokler ga ne odpnete. Če ga želite odpeti, pridržite gumb za začetni zaslon."</string>
+ <string name="screen_pinning_description" msgid="8699395373875667743">"S tem ostane vidna, dokler je ne odpnete. Če jo želite odpeti, hkrati pridržite gumba za nazaj in pregled."</string>
+ <string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"S tem ostane vidna, dokler je ne odpnete. Če jo želite odpeti, hkrati pridržite gumba za nazaj in za začetni zaslon."</string>
+ <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"S tem ostane vidna, dokler je ne odpnete. Če jo želite odpeti, povlecite navzgor in pridržite."</string>
+ <string name="screen_pinning_description_accessible" msgid="7386449191953535332">"S tem ostane vidna, dokler je ne odpnete. Če jo želite odpeti, pridržite gumb za pregled."</string>
+ <string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"S tem ostane vidna, dokler je ne odpnete. Če jo želite odpeti, pridržite gumb za začetni zaslon."</string>
<string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"Dostopni so lahko osebni podatki (na primer stiki in vsebina e-poštnih sporočil)."</string>
<string name="screen_pinning_can_open_other_apps" msgid="7529756813231421455">"Pripeta aplikacija lahko odpre druge aplikacije."</string>
<string name="screen_pinning_toast" msgid="8177286912533744328">"Če želite odpeti to aplikacijo, hkrati pridržite gumba za nazaj in za pregled."</string>
@@ -1012,8 +1012,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Premakni zgoraj desno"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Premakni spodaj levo"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Premakni spodaj desno"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Opusti oblaček"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Pogovora ne prikaži v oblačku"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Klepet z oblački"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Novi pogovori so prikazani kot lebdeče ikone ali oblački. Če želite odpreti oblaček, se ga dotaknite. Če ga želite premakniti, ga povlecite."</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index a23a85bf97fa..ab6c93e94fc9 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Pastroji të gjitha"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Menaxho"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historiku"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Hyrëse"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Të reja"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Në heshtje"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Njoftimet"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Bisedat"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Lëviz lart djathtas"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Zhvendos poshtë majtas"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Lëvize poshtë djathtas"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Hiqe flluskën"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Mos e vendos bisedën në flluskë"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Bisedo duke përdorur flluskat"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Bisedat e reja shfaqen si ikona pluskuese ose flluska. Trokit për të hapur flluskën. Zvarrit për ta zhvendosur."</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index fde6413e4b97..f692e1cc20d9 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -513,7 +513,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Обриши све"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Управљајте"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Историја"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Долазно"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Ново"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Нечујно"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Обавештења"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Конверзације"</string>
@@ -598,7 +598,7 @@
<string name="screen_pinning_title" msgid="9058007390337841305">"Апликација је закачена"</string>
<string name="screen_pinning_description" msgid="8699395373875667743">"На овај начин се ово стално приказује док га не откачите. Додирните и задржите Назад и Преглед да бисте га откачили."</string>
<string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"На овај начин се ово стално приказује док га не откачите. Додирните и задржите Назад и Почетна да бисте га откачили."</string>
- <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"На овај начин се стално приказује док га не откачите. Превуците нагоре и задржите да бисте га откачили."</string>
+ <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Стално ће се приказивати док је не откачите. Превуците нагоре и задржите да бисте је откачили."</string>
<string name="screen_pinning_description_accessible" msgid="7386449191953535332">"На овај начин се ово стално приказује док га не откачите. Додирните и задржите Преглед да бисте га откачили."</string>
<string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"На овај начин се ово стално приказује док га не откачите. Додирните и задржите Почетна да бисте га откачили."</string>
<string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"Могу да буду доступни лични подаци (као што су контакти и садржај имејлова)."</string>
@@ -1007,8 +1007,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Премести горе десно"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Премести доле лево"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Премести доле десно"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Одбацивање облачића"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Не користи облачиће за конверзацију"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Ћаскајте у облачићима"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Нове конверзације се приказују као плутајуће иконе или облачићи. Додирните да бисте отворили облачић. Превуците да бисте га преместили."</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index af3839bc8e8b..73eb647303ba 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Rensa alla"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Hantera"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historik"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Inkommande"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Ny"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Ljudlöst"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Aviseringar"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Konversationer"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Flytta högst upp till höger"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Flytta längst ned till vänster"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Flytta längst ned till höger"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Stäng bubbla"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Visa inte konversationen i bubblor"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Chatta med bubblor"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Nya konversationer visas som flytande ikoner, så kallade bubblor. Tryck på bubblan om du vill öppna den. Dra den om du vill flytta den."</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index f5ec81bf8fec..1a0b9ec653ae 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -92,7 +92,7 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Inachakata rekodi ya skrini"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Arifa inayoendelea ya kipindi cha kurekodi skrini"</string>
<string name="screenrecord_start_label" msgid="1750350278888217473">"Ungependa kuanza kurekodi?"</string>
- <string name="screenrecord_description" msgid="1123231719680353736">"Wakati wa kurekodi, Mfumo wa Android unaweza kusana maelezo yoyote nyeti yanayoonekana kwenye skrini au yanayochezwa kwenye kifaa chako. Hii ni pamoja na manenosiri, maelezo ya malipo, picha, ujumbe na sauti."</string>
+ <string name="screenrecord_description" msgid="1123231719680353736">"Wakati wa kurekodi, Mfumo wa Android unaweza kunasa maelezo yoyote nyeti yanayoonekana kwenye skrini au yanayochezwa kwenye kifaa chako. Hii ni pamoja na manenosiri, maelezo ya malipo, picha, ujumbe na sauti."</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Rekodi sauti"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Sauti ya kifaa"</string>
<string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sauti kutoka kwenye kifaa chako, kama vile muziki, simu na milio ya simu"</string>
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Futa zote"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Dhibiti"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Historia"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Simu inayoingia"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Mpya"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Kimya"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Arifa"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Mazungumzo"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Sogeza juu kulia"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Sogeza chini kushoto"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Sogeza chini kulia"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Ondoa kiputo"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Usiweke viputo kwenye mazungumzo"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Piga gumzo ukitumia viputo"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Mazungumzo mapya huonekena kama aikoni au viputo vinavyoelea. Gusa ili ufungue kiputo. Buruta ili ukisogeze."</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 4a32ecf8d229..29a186f481cc 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -146,7 +146,7 @@
<string name="biometric_dialog_face_icon_description_authenticating" msgid="3401633342366146535">"உங்கள் முகத்தை அங்கீகரிக்கிறது"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"முகம் அங்கீகரிக்கப்பட்டது"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"உறுதிப்படுத்தப்பட்டது"</string>
- <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"முடிக்க \'உறுதிப்படுத்து\' என்பதை தட்டவும்"</string>
+ <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"முடிக்க \'உறுதிப்படுத்துக\' என்பதை தட்டவும்"</string>
<string name="biometric_dialog_authenticated" msgid="7337147327545272484">"அங்கீகரிக்கப்பட்டது"</string>
<string name="biometric_dialog_use_pin" msgid="8385294115283000709">"பின்னைப் பயன்படுத்து"</string>
<string name="biometric_dialog_use_pattern" msgid="2315593393167211194">"பேட்டர்னைப் பயன்படுத்து"</string>
@@ -168,7 +168,7 @@
<string name="biometric_dialog_last_password_attempt_before_wipe_profile" msgid="8538032972389729253">"அடுத்த முறை தவறான கடவுச்சொல்லை உள்ளிட்டால் உங்கள் பணிக் கணக்கும் அதன் தரவும் நீக்கப்படும்."</string>
<string name="biometric_dialog_failed_attempts_now_wiping_device" msgid="6585503524026243042">"பலமுறை தவறாக முயன்ற காரணத்தால் இந்தச் சாதனத்தின் தரவு நீக்கப்படும்."</string>
<string name="biometric_dialog_failed_attempts_now_wiping_user" msgid="7015008539146949115">"பலமுறை தவறாக முயன்ற காரணத்தால் இந்தப் பயனர் நீக்கப்படுவார்."</string>
- <string name="biometric_dialog_failed_attempts_now_wiping_profile" msgid="5239378521440749682">"பலமுறை தவறாக முயன்ற காரணத்தால் இந்தப் பணிக் கணக்கும் அதன் தரவும் நீக்கப்படும்."</string>
+ <string name="biometric_dialog_failed_attempts_now_wiping_profile" msgid="5239378521440749682">"பலமுறை தவறாக முயன்றதால், இந்தப் பணிக் கணக்கும் அதன் தரவும் நீக்கப்படும்"</string>
<string name="biometric_dialog_now_wiping_dialog_dismiss" msgid="7189432882125106154">"நிராகரி"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"கைரேகை சென்சாரைத் தொடவும்"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="4465698996175640549">"கைரேகை ஐகான்"</string>
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"எல்லாவற்றையும் அழி"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"அறிவிப்புகளை நிர்வகி"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"வரலாறு"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"உள்வருவது"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"புதிது"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"நிசப்தம்"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"அறிவிப்புகள்"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"உரையாடல்கள்"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"மேலே வலப்புறமாக நகர்த்து"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"கீழே இடப்புறமாக நகர்த்து"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"கீழே வலதுபுறமாக நகர்த்து"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"குமிழை அகற்று"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"உரையாடலைக் குமிழாக்காதே"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"குமிழ்களைப் பயன்படுத்தி அரட்டையடியுங்கள்"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"புதிய உரையாடல்கள் மிதக்கும் ஐகான்களாகவோ குமிழ்களாகவோ தோன்றும். குமிழைத் திறக்க தட்டவும். நகர்த்த இழுக்கவும்."</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 73754a20cccc..4b56e157b34f 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"అన్నీ క్లియర్ చేయండి"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"నిర్వహించండి"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"చరిత్ర"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"ఇన్‌కమింగ్"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"కొత్తవి"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"నిశ్శబ్దం"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"నోటిఫికేషన్‌లు"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"సంభాషణలు"</string>
@@ -721,7 +721,7 @@
<string name="notification_priority_title" msgid="2079708866333537093">"ప్రాధాన్యత"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> సంభాషణ ఫీచర్‌లను సపోర్ట్ చేయదు"</string>
<string name="bubble_overflow_empty_title" msgid="3120029421991510842">"ఇటీవలి బబుల్స్ ఏవీ లేవు"</string>
- <string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"ఇటీవలి బబుల్స్, తీసివేసిన బబుల్స్ ఇక్కడ కనిపిస్తాయి"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"ఇటీవలి బబుల్స్ మరియు తీసివేసిన బబుల్స్ ఇక్కడ కనిపిస్తాయి"</string>
<string name="notification_unblockable_desc" msgid="2073030886006190804">"ఈ నోటిఫికేషన్‌లను సవరించడం వీలుపడదు."</string>
<string name="notification_multichannel_desc" msgid="7414593090056236179">"ఈ నోటిఫికేషన్‌ల సమూహాన్ని ఇక్కడ కాన్ఫిగర్ చేయలేము"</string>
<string name="notification_delegate_header" msgid="1264510071031479920">"ప్రాక్సీ చేయబడిన నోటిఫికేషన్"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"ఎగువ కుడివైపునకు జరుపు"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"దిగువ ఎడమవైపునకు తరలించు"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"దిగవు కుడివైపునకు జరుపు"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"బబుల్‌ను విస్మరించు"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"సంభాషణను బబుల్ చేయవద్దు"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"బబుల్స్‌ను ఉపయోగించి చాట్ చేయండి"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"కొత్త సంభాషణలు తేలియాడే చిహ్నాలుగా లేదా బబుల్స్ లాగా కనిపిస్తాయి. బబుల్‌ని తెరవడానికి నొక్కండి. తరలించడానికి లాగండి."</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index d61533d0be01..585228d6411e 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -92,7 +92,7 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"กำลังประมวลผลการอัดหน้าจอ"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"การแจ้งเตือนต่อเนื่องสำหรับเซสชันการบันทึกหน้าจอ"</string>
<string name="screenrecord_start_label" msgid="1750350278888217473">"เริ่มบันทึกเลยไหม"</string>
- <string name="screenrecord_description" msgid="1123231719680353736">"ขณะบันทึก ระบบ Android จะบันทึกข้อมูลที่ละเอียดอ่อนที่ปรากฏบนหน้าจอหรือเล่นในอุปกรณ์ได้ ซึ่งรวมถึงรหัสผ่าน ข้อมูลการชำระเงิน รูปภาพ ข้อความ และเสียง"</string>
+ <string name="screenrecord_description" msgid="1123231719680353736">"ขณะบันทึก ระบบ Android อาจบันทึกข้อมูลที่ละเอียดอ่อนที่ปรากฏบนหน้าจอหรือเล่นในอุปกรณ์ได้ ซึ่งรวมถึงรหัสผ่าน ข้อมูลการชำระเงิน รูปภาพ ข้อความ และเสียง"</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"บันทึกเสียง"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"เสียงจากอุปกรณ์"</string>
<string name="screenrecord_device_audio_description" msgid="4922694220572186193">"เสียงจากอุปกรณ์ เช่น เพลง การโทร และเสียงเรียกเข้า"</string>
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"ล้างทั้งหมด"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"จัดการ"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ประวัติ"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"เข้ามาใหม่"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"ใหม่"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"เงียบ"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"การแจ้งเตือน"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"การสนทนา"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"ย้ายไปด้านขวาบน"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"ย้ายไปด้านซ้ายล่าง"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"ย้ายไปด้านขาวล่าง"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"ปิดบับเบิล"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"ไม่ต้องแสดงการสนทนาเป็นบับเบิล"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"แชทโดยใช้บับเบิล"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"การสนทนาใหม่ๆ จะปรากฏเป็นไอคอนแบบลอยหรือบับเบิล แตะเพื่อเปิดบับเบิล ลากเพื่อย้ายที่"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 041214f2111e..e25e52bdf4ab 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -122,7 +122,7 @@
<string name="accessibility_back" msgid="6530104400086152611">"Bumalik"</string>
<string name="accessibility_home" msgid="5430449841237966217">"Home"</string>
<string name="accessibility_menu" msgid="2701163794470513040">"Menu"</string>
- <string name="accessibility_accessibility_button" msgid="4089042473497107709">"Pagiging Accessible"</string>
+ <string name="accessibility_accessibility_button" msgid="4089042473497107709">"Accessibility"</string>
<string name="accessibility_rotate_button" msgid="1238584767612362586">"I-rotate ang screen"</string>
<string name="accessibility_recent" msgid="901641734769533575">"Overview"</string>
<string name="accessibility_search_light" msgid="524741790416076988">"Hanapin"</string>
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"I-clear lahat"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Pamahalaan"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"History"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Papasok"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Bago"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Naka-silent"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Mga Notification"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Mga Pag-uusap"</string>
@@ -595,7 +595,7 @@
<string name="screen_pinning_title" msgid="9058007390337841305">"Naka-pin ang app"</string>
<string name="screen_pinning_description" msgid="8699395373875667743">"Pinapanatili nitong nakikita ito hanggang sa mag-unpin ka. Pindutin nang matagal ang Bumalik at Overview upang mag-unpin."</string>
<string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"Pinapanatili nitong nakikita ito hanggang sa mag-unpin ka. Pindutin nang matagal ang Bumalik at Home upang mag-unpin."</string>
- <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Pinapanatili nitong nakikita ito hanggang sa mag-unpin ka. Mag-swipe pataas at i-hold para i-unpin."</string>
+ <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Mananatiling nakikita ang app hanggang sa mag-unpin ka. Mag-swipe pataas at i-hold para i-unpin."</string>
<string name="screen_pinning_description_accessible" msgid="7386449191953535332">"Pinapanatili nitong nakikita ito hanggang sa mag-unpin ka. Pindutin nang matagal ang Overview upang mag-unpin."</string>
<string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"Pinapanatili nitong nakikita ito hanggang sa mag-unpin ka. Pindutin nang matagal ang Home upang mag-unpin."</string>
<string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"Puwedeng ma-access ang personal na data (tulad ng mga contact at content ng email)."</string>
@@ -618,7 +618,7 @@
<string name="stream_notification" msgid="7930294049046243939">"Notification"</string>
<string name="stream_bluetooth_sco" msgid="6234562365528664331">"Bluetooth"</string>
<string name="stream_dtmf" msgid="7322536356554673067">"Dual multi tone frequency"</string>
- <string name="stream_accessibility" msgid="3873610336741987152">"Pagiging Accessible"</string>
+ <string name="stream_accessibility" msgid="3873610336741987152">"Accessibility"</string>
<string name="ring_toggle_title" msgid="5973120187287633224">"Mga Tawag"</string>
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Ipa-ring"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"I-vibrate"</string>
@@ -1002,10 +1002,9 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Ilipat sa kanan sa itaas"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Ilipat sa kaliwa sa ibaba"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Ilipat sa kanan sa ibaba"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"I-dismiss ang bubble"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Huwag ipakita sa bubble ang mga pag-uusap"</string>
- <string name="bubbles_user_education_title" msgid="5547017089271445797">"Makipag-chat gamit ang mga bubble"</string>
+ <string name="bubbles_user_education_title" msgid="5547017089271445797">"Mag-chat gamit ang bubbles"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Lumalabas bilang mga nakalutang na icon o bubble ang mga bagong pag-uusap. I-tap para buksan ang bubble. I-drag para ilipat ito."</string>
<string name="bubbles_user_education_manage_title" msgid="2848511858160342320">"Kontrolin ang mga bubble anumang oras"</string>
<string name="bubbles_user_education_manage" msgid="1391639189507036423">"I-tap ang Pamahalaan para i-off ang mga bubble mula sa app na ito"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index e4892cf934a3..6c6b5bf47b3b 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -139,7 +139,7 @@
<string name="voice_assist_label" msgid="3725967093735929020">"sesli yardımı aç"</string>
<string name="camera_label" msgid="8253821920931143699">"kamerayı aç"</string>
<string name="cancel" msgid="1089011503403416730">"İptal"</string>
- <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Onaylayın"</string>
+ <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Onayla"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Tekrar dene"</string>
<string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Kimlik doğrulama işlemini iptal etmek için dokunun"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4351777022315116816">"Lütfen tekrar deneyin"</string>
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Tümünü temizle"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Yönet"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Geçmiş"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Gelen"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Yeni"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Sessiz"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Bildirimler"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Görüşmeler"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Sağ üste taşı"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Sol alta taşı"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Sağ alta taşı"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Baloncuğu kapat"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Görüşmeyi baloncuk olarak görüntüleme"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Baloncukları kullanarak sohbet edin"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Yeni görüşmeler kayan simgeler veya baloncuk olarak görünür. Açmak için baloncuğa dokunun. Baloncuğu taşımak için sürükleyin."</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 4c39f43a8845..b163c0c49dfe 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -516,7 +516,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Очистити все"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Керувати"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Історія"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Нові"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Нові"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Без звуку"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Сповіщення"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Розмови"</string>
@@ -1012,8 +1012,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Перемістити праворуч угору"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Перемістити ліворуч униз"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Перемістити праворуч униз"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Закрити підказку"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Не показувати спливаючі чати для розмов"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Спливаючий чат"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Нові повідомлення чату з\'являються у вигляді спливаючих значків. Щоб відкрити чат, натисніть його, а щоб перемістити – перетягніть."</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index feb894a56eb0..8246048c51fa 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -510,7 +510,8 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"سبھی کو صاف کریں"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"نظم کریں"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"سرگزشت"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"اِن کمنگ"</string>
+ <!-- no translation found for notification_section_header_incoming (850925217908095197) -->
+ <skip />
<string name="notification_section_header_gentle" msgid="6804099527336337197">"خاموش"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"اطلاعات"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"گفتگوئیں"</string>
@@ -1002,8 +1003,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"اوپر دائیں جانب لے جائيں"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"نیچے بائیں جانب لے جائیں"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"نیچے دائیں جانب لے جائیں"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"بلبلہ برخاست کریں"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"بلبلہ گفتگو نہ کریں"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"بلبلے کے ذریعے چیٹ کریں"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"نئی گفتگوئیں فلوٹنگ آئیکن یا بلبلے کے طور پر ظاہر ہوں گی۔ بلبلہ کھولنے کے لیے تھپتھپائیں۔ اسے منتقل کرنے کے لیے گھسیٹیں۔"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 1145fa7b92df..3099270c0d80 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Hammasini tozalash"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Boshqarish"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Tarix"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Kiruvchi"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Yangi"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Sokin"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Bildirishnomalar"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Suhbatlar"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Yuqori oʻngga surish"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Quyi chapga surish"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Quyi oʻngga surish"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Pufakni yopish"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Suhbatlar bulutchalar shaklida chiqmasin"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Bulutchalar yordamida subhatlashish"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Yangi xabarlar qalqib chiquvchi belgilar yoki bulutchalar kabi chiqadi. Xabarni ochish uchun bildirishnoma ustiga bosing. Xabarni qayta joylash uchun bildirishnomani suring."</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 8cd238c8529a..ab564864c737 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Xóa tất cả"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Quản lý"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Lịch sử"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Hiển thị gần đây"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Mới"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Im lặng"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Thông báo"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Cuộc trò chuyện"</string>
@@ -1002,11 +1002,10 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Chuyển lên trên cùng bên phải"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Chuyển tới dưới cùng bên trái"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Chuyển tới dưới cùng bên phải"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Đóng bong bóng"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Dừng trò chuyện bằng bong bóng"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Trò chuyện bằng bong bóng trò chuyện"</string>
- <string name="bubbles_user_education_description" msgid="1160281719576715211">"Các cuộc trò chuyện mới hiển thị dưới dạng biểu tượng nổi hoặc bong bóng trò chuyện. Nhấn để mở bong bóng trò chuyện. Kéo để di chuyển bong bóng trò chuyện."</string>
+ <string name="bubbles_user_education_description" msgid="1160281719576715211">"Các cuộc trò chuyện mới sẽ xuất hiện dưới dạng biểu tượng nổi hoặc bong bóng trò chuyện. Nhấn để mở bong bóng trò chuyện. Kéo để di chuyển bong bóng trò chuyện."</string>
<string name="bubbles_user_education_manage_title" msgid="2848511858160342320">"Kiểm soát tùy chọn cài đặt bong bóng trò chuyện bất mọi lúc"</string>
<string name="bubbles_user_education_manage" msgid="1391639189507036423">"Nhấn vào nút Quản lý để tắt bong bóng trò chuyện từ ứng dụng này"</string>
<string name="bubbles_user_education_got_it" msgid="8282812431953161143">"OK"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 86f43c9d55f0..73fac041f446 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"全部清除"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"管理"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"历史记录"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"收到的通知"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"新"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"静音"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"通知"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"对话"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"移至右上角"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"移至左下角"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"移至右下角"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"关闭对话泡"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"不以对话泡形式显示对话"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"使用对话泡聊天"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"新对话会以浮动图标或对话泡形式显示。点按即可打开对话泡。拖动即可移动对话泡。"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 6d9ee9913981..5f5c85fcc8ef 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"全部清除"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"管理"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"記錄"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"收到的通知"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"新"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"靜音"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"通知"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"對話"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"移去右上角"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"移去左下角"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"移去右下角"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"關閉對話氣泡"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"不要透過小視窗顯示對話"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"使用小視窗進行即時通訊"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"新對話會以浮動圖示 (小視窗) 顯示。輕按即可開啟小視窗。拖曳即可移動小視窗。"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 11e2be069a2f..84611483e883 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"全部清除"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"管理"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"記錄"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"收到的通知"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"最新"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"靜音"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"通知"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"對話"</string>
@@ -1002,10 +1002,9 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"移至右上方"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"移至左下方"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"移至右下方"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"關閉對話框"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"不要以對話框形式顯示對話"</string>
- <string name="bubbles_user_education_title" msgid="5547017089271445797">"使用對話框進行即時通訊"</string>
+ <string name="bubbles_user_education_title" msgid="5547017089271445797">"透過對話框來聊天"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"新的對話會以浮動圖示或對話框形式顯示。輕觸即可開啟對話框,拖曳則可移動對話框。"</string>
<string name="bubbles_user_education_manage_title" msgid="2848511858160342320">"你隨時可以控管對話框的各項設定"</string>
<string name="bubbles_user_education_manage" msgid="1391639189507036423">"輕觸 [管理] 即可關閉來自這個應用程式的對話框"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 358792161575..9b76660e6cd3 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -510,7 +510,7 @@
<string name="clear_all_notifications_text" msgid="348312370303046130">"Sula konke"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Phatha"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Umlando"</string>
- <string name="notification_section_header_incoming" msgid="5295312809341711367">"Okungenayo"</string>
+ <string name="notification_section_header_incoming" msgid="850925217908095197">"Okusha"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Kuthulile"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Izaziso"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Izingxoxo"</string>
@@ -1002,8 +1002,7 @@
<string name="bubble_accessibility_action_move_top_right" msgid="6916868852433483569">"Hambisa phezulu ngakwesokudla"</string>
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Hambisa inkinobho ngakwesokunxele"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Hambisa inkinobho ngakwesokudla"</string>
- <!-- no translation found for bubble_dismiss_text (1314082410868930066) -->
- <skip />
+ <string name="bubble_dismiss_text" msgid="1314082410868930066">"Cashisa ibhamuza"</string>
<string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Ungayibhamuzi ingxoxo"</string>
<string name="bubbles_user_education_title" msgid="5547017089271445797">"Xoxa usebenzisa amabhamuza"</string>
<string name="bubbles_user_education_description" msgid="1160281719576715211">"Izingxoxo ezintsha zivela njengezithonjana ezintantayo, noma amabhamuza. Thepha ukuze uvule ibhamuza. Hudula ukuze ulihambise."</string>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 837627c02638..4fc904b21741 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -202,6 +202,10 @@
<color name="global_screenshot_dismiss_foreground">@color/GM2_grey_500</color>
<color name="global_screenshot_background_protection_start">#40000000</color> <!-- 25% black -->
+ <!-- Bubbles -->
+ <color name="bubbles_pointer_light">#FFFFFF</color>
+ <color name="bubbles_pointer_dark">@color/GM2_grey_800</color>
+
<!-- GM2 colors -->
<color name="GM2_grey_50">#F8F9FA</color>
<color name="GM2_grey_100">#F1F3F4</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 73d8e9a0d8a7..44d3b02f0db7 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -631,10 +631,10 @@
<dimen name="notification_section_divider_height">@dimen/notification_side_paddings</dimen>
<!-- Size of the face pile shown on one-line (children of a group) conversation notifications -->
- <dimen name="conversation_single_line_face_pile_size">36dp</dimen>
+ <dimen name="conversation_single_line_face_pile_size">25dp</dimen>
<!-- Size of an avatar shown on one-line (children of a group) conversation notifications -->
- <dimen name="conversation_single_line_avatar_size">24dp</dimen>
+ <dimen name="conversation_single_line_avatar_size">20dp</dimen>
<!-- Border width for avatars in the face pile shown on one-line (children of a group) conversation notifications -->
<dimen name="conversation_single_line_face_pile_protection_width">1dp</dimen>
@@ -1222,10 +1222,6 @@
<dimen name="bubble_dismiss_slop">16dp</dimen>
<!-- Height of button allowing users to adjust settings for bubbles. -->
<dimen name="bubble_manage_button_height">48dp</dimen>
- <!-- How far, horizontally, to animate the expanded view over when animating in/out. -->
- <dimen name="bubble_expanded_animate_x_distance">100dp</dimen>
- <!-- How far, vertically, to animate the expanded view over when animating in/out. -->
- <dimen name="bubble_expanded_animate_y_distance">500dp</dimen>
<!-- Max width of the message bubble-->
<dimen name="bubble_message_max_width">144dp</dimen>
<!-- Min width of the message bubble -->
@@ -1252,7 +1248,7 @@
<!-- Bubbles user education views -->
<dimen name="bubbles_manage_education_width">160dp</dimen>
<!-- The inset from the top bound of the manage button to place the user education. -->
- <dimen name="bubbles_manage_education_top_inset">10dp</dimen>
+ <dimen name="bubbles_manage_education_top_inset">65dp</dimen>
<!-- Size of padding for the user education cling, this should at minimum be larger than
individual_bubble_size + some padding. -->
<dimen name="bubble_stack_user_education_side_inset">72dp</dimen>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 39f78bf46028..8097c01cb042 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -549,6 +549,13 @@
<style name="TextAppearance.NotificationImportanceChannelGroup">
<item name="android:textSize">@dimen/notification_importance_channel_group_text</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textColor">@color/notification_guts_header_text_color</item>
+ <item name="android:textSize">@dimen/notification_importance_channel_group_text</item>
+ </style>
+
+ <style name="TextAppearance.NotificationImportanceApp">
+ <item name="android:textSize">@dimen/notification_importance_channel_group_text</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:textColor">@color/notification_guts_sub_text_color</item>
<item name="android:textSize">@dimen/notification_importance_channel_group_text</item>
</style>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/BlurUtils.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/BlurUtils.java
new file mode 100644
index 000000000000..9f26d851f775
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/BlurUtils.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.shared.system;
+
+import android.app.ActivityManager;
+import android.os.SystemProperties;
+
+public abstract class BlurUtils {
+
+ private static boolean mBlurSupportedSysProp = SystemProperties
+ .getBoolean("ro.surface_flinger.supports_background_blur", false);
+ private static boolean mBlurDisabledSysProp = SystemProperties
+ .getBoolean("persist.sys.sf.disable_blurs", false);
+
+ /**
+ * If this device can render blurs.
+ *
+ * @return {@code true} when supported.
+ */
+ public static boolean supportsBlursOnWindows() {
+ return mBlurSupportedSysProp && !mBlurDisabledSysProp && ActivityManager.isHighEndGfx();
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
index ebed1fcdb48b..27e4c85e1e02 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
@@ -63,8 +63,9 @@ public class InputConsumerController {
*/
private final class InputEventReceiver extends BatchedInputEventReceiver {
- public InputEventReceiver(InputChannel inputChannel, Looper looper) {
- super(inputChannel, looper, Choreographer.getInstance());
+ InputEventReceiver(InputChannel inputChannel, Looper looper,
+ Choreographer choreographer) {
+ super(inputChannel, looper, choreographer);
}
@Override
@@ -143,6 +144,14 @@ public class InputConsumerController {
* Registers the input consumer.
*/
public void registerInputConsumer() {
+ registerInputConsumer(false);
+ }
+
+ /**
+ * Registers the input consumer.
+ * @param withSfVsync the flag set using sf vsync signal or no
+ */
+ public void registerInputConsumer(boolean withSfVsync) {
if (mInputEventReceiver == null) {
final InputChannel inputChannel = new InputChannel();
try {
@@ -152,7 +161,8 @@ public class InputConsumerController {
} catch (RemoteException e) {
Log.e(TAG, "Failed to create input consumer", e);
}
- mInputEventReceiver = new InputEventReceiver(inputChannel, Looper.myLooper());
+ mInputEventReceiver = new InputEventReceiver(inputChannel, Looper.myLooper(),
+ withSfVsync ? Choreographer.getSfInstance() : Choreographer.getInstance());
if (mRegistrationListener != null) {
mRegistrationListener.onRegistrationChanged(true /* isRegistered */);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index ee31706c0b94..34c85877577b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1903,6 +1903,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private boolean shouldListenForFingerprint() {
final boolean allowedOnBouncer =
!(mFingerprintLockedOut && mBouncer && mCredentialAttempted);
+ final int user = getCurrentUser();
+ final int strongAuth = mStrongAuthTracker.getStrongAuthForUser(user);
+ final boolean isLockDown =
+ containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW)
+ || containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+ final boolean isEncrypted = containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT);
// Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
// instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
@@ -1911,7 +1917,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
shouldListenForFingerprintAssistant() || (mKeyguardOccluded && mIsDreaming))
&& !mSwitchingUser && !isFingerprintDisabled(getCurrentUser())
&& (!mKeyguardGoingAway || !mDeviceInteractive) && mIsPrimaryUser
- && allowedOnBouncer;
+ && allowedOnBouncer && !isLockDown && !isEncrypted;
return shouldListen;
}
@@ -1928,9 +1934,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
final boolean isLockDown =
containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW)
|| containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
- final boolean isEncryptedOrTimedOut =
- containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT)
- || containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT);
+ final boolean isEncrypted =
+ containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT);
+ final boolean isTimedOut =
+ containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT);
boolean canBypass = mKeyguardBypassController != null
&& mKeyguardBypassController.canBypass();
@@ -1939,10 +1946,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
// TrustAgents or biometrics are keeping the device unlocked.
boolean becauseCannotSkipBouncer = !getUserCanSkipBouncer(user) || canBypass;
- // Scan even when encrypted or timeout to show a preemptive bouncer when bypassing.
+ // Scan even when timeout to show a preemptive bouncer when bypassing.
// Lock-down mode shouldn't scan, since it is more explicit.
- boolean strongAuthAllowsScanning = (!isEncryptedOrTimedOut || canBypass && !mBouncer)
- && !isLockDown;
+ boolean strongAuthAllowsScanning = (!isTimedOut || canBypass && !mBouncer);
// Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
// instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
@@ -1952,7 +1958,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
&& !mSwitchingUser && !isFaceDisabled(user) && becauseCannotSkipBouncer
&& !mKeyguardGoingAway && mFaceSettingEnabledForUser.get(user) && !mLockIconPressed
&& strongAuthAllowsScanning && mIsPrimaryUser
- && !mSecureCameraLaunched;
+ && !mSecureCameraLaunched && !isLockDown && !isEncrypted;
// Aggregate relevant fields for debug logging.
if (DEBUG_FACE || DEBUG_SPEW) {
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index ccb506de6d8b..0218cd237037 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -124,7 +124,7 @@ public final class Prefs {
String HAS_SEEN_BUBBLES_MANAGE_EDUCATION = "HasSeenBubblesManageOnboarding";
String CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT = "ControlsStructureSwipeTooltipCount";
/** Tracks whether the user has seen the onboarding screen for priority conversations */
- String HAS_SEEN_PRIORITY_ONBOARDING = "HaveShownPriorityOnboarding";
+ String HAS_SEEN_PRIORITY_ONBOARDING = "HasUserSeenPriorityOnboarding";
}
public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) {
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
index 4269605cef12..67c0c620f136 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
@@ -16,8 +16,10 @@
package com.android.systemui.broadcast
+import android.app.ActivityManager
import android.content.BroadcastReceiver
import android.content.Context
+import android.content.Intent
import android.content.IntentFilter
import android.os.Handler
import android.os.HandlerExecutor
@@ -28,15 +30,11 @@ import android.text.TextUtils
import android.util.SparseArray
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.Dumpable
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
import com.android.systemui.dump.DumpManager
import java.io.FileDescriptor
import java.io.PrintWriter
-import java.lang.IllegalStateException
import java.util.concurrent.Executor
-import javax.inject.Inject
-import javax.inject.Singleton
data class ReceiverData(
val receiver: BroadcastReceiver,
@@ -48,6 +46,8 @@ data class ReceiverData(
private const val MSG_ADD_RECEIVER = 0
private const val MSG_REMOVE_RECEIVER = 1
private const val MSG_REMOVE_RECEIVER_FOR_USER = 2
+private const val MSG_USER_SWITCH = 3
+private const val MSG_SET_STARTING_USER = 99
private const val TAG = "BroadcastDispatcher"
private const val DEBUG = true
@@ -62,20 +62,27 @@ private const val DEBUG = true
* permissions, schemes, data types, data authorities or priority different than 0.
* Cannot be used for getting sticky broadcasts (either as return of registering or as re-delivery).
*/
-@Singleton
-open class BroadcastDispatcher @Inject constructor (
+open class BroadcastDispatcher constructor (
private val context: Context,
- @Main private val mainHandler: Handler,
- @Background private val bgLooper: Looper,
- dumpManager: DumpManager
-) : Dumpable {
+ private val bgLooper: Looper,
+ private val dumpManager: DumpManager,
+ private val logger: BroadcastDispatcherLogger
+) : Dumpable, BroadcastReceiver() {
// Only modify in BG thread
private val receiversByUser = SparseArray<UserBroadcastDispatcher>(20)
- init {
- // TODO: Don't do this in the constructor
+ fun initialize() {
dumpManager.registerDumpable(javaClass.name, this)
+ handler.sendEmptyMessage(MSG_SET_STARTING_USER)
+ registerReceiver(this, IntentFilter(Intent.ACTION_USER_SWITCHED), null, UserHandle.ALL)
+ }
+
+ override fun onReceive(context: Context, intent: Intent) {
+ if (intent.action == Intent.ACTION_USER_SWITCHED) {
+ val user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL)
+ handler.obtainMessage(MSG_USER_SWITCH, user, 0).sendToTarget()
+ }
}
/**
@@ -87,7 +94,7 @@ open class BroadcastDispatcher @Inject constructor (
* have at least one action.
* @param handler A handler to dispatch [BroadcastReceiver.onReceive].
* @param user A user handle to determine which broadcast should be dispatched to this receiver.
- * By default, it is the current user.
+ * By default, it is the user of the context (system user in SystemUI).
* @throws IllegalArgumentException if the filter has other constraints that are not actions or
* categories or the filter has no actions.
*/
@@ -113,7 +120,7 @@ open class BroadcastDispatcher @Inject constructor (
* @param executor An executor to dispatch [BroadcastReceiver.onReceive]. Pass null to use an
* executor in the main thread (default).
* @param user A user handle to determine which broadcast should be dispatched to this receiver.
- * By default, it is the current user.
+ * By default, it is the user of the context (system user in SystemUI).
* @throws IllegalArgumentException if the filter has other constraints that are not actions or
* categories or the filter has no actions.
*/
@@ -156,7 +163,7 @@ open class BroadcastDispatcher @Inject constructor (
/**
* Unregister receiver for a particular user.
*
- * @param receiver The receiver to unregister. It will be unregistered for all users.
+ * @param receiver The receiver to unregister.
* @param user The user associated to the registered [receiver]. It can be [UserHandle.ALL].
*/
open fun unregisterReceiverForUser(receiver: BroadcastReceiver, user: UserHandle) {
@@ -166,10 +173,11 @@ open class BroadcastDispatcher @Inject constructor (
@VisibleForTesting
protected open fun createUBRForUser(userId: Int) =
- UserBroadcastDispatcher(context, userId, bgLooper)
+ UserBroadcastDispatcher(context, userId, bgLooper, logger)
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
pw.println("Broadcast dispatcher:")
+ pw.println(" Current user: ${handler.currentUser}")
for (index in 0 until receiversByUser.size()) {
pw.println(" User ${receiversByUser.keyAt(index)}")
receiversByUser.valueAt(index).dump(fd, pw, args)
@@ -177,6 +185,8 @@ open class BroadcastDispatcher @Inject constructor (
}
private val handler = object : Handler(bgLooper) {
+ var currentUser = UserHandle.USER_SYSTEM
+
override fun handleMessage(msg: Message) {
when (msg.what) {
MSG_ADD_RECEIVER -> {
@@ -184,7 +194,7 @@ open class BroadcastDispatcher @Inject constructor (
// If the receiver asked to be registered under the current user, we register
// under the actual current user.
val userId = if (data.user.identifier == UserHandle.USER_CURRENT) {
- context.userId
+ currentUser
} else {
data.user.identifier
}
@@ -207,6 +217,13 @@ open class BroadcastDispatcher @Inject constructor (
receiversByUser.get(msg.arg1)?.unregisterReceiver(msg.obj as BroadcastReceiver)
}
+ MSG_USER_SWITCH -> {
+ currentUser = msg.arg1
+ }
+ MSG_SET_STARTING_USER -> {
+ currentUser = ActivityManager.getCurrentUser()
+ }
+
else -> super.handleMessage(msg)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt
index 3272fb7545e2..96f5a1f6fbe8 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt
@@ -30,6 +30,7 @@ import android.util.Log
import androidx.annotation.VisibleForTesting
import com.android.internal.util.Preconditions
import com.android.systemui.Dumpable
+import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
import java.io.FileDescriptor
import java.io.PrintWriter
import java.lang.IllegalArgumentException
@@ -54,7 +55,8 @@ private const val DEBUG = false
class UserBroadcastDispatcher(
private val context: Context,
private val userId: Int,
- private val bgLooper: Looper
+ private val bgLooper: Looper,
+ private val logger: BroadcastDispatcherLogger
) : BroadcastReceiver(), Dumpable {
companion object {
@@ -109,10 +111,12 @@ class UserBroadcastDispatcher(
}
override fun onReceive(context: Context, intent: Intent) {
- val id = if (DEBUG) index.getAndIncrement() else 0
+ val id = index.getAndIncrement()
if (DEBUG) Log.w(TAG, "[$id] Received $intent")
+ logger.logBroadcastReceived(id, userId, intent)
bgHandler.post(
- HandleBroadcastRunnable(actionsToReceivers, context, intent, pendingResult, id))
+ HandleBroadcastRunnable(
+ actionsToReceivers, context, intent, pendingResult, id, logger))
}
/**
@@ -143,6 +147,7 @@ class UserBroadcastDispatcher(
ArraySet()
}.add(receiverData)
}
+ logger.logReceiverRegistered(userId, receiverData.receiver)
if (changed) {
createFilterAndRegisterReceiverBG()
}
@@ -163,6 +168,7 @@ class UserBroadcastDispatcher(
actionsToReceivers.remove(action)
}
}
+ logger.logReceiverUnregistered(userId, receiver)
if (changed) {
createFilterAndRegisterReceiverBG()
}
@@ -187,7 +193,8 @@ class UserBroadcastDispatcher(
val context: Context,
val intent: Intent,
val pendingResult: PendingResult,
- val index: Int
+ val index: Int,
+ val logger: BroadcastDispatcherLogger
) : Runnable {
override fun run() {
if (DEBUG) Log.w(TAG, "[$index] Dispatching $intent")
@@ -199,6 +206,7 @@ class UserBroadcastDispatcher(
it.executor.execute {
if (DEBUG) Log.w(TAG,
"[$index] Dispatching ${intent.action} to ${it.receiver}")
+ logger.logBroadcastDispatched(index, intent.action, it.receiver)
it.receiver.pendingResult = pendingResult
it.receiver.onReceive(context, intent)
}
@@ -215,6 +223,7 @@ class UserBroadcastDispatcher(
if (registered.get()) {
try {
context.unregisterReceiver(this@UserBroadcastDispatcher)
+ logger.logContextReceiverUnregistered(userId)
} catch (e: IllegalArgumentException) {
Log.e(TAG, "Trying to unregister unregistered receiver for user $userId",
IllegalStateException(e))
@@ -230,6 +239,7 @@ class UserBroadcastDispatcher(
null,
bgHandler)
registered.set(true)
+ logger.logContextReceiverRegistered(userId, intentFilter)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt b/packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt
new file mode 100644
index 000000000000..123a8ae6307d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.broadcast.logging
+
+import android.content.BroadcastReceiver
+import android.content.Intent
+import android.content.IntentFilter
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.LogLevel.DEBUG
+import com.android.systemui.log.LogLevel.INFO
+import com.android.systemui.log.LogMessage
+import com.android.systemui.log.dagger.BroadcastDispatcherLog
+import javax.inject.Inject
+
+private const val TAG = "BroadcastDispatcherLog"
+
+class BroadcastDispatcherLogger @Inject constructor(
+ @BroadcastDispatcherLog private val buffer: LogBuffer
+) {
+
+ fun logBroadcastReceived(broadcastId: Int, user: Int, intent: Intent) {
+ val intentString = intent.toString()
+ log(INFO, {
+ int1 = broadcastId
+ int2 = user
+ str1 = intentString
+ }, {
+ "[$int1] Broadcast received for user $int2: $str1"
+ })
+ }
+
+ fun logBroadcastDispatched(broadcastId: Int, action: String?, receiver: BroadcastReceiver) {
+ val receiverString = receiver.toString()
+ log(DEBUG, {
+ int1 = broadcastId
+ str1 = action
+ str2 = receiverString
+ }, {
+ "Broadcast $int1 ($str1) dispatched to $str2"
+ })
+ }
+
+ fun logReceiverRegistered(user: Int, receiver: BroadcastReceiver) {
+ val receiverString = receiver.toString()
+ log(INFO, {
+ int1 = user
+ str1 = receiverString
+ }, {
+ "Receiver $str1 registered for user $int1"
+ })
+ }
+
+ fun logReceiverUnregistered(user: Int, receiver: BroadcastReceiver) {
+ val receiverString = receiver.toString()
+ log(INFO, {
+ int1 = user
+ str1 = receiverString
+ }, {
+ "Receiver $str1 unregistered for user $int1"
+ })
+ }
+
+ fun logContextReceiverRegistered(user: Int, filter: IntentFilter) {
+ val actions = filter.actionsIterator().asSequence()
+ .joinToString(separator = ",", prefix = "Actions(", postfix = ")")
+ val categories = if (filter.countCategories() != 0) {
+ filter.categoriesIterator().asSequence()
+ .joinToString(separator = ",", prefix = "Categories(", postfix = ")")
+ } else {
+ ""
+ }
+ log(INFO, {
+ int1 = user
+ str1 = if (categories != "") {
+ "${actions}\n$categories"
+ } else {
+ actions
+ }
+ }, {
+ """
+ Receiver registered with Context for user $int1.
+ $str1
+ """.trimIndent()
+ })
+ }
+
+ fun logContextReceiverUnregistered(user: Int) {
+ log(INFO, {
+ int1 = user
+ }, {
+ "Receiver unregistered with Context for user $int1."
+ })
+ }
+
+ private inline fun log(
+ logLevel: LogLevel,
+ initializer: LogMessage.() -> Unit,
+ noinline printer: LogMessage.() -> String
+ ) {
+ buffer.log(TAG, logLevel, initializer, printer)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 18d07f07fcc4..c4c5da42ec06 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -55,7 +55,6 @@ import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
-import android.graphics.Rect;
import android.os.Binder;
import android.os.Handler;
import android.os.RemoteException;
@@ -180,6 +179,9 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
// Callback that updates BubbleOverflowActivity on data change.
@Nullable private Runnable mOverflowCallback = null;
+ // Only load overflow data from disk once
+ private boolean mOverflowDataLoaded = false;
+
private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private IStatusBarService mBarService;
private WindowManager mWindowManager;
@@ -193,9 +195,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
/** Whether or not the BubbleStackView has been added to the WindowManager. */
private boolean mAddedToWindowManager = false;
- // Used for determining view rect for touch interaction
- private Rect mTempRect = new Rect();
-
// Listens to user switch so bubbles can be saved and restored.
private final NotificationLockscreenUserManager mNotifUserManager;
@@ -419,6 +418,16 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
mCallbacks.add(callback);
}
+ /**
+ * Dispatches a back press into the expanded Bubble's ActivityView if its IME is visible,
+ * causing it to hide.
+ */
+ public void hideImeFromExpandedBubble() {
+ if (mStackView != null) {
+ mStackView.hideImeFromExpandedBubble();
+ }
+ }
+
private void setupNEM() {
mNotificationEntryManager.addNotificationEntryListener(
new NotificationEntryListener() {
@@ -705,6 +714,9 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
* the new params if the stack has been added.
*/
private void updateWmFlags() {
+ if (mStackView == null) {
+ return;
+ }
if (isStackExpanded() && !mImeVisible) {
// If we're expanded, and the IME isn't visible, we want to be focusable. This ensures
// that any taps within Bubbles (including on the ActivityView) results in Bubbles
@@ -716,7 +728,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
mWmLayoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
}
- if (mStackView != null && mAddedToWindowManager) {
+ if (mAddedToWindowManager) {
try {
mWindowManager.updateViewLayout(mStackView, mWmLayoutParams);
} catch (IllegalArgumentException e) {
@@ -901,7 +913,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
mLogger.log(bubble, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_BACK_TO_STACK);
bubble.setInflateSynchronously(mInflateSynchronously);
bubble.setShouldAutoExpand(true);
- bubble.markUpdatedAt(System.currentTimeMillis());
+ bubble.markAsAccessedAt(System.currentTimeMillis());
setIsBubble(bubble, true /* isBubble */);
}
@@ -952,13 +964,14 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
* Fills the overflow bubbles by loading them from disk.
*/
void loadOverflowBubblesFromDisk() {
- if (!mBubbleData.getOverflowBubbles().isEmpty()) {
+ if (!mBubbleData.getOverflowBubbles().isEmpty() || mOverflowDataLoaded) {
// we don't need to load overflow bubbles from disk if it is already in memory
return;
}
+ mOverflowDataLoaded = true;
mDataRepository.loadBubbles((bubbles) -> {
bubbles.forEach(bubble -> {
- if (mBubbleData.getBubbles().contains(bubble)) {
+ if (mBubbleData.hasAnyBubbleWithKey(bubble.getKey())) {
// if the bubble is already active, there's no need to push it to overflow
return;
}
@@ -1156,7 +1169,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
if (b.getEntry() != null) {
// Updating the entry to be a bubble will trigger our normal update flow
setIsBubble(b.getEntry(), isBubble, b.shouldAutoExpand());
- } else {
+ } else if (isBubble) {
// If we have no entry to update, it's a persisted bubble so
// we need to add it to the stack ourselves
Bubble bubble = mBubbleData.getOrCreateBubble(null, b /* persistedBubble */);
@@ -1214,7 +1227,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
}
} else {
- if (bubble.isBubble() && bubble.showInShade()) {
+ if (bubble.isBubble()) {
setIsBubble(bubble, false /* isBubble */);
}
if (bubble.getEntry() != null && bubble.getEntry().getRow() != null) {
@@ -1235,24 +1248,23 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
mDataRepository.removeBubbles(mCurrentUserId, bubblesToBeRemovedFromRepository);
- if (update.addedBubble != null) {
+ if (update.addedBubble != null && mStackView != null) {
mDataRepository.addBubble(mCurrentUserId, update.addedBubble);
mStackView.addBubble(update.addedBubble);
-
}
- if (update.updatedBubble != null) {
+ if (update.updatedBubble != null && mStackView != null) {
mStackView.updateBubble(update.updatedBubble);
}
// At this point, the correct bubbles are inflated in the stack.
// Make sure the order in bubble data is reflected in bubble row.
- if (update.orderChanged) {
+ if (update.orderChanged && mStackView != null) {
mDataRepository.addBubbles(mCurrentUserId, update.bubbles);
mStackView.updateBubbleOrder(update.bubbles);
}
- if (update.selectionChanged) {
+ if (update.selectionChanged && mStackView != null) {
mStackView.setSelectedBubble(update.selectedBubble);
if (update.selectedBubble != null && update.selectedBubble.getEntry() != null) {
mNotificationGroupManager.updateSuppression(
@@ -1262,7 +1274,9 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
// Expanding? Apply this last.
if (update.expandedChanged && update.expanded) {
- mStackView.setExpanded(true);
+ if (mStackView != null) {
+ mStackView.setExpanded(true);
+ }
}
for (NotifCallback cb : mCallbacks) {
@@ -1368,7 +1382,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
/**
- * Lets any listeners know if bubble state has changed.
* Updates the visibility of the bubbles based on current state.
* Does not un-bubble, just hides or un-hides.
* Updates stack description for TalkBack focus.
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index 24d44d5cb291..20a9a8cf324c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -417,7 +417,8 @@ public class BubbleData {
if (mBubbles.size() == 1) {
// Going to become empty, handle specially.
setExpandedInternal(false);
- setSelectedBubbleInternal(null);
+ // Don't use setSelectedBubbleInternal because we don't want to trigger an applyUpdate
+ mSelectedBubble = null;
}
if (indexToRemove < mBubbles.size() - 1) {
// Removing anything but the last bubble means positions will change.
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index db7980251daf..6dcc9dcdc63c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -22,6 +22,7 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.graphics.PixelFormat.TRANSPARENT;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.InsetsState.ITYPE_IME;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.ViewRootImpl.sNewInsetsMode;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
@@ -33,7 +34,6 @@ import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_EXPAND
import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
-import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
@@ -42,10 +42,12 @@ import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Insets;
+import android.graphics.Outline;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.ShapeDrawable;
@@ -55,12 +57,19 @@ import android.os.RemoteException;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
+import android.view.SurfaceControl;
+import android.view.SurfaceView;
import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.FrameLayout;
import android.widget.LinearLayout;
+import androidx.annotation.Nullable;
+
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.systemui.Dependency;
import com.android.systemui.R;
@@ -88,6 +97,7 @@ public class BubbleExpandedView extends LinearLayout {
// The triangle pointing to the expanded view
private View mPointerView;
private int mPointerMargin;
+ @Nullable private int[] mExpandedViewContainerLocation;
private AlphaOptimizedButton mSettingsIcon;
@@ -121,6 +131,16 @@ public class BubbleExpandedView extends LinearLayout {
private View mVirtualImeView;
private WindowManager mVirtualDisplayWindowManager;
private boolean mImeShowing = false;
+ private float mCornerRadius = 0f;
+
+ /**
+ * Container for the ActivityView that has a solid, round-rect background that shows if the
+ * ActivityView hasn't loaded.
+ */
+ private FrameLayout mActivityViewContainer = new FrameLayout(getContext());
+
+ /** The SurfaceView that the ActivityView draws to. */
+ @Nullable private SurfaceView mActivitySurface;
private ActivityView.StateCallback mStateCallback = new ActivityView.StateCallback() {
@Override
@@ -143,6 +163,11 @@ public class BubbleExpandedView extends LinearLayout {
Log.d(TAG, "onActivityViewReady: calling startActivity, "
+ "bubble=" + getBubbleKey());
}
+ if (mActivityView == null) {
+ mBubbleController.removeBubble(getBubbleKey(),
+ BubbleController.DISMISS_INVALID_INTENT);
+ return;
+ }
try {
if (!mIsOverflow && mBubble.usingShortcutInfo()) {
options.setApplyActivityFlagsForBubbles(true);
@@ -255,8 +280,6 @@ public class BubbleExpandedView extends LinearLayout {
mPointerDrawable = new ShapeDrawable(TriangleShape.create(
mPointerWidth, mPointerHeight, true /* pointUp */));
- mPointerDrawable.setTint(Color.WHITE);
- mPointerView.setBackground(mPointerDrawable);
mPointerView.setVisibility(INVISIBLE);
mSettingsIconHeight = getContext().getResources().getDimensionPixelSize(
@@ -269,7 +292,28 @@ public class BubbleExpandedView extends LinearLayout {
// Set ActivityView's alpha value as zero, since there is no view content to be shown.
setContentVisibility(false);
- addView(mActivityView);
+
+ mActivityViewContainer.setBackgroundColor(Color.WHITE);
+ mActivityViewContainer.setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mCornerRadius);
+ }
+ });
+ mActivityViewContainer.setClipToOutline(true);
+ mActivityViewContainer.addView(mActivityView);
+ mActivityViewContainer.setLayoutParams(
+ new ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
+ addView(mActivityViewContainer);
+
+ if (mActivityView != null
+ && mActivityView.getChildCount() > 0
+ && mActivityView.getChildAt(0) instanceof SurfaceView) {
+ // Retrieve the surface from the ActivityView so we can screenshot it and change its
+ // z-ordering. This should always be possible, since ActivityView's constructor adds the
+ // SurfaceView as its first child.
+ mActivitySurface = (SurfaceView) mActivityView.getChildAt(0);
+ }
// Expanded stack layout, top to bottom:
// Expanded view container
@@ -311,7 +355,10 @@ public class BubbleExpandedView extends LinearLayout {
// ActivityView's vertical bounds. These events are part of a back gesture, and so they
// should not collapse the stack (which all other touches on areas around the AV would
// do).
- if (motionEvent.getRawY() >= avBounds.top && motionEvent.getRawY() <= avBounds.bottom) {
+ if (motionEvent.getRawY() >= avBounds.top
+ && motionEvent.getRawY() <= avBounds.bottom
+ && (motionEvent.getRawX() < avBounds.left
+ || motionEvent.getRawX() > avBounds.right)) {
return true;
}
@@ -327,6 +374,39 @@ public class BubbleExpandedView extends LinearLayout {
return mBubble != null ? mBubble.getKey() : "null";
}
+ /**
+ * Asks the ActivityView's surface to draw on top of all other views in the window. This is
+ * useful for ordering surfaces during animations, but should otherwise be set to false so that
+ * bubbles and menus can draw over the ActivityView.
+ */
+ void setSurfaceZOrderedOnTop(boolean onTop) {
+ if (mActivitySurface == null) {
+ return;
+ }
+
+ mActivitySurface.setZOrderedOnTop(onTop, true);
+ }
+
+ /** Return a GraphicBuffer with the contents of the ActivityView's underlying surface. */
+ @Nullable SurfaceControl.ScreenshotGraphicBuffer snapshotActivitySurface() {
+ if (mActivitySurface == null) {
+ return null;
+ }
+
+ return SurfaceControl.captureLayers(
+ mActivitySurface.getSurfaceControl(),
+ new Rect(0, 0, mActivityView.getWidth(), mActivityView.getHeight()),
+ 1 /* scale */);
+ }
+
+ int[] getActivityViewLocationOnScreen() {
+ if (mActivityView != null) {
+ return mActivityView.getLocationOnScreen();
+ } else {
+ return new int[]{0, 0};
+ }
+ }
+
void setManageClickListener(OnClickListener manageClickListener) {
findViewById(R.id.settings_button).setOnClickListener(manageClickListener);
}
@@ -345,13 +425,25 @@ public class BubbleExpandedView extends LinearLayout {
void applyThemeAttrs() {
final TypedArray ta = mContext.obtainStyledAttributes(
new int[] {android.R.attr.dialogCornerRadius});
- float cornerRadius = ta.getDimensionPixelSize(0, 0);
+ mCornerRadius = ta.getDimensionPixelSize(0, 0);
ta.recycle();
if (mActivityView != null && ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
mContext.getResources())) {
- mActivityView.setCornerRadius(cornerRadius);
+ mActivityView.setCornerRadius(mCornerRadius);
+ }
+
+ final int mode =
+ getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+ switch (mode) {
+ case Configuration.UI_MODE_NIGHT_NO:
+ mPointerDrawable.setTint(getResources().getColor(R.color.bubbles_pointer_light));
+ break;
+ case Configuration.UI_MODE_NIGHT_YES:
+ mPointerDrawable.setTint(getResources().getColor(R.color.bubbles_pointer_dark));
+ break;
}
+ mPointerView.setBackground(mPointerDrawable);
}
/**
@@ -398,6 +490,7 @@ public class BubbleExpandedView extends LinearLayout {
mPointerView.setAlpha(alpha);
if (mActivityView != null) {
mActivityView.setAlpha(alpha);
+ mActivityView.bringToFront();
}
}
@@ -557,6 +650,11 @@ public class BubbleExpandedView extends LinearLayout {
if (DEBUG_BUBBLE_EXPANDED_VIEW) {
Log.d(TAG, "updateHeight: bubble=" + getBubbleKey());
}
+
+ if (mExpandedViewContainerLocation == null) {
+ return;
+ }
+
if (usingActivityView()) {
float desiredHeight = mOverflowHeight;
if (!mIsOverflow) {
@@ -564,7 +662,7 @@ public class BubbleExpandedView extends LinearLayout {
}
float height = Math.min(desiredHeight, getMaxExpandedHeight());
height = Math.max(height, mIsOverflow? mOverflowHeight : mMinHeight);
- LayoutParams lp = (LayoutParams) mActivityView.getLayoutParams();
+ ViewGroup.LayoutParams lp = mActivityView.getLayoutParams();
mNeedsNewHeight = lp.height != height;
if (!mKeyboardVisible) {
// If the keyboard is visible... don't adjust the height because that will cause
@@ -574,7 +672,8 @@ public class BubbleExpandedView extends LinearLayout {
mNeedsNewHeight = false;
}
if (DEBUG_BUBBLE_EXPANDED_VIEW) {
- Log.d(TAG, "updateHeight: bubble=" + getBubbleKey() + " height=" + height
+ Log.d(TAG, "updateHeight: bubble=" + getBubbleKey()
+ + " height=" + height
+ " mNeedsNewHeight=" + mNeedsNewHeight);
}
}
@@ -582,28 +681,40 @@ public class BubbleExpandedView extends LinearLayout {
private int getMaxExpandedHeight() {
mWindowManager.getDefaultDisplay().getRealSize(mDisplaySize);
- int[] windowLocation = mActivityView.getLocationOnScreen();
int bottomInset = getRootWindowInsets() != null
? getRootWindowInsets().getStableInsetBottom()
: 0;
- return mDisplaySize.y - windowLocation[1] - mSettingsIconHeight - mPointerHeight
+
+ return mDisplaySize.y
+ - mExpandedViewContainerLocation[1]
+ - getPaddingTop()
+ - getPaddingBottom()
+ - mSettingsIconHeight
+ - mPointerHeight
- mPointerMargin - bottomInset;
}
/**
* Update appearance of the expanded view being displayed.
+ *
+ * @param containerLocationOnScreen The location on-screen of the container the expanded view is
+ * added to. This allows us to calculate max height without
+ * waiting for layout.
*/
- public void updateView() {
+ public void updateView(int[] containerLocationOnScreen) {
if (DEBUG_BUBBLE_EXPANDED_VIEW) {
Log.d(TAG, "updateView: bubble="
+ getBubbleKey());
}
+
+ mExpandedViewContainerLocation = containerLocationOnScreen;
+
if (usingActivityView()
&& mActivityView.getVisibility() == VISIBLE
&& mActivityView.isAttachedToWindow()) {
mActivityView.onLocationChanged();
+ updateHeight();
}
- updateHeight();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleManageEducationView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleManageEducationView.java
index f4d64322c7ff..47120124a55f 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleManageEducationView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleManageEducationView.java
@@ -17,10 +17,8 @@
package com.android.systemui.bubbles;
import android.content.Context;
-import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
-import android.graphics.drawable.ShapeDrawable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;
@@ -28,7 +26,6 @@ import android.widget.TextView;
import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.R;
-import com.android.systemui.recents.TriangleShape;
/**
* Educational view to highlight the manage button that allows a user to configure the settings
@@ -36,7 +33,6 @@ import com.android.systemui.recents.TriangleShape;
*/
public class BubbleManageEducationView extends LinearLayout {
- private View mPointerView;
private View mManageView;
public BubbleManageEducationView(Context context) {
@@ -70,26 +66,8 @@ public class BubbleManageEducationView extends LinearLayout {
ta.recycle();
textColor = ContrastColorUtil.ensureTextContrast(textColor, bgColor, true);
+ ((TextView) findViewById(R.id.user_education_title)).setTextColor(textColor);
((TextView) findViewById(R.id.user_education_description)).setTextColor(textColor);
-
- final Resources res = getResources();
- final int pointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width);
- final int pointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
-
- ShapeDrawable triangleShape =
- new ShapeDrawable(TriangleShape.create(
- pointerWidth, pointerHeight, false /* isPointingUp */));
- triangleShape.setTint(bgColor);
-
- mPointerView = findViewById(R.id.user_education_pointer);
- mPointerView.setBackground(triangleShape);
- }
-
- /**
- * Specifies the x value this pointer should point to.
- */
- public void setPointerPosition(int x) {
- mPointerView.setTranslationX(x - (mPointerView.getWidth() / 2));
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
index b77e2261e39b..0c62e9f9f548 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
@@ -62,13 +62,12 @@ public class BubbleOverflow implements BubbleViewProvider {
void setUpOverflow(ViewGroup parentViewGroup, BubbleStackView stackView) {
updateDimensions();
-
mExpandedView = (BubbleExpandedView) mInflater.inflate(
R.layout.bubble_expanded_view, parentViewGroup /* root */,
false /* attachToRoot */);
mExpandedView.setOverflow(true);
mExpandedView.setStackView(stackView);
-
+ mExpandedView.applyThemeAttrs();
updateIcon(mContext, parentViewGroup);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index ba563466ca4e..d5fe9b2953de 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -56,6 +56,8 @@ import android.view.DisplayCutout;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
+import android.view.SurfaceControl;
+import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
@@ -82,6 +84,7 @@ import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.Interpolators;
import com.android.systemui.Prefs;
import com.android.systemui.R;
+import com.android.systemui.bubbles.animation.AnimatableScaleMatrix;
import com.android.systemui.bubbles.animation.ExpandedAnimationController;
import com.android.systemui.bubbles.animation.PhysicsAnimationLayout;
import com.android.systemui.bubbles.animation.StackAnimationController;
@@ -90,7 +93,6 @@ import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.util.DismissCircleView;
import com.android.systemui.util.FloatingContentCoordinator;
import com.android.systemui.util.RelativeTouchListener;
@@ -147,6 +149,16 @@ public class BubbleStackView extends FrameLayout
StackAnimationController.IME_ANIMATION_STIFFNESS,
StackAnimationController.DEFAULT_BOUNCINESS);
+ private final PhysicsAnimator.SpringConfig mScaleInSpringConfig =
+ new PhysicsAnimator.SpringConfig(300f, 0.9f);
+
+ private final PhysicsAnimator.SpringConfig mScaleOutSpringConfig =
+ new PhysicsAnimator.SpringConfig(900f, 1f);
+
+ private final PhysicsAnimator.SpringConfig mTranslateSpringConfig =
+ new PhysicsAnimator.SpringConfig(
+ SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_NO_BOUNCY);
+
/**
* Interface to synchronize {@link View} state and the screen.
*
@@ -186,8 +198,6 @@ public class BubbleStackView extends FrameLayout
private Point mDisplaySize;
- private final SpringAnimation mExpandedViewXAnim;
- private final SpringAnimation mExpandedViewYAnim;
private final BubbleData mBubbleData;
private final ValueAnimator mDesaturateAndDarkenAnimator;
@@ -199,6 +209,24 @@ public class BubbleStackView extends FrameLayout
private FrameLayout mExpandedViewContainer;
+ /** Matrix used to scale the expanded view container with a given pivot point. */
+ private final AnimatableScaleMatrix mExpandedViewContainerMatrix = new AnimatableScaleMatrix();
+
+ /**
+ * SurfaceView that we draw screenshots of animating-out bubbles into. This allows us to animate
+ * between bubble activities without needing both to be alive at the same time.
+ */
+ private SurfaceView mAnimatingOutSurfaceView;
+
+ /** Container for the animating-out SurfaceView. */
+ private FrameLayout mAnimatingOutSurfaceContainer;
+
+ /**
+ * Buffer containing a screenshot of the animating-out bubble. This is drawn into the
+ * SurfaceView during animations.
+ */
+ private SurfaceControl.ScreenshotGraphicBuffer mAnimatingOutBubbleBuffer;
+
private BubbleFlyoutView mFlyout;
/** Runnable that fades out the flyout and then sets it to GONE. */
private Runnable mHideFlyout = () -> animateFlyoutCollapsed(true, 0 /* velX */);
@@ -230,8 +258,7 @@ public class BubbleStackView extends FrameLayout
private int mBubblePaddingTop;
private int mBubbleTouchPadding;
private int mExpandedViewPadding;
- private int mExpandedAnimateXDistance;
- private int mExpandedAnimateYDistance;
+ private int mCornerRadius;
private int mPointerHeight;
private int mStatusBarHeight;
private int mImeOffset;
@@ -680,10 +707,6 @@ public class BubbleStackView extends FrameLayout
mBubbleElevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
mBubbleTouchPadding = res.getDimensionPixelSize(R.dimen.bubble_touch_padding);
- mExpandedAnimateXDistance =
- res.getDimensionPixelSize(R.dimen.bubble_expanded_animate_x_distance);
- mExpandedAnimateYDistance =
- res.getDimensionPixelSize(R.dimen.bubble_expanded_animate_y_distance);
mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
mStatusBarHeight =
@@ -698,6 +721,11 @@ public class BubbleStackView extends FrameLayout
mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding);
int elevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
+ final TypedArray ta = mContext.obtainStyledAttributes(
+ new int[] {android.R.attr.dialogCornerRadius});
+ mCornerRadius = ta.getDimensionPixelSize(0, 0);
+ ta.recycle();
+
final Runnable onBubbleAnimatedOut = () -> {
if (getBubbleCount() == 0) {
allBubblesAnimatedOutAction.run();
@@ -731,6 +759,24 @@ public class BubbleStackView extends FrameLayout
mExpandedViewContainer.setClipChildren(false);
addView(mExpandedViewContainer);
+ mAnimatingOutSurfaceContainer = new FrameLayout(getContext());
+ mAnimatingOutSurfaceContainer.setLayoutParams(
+ new ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
+ addView(mAnimatingOutSurfaceContainer);
+
+ mAnimatingOutSurfaceView = new SurfaceView(getContext());
+ mAnimatingOutSurfaceView.setUseAlpha();
+ mAnimatingOutSurfaceView.setZOrderOnTop(true);
+ mAnimatingOutSurfaceView.setCornerRadius(mCornerRadius);
+ mAnimatingOutSurfaceView.setLayoutParams(new ViewGroup.LayoutParams(0, 0));
+ mAnimatingOutSurfaceContainer.addView(mAnimatingOutSurfaceView);
+
+ mAnimatingOutSurfaceContainer.setPadding(
+ mExpandedViewPadding,
+ mExpandedViewPadding,
+ mExpandedViewPadding,
+ mExpandedViewPadding);
+
setUpManageMenu();
setUpFlyout();
@@ -776,26 +822,6 @@ public class BubbleStackView extends FrameLayout
// MagnetizedObjects.
mMagneticTarget = new MagnetizedObject.MagneticTarget(mDismissTargetCircle, dismissRadius);
- mExpandedViewXAnim =
- new SpringAnimation(mExpandedViewContainer, DynamicAnimation.TRANSLATION_X);
- mExpandedViewXAnim.setSpring(
- new SpringForce()
- .setStiffness(SpringForce.STIFFNESS_LOW)
- .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
-
- mExpandedViewYAnim =
- new SpringAnimation(mExpandedViewContainer, DynamicAnimation.TRANSLATION_Y);
- mExpandedViewYAnim.setSpring(
- new SpringForce()
- .setStiffness(SpringForce.STIFFNESS_LOW)
- .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
- mExpandedViewYAnim.addEndListener((anim, cancelled, value, velocity) -> {
- if (mIsExpanded && mExpandedBubble != null
- && mExpandedBubble.getExpandedView() != null) {
- mExpandedBubble.getExpandedView().updateView();
- }
- });
-
setClipChildren(false);
setFocusable(true);
mBubbleContainer.bringToFront();
@@ -830,7 +856,7 @@ public class BubbleStackView extends FrameLayout
if (mIsExpanded) {
mExpandedViewContainer.setTranslationY(getExpandedViewY());
if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
- mExpandedBubble.getExpandedView().updateView();
+ mExpandedBubble.getExpandedView().updateView(getLocationOnScreen());
}
}
@@ -954,15 +980,10 @@ public class BubbleStackView extends FrameLayout
PhysicsAnimator.getInstance(mManageMenu).setDefaultSpringConfig(mManageSpringConfig);
- final TypedArray ta = mContext.obtainStyledAttributes(
- new int[] {android.R.attr.dialogCornerRadius});
- final int menuCornerRadius = ta.getDimensionPixelSize(0, 0);
- ta.recycle();
-
mManageMenu.setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
- outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), menuCornerRadius);
+ outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mCornerRadius);
}
});
mManageMenu.setClipToOutline(true);
@@ -1078,13 +1099,11 @@ public class BubbleStackView extends FrameLayout
mBubbleOverflow.setUpOverflow(mBubbleContainer, this);
} else {
mBubbleContainer.removeView(mBubbleOverflow.getBtn());
- mBubbleOverflow.updateDimensions();
- mBubbleOverflow.updateIcon(mContext,this);
+ mBubbleOverflow.setUpOverflow(mBubbleContainer, this);
overflowBtnIndex = mBubbleContainer.getChildCount();
}
mBubbleContainer.addView(mBubbleOverflow.getBtn(), overflowBtnIndex,
new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
-
mBubbleOverflow.getBtn().setOnClickListener(v -> setSelectedBubble(mBubbleOverflow));
}
/**
@@ -1095,6 +1114,7 @@ public class BubbleStackView extends FrameLayout
setUpOverflow();
setUpUserEducation();
setUpManageMenu();
+ updateExpandedViewTheme();
}
/** Respond to the phone being rotated by repositioning the stack and hiding any flyouts. */
@@ -1196,6 +1216,18 @@ public class BubbleStackView extends FrameLayout
setupLocalMenu(info);
}
+ void updateExpandedViewTheme() {
+ final List<Bubble> bubbles = mBubbleData.getBubbles();
+ if (bubbles.isEmpty()) {
+ return;
+ }
+ bubbles.forEach(bubble -> {
+ if (bubble.getExpandedView() != null) {
+ bubble.getExpandedView().applyThemeAttrs();
+ }
+ });
+ }
+
void setupLocalMenu(AccessibilityNodeInfo info) {
Resources res = mContext.getResources();
@@ -1447,6 +1479,31 @@ public class BubbleStackView extends FrameLayout
mBubbleData.setShowingOverflow(true);
}
+ // If we're expanded, screenshot the currently expanded bubble (before expanding the newly
+ // selected bubble) so we can animate it out.
+ if (mIsExpanded && mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+ if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+ // Before screenshotting, have the real ActivityView show on top of other surfaces
+ // so that the screenshot doesn't flicker on top of it.
+ mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(true);
+ }
+
+ try {
+ screenshotAnimatingOutBubbleIntoSurface((success) -> {
+ mAnimatingOutSurfaceContainer.setVisibility(
+ success ? View.VISIBLE : View.INVISIBLE);
+ showNewlySelectedBubble(bubbleToSelect);
+ });
+ } catch (Exception e) {
+ showNewlySelectedBubble(bubbleToSelect);
+ e.printStackTrace();
+ }
+ } else {
+ showNewlySelectedBubble(bubbleToSelect);
+ }
+ }
+
+ private void showNewlySelectedBubble(BubbleViewProvider bubbleToSelect) {
final BubbleViewProvider previouslySelected = mExpandedBubble;
mExpandedBubble = bubbleToSelect;
updatePointerPosition();
@@ -1457,7 +1514,15 @@ public class BubbleStackView extends FrameLayout
// expanded view becomes visible on the screen. See b/126856255
mExpandedViewContainer.setAlpha(0.0f);
mSurfaceSynchronizer.syncSurfaceAndRun(() -> {
- previouslySelected.setContentVisibility(false);
+ if (previouslySelected != null) {
+ previouslySelected.setContentVisibility(false);
+ }
+ if (previouslySelected != null && previouslySelected.getExpandedView() != null) {
+ // Hide the currently expanded bubble's IME if it's visible before switching
+ // to a new bubble.
+ previouslySelected.getExpandedView().hideImeIfVisible();
+ }
+
updateExpandedBubble();
requestUpdate();
@@ -1574,12 +1639,19 @@ public class BubbleStackView extends FrameLayout
final int inset = getResources().getDimensionPixelSize(
R.dimen.bubbles_manage_education_top_inset);
mManageEducationView.bringToFront();
- mManageEducationView.setManageViewPosition(mTempRect.left,
- mTempRect.top - viewHeight + inset);
- mManageEducationView.setPointerPosition(mTempRect.centerX() - mTempRect.left);
+ mManageEducationView.setManageViewPosition(0, mTempRect.top - viewHeight + inset);
mManageEducationView.animate()
.setDuration(ANIMATE_STACK_USER_EDUCATION_DURATION)
.setInterpolator(FAST_OUT_SLOW_IN).alpha(1);
+ mManageEducationView.findViewById(R.id.manage).setOnClickListener(view -> {
+ mExpandedBubble.getExpandedView().findViewById(R.id.settings_button)
+ .performClick();
+ maybeShowManageEducation(false);
+ });
+ mManageEducationView.findViewById(R.id.got_it).setOnClickListener(view ->
+ maybeShowManageEducation(false));
+ mManageEducationView.setOnClickListener(view ->
+ maybeShowManageEducation(false));
});
Prefs.putBoolean(getContext(), HAS_SEEN_BUBBLES_MANAGE_EDUCATION, true);
} else if (!show
@@ -1636,84 +1708,233 @@ public class BubbleStackView extends FrameLayout
}
}
+ void hideImeFromExpandedBubble() {
+ if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+ // Hide the currently expanded bubble's IME if it's visible before switching to a new
+ // bubble.
+ mExpandedBubble.getExpandedView().hideImeIfVisible();
+ }
+ }
+
private void beforeExpandedViewAnimation() {
+ mIsExpansionAnimating = true;
hideFlyoutImmediate();
updateExpandedBubble();
updateExpandedView();
- mIsExpansionAnimating = true;
}
private void afterExpandedViewAnimation() {
- updateExpandedView();
mIsExpansionAnimating = false;
+ updateExpandedView();
requestUpdate();
}
+ private void animateExpansion() {
+ mIsExpanded = true;
+ hideStackUserEducation(true /* fromExpansion */);
+ beforeExpandedViewAnimation();
+
+ mBubbleContainer.setActiveController(mExpandedAnimationController);
+ updateOverflowVisibility();
+ updatePointerPosition();
+ mExpandedAnimationController.expandFromStack(() -> {
+ afterExpandedViewAnimation();
+ maybeShowManageEducation(true);
+ } /* after */);
+
+ mExpandedViewContainer.setTranslationX(0);
+ mExpandedViewContainer.setTranslationY(getExpandedViewY());
+ mExpandedViewContainer.setAlpha(1f);
+
+ // X-value of the bubble we're expanding, once it's settled in its row.
+ final float bubbleWillBeAtX =
+ mExpandedAnimationController.getBubbleLeft(
+ mBubbleData.getBubbles().indexOf(mExpandedBubble));
+
+ // How far horizontally the bubble will be animating. We'll wait a bit longer for bubbles
+ // that are animating farther, so that the expanded view doesn't move as much.
+ final float horizontalDistanceAnimated =
+ Math.abs(bubbleWillBeAtX
+ - mStackAnimationController.getStackPosition().x);
+
+ // Wait for the path animation target to reach its end, and add a small amount of extra time
+ // if the bubble is moving a lot horizontally.
+ long startDelay = 0L;
+
+ // Should not happen since we lay out before expanding, but just in case...
+ if (getWidth() > 0) {
+ startDelay = (long)
+ (ExpandedAnimationController.EXPAND_COLLAPSE_TARGET_ANIM_DURATION
+ + (horizontalDistanceAnimated / getWidth()) * 30);
+ }
+
+ // Set the pivot point for the scale, so the expanded view animates out from the bubble.
+ mExpandedViewContainerMatrix.setScale(
+ 0f, 0f,
+ bubbleWillBeAtX + mBubbleSize / 2f, getExpandedViewY());
+ mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
+
+ if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+ mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(false);
+ }
+
+ postDelayed(() -> PhysicsAnimator.getInstance(mExpandedViewContainerMatrix)
+ .spring(AnimatableScaleMatrix.SCALE_X,
+ AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f),
+ mScaleInSpringConfig)
+ .spring(AnimatableScaleMatrix.SCALE_Y,
+ AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f),
+ mScaleInSpringConfig)
+ .addUpdateListener((target, values) -> {
+ if (mExpandedBubble.getIconView() == null) {
+ return;
+ }
+ mExpandedViewContainerMatrix.postTranslate(
+ mExpandedBubble.getIconView().getTranslationX()
+ - bubbleWillBeAtX,
+ 0);
+ mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
+ })
+ .withEndActions(() -> {
+ if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+ mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(false);
+ }
+ })
+ .start(), startDelay);
+
+ }
+
private void animateCollapse() {
// Hide the menu if it's visible.
showManageMenu(false);
mIsExpanded = false;
- final BubbleViewProvider previouslySelected = mExpandedBubble;
- beforeExpandedViewAnimation();
- maybeShowManageEducation(false);
- if (DEBUG_BUBBLE_STACK_VIEW) {
- Log.d(TAG, "animateCollapse");
- Log.d(TAG, BubbleDebugConfig.formatBubblesString(getBubblesOnScreen(),
- mExpandedBubble));
- }
- updateOverflowVisibility();
mBubbleContainer.cancelAllAnimations();
- mExpandedAnimationController.collapseBackToStack(
+
+ // If we were in the middle of swapping, the animating-out surface would have been scaling
+ // to zero - finish it off.
+ PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer).cancel();
+ mAnimatingOutSurfaceContainer.setScaleX(0f);
+ mAnimatingOutSurfaceContainer.setScaleY(0f);
+
+ if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+ mExpandedBubble.getExpandedView().hideImeIfVisible();
+ }
+
+ // Let the expanded animation controller know that it shouldn't animate child adds/reorders
+ // since we're about to animate collapsed.
+ mExpandedAnimationController.notifyPreparingToCollapse();
+
+ final long startDelay =
+ (long) (ExpandedAnimationController.EXPAND_COLLAPSE_TARGET_ANIM_DURATION * 0.6f);
+ postDelayed(() -> mExpandedAnimationController.collapseBackToStack(
mStackAnimationController.getStackPositionAlongNearestHorizontalEdge()
/* collapseTo */,
() -> {
mBubbleContainer.setActiveController(mStackAnimationController);
+ }), startDelay);
+
+ // We want to visually collapse into this bubble during the animation.
+ final View expandingFromBubble = mExpandedBubble.getIconView();
+
+ // X-value the bubble is animating from (back into the stack).
+ final float expandingFromBubbleAtX =
+ mExpandedAnimationController.getBubbleLeft(
+ mBubbleData.getBubbles().indexOf(mExpandedBubble));
+
+ // Set the pivot point.
+ mExpandedViewContainerMatrix.setScale(
+ 1f, 1f,
+ expandingFromBubbleAtX + mBubbleSize / 2f,
+ getExpandedViewY());
+
+ PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel();
+ PhysicsAnimator.getInstance(mExpandedViewContainerMatrix)
+ .spring(AnimatableScaleMatrix.SCALE_X, 0f, mScaleOutSpringConfig)
+ .spring(AnimatableScaleMatrix.SCALE_Y, 0f, mScaleOutSpringConfig)
+ .addUpdateListener((target, values) -> {
+ if (expandingFromBubble != null) {
+ // Follow the bubble as it translates!
+ mExpandedViewContainerMatrix.postTranslate(
+ expandingFromBubble.getTranslationX()
+ - expandingFromBubbleAtX, 0f);
+ }
+
+ mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
+
+ // Hide early so we don't have a tiny little expanded view still visible at the
+ // end of the scale animation.
+ if (mExpandedViewContainerMatrix.getScaleX() < 0.05f) {
+ mExpandedViewContainer.setVisibility(View.INVISIBLE);
+ }
+ })
+ .withEndActions(() -> {
+ final BubbleViewProvider previouslySelected = mExpandedBubble;
+ beforeExpandedViewAnimation();
+ maybeShowManageEducation(false);
+
+ if (DEBUG_BUBBLE_STACK_VIEW) {
+ Log.d(TAG, "animateCollapse");
+ Log.d(TAG, BubbleDebugConfig.formatBubblesString(getBubblesOnScreen(),
+ mExpandedBubble));
+ }
+ updateOverflowVisibility();
+
afterExpandedViewAnimation();
previouslySelected.setContentVisibility(false);
- });
-
- mExpandedViewXAnim.animateToFinalPosition(getCollapsedX());
- mExpandedViewYAnim.animateToFinalPosition(getCollapsedY());
- mExpandedViewContainer.animate()
- .setDuration(100)
- .alpha(0f);
+ })
+ .start();
}
- private void animateExpansion() {
- mIsExpanded = true;
- hideStackUserEducation(true /* fromExpansion */);
- beforeExpandedViewAnimation();
+ private void animateSwitchBubbles() {
+ // The surface contains a screenshot of the animating out bubble, so we just need to animate
+ // it out (and then release the GraphicBuffer).
+ PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer).cancel();
+ PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer)
+ .spring(DynamicAnimation.SCALE_X, 0f, mScaleOutSpringConfig)
+ .spring(DynamicAnimation.SCALE_Y, 0f, mScaleOutSpringConfig)
+ .spring(DynamicAnimation.TRANSLATION_Y,
+ mAnimatingOutSurfaceContainer.getTranslationY() - mBubbleSize * 2,
+ mTranslateSpringConfig)
+ .withEndActions(this::releaseAnimatingOutBubbleBuffer)
+ .start();
- mBubbleContainer.setActiveController(mExpandedAnimationController);
- updateOverflowVisibility();
- mExpandedAnimationController.expandFromStack(() -> {
- updatePointerPosition();
- afterExpandedViewAnimation();
- maybeShowManageEducation(true);
- } /* after */);
+ boolean isOverflow = mExpandedBubble != null
+ && mExpandedBubble.getKey().equals(BubbleOverflow.KEY);
+ float expandingFromBubbleDestinationX =
+ mExpandedAnimationController.getBubbleLeft(isOverflow ? getBubbleCount()
+ : mBubbleData.getBubbles().indexOf(mExpandedBubble));
- mExpandedViewContainer.setTranslationX(getCollapsedX());
- mExpandedViewContainer.setTranslationY(getCollapsedY());
- mExpandedViewContainer.setAlpha(0f);
+ mExpandedViewContainer.setAlpha(1f);
+ mExpandedViewContainer.setVisibility(View.VISIBLE);
- mExpandedViewXAnim.animateToFinalPosition(0f);
- mExpandedViewYAnim.animateToFinalPosition(getExpandedViewY());
- mExpandedViewContainer.animate()
- .setDuration(100)
- .alpha(1f);
- }
+ mExpandedViewContainerMatrix.setScale(
+ 0f, 0f, expandingFromBubbleDestinationX + mBubbleSize / 2f, getExpandedViewY());
+ mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
- private float getCollapsedX() {
- return mStackAnimationController.getStackPosition().x < getWidth() / 2
- ? -mExpandedAnimateXDistance
- : mExpandedAnimateXDistance;
- }
+ mExpandedViewContainer.postDelayed(() -> {
+ if (!mIsExpanded) {
+ return;
+ }
- private float getCollapsedY() {
- return Math.min(mStackAnimationController.getStackPosition().y,
- mExpandedAnimateYDistance);
+ PhysicsAnimator.getInstance(mExpandedViewContainerMatrix)
+ .spring(AnimatableScaleMatrix.SCALE_X,
+ AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f),
+ mScaleInSpringConfig)
+ .spring(AnimatableScaleMatrix.SCALE_Y,
+ AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f),
+ mScaleInSpringConfig)
+ .addUpdateListener((target, values) -> {
+ mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
+ })
+ .withEndActions(() -> {
+ if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+ mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(false);
+ }
+ })
+ .start();
+ }, 25);
}
private void notifyExpansionChanged(BubbleViewProvider bubble, boolean expanded) {
@@ -1780,9 +2001,13 @@ public class BubbleStackView extends FrameLayout
*/
@Override
public void subtractObscuredTouchableRegion(Region touchableRegion, View view) {
- // If the notification shade is expanded, or the manage menu is open, we shouldn't let the
- // ActivityView steal any touch events from any location.
- if (!mIsExpanded || mShowingManage) {
+ // If the notification shade is expanded, or the manage menu is open, or we are showing
+ // manage bubbles user education, we shouldn't let the ActivityView steal any touch events
+ // from any location.
+ if (!mIsExpanded
+ || mShowingManage
+ || (mManageEducationView != null
+ && mManageEducationView.getVisibility() == VISIBLE)) {
touchableRegion.setEmpty();
}
}
@@ -2218,21 +2443,118 @@ public class BubbleStackView extends FrameLayout
Log.d(TAG, "updateExpandedBubble()");
}
- if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
- // Hide the currently expanded bubble's IME if it's visible before switching to a new
- // bubble.
- mExpandedBubble.getExpandedView().hideImeIfVisible();
- }
-
mExpandedViewContainer.removeAllViews();
if (mIsExpanded && mExpandedBubble != null
&& mExpandedBubble.getExpandedView() != null) {
BubbleExpandedView bev = mExpandedBubble.getExpandedView();
+ bev.setContentVisibility(false);
+ mExpandedViewContainerMatrix.setScaleX(0f);
+ mExpandedViewContainerMatrix.setScaleY(0f);
+ mExpandedViewContainer.setVisibility(View.INVISIBLE);
+ mExpandedViewContainer.setAlpha(0f);
mExpandedViewContainer.addView(bev);
bev.setManageClickListener((view) -> showManageMenu(!mShowingManage));
bev.populateExpandedView();
- mExpandedViewContainer.setVisibility(VISIBLE);
- mExpandedViewContainer.setAlpha(1.0f);
+
+ if (!mIsExpansionAnimating) {
+ mSurfaceSynchronizer.syncSurfaceAndRun(() -> {
+ post(this::animateSwitchBubbles);
+ });
+ }
+ }
+ }
+
+ /**
+ * Requests a snapshot from the currently expanded bubble's ActivityView and displays it in a
+ * SurfaceView. This allows us to load a newly expanded bubble's Activity into the ActivityView,
+ * while animating the (screenshot of the) previously selected bubble's content away.
+ *
+ * @param onComplete Callback to run once we're done here - called with 'false' if something
+ * went wrong, or 'true' if the SurfaceView is now showing a screenshot of the
+ * expanded bubble.
+ */
+ private void screenshotAnimatingOutBubbleIntoSurface(Consumer<Boolean> onComplete) {
+ if (mExpandedBubble == null || mExpandedBubble.getExpandedView() == null) {
+ // You can't animate null.
+ onComplete.accept(false);
+ return;
+ }
+
+ final BubbleExpandedView animatingOutExpandedView = mExpandedBubble.getExpandedView();
+
+ // Release the previous screenshot if it hasn't been released already.
+ if (mAnimatingOutBubbleBuffer != null) {
+ releaseAnimatingOutBubbleBuffer();
+ }
+
+ try {
+ mAnimatingOutBubbleBuffer = animatingOutExpandedView.snapshotActivitySurface();
+ } catch (Exception e) {
+ // If we fail for any reason, print the stack trace and then notify the callback of our
+ // failure. This is not expected to occur, but it's not worth crashing over.
+ Log.wtf(TAG, e);
+ onComplete.accept(false);
+ }
+
+ if (mAnimatingOutBubbleBuffer == null
+ || mAnimatingOutBubbleBuffer.getGraphicBuffer() == null) {
+ // While no exception was thrown, we were unable to get a snapshot.
+ onComplete.accept(false);
+ return;
+ }
+
+ // Make sure the surface container's properties have been reset.
+ PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer).cancel();
+ mAnimatingOutSurfaceContainer.setScaleX(1f);
+ mAnimatingOutSurfaceContainer.setScaleY(1f);
+ mAnimatingOutSurfaceContainer.setTranslationX(0);
+ mAnimatingOutSurfaceContainer.setTranslationY(0);
+
+ final int[] activityViewLocation =
+ mExpandedBubble.getExpandedView().getActivityViewLocationOnScreen();
+ final int[] surfaceViewLocation = mAnimatingOutSurfaceView.getLocationOnScreen();
+
+ // Translate the surface to overlap the real ActivityView.
+ mAnimatingOutSurfaceContainer.setTranslationY(
+ activityViewLocation[1] - surfaceViewLocation[1]);
+
+ // Set the width/height of the SurfaceView to match the snapshot.
+ mAnimatingOutSurfaceView.getLayoutParams().width =
+ mAnimatingOutBubbleBuffer.getGraphicBuffer().getWidth();
+ mAnimatingOutSurfaceView.getLayoutParams().height =
+ mAnimatingOutBubbleBuffer.getGraphicBuffer().getHeight();
+ mAnimatingOutSurfaceView.requestLayout();
+
+ // Post to wait for layout.
+ post(() -> {
+ // The buffer might have been destroyed if the user is mashing on bubbles, that's okay.
+ if (mAnimatingOutBubbleBuffer.getGraphicBuffer().isDestroyed()) {
+ onComplete.accept(false);
+ return;
+ }
+
+ if (!mIsExpanded) {
+ onComplete.accept(false);
+ return;
+ }
+
+ // Attach the buffer! We're now displaying the snapshot.
+ mAnimatingOutSurfaceView.getHolder().getSurface().attachAndQueueBufferWithColorSpace(
+ mAnimatingOutBubbleBuffer.getGraphicBuffer(),
+ mAnimatingOutBubbleBuffer.getColorSpace());
+
+ mSurfaceSynchronizer.syncSurfaceAndRun(() -> post(() -> onComplete.accept(true)));
+ });
+ }
+
+ /**
+ * Releases the buffer containing the screenshot of the animating-out bubble, if it exists and
+ * isn't yet destroyed.
+ */
+ private void releaseAnimatingOutBubbleBuffer() {
+ if (mAnimatingOutBubbleBuffer != null
+ && !mAnimatingOutBubbleBuffer.getGraphicBuffer().isDestroyed()) {
+ mAnimatingOutBubbleBuffer.getGraphicBuffer().destroy();
}
}
@@ -2242,19 +2564,10 @@ public class BubbleStackView extends FrameLayout
}
mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
- if (mIsExpanded) {
- final float y = getExpandedViewY();
- if (!mExpandedViewYAnim.isRunning()) {
- // We're not animating so set the value
- mExpandedViewContainer.setTranslationY(y);
- if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
- mExpandedBubble.getExpandedView().updateView();
- }
- } else {
- // We are animating so update the value; there is an end listener on the animator
- // that will ensure expandedeView.updateView gets called.
- mExpandedViewYAnim.animateToFinalPosition(y);
- }
+ if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+ mExpandedViewContainer.setTranslationY(getExpandedViewY());
+ mExpandedBubble.getExpandedView().updateView(
+ mExpandedViewContainer.getLocationOnScreen());
}
mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/AnimatableScaleMatrix.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/AnimatableScaleMatrix.java
new file mode 100644
index 000000000000..ae7833634794
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/AnimatableScaleMatrix.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.bubbles.animation;
+
+import android.graphics.Matrix;
+
+import androidx.dynamicanimation.animation.DynamicAnimation;
+import androidx.dynamicanimation.animation.FloatPropertyCompat;
+
+/**
+ * Matrix whose scale properties can be animated using physics animations, via the {@link #SCALE_X}
+ * and {@link #SCALE_Y} FloatProperties.
+ *
+ * This is useful when you need to perform a scale animation with a pivot point, since pivot points
+ * are not supported by standard View scale operations but are supported by matrices.
+ *
+ * NOTE: DynamicAnimation assumes that all custom properties are denominated in pixels, and thus
+ * considers 1 to be the smallest user-visible change for custom properties. This means that if you
+ * animate {@link #SCALE_X} and {@link #SCALE_Y} to 3f, for example, the animation would have only
+ * three frames.
+ *
+ * To work around this, whenever animating to a desired scale value, animate to the value returned
+ * by {@link #getAnimatableValueForScaleFactor} instead. The SCALE_X and SCALE_Y properties will
+ * convert that (larger) value into the appropriate scale factor when scaling the matrix.
+ */
+public class AnimatableScaleMatrix extends Matrix {
+
+ /**
+ * The X value of the scale.
+ *
+ * NOTE: This must be set or animated to the value returned by
+ * {@link #getAnimatableValueForScaleFactor}, not the desired scale factor itself.
+ */
+ public static final FloatPropertyCompat<AnimatableScaleMatrix> SCALE_X =
+ new FloatPropertyCompat<AnimatableScaleMatrix>("matrixScaleX") {
+ @Override
+ public float getValue(AnimatableScaleMatrix object) {
+ return getAnimatableValueForScaleFactor(object.mScaleX);
+ }
+
+ @Override
+ public void setValue(AnimatableScaleMatrix object, float value) {
+ object.setScaleX(value * DynamicAnimation.MIN_VISIBLE_CHANGE_SCALE);
+ }
+ };
+
+ /**
+ * The Y value of the scale.
+ *
+ * NOTE: This must be set or animated to the value returned by
+ * {@link #getAnimatableValueForScaleFactor}, not the desired scale factor itself.
+ */
+ public static final FloatPropertyCompat<AnimatableScaleMatrix> SCALE_Y =
+ new FloatPropertyCompat<AnimatableScaleMatrix>("matrixScaleY") {
+ @Override
+ public float getValue(AnimatableScaleMatrix object) {
+ return getAnimatableValueForScaleFactor(object.mScaleY);
+ }
+
+ @Override
+ public void setValue(AnimatableScaleMatrix object, float value) {
+ object.setScaleY(value * DynamicAnimation.MIN_VISIBLE_CHANGE_SCALE);
+ }
+ };
+
+ private float mScaleX = 1f;
+ private float mScaleY = 1f;
+
+ private float mPivotX = 0f;
+ private float mPivotY = 0f;
+
+ /**
+ * Return the value to animate SCALE_X or SCALE_Y to in order to achieve the desired scale
+ * factor.
+ */
+ public static float getAnimatableValueForScaleFactor(float scale) {
+ return scale * (1f / DynamicAnimation.MIN_VISIBLE_CHANGE_SCALE);
+ }
+
+ @Override
+ public void setScale(float sx, float sy, float px, float py) {
+ mScaleX = sx;
+ mScaleY = sy;
+ mPivotX = px;
+ mPivotY = py;
+ super.setScale(mScaleX, mScaleY, mPivotX, mPivotY);
+ }
+
+ public void setScaleX(float scaleX) {
+ mScaleX = scaleX;
+ super.setScale(mScaleX, mScaleY, mPivotX, mPivotY);
+ }
+
+ public void setScaleY(float scaleY) {
+ mScaleY = scaleY;
+ super.setScale(mScaleX, mScaleY, mPivotX, mPivotY);
+ }
+
+ public void setPivotX(float pivotX) {
+ mPivotX = pivotX;
+ super.setScale(mScaleX, mScaleY, mPivotX, mPivotY);
+ }
+
+ public void setPivotY(float pivotY) {
+ mPivotY = pivotY;
+ super.setScale(mScaleX, mScaleY, mPivotX, mPivotY);
+ }
+
+ public float getScaleX() {
+ return mScaleX;
+ }
+
+ public float getScaleY() {
+ return mScaleY;
+ }
+
+ public float getPivotX() {
+ return mPivotX;
+ }
+
+ public float getPivotY() {
+ return mPivotY;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
index 76ff1afef3f7..cb8995a72dc3 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -56,7 +56,7 @@ public class ExpandedAnimationController
private static final int ANIMATE_TRANSLATION_FACTOR = 4;
/** Duration of the expand/collapse target path animation. */
- private static final int EXPAND_COLLAPSE_TARGET_ANIM_DURATION = 175;
+ public static final int EXPAND_COLLAPSE_TARGET_ANIM_DURATION = 175;
/** Stiffness for the expand/collapse path-following animation. */
private static final int EXPAND_COLLAPSE_ANIM_STIFFNESS = 1000;
@@ -92,6 +92,14 @@ public class ExpandedAnimationController
private int mScreenOrientation;
private boolean mAnimatingExpand = false;
+
+ /**
+ * Whether we are animating other Bubbles UI elements out in preparation for a call to
+ * {@link #collapseBackToStack}. If true, we won't animate bubbles in response to adds or
+ * reorders.
+ */
+ private boolean mPreparingToCollapse = false;
+
private boolean mAnimatingCollapse = false;
private @Nullable Runnable mAfterExpand;
private Runnable mAfterCollapse;
@@ -150,6 +158,7 @@ public class ExpandedAnimationController
*/
public void expandFromStack(
@Nullable Runnable after, @Nullable Runnable leadBubbleEndAction) {
+ mPreparingToCollapse = false;
mAnimatingCollapse = false;
mAnimatingExpand = true;
mAfterExpand = after;
@@ -165,9 +174,20 @@ public class ExpandedAnimationController
expandFromStack(after, null /* leadBubbleEndAction */);
}
+ /**
+ * Sets that we're animating the stack collapsed, but haven't yet called
+ * {@link #collapseBackToStack}. This will temporarily suspend animations for bubbles that are
+ * added or re-ordered, since the upcoming collapse animation will handle positioning those
+ * bubbles in the collapsed stack.
+ */
+ public void notifyPreparingToCollapse() {
+ mPreparingToCollapse = true;
+ }
+
/** Animate collapsing the bubbles back to their stacked position. */
public void collapseBackToStack(PointF collapsePoint, Runnable after) {
mAnimatingExpand = false;
+ mPreparingToCollapse = false;
mAnimatingCollapse = true;
mAfterCollapse = after;
mCollapsePoint = collapsePoint;
@@ -501,12 +521,18 @@ public class ExpandedAnimationController
startOrUpdatePathAnimation(false /* expanding */);
} else {
child.setTranslationX(getBubbleLeft(index));
- animationForChild(child)
- .translationY(
- getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR, /* from */
- getExpandedY() /* to */)
- .start();
- updateBubblePositions();
+
+ // If we're preparing to collapse, don't start animations since the collapse animation
+ // will take over and animate the new bubble into the correct (stacked) position.
+ if (!mPreparingToCollapse) {
+ animationForChild(child)
+ .translationY(
+ getExpandedY()
+ - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR, /* from */
+ getExpandedY() /* to */)
+ .start();
+ updateBubblePositions();
+ }
}
}
@@ -532,12 +558,20 @@ public class ExpandedAnimationController
@Override
void onChildReordered(View child, int oldIndex, int newIndex) {
- updateBubblePositions();
+ if (mPreparingToCollapse) {
+ // If a re-order is received while we're preparing to collapse, ignore it. Once started,
+ // the collapse animation will animate all of the bubbles to their correct (stacked)
+ // position.
+ return;
+ }
- // We expect reordering during collapse, since we'll put the last selected bubble on top.
- // Update the collapse animation so they end up in the right stacked positions.
if (mAnimatingCollapse) {
+ // If a re-order is received during collapse, update the animation so that the bubbles
+ // end up in the correct (stacked) position.
startOrUpdatePathAnimation(false /* expanding */);
+ } else {
+ // Otherwise, animate the bubbles around to reflect their new order.
+ updateBubblePositions();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
index fd1f879e7f4b..3ef20444cb52 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
@@ -1034,13 +1034,13 @@ public class StackAnimationController extends
mMagnetizedStack.getFlingToTargetMinVelocity() /* default */);
final float maxVelocity = Settings.Secure.getFloat(contentResolver,
"bubble_dismiss_stick_max_velocity",
- mMagnetizedStack.getStickToTargetMaxVelocity() /* default */);
+ mMagnetizedStack.getStickToTargetMaxXVelocity() /* default */);
final float targetWidth = Settings.Secure.getFloat(contentResolver,
"bubble_dismiss_target_width_percent",
mMagnetizedStack.getFlingToTargetWidthPercent() /* default */);
mMagnetizedStack.setFlingToTargetMinVelocity(minVelocity);
- mMagnetizedStack.setStickToTargetMaxVelocity(maxVelocity);
+ mMagnetizedStack.setStickToTargetMaxXVelocity(maxVelocity);
mMagnetizedStack.setFlingToTargetWidthPercent(targetWidth);
return mMagnetizedStack;
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
index d4d4d2a7d8fe..eed55315e836 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
@@ -20,7 +20,7 @@ import android.content.ComponentName
import android.service.controls.Control
import android.service.controls.ControlsProviderService
import android.service.controls.actions.ControlAction
-import com.android.systemui.controls.UserAwareController
+import com.android.systemui.util.UserAwareController
import java.util.function.Consumer
/**
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
index 45ba1e6012fe..496741b1cd6f 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
@@ -21,7 +21,7 @@ import android.service.controls.Control
import android.service.controls.ControlsProviderService
import android.service.controls.actions.ControlAction
import com.android.systemui.controls.ControlStatus
-import com.android.systemui.controls.UserAwareController
+import com.android.systemui.util.UserAwareController
import com.android.systemui.controls.management.ControlsFavoritingActivity
import com.android.systemui.controls.ui.ControlsUiController
import java.util.function.Consumer
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index ebdcdccead90..40c8c6bfa9f7 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -200,9 +200,9 @@ class ControlsControllerImpl @Inject constructor (
GlobalActionsDialog.PREFS_CONTROLS_FILE, Context.MODE_PRIVATE)
val completedSeedingPackageSet = prefs.getStringSet(
GlobalActionsDialog.PREFS_CONTROLS_SEEDING_COMPLETED, mutableSetOf<String>())
- val favoritePackageSet = favoriteComponentSet.map { it.packageName }
+ val servicePackageSet = serviceInfoSet.map { it.packageName }
prefs.edit().putStringSet(GlobalActionsDialog.PREFS_CONTROLS_SEEDING_COMPLETED,
- completedSeedingPackageSet.intersect(favoritePackageSet)).apply()
+ completedSeedingPackageSet.intersect(servicePackageSet)).apply()
var changed = false
favoriteComponentSet.subtract(serviceInfoSet).forEach {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt
index 647daccca8bd..b9f16665944f 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt
@@ -18,7 +18,7 @@ package com.android.systemui.controls.management
import android.content.ComponentName
import com.android.systemui.controls.ControlsServiceInfo
-import com.android.systemui.controls.UserAwareController
+import com.android.systemui.util.UserAwareController
import com.android.systemui.statusbar.policy.CallbackController
/**
@@ -26,7 +26,7 @@ import com.android.systemui.statusbar.policy.CallbackController
*/
interface ControlsListingController :
CallbackController<ControlsListingController.ControlsListingCallback>,
- UserAwareController {
+ UserAwareController {
/**
* @return the current list of services that satisfies the [ServiceListing].
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index 353367ead7e6..ee02b85e4a00 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -89,6 +89,7 @@ class ControlViewHolder(
return when {
status != Control.STATUS_OK -> StatusBehavior::class
deviceType == DeviceTypes.TYPE_CAMERA -> TouchBehavior::class
+ template == ControlTemplate.NO_TEMPLATE -> TouchBehavior::class
template is ToggleTemplate -> ToggleBehavior::class
template is StatelessTemplate -> TouchBehavior::class
template is ToggleRangeTemplate -> ToggleRangeBehavior::class
@@ -235,7 +236,10 @@ class ControlViewHolder(
controlsController.action(cws.componentName, cws.ci, action)
}
- fun usePanel(): Boolean = deviceType in ControlViewHolder.FORCE_PANEL_DEVICES
+ fun usePanel(): Boolean {
+ return deviceType in ControlViewHolder.FORCE_PANEL_DEVICES ||
+ controlTemplate == ControlTemplate.NO_TEMPLATE
+ }
fun bindBehavior(
existingBehavior: Behavior?,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index 91f032d86a94..28bcf3a35117 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -24,6 +24,8 @@ import com.android.systemui.keyguard.WorkLockActivity;
import com.android.systemui.screenrecord.ScreenRecordDialog;
import com.android.systemui.settings.BrightnessDialog;
import com.android.systemui.tuner.TunerActivity;
+import com.android.systemui.usb.UsbDebuggingActivity;
+import com.android.systemui.usb.UsbDebuggingSecondaryUserActivity;
import dagger.Binds;
import dagger.Module;
@@ -70,4 +72,17 @@ public abstract class DefaultActivityBinder {
@IntoMap
@ClassKey(BubbleOverflowActivity.class)
public abstract Activity bindBubbleOverflowActivity(BubbleOverflowActivity activity);
+
+ /** Inject into UsbDebuggingActivity. */
+ @Binds
+ @IntoMap
+ @ClassKey(UsbDebuggingActivity.class)
+ public abstract Activity bindUsbDebuggingActivity(UsbDebuggingActivity activity);
+
+ /** Inject into UsbDebuggingSecondaryUserActivity. */
+ @Binds
+ @IntoMap
+ @ClassKey(UsbDebuggingSecondaryUserActivity.class)
+ public abstract Activity bindUsbDebuggingSecondaryUserActivity(
+ UsbDebuggingSecondaryUserActivity activity);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index 8368b2c1ae86..1f30305ab169 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -25,6 +25,7 @@ import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.display.NightDisplayListener;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.Looper;
import android.os.ServiceManager;
import android.util.DisplayMetrics;
import android.view.Choreographer;
@@ -39,9 +40,12 @@ import com.android.internal.util.NotificationMessagingUtil;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.Prefs;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.AlwaysOnDisplayPolicy;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.plugins.PluginInitializerImpl;
import com.android.systemui.shared.plugins.PluginManager;
@@ -178,6 +182,21 @@ public class DependencyProvider {
return ActivityManagerWrapper.getInstance();
}
+ /** Provides and initializes the {#link BroadcastDispatcher} for SystemUI */
+ @Singleton
+ @Provides
+ public BroadcastDispatcher providesBroadcastDispatcher(
+ Context context,
+ @Background Looper backgroundLooper,
+ DumpManager dumpManager,
+ BroadcastDispatcherLogger logger
+ ) {
+ BroadcastDispatcher bD =
+ new BroadcastDispatcher(context, backgroundLooper, dumpManager, logger);
+ bD.initialize();
+ return bD;
+ }
+
@Singleton
@Provides
public DevicePolicyManagerWrapper provideDevicePolicyManagerWrapper() {
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index bc95a2514c0b..915092134cc5 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -247,7 +247,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private final Executor mBackgroundExecutor;
private List<ControlsServiceInfo> mControlsServiceInfos = new ArrayList<>();
private Optional<ControlsController> mControlsControllerOptional;
- private SharedPreferences mControlsPreferences;
private final RingerModeTracker mRingerModeTracker;
private int mDialogPressDelay = DIALOG_PRESS_DELAY; // ms
private Handler mMainHandler;
@@ -405,12 +404,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
});
}
- // Need to be user-specific with the context to make sure we read the correct prefs
- Context userContext = context.createContextAsUser(
- new UserHandle(mUserManager.getUserHandle()), 0);
- mControlsPreferences = userContext.getSharedPreferences(PREFS_CONTROLS_FILE,
- Context.MODE_PRIVATE);
-
// Listen for changes to show controls on the power menu while locked
onPowerMenuLockScreenSettingsChanged();
mContext.getContentResolver().registerContentObserver(
@@ -444,19 +437,22 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
Collections.emptySet());
List<ComponentName> componentsToSeed = new ArrayList<>();
- for (ControlsServiceInfo info : mControlsServiceInfos) {
- String pkg = info.componentName.getPackageName();
- if (seededPackages.contains(pkg)
- || mControlsControllerOptional.get().countFavoritesForComponent(
- info.componentName) > 0) {
- continue;
- }
-
- for (int i = 0; i < Math.min(SEEDING_MAX, preferredControlsPackages.length); i++) {
- if (pkg.equals(preferredControlsPackages[i])) {
- componentsToSeed.add(info.componentName);
+ for (int i = 0; i < Math.min(SEEDING_MAX, preferredControlsPackages.length); i++) {
+ String pkg = preferredControlsPackages[i];
+ for (ControlsServiceInfo info : mControlsServiceInfos) {
+ if (!pkg.equals(info.componentName.getPackageName())) continue;
+ if (seededPackages.contains(pkg)) {
+ break;
+ } else if (mControlsControllerOptional.get()
+ .countFavoritesForComponent(info.componentName) > 0) {
+ // When there are existing controls but no saved preference, assume it
+ // is out of sync, perhaps through a device restore, and update the
+ // preference
+ addPackageToSeededSet(prefs, pkg);
break;
}
+ componentsToSeed.add(info.componentName);
+ break;
}
}
@@ -466,16 +462,20 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
componentsToSeed,
(response) -> {
Log.d(TAG, "Controls seeded: " + response);
- Set<String> completedPkgs = prefs.getStringSet(PREFS_CONTROLS_SEEDING_COMPLETED,
- new HashSet<String>());
if (response.getAccepted()) {
- completedPkgs.add(response.getPackageName());
- prefs.edit().putStringSet(PREFS_CONTROLS_SEEDING_COMPLETED,
- completedPkgs).apply();
+ addPackageToSeededSet(prefs, response.getPackageName());
}
});
}
+ private void addPackageToSeededSet(SharedPreferences prefs, String pkg) {
+ Set<String> seededPackages = prefs.getStringSet(PREFS_CONTROLS_SEEDING_COMPLETED,
+ Collections.emptySet());
+ Set<String> updatedPkgs = new HashSet<>(seededPackages);
+ updatedPkgs.add(pkg);
+ prefs.edit().putStringSet(PREFS_CONTROLS_SEEDING_COMPLETED, updatedPkgs).apply();
+ }
+
/**
* Show the global actions dialog (creating if necessary)
*
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index b26dc5f91245..53251ed4362d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -654,7 +654,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable {
@Override
public void onBouncerVisiblityChanged(boolean shown) {
synchronized (KeyguardViewMediator.this) {
- adjustStatusBarLocked(shown);
+ adjustStatusBarLocked(shown, false);
}
}
@@ -2003,10 +2003,12 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable {
}
private void adjustStatusBarLocked() {
- adjustStatusBarLocked(false /* forceHideHomeRecentsButtons */);
+ adjustStatusBarLocked(false /* forceHideHomeRecentsButtons */,
+ false /* forceClearFlags */);
}
- private void adjustStatusBarLocked(boolean forceHideHomeRecentsButtons) {
+ private void adjustStatusBarLocked(boolean forceHideHomeRecentsButtons,
+ boolean forceClearFlags) {
if (mStatusBarManager == null) {
mStatusBarManager = (StatusBarManager)
mContext.getSystemService(Context.STATUS_BAR_SERVICE);
@@ -2018,6 +2020,13 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable {
// Disable aspects of the system/status/navigation bars that must not be re-enabled by
// windows that appear on top, ever
int flags = StatusBarManager.DISABLE_NONE;
+
+ // TODO (b/155663717) After restart, status bar will not properly hide home button
+ // unless disable is called to show un-hide it once first
+ if (forceClearFlags) {
+ mStatusBarManager.disable(flags);
+ }
+
if (forceHideHomeRecentsButtons || isShowingAndNotOccluded()) {
if (!mShowHomeOverLockscreen || !mInGestureNavigationMode) {
flags |= StatusBarManager.DISABLE_HOME;
@@ -2141,6 +2150,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable {
public void onBootCompleted() {
synchronized (this) {
mBootCompleted = true;
+ adjustStatusBarLocked(false, true);
if (mBootSendUserPresent) {
sendUserPresentBroadcast();
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/BroadcastDispatcherLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/BroadcastDispatcherLog.java
new file mode 100644
index 000000000000..7d1f1c2709fa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/BroadcastDispatcherLog.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.android.systemui.log.LogBuffer;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+/** A {@link LogBuffer} for BroadcastDispatcher-related messages. */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface BroadcastDispatcherLog {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 9c89fee5cba1..a2086e8d3ac6 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -97,6 +97,18 @@ public class LogModule {
return buffer;
}
+ /** Provides a logging buffer for {@link com.android.systemui.broadcast.BroadcastDispatcher} */
+ @Provides
+ @Singleton
+ @BroadcastDispatcherLog
+ public static LogBuffer provideBroadcastDispatcherLogBuffer(
+ LogcatEchoTracker bufferFilter,
+ DumpManager dumpManager) {
+ LogBuffer buffer = new LogBuffer("BroadcastDispatcherLog", 500, 10, bufferFilter);
+ buffer.attach(dumpManager);
+ return buffer;
+ }
+
/** Allows logging buffers to be tweaked via adb on debug builds but not on prod builds. */
@Provides
@Singleton
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index f039fc2d84ae..50081001e65a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -35,7 +35,6 @@ import android.util.Log;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
-import android.widget.SeekBar;
import android.widget.TextView;
import androidx.annotation.NonNull;
@@ -165,9 +164,7 @@ public class MediaControlPanel {
TransitionLayout player = vh.getPlayer();
mSeekBarObserver = new SeekBarObserver(vh);
mSeekBarViewModel.getProgress().observeForever(mSeekBarObserver);
- SeekBar bar = vh.getSeekBar();
- bar.setOnSeekBarChangeListener(mSeekBarViewModel.getSeekBarListener());
- bar.setOnTouchListener(mSeekBarViewModel.getSeekBarTouchListener());
+ mSeekBarViewModel.attachTouchHandlers(vh.getSeekBar());
mMediaViewController.attach(player);
}
@@ -302,11 +299,14 @@ public class MediaControlPanel {
button.setContentDescription(mediaAction.getContentDescription());
Runnable action = mediaAction.getAction();
- button.setOnClickListener(v -> {
- if (action != null) {
+ if (action == null) {
+ button.setEnabled(false);
+ } else {
+ button.setEnabled(true);
+ button.setOnClickListener(v -> {
action.run();
- }
- });
+ });
+ }
boolean visibleInCompat = actionsWhenCollapsed.contains(i);
setVisibleAndAlpha(collapsedSet, actionId, visibleInCompat);
setVisibleAndAlpha(expandedSet, actionId, true /*visible */);
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 094c5bef3c18..6ea36ab3af2f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -18,8 +18,11 @@ package com.android.systemui.media
import android.app.Notification
import android.app.PendingIntent
+import android.content.BroadcastReceiver
import android.content.ContentResolver
import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
@@ -30,11 +33,13 @@ import android.media.MediaDescription
import android.media.MediaMetadata
import android.media.session.MediaSession
import android.net.Uri
+import android.os.UserHandle
import android.service.notification.StatusBarNotification
import android.text.TextUtils
import android.util.Log
import com.android.internal.graphics.ColorUtils
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.statusbar.NotificationMediaManager
@@ -87,13 +92,23 @@ class MediaDataManager @Inject constructor(
private val notificationEntryManager: NotificationEntryManager,
private val mediaResumeListener: MediaResumeListener,
@Background private val backgroundExecutor: Executor,
- @Main private val foregroundExecutor: Executor
+ @Main private val foregroundExecutor: Executor,
+ private val broadcastDispatcher: BroadcastDispatcher
) {
private val listeners: MutableSet<Listener> = mutableSetOf()
private val mediaEntries: LinkedHashMap<String, MediaData> = LinkedHashMap()
private val useMediaResumption: Boolean = Utils.useMediaResumption(context)
+ private val userChangeReceiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ if (Intent.ACTION_USER_SWITCHED == intent.action) {
+ // Remove all controls, regardless of state
+ clearData()
+ }
+ }
+ }
+
init {
mediaTimeoutListener.timeoutCallback = { token: String, timedOut: Boolean ->
setTimedOut(token, timedOut) }
@@ -111,6 +126,9 @@ class MediaDataManager @Inject constructor(
}
addListener(mediaResumeListener)
}
+
+ val userFilter = IntentFilter(Intent.ACTION_USER_SWITCHED)
+ broadcastDispatcher.registerReceiver(userChangeReceiver, userFilter, null, UserHandle.ALL)
}
fun onNotificationAdded(key: String, sbn: StatusBarNotification) {
@@ -131,6 +149,17 @@ class MediaDataManager @Inject constructor(
}
}
+ private fun clearData() {
+ // Called on user change. Remove all current MediaData objects and inform listeners
+ val listenersCopy = listeners.toSet()
+ mediaEntries.forEach {
+ listenersCopy.forEach { listener ->
+ listener.onMediaDataRemoved(it.key)
+ }
+ }
+ mediaEntries.clear()
+ }
+
private fun addResumptionControls(
desc: MediaDescription,
action: Runnable,
@@ -337,15 +366,20 @@ class MediaDataManager @Inject constructor(
actionsToShowCollapsed.remove(index)
continue
}
+ val runnable = if (action.actionIntent != null) {
+ Runnable {
+ try {
+ action.actionIntent.send()
+ } catch (e: PendingIntent.CanceledException) {
+ Log.d(TAG, "Intent canceled", e)
+ }
+ }
+ } else {
+ null
+ }
val mediaAction = MediaAction(
action.getIcon().loadDrawable(packageContext),
- Runnable {
- try {
- action.actionIntent.send()
- } catch (e: PendingIntent.CanceledException) {
- Log.d(TAG, "Intent canceled", e)
- }
- },
+ runnable,
action.title)
actionIcons.add(mediaAction)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
index 2f521ea39242..de0af16bc2fa 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
@@ -34,7 +34,6 @@ class MediaDeviceManager @Inject constructor(
private val context: Context,
private val localMediaManagerFactory: LocalMediaManagerFactory,
private val mr2manager: MediaRouter2Manager,
- private val featureFlag: MediaFeatureFlag,
@Main private val fgExecutor: Executor,
private val mediaDataManager: MediaDataManager
) : MediaDataManager.Listener {
@@ -56,20 +55,19 @@ class MediaDeviceManager @Inject constructor(
fun removeListener(listener: Listener) = listeners.remove(listener)
override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
- if (featureFlag.enabled) {
- if (oldKey != null && oldKey != key) {
- val oldToken = entries.remove(oldKey)
- oldToken?.stop()
- }
- var tok = entries[key]
- if (tok == null && data.token != null) {
- val controller = MediaController(context, data.token!!)
- tok = Token(key, controller, localMediaManagerFactory.create(data.packageName))
- entries[key] = tok
- tok.start()
+ if (oldKey != null && oldKey != key) {
+ val oldEntry = entries.remove(oldKey)
+ oldEntry?.stop()
+ }
+ var entry = entries[key]
+ if (entry == null || entry?.token != data.token) {
+ entry?.stop()
+ val controller = data.token?.let {
+ MediaController(context, it)
}
- } else {
- onMediaDataRemoved(key)
+ entry = Token(key, controller, localMediaManagerFactory.create(data.packageName))
+ entries[key] = entry
+ entry.start()
}
}
@@ -100,9 +98,11 @@ class MediaDeviceManager @Inject constructor(
private inner class Token(
val key: String,
- val controller: MediaController,
+ val controller: MediaController?,
val localMediaManager: LocalMediaManager
) : LocalMediaManager.DeviceCallback {
+ val token
+ get() = controller?.sessionToken
private var started = false
private var current: MediaDevice? = null
set(value) {
@@ -132,10 +132,14 @@ class MediaDeviceManager @Inject constructor(
}
private fun updateCurrent() {
val device = localMediaManager.getCurrentConnectedDevice()
- val route = mr2manager.getRoutingSessionForMediaController(controller)
- // If we get a null route, then don't trust the device. Just set to null to disable the
- // output switcher chip.
- current = if (route != null) device else null
+ controller?.let {
+ val route = mr2manager.getRoutingSessionForMediaController(it)
+ // If we get a null route, then don't trust the device. Just set to null to disable the
+ // output switcher chip.
+ current = if (route != null) device else null
+ } ?: run {
+ current = device
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
index 6bbe0d1651dd..e8a4b1e46fec 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
@@ -40,7 +40,7 @@ import javax.inject.Singleton
private const val TAG = "MediaResumeListener"
private const val MEDIA_PREFERENCES = "media_control_prefs"
-private const val MEDIA_PREFERENCE_KEY = "browser_components"
+private const val MEDIA_PREFERENCE_KEY = "browser_components_"
@Singleton
class MediaResumeListener @Inject constructor(
@@ -63,11 +63,16 @@ class MediaResumeListener @Inject constructor(
lateinit var resumeComponentFoundCallback: (String, Runnable?) -> Unit
private var mediaBrowser: ResumeMediaBrowser? = null
+ private var currentUserId: Int
- private val unlockReceiver = object : BroadcastReceiver() {
+ private val userChangeReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (Intent.ACTION_USER_UNLOCKED == intent.action) {
loadMediaResumptionControls()
+ } else if (Intent.ACTION_USER_SWITCHED == intent.action) {
+ currentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1)
+ loadSavedComponents()
+ loadMediaResumptionControls()
}
}
}
@@ -97,18 +102,22 @@ class MediaResumeListener @Inject constructor(
}
init {
+ currentUserId = context.userId
if (useMediaResumption) {
val unlockFilter = IntentFilter()
unlockFilter.addAction(Intent.ACTION_USER_UNLOCKED)
- broadcastDispatcher.registerReceiver(unlockReceiver, unlockFilter, null, UserHandle.ALL)
+ unlockFilter.addAction(Intent.ACTION_USER_SWITCHED)
+ broadcastDispatcher.registerReceiver(userChangeReceiver, unlockFilter, null,
+ UserHandle.ALL)
loadSavedComponents()
}
}
private fun loadSavedComponents() {
- val userContext = context.createContextAsUser(context.getUser(), 0)
- val prefs = userContext.getSharedPreferences(MEDIA_PREFERENCES, Context.MODE_PRIVATE)
- val listString = prefs.getString(MEDIA_PREFERENCE_KEY, null)
+ // Make sure list is empty (if we switched users)
+ resumeComponents.clear()
+ val prefs = context.getSharedPreferences(MEDIA_PREFERENCES, Context.MODE_PRIVATE)
+ val listString = prefs.getString(MEDIA_PREFERENCE_KEY + currentUserId, null)
val components = listString?.split(ResumeMediaBrowser.DELIMITER.toRegex())
?.dropLastWhile { it.isEmpty() }
components?.forEach {
@@ -133,7 +142,6 @@ class MediaResumeListener @Inject constructor(
val browser = ResumeMediaBrowser(context, mediaBrowserCallback, it)
browser.findRecentMedia()
}
- broadcastDispatcher.unregisterReceiver(unlockReceiver) // only need to load once
}
override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
@@ -212,9 +220,8 @@ class MediaResumeListener @Inject constructor(
sb.append(it.flattenToString())
sb.append(ResumeMediaBrowser.DELIMITER)
}
- val userContext = context.createContextAsUser(context.getUser(), 0)
- val prefs = userContext.getSharedPreferences(MEDIA_PREFERENCES, Context.MODE_PRIVATE)
- prefs.edit().putString(MEDIA_PREFERENCE_KEY, sb.toString()).apply()
+ val prefs = context.getSharedPreferences(MEDIA_PREFERENCES, Context.MODE_PRIVATE)
+ prefs.edit().putString(MEDIA_PREFERENCE_KEY + currentUserId, sb.toString()).apply()
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
index efc476d0c86f..1dca3f1297b1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
@@ -20,19 +20,22 @@ import android.media.MediaMetadata
import android.media.session.MediaController
import android.media.session.PlaybackState
import android.os.SystemClock
+import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View
+import android.view.ViewConfiguration
import android.widget.SeekBar
import androidx.annotation.AnyThread
import androidx.annotation.WorkerThread
+import androidx.core.view.GestureDetectorCompat
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.LiveData
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.util.concurrency.RepeatableExecutor
-import java.util.concurrent.Executor
import javax.inject.Inject
private const val POSITION_UPDATE_INTERVAL_MILLIS = 100L
+private const val MIN_FLING_VELOCITY_SCALE_FACTOR = 10
private fun PlaybackState.isInMotion(): Boolean {
return this.state == PlaybackState.STATE_PLAYING ||
@@ -68,7 +71,6 @@ private fun PlaybackState.computePosition(duration: Long): Long {
/** ViewModel for seek bar in QS media player. */
class SeekBarViewModel @Inject constructor(@Background private val bgExecutor: RepeatableExecutor) {
-
private var _data = Progress(false, false, null, null)
set(value) {
field = value
@@ -104,6 +106,9 @@ class SeekBarViewModel @Inject constructor(@Background private val bgExecutor: R
}
private var cancel: Runnable? = null
+ /** Indicates if the seek interaction is considered a false guesture. */
+ private var isFalseSeek = false
+
/** Listening state (QS open or closed) is used to control polling of progress. */
var listening = true
set(value) = bgExecutor.execute {
@@ -113,16 +118,61 @@ class SeekBarViewModel @Inject constructor(@Background private val bgExecutor: R
}
}
+ /** Set to true when the user is touching the seek bar to change the position. */
+ private var scrubbing = false
+ set(value) {
+ if (field != value) {
+ field = value
+ checkIfPollingNeeded()
+ }
+ }
+
+ /**
+ * Event indicating that the user has started interacting with the seek bar.
+ */
+ @AnyThread
+ fun onSeekStarting() = bgExecutor.execute {
+ scrubbing = true
+ isFalseSeek = false
+ }
+
+ /**
+ * Event indicating that the user has moved the seek bar but hasn't yet finished the gesture.
+ * @param position Current location in the track.
+ */
+ @AnyThread
+ fun onSeekProgress(position: Long) = bgExecutor.execute {
+ if (scrubbing) {
+ _data = _data.copy(elapsedTime = position.toInt())
+ }
+ }
+
+ /**
+ * Event indicating that the seek interaction is a false gesture and it should be ignored.
+ */
+ @AnyThread
+ fun onSeekFalse() = bgExecutor.execute {
+ if (scrubbing) {
+ isFalseSeek = true
+ }
+ }
+
/**
* Handle request to change the current position in the media track.
* @param position Place to seek to in the track.
*/
- @WorkerThread
- fun onSeek(position: Long) {
- controller?.transportControls?.seekTo(position)
- // Invalidate the cached playbackState to avoid the thumb jumping back to the previous
- // position.
- playbackState = null
+ @AnyThread
+ fun onSeek(position: Long) = bgExecutor.execute {
+ if (isFalseSeek) {
+ scrubbing = false
+ checkPlaybackPosition()
+ } else {
+ controller?.transportControls?.seekTo(position)
+ // Invalidate the cached playbackState to avoid the thumb jumping back to the previous
+ // position.
+ playbackState = null
+ scrubbing = false
+ }
}
/**
@@ -180,7 +230,7 @@ class SeekBarViewModel @Inject constructor(@Background private val bgExecutor: R
@WorkerThread
private fun checkIfPollingNeeded() {
- val needed = listening && playbackState?.isInMotion() ?: false
+ val needed = listening && !scrubbing && playbackState?.isInMotion() ?: false
if (needed) {
if (cancel == null) {
cancel = bgExecutor.executeRepeatedly(this::checkPlaybackPosition, 0L,
@@ -195,41 +245,162 @@ class SeekBarViewModel @Inject constructor(@Background private val bgExecutor: R
/** Gets a listener to attach to the seek bar to handle seeking. */
val seekBarListener: SeekBar.OnSeekBarChangeListener
get() {
- return SeekBarChangeListener(this, bgExecutor)
+ return SeekBarChangeListener(this)
}
- /** Gets a listener to attach to the seek bar to disable touch intercepting. */
- val seekBarTouchListener: View.OnTouchListener
- get() {
- return SeekBarTouchListener()
- }
+ /** Attach touch handlers to the seek bar view. */
+ fun attachTouchHandlers(bar: SeekBar) {
+ bar.setOnSeekBarChangeListener(seekBarListener)
+ bar.setOnTouchListener(SeekBarTouchListener(this, bar))
+ }
private class SeekBarChangeListener(
- val viewModel: SeekBarViewModel,
- val bgExecutor: Executor
+ val viewModel: SeekBarViewModel
) : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(bar: SeekBar, progress: Int, fromUser: Boolean) {
if (fromUser) {
- bgExecutor.execute {
- viewModel.onSeek(progress.toLong())
- }
+ viewModel.onSeekProgress(progress.toLong())
}
}
+
override fun onStartTrackingTouch(bar: SeekBar) {
+ viewModel.onSeekStarting()
}
+
override fun onStopTrackingTouch(bar: SeekBar) {
- val pos = bar.progress.toLong()
- bgExecutor.execute {
- viewModel.onSeek(pos)
- }
+ viewModel.onSeek(bar.progress.toLong())
}
}
- private class SeekBarTouchListener : View.OnTouchListener {
+ /**
+ * Responsible for intercepting touch events before they reach the seek bar.
+ *
+ * This reduces the gestures seen by the seek bar so that users don't accidentially seek when
+ * they intend to scroll the carousel.
+ */
+ private class SeekBarTouchListener(
+ private val viewModel: SeekBarViewModel,
+ private val bar: SeekBar
+ ) : View.OnTouchListener, GestureDetector.OnGestureListener {
+
+ // Gesture detector helps decide which touch events to intercept.
+ private val detector = GestureDetectorCompat(bar.context, this)
+ // Velocity threshold used to decide when a fling is considered a false gesture.
+ private val flingVelocity: Int = ViewConfiguration.get(bar.context).run {
+ getScaledMinimumFlingVelocity() * MIN_FLING_VELOCITY_SCALE_FACTOR
+ }
+ // Indicates if the gesture should go to the seek bar or if it should be intercepted.
+ private var shouldGoToSeekBar = false
+
+ /**
+ * Decide which touch events to intercept before they reach the seek bar.
+ *
+ * Based on the gesture detected, we decide whether we want the event to reach the seek bar.
+ * If we want the seek bar to see the event, then we return false so that the event isn't
+ * handled here and it will be passed along. If, however, we don't want the seek bar to see
+ * the event, then return true so that the event is handled here.
+ *
+ * When the seek bar is contained in the carousel, the carousel still has the ability to
+ * intercept the touch event. So, even though we may handle the event here, the carousel can
+ * still intercept the event. This way, gestures that we consider falses on the seek bar can
+ * still be used by the carousel for paging.
+ *
+ * Returns true for events that we don't want dispatched to the seek bar.
+ */
override fun onTouch(view: View, event: MotionEvent): Boolean {
- view.parent.requestDisallowInterceptTouchEvent(true)
- return view.onTouchEvent(event)
+ if (view != bar) {
+ return false
+ }
+ detector.onTouchEvent(event)
+ return !shouldGoToSeekBar
+ }
+
+ /**
+ * Handle down events that press down on the thumb.
+ *
+ * On the down action, determine a target box around the thumb to know when a scroll
+ * gesture starts by clicking on the thumb. The target box will be used by subsequent
+ * onScroll events.
+ *
+ * Returns true when the down event hits within the target box of the thumb.
+ */
+ override fun onDown(event: MotionEvent): Boolean {
+ val padL = bar.paddingLeft
+ val padR = bar.paddingRight
+ // Compute the X location of the thumb as a function of the seek bar progress.
+ // TODO: account for thumb offset
+ val progress = bar.getProgress()
+ val range = bar.max - bar.min
+ val widthFraction = if (range > 0) {
+ (progress - bar.min).toDouble() / range
+ } else {
+ 0.0
+ }
+ val availableWidth = bar.width - padL - padR
+ val thumbX = if (bar.isLayoutRtl()) {
+ padL + availableWidth * (1 - widthFraction)
+ } else {
+ padL + availableWidth * widthFraction
+ }
+ // Set the min, max boundaries of the thumb box.
+ // I'm cheating by using the height of the seek bar as the width of the box.
+ val halfHeight: Int = bar.height / 2
+ val targetBoxMinX = (Math.round(thumbX) - halfHeight).toInt()
+ val targetBoxMaxX = (Math.round(thumbX) + halfHeight).toInt()
+ // If the x position of the down event is within the box, then request that the parent
+ // not intercept the event.
+ val x = Math.round(event.x)
+ shouldGoToSeekBar = x >= targetBoxMinX && x <= targetBoxMaxX
+ if (shouldGoToSeekBar) {
+ bar.parent?.requestDisallowInterceptTouchEvent(true)
+ }
+ return shouldGoToSeekBar
+ }
+
+ /**
+ * Always handle single tap up.
+ *
+ * This enables the user to single tap anywhere on the seek bar to seek to that position.
+ */
+ override fun onSingleTapUp(event: MotionEvent): Boolean {
+ shouldGoToSeekBar = true
+ return true
}
+
+ /**
+ * Handle scroll events when the down event is on the thumb.
+ *
+ * Returns true when the down event of the scroll hits within the target box of the thumb.
+ */
+ override fun onScroll(
+ eventStart: MotionEvent,
+ event: MotionEvent,
+ distanceX: Float,
+ distanceY: Float
+ ): Boolean {
+ return shouldGoToSeekBar
+ }
+
+ /**
+ * Handle fling events when the down event is on the thumb.
+ *
+ * Gestures that include a fling are considered a false gesture on the seek bar.
+ */
+ override fun onFling(
+ eventStart: MotionEvent,
+ event: MotionEvent,
+ velocityX: Float,
+ velocityY: Float
+ ): Boolean {
+ if (Math.abs(velocityX) > flingVelocity || Math.abs(velocityY) > flingVelocity) {
+ viewModel.onSeekFalse()
+ }
+ return shouldGoToSeekBar
+ }
+
+ override fun onShowPress(event: MotionEvent) {}
+
+ override fun onLongPress(event: MotionEvent) {}
}
/** State seen by seek bar UI. */
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
index 7f7e1085d497..2980f11b3cbc 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
@@ -16,6 +16,7 @@
package com.android.systemui.pip;
+import android.animation.AnimationHandler;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
@@ -26,6 +27,7 @@ import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -79,6 +81,13 @@ public class PipAnimationController {
private PipTransitionAnimator mCurrentAnimator;
+ private ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal =
+ ThreadLocal.withInitial(() -> {
+ AnimationHandler handler = new AnimationHandler();
+ handler.setProvider(new SfVsyncFrameCallbackProvider());
+ return handler;
+ });
+
@Inject
PipAnimationController(Context context, PipSurfaceTransactionHelper helper) {
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
@@ -135,6 +144,7 @@ public class PipAnimationController {
animator.setSurfaceTransactionHelper(mSurfaceTransactionHelper);
animator.setInterpolator(mFastOutSlowInInterpolator);
animator.setFloatValues(FRACTION_START, FRACTION_END);
+ animator.setAnimationHandler(mSfAnimationHandlerThreadLocal.get());
return animator;
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index c6f144aa57a1..b93e07e65c73 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -504,8 +504,18 @@ public class PipTaskOrganizer extends TaskOrganizer implements
// this could happen if rotation finishes before the animation
mLastReportedBounds.set(destinationBoundsOut);
scheduleFinishResizePip(mLastReportedBounds);
- } else if (!mLastReportedBounds.isEmpty()) {
- destinationBoundsOut.set(mLastReportedBounds);
+ } else {
+ // There could be an animation on-going. If there is one on-going, last-reported
+ // bounds isn't yet updated. We'll use the animator's bounds instead.
+ if (animator != null && animator.isRunning()) {
+ if (!animator.getDestinationBounds().isEmpty()) {
+ destinationBoundsOut.set(animator.getDestinationBounds());
+ }
+ } else {
+ if (!mLastReportedBounds.isEmpty()) {
+ destinationBoundsOut.set(mLastReportedBounds);
+ }
+ }
}
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index b714bff15d8a..e38bfb441a0b 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -269,7 +269,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
if (stackInfo != null) {
// If SystemUI restart, and it already existed a pinned stack,
// register the pip input consumer to ensure touch can send to it.
- mInputConsumerController.registerInputConsumer();
+ mInputConsumerController.registerInputConsumer(true /* withSfVsync */);
}
} catch (RemoteException | UnsupportedOperationException e) {
e.printStackTrace();
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index a3185a2ad796..6ab73fcce399 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -295,6 +295,14 @@ public class PipMenuActivity extends Activity {
}
@Override
+ public void onTopResumedActivityChanged(boolean isTopResumedActivity) {
+ super.onTopResumedActivityChanged(isTopResumedActivity);
+ if (!isTopResumedActivity && mMenuState != MENU_STATE_NONE) {
+ hideMenu();
+ }
+ }
+
+ @Override
protected void onStop() {
super.onStop();
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index 8b4d932619a9..31d292fa3fd7 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -197,7 +197,7 @@ public class PipMenuActivityController {
}
public void onActivityPinned() {
- mInputConsumerController.registerInputConsumer();
+ mInputConsumerController.registerInputConsumer(true /* withSfVsync */);
}
public void onActivityUnpinned() {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index 2e75bab7ae32..d077666f8184 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -23,9 +23,11 @@ import android.content.Context;
import android.graphics.Rect;
import android.os.Debug;
import android.util.Log;
+import android.view.Choreographer;
import androidx.dynamicanimation.animation.SpringForce;
+import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.pip.PipSnapAlgorithm;
import com.android.systemui.pip.PipTaskOrganizer;
import com.android.systemui.util.FloatingContentCoordinator;
@@ -68,6 +70,9 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
/** The region that all of PIP must stay within. */
private final Rect mFloatingAllowedArea = new Rect();
+ private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider =
+ new SfVsyncFrameCallbackProvider();
+
/**
* Bounds that are animated using the physics animator.
*/
@@ -79,6 +84,10 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
/** Coordinator instance for resolving conflicts with other floating content. */
private FloatingContentCoordinator mFloatingContentCoordinator;
+ /** Callback that re-sizes PIP to the animated bounds. */
+ private final Choreographer.FrameCallback mResizePipVsyncCallback =
+ l -> resizePipUnchecked(mAnimatedBounds);
+
/**
* PhysicsAnimator instance for animating {@link #mAnimatedBounds} using physics animations.
*/
@@ -89,7 +98,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
* Update listener that resizes the PIP to {@link #mAnimatedBounds}.
*/
final PhysicsAnimator.UpdateListener<Rect> mResizePipUpdateListener =
- (target, values) -> resizePipUnchecked(mAnimatedBounds);
+ (target, values) -> mSfVsyncFrameProvider.postFrameCallback(mResizePipVsyncCallback);
/** FlingConfig instances provided to PhysicsAnimator for fling gestures. */
private PhysicsAnimator.FlingConfig mFlingConfigX;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
index bafbd216b1a2..f6b212c6f19f 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
@@ -32,6 +32,8 @@ import android.hardware.input.InputManager;
import android.os.Handler;
import android.os.Looper;
import android.provider.DeviceConfig;
+import android.view.BatchedInputEventReceiver;
+import android.view.Choreographer;
import android.view.InputChannel;
import android.view.InputEvent;
import android.view.InputEventReceiver;
@@ -323,9 +325,9 @@ public class PipResizeGestureHandler {
mMinSize.set(minX, minY);
}
- class SysUiInputEventReceiver extends InputEventReceiver {
+ class SysUiInputEventReceiver extends BatchedInputEventReceiver {
SysUiInputEventReceiver(InputChannel channel, Looper looper) {
- super(channel, looper);
+ super(channel, looper, Choreographer.getSfInstance());
}
public void onInputEvent(InputEvent event) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java
index 2365e67fec2f..0a84f5ee1bb9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java
@@ -23,6 +23,7 @@ import static com.android.systemui.statusbar.phone.AutoTileManager.WORK;
import android.content.Context;
import android.database.ContentObserver;
import android.os.Handler;
+import android.os.UserHandle;
import android.provider.Settings.Secure;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -30,6 +31,7 @@ import android.util.ArraySet;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Prefs;
import com.android.systemui.Prefs.Key;
+import com.android.systemui.util.UserAwareController;
import java.util.Arrays;
import java.util.Collection;
@@ -37,7 +39,7 @@ import java.util.Collections;
import javax.inject.Inject;
-public class AutoAddTracker {
+public class AutoAddTracker implements UserAwareController {
private static final String[][] CONVERT_PREFS = {
{Key.QS_HOTSPOT_ADDED, HOTSPOT},
@@ -49,20 +51,39 @@ public class AutoAddTracker {
private final ArraySet<String> mAutoAdded;
private final Context mContext;
+ private int mUserId;
- @Inject
- public AutoAddTracker(Context context) {
+ public AutoAddTracker(Context context, int userId) {
mContext = context;
+ mUserId = userId;
mAutoAdded = new ArraySet<>(getAdded());
// TODO: remove migration code and shared preferences keys after P release
- for (String[] convertPref : CONVERT_PREFS) {
- if (Prefs.getBoolean(context, convertPref[0], false)) {
- setTileAdded(convertPref[1]);
- Prefs.remove(context, convertPref[0]);
+ if (mUserId == UserHandle.USER_SYSTEM) {
+ for (String[] convertPref : CONVERT_PREFS) {
+ if (Prefs.getBoolean(context, convertPref[0], false)) {
+ setTileAdded(convertPref[1]);
+ Prefs.remove(context, convertPref[0]);
+ }
}
}
mContext.getContentResolver().registerContentObserver(
- Secure.getUriFor(Secure.QS_AUTO_ADDED_TILES), false, mObserver);
+ Secure.getUriFor(Secure.QS_AUTO_ADDED_TILES), false, mObserver,
+ UserHandle.USER_ALL);
+ }
+
+ @Override
+ public void changeUser(UserHandle newUser) {
+ if (newUser.getIdentifier() == mUserId) {
+ return;
+ }
+ mUserId = newUser.getIdentifier();
+ mAutoAdded.clear();
+ mAutoAdded.addAll(getAdded());
+ }
+
+ @Override
+ public int getCurrentUserId() {
+ return mUserId;
}
public boolean isAdded(String tile) {
@@ -86,12 +107,13 @@ public class AutoAddTracker {
}
private void saveTiles() {
- Secure.putString(mContext.getContentResolver(), Secure.QS_AUTO_ADDED_TILES,
- TextUtils.join(",", mAutoAdded));
+ Secure.putStringForUser(mContext.getContentResolver(), Secure.QS_AUTO_ADDED_TILES,
+ TextUtils.join(",", mAutoAdded), mUserId);
}
private Collection<String> getAdded() {
- String current = Secure.getString(mContext.getContentResolver(), Secure.QS_AUTO_ADDED_TILES);
+ String current = Secure.getStringForUser(mContext.getContentResolver(),
+ Secure.QS_AUTO_ADDED_TILES, mUserId);
if (current == null) {
return Collections.emptyList();
}
@@ -102,7 +124,27 @@ public class AutoAddTracker {
protected final ContentObserver mObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
+ mAutoAdded.clear();
mAutoAdded.addAll(getAdded());
}
};
+
+ public static class Builder {
+ private final Context mContext;
+ private int mUserId;
+
+ @Inject
+ public Builder(Context context) {
+ mContext = context;
+ }
+
+ public Builder setUserId(int userId) {
+ mUserId = userId;
+ return this;
+ }
+
+ public AutoAddTracker build() {
+ return new AutoAddTracker(mContext, mUserId);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 65d3572d04a3..858a7e2e30e0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -255,6 +255,9 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
int currentUser = ActivityManager.getCurrentUser();
if (currentUser != mCurrentUser) {
mUserContext = mContext.createContextAsUser(UserHandle.of(currentUser), 0);
+ if (mAutoTiles != null) {
+ mAutoTiles.changeUser(UserHandle.of(currentUser));
+ }
}
if (tileSpecs.equals(mTileSpecs) && currentUser == mCurrentUser) return;
mTiles.entrySet().stream().filter(tile -> !tileSpecs.contains(tile.getKey())).forEach(
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index a624479fa63c..7babe2f4f2db 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -41,13 +41,19 @@ import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Outline;
import android.graphics.PixelFormat;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
+import android.graphics.drawable.InsetDrawable;
+import android.graphics.drawable.LayerDrawable;
import android.media.MediaActionSound;
import android.net.Uri;
import android.os.Handler;
@@ -114,7 +120,6 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
public Consumer<Uri> finisher;
public GlobalScreenshot.ActionsReadyListener mActionsReadyListener;
public int errorMsgResId;
- public boolean createDeleteAction;
void clearImage() {
image = null;
@@ -168,6 +173,8 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
private static final long SCREENSHOT_FLASH_IN_DURATION_MS = 133;
private static final long SCREENSHOT_FLASH_OUT_DURATION_MS = 217;
+ // delay before starting to fade in dismiss button
+ private static final long SCREENSHOT_TO_CORNER_DISMISS_DELAY_MS = 200;
private static final long SCREENSHOT_TO_CORNER_X_DURATION_MS = 234;
private static final long SCREENSHOT_TO_CORNER_Y_DURATION_MS = 500;
private static final long SCREENSHOT_TO_CORNER_SCALE_DURATION_MS = 234;
@@ -208,6 +215,7 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
private Animator mScreenshotAnimation;
private Runnable mOnCompleteRunnable;
private Animator mDismissAnimation;
+ private SavedImageData mImageData;
private boolean mInDarkMode = false;
private boolean mDirectionLTR = true;
private boolean mOrientationPortrait = true;
@@ -226,6 +234,9 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
switch (msg.what) {
case MESSAGE_CORNER_TIMEOUT:
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_INTERACTION_TIMEOUT);
+ if (mImageData != null) {
+ mNotificationsController.showSilentScreenshotNotification(mImageData);
+ }
GlobalScreenshot.this.dismissScreenshot("timeout", false);
mOnCompleteRunnable.run();
break;
@@ -261,7 +272,8 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
- | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED,
+ | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+ | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
PixelFormat.TRANSLUCENT);
mWindowLayoutParams.setTitle("ScreenshotAnimation");
mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
@@ -396,6 +408,9 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
mDismissButton = mScreenshotLayout.findViewById(R.id.global_screenshot_dismiss_button);
mDismissButton.setOnClickListener(view -> {
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EXPLICIT_DISMISSAL);
+ if (mImageData != null) {
+ mNotificationsController.showSilentScreenshotNotification(mImageData);
+ }
dismissScreenshot("dismiss_button", false);
mOnCompleteRunnable.run();
});
@@ -424,7 +439,6 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
data.image = mScreenBitmap;
data.finisher = finisher;
data.mActionsReadyListener = actionsReadyListener;
- data.createDeleteAction = false;
if (mSaveInBgTask != null) {
// just log success/failure for the pre-existing screenshot
@@ -436,6 +450,10 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
});
}
+ mImageData = null; // make sure we clear the current stored data
+ mNotificationsController.reset();
+ mNotificationsController.setImage(mScreenBitmap);
+
mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data);
mSaveInBgTask.execute();
}
@@ -449,10 +467,12 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
int rot = mDisplay.getRotation();
int width = crop.width();
int height = crop.height();
- takeScreenshot(SurfaceControl.screenshot(crop, width, height, rot), finisher, screenRect);
+ takeScreenshot(SurfaceControl.screenshot(crop, width, height, rot), finisher, screenRect,
+ Insets.NONE, true);
}
- private void takeScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect) {
+ private void takeScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
+ Insets screenInsets, boolean showFlash) {
dismissScreenshot("new screenshot requested", true);
mScreenBitmap = screenshot;
@@ -482,7 +502,7 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
mDismissAnimation.cancel();
}
// Start the post-screenshot animation
- startAnimation(finisher, mScreenBitmap.getWidth(), mScreenBitmap.getHeight(), screenRect);
+ startAnimation(finisher, screenRect, screenInsets, showFlash);
}
void takeScreenshot(Consumer<Uri> finisher, Runnable onComplete) {
@@ -498,9 +518,15 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
Insets visibleInsets, int taskId, int userId, ComponentName topComponent,
Consumer<Uri> finisher, Runnable onComplete) {
// TODO: use task Id, userId, topComponent for smart handler
- // TODO: use visibleInsets for animation
+
mOnCompleteRunnable = onComplete;
- takeScreenshot(screenshot, finisher, screenshotScreenBounds);
+ if (aspectRatiosMatch(screenshot, visibleInsets, screenshotScreenBounds)) {
+ takeScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, false);
+ } else {
+ takeScreenshot(screenshot, finisher,
+ new Rect(0, 0, screenshot.getWidth(), screenshot.getHeight()), Insets.NONE,
+ true);
+ }
}
/**
@@ -621,8 +647,9 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
}
// Clear any references to the bitmap
- mScreenshotPreview.setImageBitmap(null);
- mScreenshotAnimatedView.setImageBitmap(null);
+ mScreenshotPreview.setImageDrawable(null);
+ mScreenshotAnimatedView.setImageDrawable(null);
+ mScreenshotAnimatedView.setVisibility(View.GONE);
mActionsContainerBackground.setVisibility(View.GONE);
mActionsContainer.setVisibility(View.GONE);
mBackgroundProtection.setAlpha(0f);
@@ -643,6 +670,7 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
*/
private void showUiOnActionsReady(SavedImageData imageData) {
logSuccessOnActionsReady(imageData);
+ mImageData = imageData;
AccessibilityManager accessibilityManager = (AccessibilityManager)
mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
@@ -688,8 +716,9 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
/**
* Starts the animation after taking the screenshot
*/
- private void startAnimation(
- final Consumer<Uri> finisher, int bitmapWidth, int bitmapHeight, Rect screenRect) {
+ private void startAnimation(final Consumer<Uri> finisher, Rect screenRect, Insets screenInsets,
+ boolean showFlash) {
+
// If power save is on, show a toast so there is some visual indication that a
// screenshot has been taken.
PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -701,9 +730,13 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
if (!mScreenshotLayout.isAttachedToWindow()) {
mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
}
- mScreenshotAnimatedView.setImageBitmap(mScreenBitmap);
- mScreenshotPreview.setImageBitmap(mScreenBitmap);
+ mScreenshotAnimatedView.setImageDrawable(
+ createScreenDrawable(mScreenBitmap, screenInsets));
+ setAnimatedViewSize(screenRect.width(), screenRect.height());
+ // Show when the animation starts
+ mScreenshotAnimatedView.setVisibility(View.GONE);
+ mScreenshotPreview.setImageDrawable(createScreenDrawable(mScreenBitmap, screenInsets));
// make static preview invisible (from gone) so we can query its location on screen
mScreenshotPreview.setVisibility(View.INVISIBLE);
@@ -711,14 +744,14 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
mScreenshotLayout.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
mScreenshotAnimation =
- createScreenshotDropInAnimation(bitmapWidth, bitmapHeight, screenRect);
+ createScreenshotDropInAnimation(screenRect, showFlash);
saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {
- @Override
- void onActionsReady(SavedImageData imageData) {
- showUiOnActionsReady(imageData);
- }
- });
+ @Override
+ void onActionsReady(SavedImageData imageData) {
+ showUiOnActionsReady(imageData);
+ }
+ });
// Play the shutter sound to notify that we've taken a screenshot
mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
@@ -730,19 +763,19 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
});
}
- private AnimatorSet createScreenshotDropInAnimation(
- int bitmapWidth, int bitmapHeight, Rect bounds) {
+ private AnimatorSet createScreenshotDropInAnimation(Rect bounds, boolean showFlash) {
Rect previewBounds = new Rect();
mScreenshotPreview.getBoundsOnScreen(previewBounds);
- float cornerScale = mCornerSizeX / (mOrientationPortrait ? bitmapWidth : bitmapHeight);
- float currentScale = bounds.height() / (float) bitmapHeight;
+ float cornerScale =
+ mCornerSizeX / (mOrientationPortrait ? bounds.width() : bounds.height());
+ final float currentScale = 1f;
mScreenshotAnimatedView.setScaleX(currentScale);
mScreenshotAnimatedView.setScaleY(currentScale);
- mScreenshotAnimatedView.setPivotX(0);
- mScreenshotAnimatedView.setPivotY(0);
+ mDismissButton.setAlpha(0);
+ mDismissButton.setVisibility(View.VISIBLE);
AnimatorSet dropInAnimation = new AnimatorSet();
ValueAnimator flashInAnimator = ValueAnimator.ofFloat(0, 1);
@@ -765,6 +798,8 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
toCorner.setDuration(SCREENSHOT_TO_CORNER_Y_DURATION_MS);
float xPositionPct =
SCREENSHOT_TO_CORNER_X_DURATION_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS;
+ float dismissPct =
+ SCREENSHOT_TO_CORNER_DISMISS_DELAY_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS;
float scalePct =
SCREENSHOT_TO_CORNER_SCALE_DURATION_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS;
toCorner.addUpdateListener(animation -> {
@@ -785,13 +820,26 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
if (t < xPositionPct) {
float xCenter = MathUtils.lerp(startPos.x, finalPos.x,
mFastOutSlowIn.getInterpolation(t / xPositionPct));
- mScreenshotAnimatedView.setX(xCenter - bitmapWidth * currentScaleX / 2f);
+ mScreenshotAnimatedView.setX(xCenter - bounds.width() * currentScaleX / 2f);
} else {
- mScreenshotAnimatedView.setX(finalPos.x - bitmapWidth * currentScaleX / 2f);
+ mScreenshotAnimatedView.setX(finalPos.x - bounds.width() * currentScaleX / 2f);
}
float yCenter = MathUtils.lerp(
startPos.y, finalPos.y, mFastOutSlowIn.getInterpolation(t));
- mScreenshotAnimatedView.setY(yCenter - bitmapHeight * currentScaleY / 2f);
+ mScreenshotAnimatedView.setY(yCenter - bounds.height() * currentScaleY / 2f);
+
+ if (t >= dismissPct) {
+ mDismissButton.setAlpha((t - dismissPct) / (1 - dismissPct));
+ float currentX = mScreenshotAnimatedView.getX();
+ float currentY = mScreenshotAnimatedView.getY();
+ mDismissButton.setY(currentY - mDismissButton.getHeight() / 2f);
+ if (mDirectionLTR) {
+ mDismissButton.setX(currentX
+ + bounds.width() * currentScaleX - mDismissButton.getWidth() / 2f);
+ } else {
+ mDismissButton.setX(currentX - mDismissButton.getWidth() / 2f);
+ }
+ }
});
toCorner.addListener(new AnimatorListenerAdapter() {
@@ -805,20 +853,31 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
mScreenshotFlash.setAlpha(0f);
mScreenshotFlash.setVisibility(View.VISIBLE);
- dropInAnimation.play(flashOutAnimator).after(flashInAnimator);
- dropInAnimation.play(flashOutAnimator).with(toCorner);
+ if (showFlash) {
+ dropInAnimation.play(flashOutAnimator).after(flashInAnimator);
+ dropInAnimation.play(flashOutAnimator).with(toCorner);
+ } else {
+ dropInAnimation.play(toCorner);
+ }
dropInAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
+ mDismissButton.setAlpha(1);
+ float dismissOffset = mDismissButton.getWidth() / 2f;
+ float finalDismissX = mDirectionLTR
+ ? finalPos.x - dismissOffset + bounds.width() * cornerScale / 2f
+ : finalPos.x - dismissOffset - bounds.width() * cornerScale / 2f;
+ mDismissButton.setX(finalDismissX);
+ mDismissButton.setY(
+ finalPos.y - dismissOffset - bounds.height() * cornerScale / 2f);
mScreenshotAnimatedView.setScaleX(1);
mScreenshotAnimatedView.setScaleY(1);
mScreenshotAnimatedView.setX(finalPos.x - bounds.width() * cornerScale / 2f);
mScreenshotAnimatedView.setY(finalPos.y - bounds.height() * cornerScale / 2f);
mScreenshotAnimatedView.setVisibility(View.GONE);
mScreenshotPreview.setVisibility(View.VISIBLE);
- mDismissButton.setVisibility(View.VISIBLE);
mScreenshotLayout.forceLayout();
}
});
@@ -972,6 +1031,71 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
return animSet;
}
+ private void setAnimatedViewSize(int width, int height) {
+ ViewGroup.LayoutParams layoutParams = mScreenshotAnimatedView.getLayoutParams();
+ layoutParams.width = width;
+ layoutParams.height = height;
+ mScreenshotAnimatedView.setLayoutParams(layoutParams);
+ }
+
+ /** Does the aspect ratio of the bitmap with insets removed match the bounds. */
+ private boolean aspectRatiosMatch(Bitmap bitmap, Insets bitmapInsets, Rect screenBounds) {
+ int insettedWidth = bitmap.getWidth() - bitmapInsets.left - bitmapInsets.right;
+ int insettedHeight = bitmap.getHeight() - bitmapInsets.top - bitmapInsets.bottom;
+
+ if (insettedHeight == 0 || insettedWidth == 0 || bitmap.getWidth() == 0
+ || bitmap.getHeight() == 0) {
+ Log.e(TAG, String.format(
+ "Provided bitmap and insets create degenerate region: %dx%d %s",
+ bitmap.getWidth(), bitmap.getHeight(), bitmapInsets));
+ return false;
+ }
+
+ float insettedBitmapAspect = ((float) insettedWidth) / insettedHeight;
+ float boundsAspect = ((float) screenBounds.width()) / screenBounds.height();
+
+ boolean matchWithinTolerance = Math.abs(insettedBitmapAspect - boundsAspect) < 0.1f;
+ if (!matchWithinTolerance) {
+ Log.d(TAG, String.format("aspectRatiosMatch: don't match bitmap: %f, bounds: %f",
+ insettedBitmapAspect, boundsAspect));
+ }
+
+ return matchWithinTolerance;
+ }
+
+ /**
+ * Create a drawable using the size of the bitmap and insets as the fractional inset parameters.
+ */
+ private Drawable createScreenDrawable(Bitmap bitmap, Insets insets) {
+ int insettedWidth = bitmap.getWidth() - insets.left - insets.right;
+ int insettedHeight = bitmap.getHeight() - insets.top - insets.bottom;
+
+ BitmapDrawable bitmapDrawable = new BitmapDrawable(mContext.getResources(), bitmap);
+ if (insettedHeight == 0 || insettedWidth == 0 || bitmap.getWidth() == 0
+ || bitmap.getHeight() == 0) {
+ Log.e(TAG, String.format(
+ "Can't create insetted drawable, using 0 insets "
+ + "bitmap and insets create degenerate region: %dx%d %s",
+ bitmap.getWidth(), bitmap.getHeight(), insets));
+ return bitmapDrawable;
+ }
+
+ InsetDrawable insetDrawable = new InsetDrawable(bitmapDrawable,
+ -1f * insets.left / insettedWidth,
+ -1f * insets.top / insettedHeight,
+ -1f * insets.right / insettedWidth,
+ -1f * insets.bottom / insettedHeight);
+
+ if (insets.left < 0 || insets.top < 0 || insets.right < 0 || insets.bottom < 0) {
+ // Are any of the insets negative, meaning the bitmap is smaller than the bounds so need
+ // to fill in the background of the drawable.
+ return new LayerDrawable(new Drawable[] {
+ new ColorDrawable(Color.BLACK), insetDrawable});
+ } else {
+ return insetDrawable;
+ }
+ }
+
/**
* Receiver to proxy the share or edit intent, used to clean up the notification and send
* appropriate signals to the system (ie. to dismiss the keyguard if necessary).
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java
deleted file mode 100644
index 0017b1f79b74..000000000000
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java
+++ /dev/null
@@ -1,567 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.graphics.Insets;
-import android.graphics.PixelFormat;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.media.MediaActionSound;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.PowerManager;
-import android.util.DisplayMetrics;
-import android.view.Display;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.SurfaceControl;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.view.animation.Interpolator;
-import android.widget.ImageView;
-import android.widget.Toast;
-
-import com.android.systemui.R;
-import com.android.systemui.dagger.qualifiers.Main;
-
-import java.util.function.Consumer;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-/**
- * Class for handling device screen shots
- *
- * @deprecated will be removed when corner flow is complete and tested
- */
-@Singleton
-@Deprecated
-public class GlobalScreenshotLegacy {
-
- // These strings are used for communicating the action invoked to
- // ScreenshotNotificationSmartActionsProvider.
- static final String EXTRA_ACTION_TYPE = "android:screenshot_action_type";
- static final String EXTRA_ID = "android:screenshot_id";
- static final String ACTION_TYPE_DELETE = "Delete";
- static final String ACTION_TYPE_SHARE = "Share";
- static final String ACTION_TYPE_EDIT = "Edit";
- static final String EXTRA_SMART_ACTIONS_ENABLED = "android:smart_actions_enabled";
- static final String EXTRA_ACTION_INTENT = "android:screenshot_action_intent";
-
- static final String SCREENSHOT_URI_ID = "android:screenshot_uri_id";
- static final String EXTRA_CANCEL_NOTIFICATION = "android:screenshot_cancel_notification";
- static final String EXTRA_DISALLOW_ENTER_PIP = "android:screenshot_disallow_enter_pip";
-
- private static final String TAG = "GlobalScreenshot";
-
- private static final int SCREENSHOT_FLASH_TO_PEAK_DURATION = 130;
- private static final int SCREENSHOT_DROP_IN_DURATION = 430;
- private static final int SCREENSHOT_DROP_OUT_DELAY = 500;
- private static final int SCREENSHOT_DROP_OUT_DURATION = 430;
- private static final int SCREENSHOT_DROP_OUT_SCALE_DURATION = 370;
- private static final int SCREENSHOT_FAST_DROP_OUT_DURATION = 320;
- private static final float BACKGROUND_ALPHA = 0.5f;
- private static final float SCREENSHOT_SCALE = 1f;
- private static final float SCREENSHOT_DROP_IN_MIN_SCALE = SCREENSHOT_SCALE * 0.725f;
- private static final float SCREENSHOT_DROP_OUT_MIN_SCALE = SCREENSHOT_SCALE * 0.45f;
- private static final float SCREENSHOT_FAST_DROP_OUT_MIN_SCALE = SCREENSHOT_SCALE * 0.6f;
- private static final float SCREENSHOT_DROP_OUT_MIN_SCALE_OFFSET = 0f;
-
- private final ScreenshotNotificationsController mNotificationsController;
-
- private Context mContext;
- private WindowManager mWindowManager;
- private WindowManager.LayoutParams mWindowLayoutParams;
- private Display mDisplay;
- private DisplayMetrics mDisplayMetrics;
-
- private Bitmap mScreenBitmap;
- private View mScreenshotLayout;
- private ScreenshotSelectorView mScreenshotSelectorView;
- private ImageView mBackgroundView;
- private ImageView mScreenshotView;
- private ImageView mScreenshotFlash;
-
- private AnimatorSet mScreenshotAnimation;
-
- private float mBgPadding;
- private float mBgPaddingScale;
-
- private AsyncTask<Void, Void, Void> mSaveInBgTask;
-
- private MediaActionSound mCameraSound;
-
- /**
- * @param context everything needs a context :(
- */
- @Inject
- public GlobalScreenshotLegacy(
- Context context, @Main Resources resources, LayoutInflater layoutInflater,
- ScreenshotNotificationsController screenshotNotificationsController) {
- mContext = context;
- mNotificationsController = screenshotNotificationsController;
-
- // Inflate the screenshot layout
- mScreenshotLayout = layoutInflater.inflate(R.layout.global_screenshot_legacy, null);
- mBackgroundView = mScreenshotLayout.findViewById(R.id.global_screenshot_legacy_background);
- mScreenshotView = mScreenshotLayout.findViewById(R.id.global_screenshot_legacy);
-
- mScreenshotFlash = mScreenshotLayout.findViewById(R.id.global_screenshot_legacy_flash);
- mScreenshotSelectorView = mScreenshotLayout.findViewById(
- R.id.global_screenshot_legacy_selector);
- mScreenshotLayout.setFocusable(true);
- mScreenshotSelectorView.setFocusable(true);
- mScreenshotSelectorView.setFocusableInTouchMode(true);
- mScreenshotLayout.setOnTouchListener((v, event) -> {
- // Intercept and ignore all touch events
- return true;
- });
-
- // Setup the window that we are going to use
- mWindowLayoutParams = new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 0, 0,
- WindowManager.LayoutParams.TYPE_SCREENSHOT,
- WindowManager.LayoutParams.FLAG_FULLSCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED,
- PixelFormat.TRANSLUCENT);
- mWindowLayoutParams.setTitle("ScreenshotAnimation");
- mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- mWindowLayoutParams.setFitInsetsTypes(0 /* types */);
- mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
- mDisplay = mWindowManager.getDefaultDisplay();
- mDisplayMetrics = new DisplayMetrics();
- mDisplay.getRealMetrics(mDisplayMetrics);
-
- // Scale has to account for both sides of the bg
- mBgPadding = (float) resources.getDimensionPixelSize(
- R.dimen.global_screenshot_legacy_bg_padding);
- mBgPaddingScale = mBgPadding / mDisplayMetrics.widthPixels;
-
-
- // Setup the Camera shutter sound
- mCameraSound = new MediaActionSound();
- mCameraSound.load(MediaActionSound.SHUTTER_CLICK);
- }
-
- /**
- * Creates a new worker thread and saves the screenshot to the media store.
- */
- private void saveScreenshotInWorkerThread(
- Consumer<Uri> finisher,
- @Nullable GlobalScreenshot.ActionsReadyListener actionsReadyListener) {
- GlobalScreenshot.SaveImageInBackgroundData data =
- new GlobalScreenshot.SaveImageInBackgroundData();
- data.image = mScreenBitmap;
- data.finisher = finisher;
- data.mActionsReadyListener = actionsReadyListener;
- data.createDeleteAction = true;
- if (mSaveInBgTask != null) {
- mSaveInBgTask.cancel(false);
- }
-
- mNotificationsController.reset();
- mNotificationsController.setImage(mScreenBitmap);
- mNotificationsController.showSavingScreenshotNotification();
-
- mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data).execute();
- }
-
- /**
- * Takes a screenshot of the current display and shows an animation.
- */
- private void takeScreenshot(Consumer<Uri> finisher, boolean statusBarVisible,
- boolean navBarVisible, Rect crop) {
- int rot = mDisplay.getRotation();
- int width = crop.width();
- int height = crop.height();
-
- takeScreenshot(SurfaceControl.screenshot(crop, width, height, rot), finisher,
- statusBarVisible, navBarVisible, null);
- }
-
- private void takeScreenshot(Bitmap screenshot, Consumer<Uri> finisher, boolean statusBarVisible,
- boolean navBarVisible, Rect screenboundsOfBitmap) {
- mScreenBitmap = screenshot;
- if (mScreenBitmap == null) {
- mNotificationsController.notifyScreenshotError(
- R.string.screenshot_failed_to_capture_text);
- finisher.accept(null);
- return;
- }
-
- // Optimizations
- mScreenBitmap.setHasAlpha(false);
- mScreenBitmap.prepareToDraw();
-
- // Start the post-screenshot animation
- startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
- statusBarVisible, navBarVisible, screenboundsOfBitmap);
- }
-
- void takeScreenshot(Consumer<Uri> finisher, boolean statusBarVisible, boolean navBarVisible) {
- mDisplay.getRealMetrics(mDisplayMetrics);
- takeScreenshot(finisher, statusBarVisible, navBarVisible,
- new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
- }
-
- void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
- Insets visibleInsets, int taskId, int userId, ComponentName topComponent,
- Consumer<Uri> finisher) {
- // TODO: use task Id, userId, topComponent for smart handler
- // TODO: use visibleInsets for animation
- takeScreenshot(screenshot, finisher, false, false, screenshotScreenBounds);
- }
-
- /**
- * Displays a screenshot selector
- */
- void takeScreenshotPartial(final Consumer<Uri> finisher, final boolean statusBarVisible,
- final boolean navBarVisible) {
- mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
- mScreenshotSelectorView.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- ScreenshotSelectorView view = (ScreenshotSelectorView) v;
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- view.startSelection((int) event.getX(), (int) event.getY());
- return true;
- case MotionEvent.ACTION_MOVE:
- view.updateSelection((int) event.getX(), (int) event.getY());
- return true;
- case MotionEvent.ACTION_UP:
- view.setVisibility(View.GONE);
- mWindowManager.removeView(mScreenshotLayout);
- final Rect rect = view.getSelectionRect();
- if (rect != null) {
- if (rect.width() != 0 && rect.height() != 0) {
- // Need mScreenshotLayout to handle it after the view disappears
- mScreenshotLayout.post(() -> takeScreenshot(
- finisher, statusBarVisible, navBarVisible, rect));
- }
- }
-
- view.stopSelection();
- return true;
- }
-
- return false;
- }
- });
- mScreenshotLayout.post(() -> {
- mScreenshotSelectorView.setVisibility(View.VISIBLE);
- mScreenshotSelectorView.requestFocus();
- });
- }
-
- /**
- * Cancels screenshot request
- */
- void stopScreenshot() {
- // If the selector layer still presents on screen, we remove it and resets its state.
- if (mScreenshotSelectorView.getSelectionRect() != null) {
- mWindowManager.removeView(mScreenshotLayout);
- mScreenshotSelectorView.stopSelection();
- }
- }
-
- /**
- * Clears current screenshot
- */
- private void clearScreenshot() {
- if (mScreenshotLayout.isAttachedToWindow()) {
- mWindowManager.removeView(mScreenshotLayout);
- }
-
- // Clear any references to the bitmap
- mScreenBitmap = null;
- mScreenshotView.setImageBitmap(null);
- mBackgroundView.setVisibility(View.GONE);
- mScreenshotView.setVisibility(View.GONE);
- mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, null);
- }
-
- /**
- * Starts the animation after taking the screenshot
- */
- private void startAnimation(final Consumer<Uri> finisher, int w, int h,
- boolean statusBarVisible, boolean navBarVisible, @Nullable Rect screenBoundsOfBitmap) {
- // If power save is on, show a toast so there is some visual indication that a screenshot
- // has been taken.
- PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- if (powerManager.isPowerSaveMode()) {
- Toast.makeText(mContext, R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show();
- }
-
- // Add the view for the animation
- mScreenshotView.setImageBitmap(mScreenBitmap);
- mScreenshotLayout.requestFocus();
-
- // Setup the animation with the screenshot just taken
- if (mScreenshotAnimation != null) {
- if (mScreenshotAnimation.isStarted()) {
- mScreenshotAnimation.end();
- }
- mScreenshotAnimation.removeAllListeners();
- }
-
- mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
- ValueAnimator screenshotDropInAnim = screenBoundsOfBitmap != null
- ? createRectAnimation(screenBoundsOfBitmap) : createScreenshotDropInAnimation();
- ValueAnimator screenshotFadeOutAnim =
- createScreenshotDropOutAnimation(w, h, statusBarVisible, navBarVisible);
- mScreenshotAnimation = new AnimatorSet();
- mScreenshotAnimation.playSequentially(screenshotDropInAnim, screenshotFadeOutAnim);
- mScreenshotAnimation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- // Save the screenshot once we have a bit of time now
- saveScreenshotInWorkerThread(finisher, new GlobalScreenshot.ActionsReadyListener() {
- @Override
- void onActionsReady(GlobalScreenshot.SavedImageData actionData) {
- if (actionData.uri == null) {
- mNotificationsController.notifyScreenshotError(
- R.string.screenshot_failed_to_capture_text);
- } else {
- mNotificationsController
- .showScreenshotActionsNotification(actionData);
- }
- }
- });
- clearScreenshot();
- }
- });
- mScreenshotLayout.post(() -> {
- // Play the shutter sound to notify that we've taken a screenshot
- mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
-
- mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- mScreenshotView.buildLayer();
- mScreenshotAnimation.start();
- });
- }
-
- private ValueAnimator createScreenshotDropInAnimation() {
- final float flashPeakDurationPct = ((float) (SCREENSHOT_FLASH_TO_PEAK_DURATION)
- / SCREENSHOT_DROP_IN_DURATION);
- final float flashDurationPct = 2f * flashPeakDurationPct;
- final Interpolator flashAlphaInterpolator = new Interpolator() {
- @Override
- public float getInterpolation(float x) {
- // Flash the flash view in and out quickly
- if (x <= flashDurationPct) {
- return (float) Math.sin(Math.PI * (x / flashDurationPct));
- }
- return 0;
- }
- };
- final Interpolator scaleInterpolator = new Interpolator() {
- @Override
- public float getInterpolation(float x) {
- // We start scaling when the flash is at it's peak
- if (x < flashPeakDurationPct) {
- return 0;
- }
- return (x - flashDurationPct) / (1f - flashDurationPct);
- }
- };
-
- Resources r = mContext.getResources();
- if ((r.getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)
- == Configuration.UI_MODE_NIGHT_YES) {
- mScreenshotView.getBackground().setTint(Color.BLACK);
- } else {
- mScreenshotView.getBackground().setTintList(null);
- }
-
- ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
- anim.setDuration(SCREENSHOT_DROP_IN_DURATION);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mBackgroundView.setAlpha(0f);
- mBackgroundView.setVisibility(View.VISIBLE);
- mScreenshotView.setAlpha(0f);
- mScreenshotView.setTranslationX(0f);
- mScreenshotView.setTranslationY(0f);
- mScreenshotView.setScaleX(SCREENSHOT_SCALE + mBgPaddingScale);
- mScreenshotView.setScaleY(SCREENSHOT_SCALE + mBgPaddingScale);
- mScreenshotView.setVisibility(View.VISIBLE);
- mScreenshotFlash.setAlpha(0f);
- mScreenshotFlash.setVisibility(View.VISIBLE);
- }
-
- @Override
- public void onAnimationEnd(android.animation.Animator animation) {
- mScreenshotFlash.setVisibility(View.GONE);
- }
- });
- anim.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float t = (Float) animation.getAnimatedValue();
- float scaleT = (SCREENSHOT_SCALE + mBgPaddingScale)
- - scaleInterpolator.getInterpolation(t)
- * (SCREENSHOT_SCALE - SCREENSHOT_DROP_IN_MIN_SCALE);
- mBackgroundView.setAlpha(scaleInterpolator.getInterpolation(t) * BACKGROUND_ALPHA);
- mScreenshotView.setAlpha(t);
- mScreenshotView.setScaleX(scaleT);
- mScreenshotView.setScaleY(scaleT);
- mScreenshotFlash.setAlpha(flashAlphaInterpolator.getInterpolation(t));
- }
- });
- return anim;
- }
-
- /**
- * If a bitmap was supplied to be used as the screenshot, animated from where that bitmap was
- * on screen, rather than using the whole screen.
- */
- private ValueAnimator createRectAnimation(Rect rect) {
- mScreenshotView.setAdjustViewBounds(true);
- mScreenshotView.setMaxHeight(rect.height());
- mScreenshotView.setMaxWidth(rect.width());
-
- final float flashPeakDurationPct = ((float) (SCREENSHOT_FLASH_TO_PEAK_DURATION)
- / SCREENSHOT_DROP_IN_DURATION);
- final float flashDurationPct = 2f * flashPeakDurationPct;
- final Interpolator scaleInterpolator = x -> {
- // We start scaling when the flash is at it's peak
- if (x < flashPeakDurationPct) {
- return 0;
- }
- return (x - flashDurationPct) / (1f - flashDurationPct);
- };
-
- ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
- anim.setDuration(SCREENSHOT_DROP_IN_DURATION);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mBackgroundView.setAlpha(0f);
- mBackgroundView.setVisibility(View.VISIBLE);
- mScreenshotView.setAlpha(0f);
- mScreenshotView.setElevation(0f);
- mScreenshotView.setTranslationX(0f);
- mScreenshotView.setTranslationY(0f);
- mScreenshotView.setScaleX(SCREENSHOT_SCALE + mBgPaddingScale);
- mScreenshotView.setScaleY(SCREENSHOT_SCALE + mBgPaddingScale);
- mScreenshotView.setVisibility(View.VISIBLE);
- }
- });
- anim.addUpdateListener(animation -> {
- float t = (Float) animation.getAnimatedValue();
- float scaleT = (SCREENSHOT_SCALE + mBgPaddingScale)
- - scaleInterpolator.getInterpolation(t)
- * (SCREENSHOT_SCALE - SCREENSHOT_DROP_IN_MIN_SCALE);
- mBackgroundView.setAlpha(scaleInterpolator.getInterpolation(t) * BACKGROUND_ALPHA);
- mScreenshotView.setAlpha(t);
- });
- return anim;
- }
-
- private ValueAnimator createScreenshotDropOutAnimation(int w, int h, boolean statusBarVisible,
- boolean navBarVisible) {
- ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
- anim.setStartDelay(SCREENSHOT_DROP_OUT_DELAY);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mBackgroundView.setVisibility(View.GONE);
- mScreenshotView.setVisibility(View.GONE);
- mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, null);
- }
- });
-
- if (!statusBarVisible || !navBarVisible) {
- // There is no status bar/nav bar, so just fade the screenshot away in place
- anim.setDuration(SCREENSHOT_FAST_DROP_OUT_DURATION);
- anim.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float t = (Float) animation.getAnimatedValue();
- float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale)
- - t * (SCREENSHOT_DROP_IN_MIN_SCALE
- - SCREENSHOT_FAST_DROP_OUT_MIN_SCALE);
- mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA);
- mScreenshotView.setAlpha(1f - t);
- mScreenshotView.setScaleX(scaleT);
- mScreenshotView.setScaleY(scaleT);
- }
- });
- } else {
- // In the case where there is a status bar, animate to the origin of the bar (top-left)
- final float scaleDurationPct = (float) SCREENSHOT_DROP_OUT_SCALE_DURATION
- / SCREENSHOT_DROP_OUT_DURATION;
- final Interpolator scaleInterpolator = new Interpolator() {
- @Override
- public float getInterpolation(float x) {
- if (x < scaleDurationPct) {
- // Decelerate, and scale the input accordingly
- return (float) (1f - Math.pow(1f - (x / scaleDurationPct), 2f));
- }
- return 1f;
- }
- };
-
- // Determine the bounds of how to scale
- float halfScreenWidth = (w - 2f * mBgPadding) / 2f;
- float halfScreenHeight = (h - 2f * mBgPadding) / 2f;
- final float offsetPct = SCREENSHOT_DROP_OUT_MIN_SCALE_OFFSET;
- final PointF finalPos = new PointF(
- -halfScreenWidth
- + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenWidth,
- -halfScreenHeight
- + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenHeight);
-
- // Animate the screenshot to the status bar
- anim.setDuration(SCREENSHOT_DROP_OUT_DURATION);
- anim.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float t = (Float) animation.getAnimatedValue();
- float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale)
- - scaleInterpolator.getInterpolation(t)
- * (SCREENSHOT_DROP_IN_MIN_SCALE - SCREENSHOT_DROP_OUT_MIN_SCALE);
- mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA);
- mScreenshotView.setAlpha(1f - scaleInterpolator.getInterpolation(t));
- mScreenshotView.setScaleX(scaleT);
- mScreenshotView.setScaleY(scaleT);
- mScreenshotView.setTranslationX(t * finalPos.x);
- mScreenshotView.setTranslationY(t * finalPos.y);
- }
- });
- }
- return anim;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index 221174f70d66..10e6902f139e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -88,7 +88,6 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
private final ScreenshotNotificationSmartActionsProvider mSmartActionsProvider;
private final String mScreenshotId;
private final boolean mSmartActionsEnabled;
- private final boolean mCreateDeleteAction;
private final Random mRandom = new Random();
SaveImageInBackgroundTask(Context context, GlobalScreenshot.SaveImageInBackgroundData data) {
@@ -102,8 +101,6 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
mImageFileName = String.format(SCREENSHOT_FILE_NAME_TEMPLATE, imageDate);
mScreenshotId = String.format(SCREENSHOT_ID_TEMPLATE, UUID.randomUUID());
- mCreateDeleteAction = data.createDeleteAction;
-
// Initialize screenshot notification smart actions provider.
mSmartActionsEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.ENABLE_SCREENSHOT_NOTIFICATION_SMART_ACTIONS, true);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
index fbcd6ba0ff47..e4e253e71fb3 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
@@ -148,43 +148,11 @@ public class ScreenshotNotificationsController {
}
/**
- * Shows a notification to inform the user that a screenshot is currently being saved.
- */
- public void showSavingScreenshotNotification() {
- final long now = System.currentTimeMillis();
-
- mPublicNotificationBuilder
- .setContentTitle(mResources.getString(R.string.screenshot_saving_title))
- .setSmallIcon(R.drawable.stat_notify_image)
- .setCategory(Notification.CATEGORY_PROGRESS)
- .setWhen(now)
- .setShowWhen(true)
- .setColor(mResources.getColor(
- com.android.internal.R.color.system_notification_accent_color));
- SystemUI.overrideNotificationAppName(mContext, mPublicNotificationBuilder, true);
-
- mNotificationBuilder
- .setContentTitle(mResources.getString(R.string.screenshot_saving_title))
- .setSmallIcon(R.drawable.stat_notify_image)
- .setWhen(now)
- .setShowWhen(true)
- .setColor(mResources.getColor(
- com.android.internal.R.color.system_notification_accent_color))
- .setStyle(mNotificationStyle)
- .setPublicVersion(mPublicNotificationBuilder.build());
- mNotificationBuilder.setFlag(Notification.FLAG_NO_CLEAR, true);
- SystemUI.overrideNotificationAppName(mContext, mNotificationBuilder, true);
-
- mNotificationManager.notify(SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT,
- mNotificationBuilder.build());
- }
-
- /**
- * Shows a notification with the saved screenshot and actions that can be taken with it.
+ * Shows a silent notification with the saved screenshot and actions that can be taken with it.
*
* @param actionData SavedImageData struct with image URI and actions
*/
- public void showScreenshotActionsNotification(
+ public void showSilentScreenshotNotification(
GlobalScreenshot.SavedImageData actionData) {
mNotificationBuilder.addAction(actionData.shareAction);
mNotificationBuilder.addAction(actionData.editAction);
@@ -206,20 +174,34 @@ public class ScreenshotNotificationsController {
.setContentTitle(mResources.getString(R.string.screenshot_saved_title))
.setContentText(mResources.getString(R.string.screenshot_saved_text))
.setContentIntent(PendingIntent.getActivity(mContext, 0, launchIntent, 0))
+ .setSmallIcon(R.drawable.stat_notify_image)
+ .setCategory(Notification.CATEGORY_PROGRESS)
.setWhen(now)
+ .setShowWhen(true)
.setAutoCancel(true)
.setColor(mContext.getColor(
- com.android.internal.R.color.system_notification_accent_color));
+ com.android.internal.R.color.system_notification_accent_color))
+ .setGroup("silent")
+ .setGroupAlertBehavior(Notification.GROUP_ALERT_SUMMARY);
mNotificationBuilder
.setContentTitle(mResources.getString(R.string.screenshot_saved_title))
.setContentText(mResources.getString(R.string.screenshot_saved_text))
.setContentIntent(PendingIntent.getActivity(mContext, 0, launchIntent, 0))
+ .setSmallIcon(R.drawable.stat_notify_image)
+ .setCategory(Notification.CATEGORY_PROGRESS)
.setWhen(now)
+ .setShowWhen(true)
.setAutoCancel(true)
.setColor(mContext.getColor(
com.android.internal.R.color.system_notification_accent_color))
.setPublicVersion(mPublicNotificationBuilder.build())
- .setFlag(Notification.FLAG_NO_CLEAR, false);
+ .setStyle(mNotificationStyle)
+ .setFlag(Notification.FLAG_NO_CLEAR, false)
+ .setGroup("silent")
+ .setGroupAlertBehavior(Notification.GROUP_ALERT_SUMMARY);
+
+ SystemUI.overrideNotificationAppName(mContext, mPublicNotificationBuilder, true);
+ SystemUI.overrideNotificationAppName(mContext, mNotificationBuilder, true);
mNotificationManager.notify(SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT,
mNotificationBuilder.build());
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 8322fe08d3c2..c05c8236def0 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -48,7 +48,6 @@ public class TakeScreenshotService extends Service {
private static final String TAG = "TakeScreenshotService";
private final GlobalScreenshot mScreenshot;
- private final GlobalScreenshotLegacy mScreenshotLegacy;
private final UserManager mUserManager;
private final UiEventLogger mUiEventLogger;
@@ -81,9 +80,6 @@ public class TakeScreenshotService extends Service {
return;
}
- // TODO: clean up once notifications flow is fully deprecated
- boolean useCornerFlow = true;
-
ScreenshotHelper.ScreenshotRequest screenshotRequest =
(ScreenshotHelper.ScreenshotRequest) msg.obj;
@@ -91,22 +87,10 @@ public class TakeScreenshotService extends Service {
switch (msg.what) {
case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
- if (useCornerFlow) {
- mScreenshot.takeScreenshot(uriConsumer, onComplete);
- } else {
- mScreenshotLegacy.takeScreenshot(
- uriConsumer, screenshotRequest.getHasStatusBar(),
- screenshotRequest.getHasNavBar());
- }
+ mScreenshot.takeScreenshot(uriConsumer, onComplete);
break;
case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION:
- if (useCornerFlow) {
- mScreenshot.takeScreenshotPartial(uriConsumer, onComplete);
- } else {
- mScreenshotLegacy.takeScreenshotPartial(
- uriConsumer, screenshotRequest.getHasStatusBar(),
- screenshotRequest.getHasNavBar());
- }
+ mScreenshot.takeScreenshotPartial(uriConsumer, onComplete);
break;
case WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE:
Bitmap screenshot = BitmapUtil.bundleToHardwareBitmap(
@@ -116,13 +100,8 @@ public class TakeScreenshotService extends Service {
int taskId = screenshotRequest.getTaskId();
int userId = screenshotRequest.getUserId();
ComponentName topComponent = screenshotRequest.getTopComponent();
- if (useCornerFlow) {
- mScreenshot.handleImageAsScreenshot(screenshot, screenBounds, insets,
- taskId, userId, topComponent, uriConsumer, onComplete);
- } else {
- mScreenshotLegacy.handleImageAsScreenshot(screenshot, screenBounds, insets,
- taskId, userId, topComponent, uriConsumer);
- }
+ mScreenshot.handleImageAsScreenshot(screenshot, screenBounds, insets,
+ taskId, userId, topComponent, uriConsumer, onComplete);
break;
default:
Log.d(TAG, "Invalid screenshot option: " + msg.what);
@@ -131,11 +110,9 @@ public class TakeScreenshotService extends Service {
};
@Inject
- public TakeScreenshotService(GlobalScreenshot globalScreenshot,
- GlobalScreenshotLegacy globalScreenshotLegacy, UserManager userManager,
+ public TakeScreenshotService(GlobalScreenshot globalScreenshot, UserManager userManager,
UiEventLogger uiEventLogger) {
mScreenshot = globalScreenshot;
- mScreenshotLegacy = globalScreenshotLegacy;
mUserManager = userManager;
mUiEventLogger = uiEventLogger;
}
@@ -148,8 +125,6 @@ public class TakeScreenshotService extends Service {
@Override
public boolean onUnbind(Intent intent) {
if (mScreenshot != null) mScreenshot.stopScreenshot();
- // TODO remove once notifications flow is fully deprecated
- if (mScreenshotLegacy != null) mScreenshotLegacy.stopScreenshot();
return true;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index 21810c0e7cf5..166934599aeb 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -253,7 +253,9 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
mSplitLayout.mSecondary = new Rect(mRotateSplitLayout.mSecondary);
mRotateSplitLayout = null;
}
- update(newConfig);
+ if (isSplitActive()) {
+ update(newConfig);
+ }
}
Handler getHandler() {
@@ -328,11 +330,6 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
mHandler.post(this::removeDivider);
}
- void onTasksReady() {
- mHandler.post(() -> update(mDisplayController.getDisplayContext(
- mContext.getDisplayId()).getResources().getConfiguration()));
- }
-
private void updateVisibility(final boolean visible) {
if (DEBUG) Slog.d(TAG, "Updating visibility " + mVisible + "->" + visible);
if (mVisible != visible) {
@@ -524,7 +521,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
void ensureMinimizedSplit() {
setHomeMinimized(true /* minimized */, mSplits.mSecondary.isResizable());
- if (!isDividerVisible()) {
+ if (mView != null && !isDividerVisible()) {
// Wasn't in split-mode yet, so enter now.
if (DEBUG) {
Slog.d(TAG, " entering split mode with minimized=true");
@@ -535,7 +532,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
void ensureNormalSplit() {
setHomeMinimized(false /* minimized */, mHomeStackResizable);
- if (!isDividerVisible()) {
+ if (mView != null && !isDividerVisible()) {
// Wasn't in split-mode, so enter now.
if (DEBUG) {
Slog.d(TAG, " enter split mode unminimized ");
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
index db324822994b..44674df3b865 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
@@ -113,8 +113,6 @@ class SplitScreenTaskOrganizer extends TaskOrganizer {
t.setColor(mSecondaryDim, new float[]{0f, 0f, 0f});
t.apply();
releaseTransaction(t);
-
- mDivider.onTasksReady();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index c8361c63e960..853a22505542 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -229,7 +229,11 @@ public class WindowManagerProxy {
// as a result, the above will not capture any tasks; yet, we need to clean-up the
// home task bounds.
List<ActivityManager.RunningTaskInfo> freeHomeAndRecents =
- TaskOrganizer.getRootTasks(Display.DEFAULT_DISPLAY, HOME_AND_RECENTS);
+ TaskOrganizer.getRootTasks(DEFAULT_DISPLAY, HOME_AND_RECENTS);
+ // Filter out the root split tasks
+ freeHomeAndRecents.removeIf(p -> p.token.equals(tiles.mSecondary.token)
+ || p.token.equals(tiles.mPrimary.token));
+
if (primaryChildren.isEmpty() && secondaryChildren.isEmpty()
&& freeHomeAndRecents.isEmpty()) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index 72d9d0ee8f8f..4d09071c6b5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -104,6 +104,9 @@ public class NotificationListener extends NotificationListenerWithPlugins {
listener.onNotificationPosted(sbn, completeMap);
}
}
+ for (NotificationHandler listener : mNotificationHandlers) {
+ listener.onNotificationsInitialized();
+ }
});
onSilentStatusBarIconsVisibilityChanged(
mNotificationManager.shouldHideSilentStatusBarIcons());
@@ -224,5 +227,10 @@ public class NotificationListener extends NotificationListenerWithPlugins {
void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap);
void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap, int reason);
void onNotificationRankingUpdate(RankingMap rankingMap);
+
+ /**
+ * Called after the listener has connected to NoMan and posted any current notifications.
+ */
+ void onNotificationsInitialized();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index 6aef6b407f37..6a3302473e63 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -41,6 +41,8 @@ import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController;
+import java.util.concurrent.Executor;
+
/**
* A class that allows activities to be launched in a seamless way where the notification
* transforms nicely into the starting window.
@@ -59,6 +61,7 @@ public class ActivityLaunchAnimator {
private final float mWindowCornerRadius;
private final NotificationShadeWindowViewController mNotificationShadeWindowViewController;
private final NotificationShadeDepthController mDepthController;
+ private final Executor mMainExecutor;
private Callback mCallback;
private final Runnable mTimeoutRunnable = () -> {
setAnimationPending(false);
@@ -73,12 +76,14 @@ public class ActivityLaunchAnimator {
Callback callback,
NotificationPanelViewController notificationPanel,
NotificationShadeDepthController depthController,
- NotificationListContainer container) {
+ NotificationListContainer container,
+ Executor mainExecutor) {
mNotificationPanel = notificationPanel;
mNotificationContainer = container;
mDepthController = depthController;
mNotificationShadeWindowViewController = notificationShadeWindowViewController;
mCallback = callback;
+ mMainExecutor = mainExecutor;
mWindowCornerRadius = ScreenDecorationsUtils
.getWindowCornerRadius(mNotificationShadeWindowViewController.getView()
.getResources());
@@ -155,7 +160,7 @@ public class ActivityLaunchAnimator {
RemoteAnimationTarget[] remoteAnimationWallpaperTargets,
IRemoteAnimationFinishedCallback iRemoteAnimationFinishedCallback)
throws RemoteException {
- mSourceNotification.post(() -> {
+ mMainExecutor.execute(() -> {
RemoteAnimationTarget primary = getPrimaryRemoteAnimationTarget(
remoteAnimationTargets);
if (primary == null) {
@@ -191,8 +196,9 @@ public class ActivityLaunchAnimator {
}
}
int targetWidth = primary.sourceContainerBounds.width();
- int notificationHeight = mSourceNotification.getActualHeight()
- - mSourceNotification.getClipBottomAmount();
+ // If the notification panel is collapsed, the clip may be larger than the height.
+ int notificationHeight = Math.max(mSourceNotification.getActualHeight()
+ - mSourceNotification.getClipBottomAmount(), 0);
int notificationWidth = mSourceNotification.getWidth();
anim.setDuration(ANIMATION_DURATION);
anim.setInterpolator(Interpolators.LINEAR);
@@ -292,7 +298,7 @@ public class ActivityLaunchAnimator {
@Override
public void onAnimationCancelled() throws RemoteException {
- mSourceNotification.post(() -> {
+ mMainExecutor.execute(() -> {
setAnimationPending(false);
mCallback.onLaunchAnimationCancelled();
});
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index adb51a5d959a..9abc66056452 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -385,6 +385,10 @@ public class NotificationEntryManager implements
public void onNotificationRankingUpdate(RankingMap rankingMap) {
updateNotificationRanking(rankingMap);
}
+
+ @Override
+ public void onNotificationsInitialized() {
+ }
};
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 45987b6eb21b..c1acfbadef45 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -47,7 +47,6 @@ import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.Notification;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
@@ -82,6 +81,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No
import com.android.systemui.statusbar.notification.collection.notifcollection.RankingAppliedEvent;
import com.android.systemui.statusbar.notification.collection.notifcollection.RankingUpdatedEvent;
import com.android.systemui.util.Assert;
+import com.android.systemui.util.time.SystemClock;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -95,6 +95,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Queue;
+import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -125,6 +126,7 @@ import javax.inject.Singleton;
@Singleton
public class NotifCollection implements Dumpable {
private final IStatusBarService mStatusBarService;
+ private final SystemClock mClock;
private final FeatureFlags mFeatureFlags;
private final NotifCollectionLogger mLogger;
private final LogBufferEulogizer mEulogizer;
@@ -142,20 +144,24 @@ public class NotifCollection implements Dumpable {
private boolean mAttached = false;
private boolean mAmDispatchingToOtherCode;
+ private long mInitializedTimestamp = 0;
@Inject
public NotifCollection(
IStatusBarService statusBarService,
- DumpManager dumpManager,
+ SystemClock clock,
FeatureFlags featureFlags,
NotifCollectionLogger logger,
- LogBufferEulogizer logBufferEulogizer) {
+ LogBufferEulogizer logBufferEulogizer,
+ DumpManager dumpManager) {
Assert.isMainThread();
mStatusBarService = statusBarService;
+ mClock = clock;
+ mFeatureFlags = featureFlags;
mLogger = logger;
mEulogizer = logBufferEulogizer;
+
dumpManager.registerDumpable(TAG, this);
- mFeatureFlags = featureFlags;
}
/** Initializes the NotifCollection and registers it to receive notification events. */
@@ -376,9 +382,10 @@ public class NotifCollection implements Dumpable {
final NotificationEntry entry = mNotificationSet.get(sbn.getKey());
if (entry == null) {
- throw mEulogizer.record(
+ crashIfNotInitializing(
new IllegalStateException("No notification to remove with key "
+ sbn.getKey()));
+ return;
}
entry.mCancellationReason = reason;
@@ -394,6 +401,10 @@ public class NotifCollection implements Dumpable {
dispatchEventsAndRebuildList();
}
+ private void onNotificationsInitialized() {
+ mInitializedTimestamp = mClock.uptimeMillis();
+ }
+
private void postNotification(
StatusBarNotification sbn,
Ranking ranking) {
@@ -401,7 +412,7 @@ public class NotifCollection implements Dumpable {
if (entry == null) {
// A new notification!
- entry = new NotificationEntry(sbn, ranking, SystemClock.uptimeMillis());
+ entry = new NotificationEntry(sbn, ranking, mClock.uptimeMillis());
mEventQueue.add(new InitEntryEvent(entry));
mEventQueue.add(new BindEntryEvent(entry, sbn));
mNotificationSet.put(sbn.getKey(), entry);
@@ -628,6 +639,23 @@ public class NotifCollection implements Dumpable {
}
}
+ // While the NotificationListener is connecting to NotificationManager, there is a short period
+ // during which it's possible for us to receive events about notifications we don't yet know
+ // about (or that otherwise don't make sense). Until that race condition is fixed, we create a
+ // "forgiveness window" of five seconds during which we won't crash if we receive nonsensical
+ // messages from system server.
+ private void crashIfNotInitializing(RuntimeException exception) {
+ final boolean isRecentlyInitialized = mInitializedTimestamp == 0
+ || mClock.uptimeMillis() - mInitializedTimestamp
+ < INITIALIZATION_FORGIVENESS_WINDOW;
+
+ if (isRecentlyInitialized) {
+ mLogger.logIgnoredError(exception.getMessage());
+ } else {
+ throw mEulogizer.record(exception);
+ }
+ }
+
private static Ranking requireRanking(RankingMap rankingMap, String key) {
// TODO: Modify RankingMap so that we don't have to make a copy here
Ranking ranking = new Ranking();
@@ -742,6 +770,11 @@ public class NotifCollection implements Dumpable {
public void onNotificationRankingUpdate(RankingMap rankingMap) {
NotifCollection.this.onNotificationRankingUpdate(rankingMap);
}
+
+ @Override
+ public void onNotificationsInitialized() {
+ NotifCollection.this.onNotificationsInitialized();
+ }
};
private static final String TAG = "NotifCollection";
@@ -773,4 +806,6 @@ public class NotifCollection implements Dumpable {
static final int REASON_NOT_CANCELED = -1;
public static final int REASON_UNKNOWN = 0;
+
+ private static final long INITIALIZATION_FORGIVENESS_WINDOW = TimeUnit.SECONDS.toMillis(5);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
index 596235cfb4d0..1710daa16735 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
@@ -153,6 +153,11 @@ public class GroupCoalescer implements Dumpable {
applyRanking(rankingMap);
mHandler.onNotificationRankingUpdate(rankingMap);
}
+
+ @Override
+ public void onNotificationsInitialized() {
+ mHandler.onNotificationsInitialized();
+ }
};
private void maybeEmitBatch(StatusBarNotification sbn) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
index 0eb2d64e8682..76751eaaecb1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
@@ -20,6 +20,7 @@ import android.os.RemoteException
import android.service.notification.NotificationListenerService.RankingMap
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel.DEBUG
+import com.android.systemui.log.LogLevel.ERROR
import com.android.systemui.log.LogLevel.INFO
import com.android.systemui.log.LogLevel.WARNING
import com.android.systemui.log.LogLevel.WTF
@@ -167,6 +168,14 @@ class NotifCollectionLogger @Inject constructor(
"LIFETIME EXTENSION ENDED for $str1 by '$str2'; $int1 remaining extensions"
})
}
+
+ fun logIgnoredError(message: String?) {
+ buffer.log(TAG, ERROR, {
+ str1 = message
+ }, {
+ "ERROR suppressed due to initialization forgiveness: $str1"
+ })
+ }
}
private const val TAG = "NotifCollection"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index f4afb91396b5..bee2f7002823 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -44,6 +44,7 @@ import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.os.Handler;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
@@ -540,7 +541,9 @@ public class NotificationConversationInfo extends LinearLayout implements
.setView(onboardingView)
.setIgnoresDnd(ignoreDnd)
.setShowsAsBubble(showAsBubble)
- .setIcon(((ImageView) findViewById(R.id.conversation_icon)).getDrawable())
+ .setIcon(mIconFactory.getBaseIconDrawable(mShortcutInfo))
+ .setBadge(mIconFactory.getAppBadge(
+ mPackageName, UserHandle.getUserId(mSbn.getUid())))
.setOnSettingsClick(mOnConversationSettingsClickListener)
.build();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt
index c88f0bdc2acb..fab367df8fde 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt
@@ -16,41 +16,57 @@
package com.android.systemui.statusbar.notification.row
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.AnimatorSet
+import android.animation.ValueAnimator
import android.app.Dialog
import android.content.Context
import android.graphics.Color
import android.graphics.PixelFormat
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
+import android.graphics.drawable.GradientDrawable
import android.text.SpannableStringBuilder
import android.text.style.BulletSpan
import android.view.Gravity
import android.view.View
+import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.view.Window
import android.view.WindowInsets.Type.statusBars
import android.view.WindowManager
+import android.view.animation.Interpolator
+import android.view.animation.PathInterpolator
import android.widget.ImageView
import android.widget.TextView
+import com.android.systemui.Interpolators.LINEAR_OUT_SLOW_IN
import com.android.systemui.Prefs
import com.android.systemui.R
import com.android.systemui.statusbar.notification.row.NotificationConversationInfo.OnConversationSettingsClickListener
import javax.inject.Inject
+
/**
* Controller to handle presenting the priority conversations onboarding dialog
*/
class PriorityOnboardingDialogController @Inject constructor(
- val view: View,
- val context: Context,
- val ignoresDnd: Boolean,
- val showsAsBubble: Boolean,
- val icon : Drawable,
- val onConversationSettingsClickListener : OnConversationSettingsClickListener
+ val view: View,
+ val context: Context,
+ private val ignoresDnd: Boolean,
+ private val showsAsBubble: Boolean,
+ val icon : Drawable,
+ private val onConversationSettingsClickListener : OnConversationSettingsClickListener,
+ val badge : Drawable
) {
private lateinit var dialog: Dialog
+ private val OVERSHOOT: Interpolator = PathInterpolator(0.4f, 0f, 0.2f, 1.4f)
+ private val IMPORTANCE_ANIM_DELAY = 150L
+ private val IMPORTANCE_ANIM_GROW_DURATION = 250L
+ private val IMPORTANCE_ANIM_SHRINK_DURATION = 200L
+ private val IMPORTANCE_ANIM_SHRINK_DELAY = 25L
fun init() {
initDialog()
@@ -81,6 +97,7 @@ class PriorityOnboardingDialogController @Inject constructor(
private lateinit var icon: Drawable
private lateinit var onConversationSettingsClickListener
: OnConversationSettingsClickListener
+ private lateinit var badge : Drawable
fun setView(v: View): Builder {
view = v
@@ -106,6 +123,10 @@ class PriorityOnboardingDialogController @Inject constructor(
icon = draw
return this
}
+ fun setBadge(badge : Drawable) : Builder {
+ this.badge = badge
+ return this
+ }
fun setOnSettingsClick(onClick : OnConversationSettingsClickListener) : Builder {
onConversationSettingsClickListener = onClick
@@ -115,7 +136,7 @@ class PriorityOnboardingDialogController @Inject constructor(
fun build(): PriorityOnboardingDialogController {
val controller = PriorityOnboardingDialogController(
view, context, ignoresDnd, showAsBubble, icon,
- onConversationSettingsClickListener)
+ onConversationSettingsClickListener, badge)
return controller
}
}
@@ -143,6 +164,65 @@ class PriorityOnboardingDialogController @Inject constructor(
}
findViewById<ImageView>(R.id.conversation_icon)?.setImageDrawable(icon)
+ findViewById<ImageView>(R.id.icon)?.setImageDrawable(badge)
+ val mImportanceRingView = findViewById<ImageView>(R.id.conversation_icon_badge_ring)
+ val conversationIconBadgeBg = findViewById<ImageView>(R.id.conversation_icon_badge_bg)
+
+ val ring: GradientDrawable = mImportanceRingView.drawable as GradientDrawable
+ ring.mutate()
+ val bg = conversationIconBadgeBg.drawable as GradientDrawable
+ bg.mutate()
+ val ringColor = context.getResources()
+ .getColor(com.android.internal.R.color.conversation_important_highlight)
+ val standardThickness = context.resources.getDimensionPixelSize(
+ com.android.internal.R.dimen.importance_ring_stroke_width)
+ val largeThickness = context.resources.getDimensionPixelSize(
+ com.android.internal.R.dimen.importance_ring_anim_max_stroke_width)
+ val standardSize = context.resources.getDimensionPixelSize(
+ com.android.internal.R.dimen.importance_ring_size)
+ val baseSize = standardSize - standardThickness * 2
+ val largeSize = baseSize + largeThickness * 2
+ val bgSize = context.resources.getDimensionPixelSize(
+ com.android.internal.R.dimen.conversation_icon_size_badged)
+
+ val animatorUpdateListener: ValueAnimator.AnimatorUpdateListener
+ = ValueAnimator.AnimatorUpdateListener { animation ->
+ val strokeWidth = animation.animatedValue as Int
+ ring.setStroke(strokeWidth, ringColor)
+ val newSize = baseSize + strokeWidth * 2
+ ring.setSize(newSize, newSize)
+ mImportanceRingView.invalidate()
+ }
+
+ val growAnimation: ValueAnimator = ValueAnimator.ofInt(0, largeThickness)
+ growAnimation.interpolator = LINEAR_OUT_SLOW_IN
+ growAnimation.duration = IMPORTANCE_ANIM_GROW_DURATION
+ growAnimation.addUpdateListener(animatorUpdateListener)
+
+ val shrinkAnimation: ValueAnimator
+ = ValueAnimator.ofInt(largeThickness, standardThickness)
+ shrinkAnimation.duration = IMPORTANCE_ANIM_SHRINK_DURATION
+ shrinkAnimation.startDelay = IMPORTANCE_ANIM_SHRINK_DELAY
+ shrinkAnimation.interpolator = OVERSHOOT
+ shrinkAnimation.addUpdateListener(animatorUpdateListener)
+ shrinkAnimation.addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationStart(animation: Animator?) {
+ // Shrink the badge bg so that it doesn't peek behind the animation
+ bg.setSize(baseSize, baseSize);
+ conversationIconBadgeBg.invalidate();
+ }
+
+ override fun onAnimationEnd(animation: Animator?) {
+ // Reset bg back to normal size
+ bg.setSize(bgSize, bgSize);
+ conversationIconBadgeBg.invalidate();
+
+ }
+ })
+
+ val anims = AnimatorSet()
+ anims.startDelay = IMPORTANCE_ANIM_DELAY
+ anims.playSequentially(growAnimation, shrinkAnimation)
val gapWidth = dialog.context.getResources().getDimensionPixelSize(
R.dimen.conversation_onboarding_bullet_gap_width)
@@ -180,6 +260,7 @@ class PriorityOnboardingDialogController @Inject constructor(
height = WRAP_CONTENT
}
}
+ anims.start()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 325c8c0e506a..684bf1958154 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -146,6 +146,7 @@ import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
+import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
@@ -6538,7 +6539,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
return row.canViewBeDismissed();
}
if (v instanceof PeopleHubView) {
- return true;
+ return ((PeopleHubView) v).getCanSwipe();
}
return false;
}
@@ -6633,6 +6634,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
MetricsEvent.ACTION_LS_SHADE,
(int) (dragLengthY / mDisplayMetrics.density),
0 /* velocityDp - N/A */);
+ mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_PULL_SHADE_OPEN);
if (!mAmbientState.isDozing() || startingChild != null) {
// We have notifications, go to locked shade.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
index a1d898fb84b0..8f77a1d776e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
@@ -72,7 +72,7 @@ class PeopleHubView(context: Context, attrs: AttributeSet) :
}
}
- var canSwipe: Boolean = true
+ var canSwipe: Boolean = false
set(value) {
if (field != value) {
if (field) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index fc8c8dbba7fd..825919f17661 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -19,6 +19,7 @@ import android.content.res.Resources;
import android.hardware.display.ColorDisplayManager;
import android.hardware.display.NightDisplayListener;
import android.os.Handler;
+import android.os.UserHandle;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -34,6 +35,7 @@ import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.DataSaverController.Listener;
import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.HotspotController.Callback;
+import com.android.systemui.util.UserAwareController;
import java.util.ArrayList;
import java.util.Objects;
@@ -43,7 +45,7 @@ import javax.inject.Inject;
/**
* Manages which tiles should be automatically added to QS.
*/
-public class AutoTileManager {
+public class AutoTileManager implements UserAwareController {
private static final String TAG = "AutoTileManager";
public static final String HOTSPOT = "hotspot";
@@ -52,7 +54,9 @@ public class AutoTileManager {
public static final String WORK = "work";
public static final String NIGHT = "night";
public static final String CAST = "cast";
- public static final String SETTING_SEPARATOR = ":";
+ static final String SETTING_SEPARATOR = ":";
+
+ private UserHandle mCurrentUser;
private final Context mContext;
private final QSTileHost mHost;
@@ -66,43 +70,56 @@ public class AutoTileManager {
private final ArrayList<AutoAddSetting> mAutoAddSettingList = new ArrayList<>();
@Inject
- public AutoTileManager(Context context, AutoAddTracker autoAddTracker, QSTileHost host,
+ public AutoTileManager(Context context, AutoAddTracker.Builder autoAddTrackerBuilder,
+ QSTileHost host,
@Background Handler handler,
HotspotController hotspotController,
DataSaverController dataSaverController,
ManagedProfileController managedProfileController,
NightDisplayListener nightDisplayListener,
CastController castController) {
- mAutoTracker = autoAddTracker;
mContext = context;
mHost = host;
+ mCurrentUser = mHost.getUserContext().getUser();
+ mAutoTracker = autoAddTrackerBuilder.setUserId(mCurrentUser.getIdentifier()).build();
mHandler = handler;
mHotspotController = hotspotController;
mDataSaverController = dataSaverController;
mManagedProfileController = managedProfileController;
mNightDisplayListener = nightDisplayListener;
mCastController = castController;
+
+ populateSettingsList();
+ startControllersAndSettingsListeners();
+ }
+
+ protected void startControllersAndSettingsListeners() {
if (!mAutoTracker.isAdded(HOTSPOT)) {
- hotspotController.addCallback(mHotspotCallback);
+ mHotspotController.addCallback(mHotspotCallback);
}
if (!mAutoTracker.isAdded(SAVER)) {
- dataSaverController.addCallback(mDataSaverListener);
+ mDataSaverController.addCallback(mDataSaverListener);
}
if (!mAutoTracker.isAdded(WORK)) {
- managedProfileController.addCallback(mProfileCallback);
+ mManagedProfileController.addCallback(mProfileCallback);
}
if (!mAutoTracker.isAdded(NIGHT)
&& ColorDisplayManager.isNightDisplayAvailable(mContext)) {
- nightDisplayListener.setCallback(mNightDisplayCallback);
+ mNightDisplayListener.setCallback(mNightDisplayCallback);
}
if (!mAutoTracker.isAdded(CAST)) {
- castController.addCallback(mCastCallback);
+ mCastController.addCallback(mCastCallback);
+ }
+
+ int settingsN = mAutoAddSettingList.size();
+ for (int i = 0; i < settingsN; i++) {
+ if (!mAutoTracker.isAdded(mAutoAddSettingList.get(i).mSpec)) {
+ mAutoAddSettingList.get(i).setListening(true);
+ }
}
- populateSettingsList();
}
- public void destroy() {
- mAutoTracker.destroy();
+ protected void stopListening() {
mHotspotController.removeCallback(mHotspotCallback);
mDataSaverController.removeCallback(mDataSaverListener);
mManagedProfileController.removeCallback(mProfileCallback);
@@ -116,6 +133,11 @@ public class AutoTileManager {
}
}
+ public void destroy() {
+ stopListening();
+ mAutoTracker.destroy();
+ }
+
/**
* Populates a list with the pairs setting:spec in the config resource.
* <p>
@@ -137,17 +159,39 @@ public class AutoTileManager {
if (split.length == 2) {
String setting = split[0];
String spec = split[1];
- if (!mAutoTracker.isAdded(spec)) {
- AutoAddSetting s = new AutoAddSetting(mContext, mHandler, setting, spec);
- mAutoAddSettingList.add(s);
- s.setListening(true);
- }
+ // Populate all the settings. As they may not have been added in other users
+ AutoAddSetting s = new AutoAddSetting(mContext, mHandler, setting, spec);
+ mAutoAddSettingList.add(s);
} else {
Log.w(TAG, "Malformed item in array: " + tile);
}
}
}
+ @Override
+ public void changeUser(UserHandle newUser) {
+ if (!Thread.currentThread().equals(mHandler.getLooper().getThread())) {
+ mHandler.post(() -> changeUser(newUser));
+ return;
+ }
+ if (newUser.getIdentifier() == mCurrentUser.getIdentifier()) {
+ return;
+ }
+ stopListening();
+ mCurrentUser = newUser;
+ int settingsN = mAutoAddSettingList.size();
+ for (int i = 0; i < settingsN; i++) {
+ mAutoAddSettingList.get(i).setUserId(newUser.getIdentifier());
+ }
+ mAutoTracker.changeUser(newUser);
+ startControllersAndSettingsListeners();
+ }
+
+ @Override
+ public int getCurrentUserId() {
+ return mCurrentUser.getIdentifier();
+ }
+
public void unmarkTileAsAutoAdded(String tabSpec) {
mAutoTracker.setTileRemoved(tabSpec);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java
index 8c3420a522ec..83d398d3e7ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java
@@ -20,6 +20,9 @@ import android.metrics.LogMaker;
import android.util.ArrayMap;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.UiEventLoggerImpl;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
import com.android.systemui.EventLogConstants;
@@ -34,6 +37,56 @@ import javax.inject.Singleton;
*/
@Singleton
public class LockscreenGestureLogger {
+
+ /**
+ * Contains Lockscreen related Westworld UiEvent enums.
+ */
+ public enum LockscreenUiEvent implements UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "Lockscreen > Pull shade open")
+ LOCKSCREEN_PULL_SHADE_OPEN(539),
+
+ @UiEvent(doc = "Lockscreen > Tap on lock, locks phone")
+ LOCKSCREEN_LOCK_TAP(540),
+
+ @UiEvent(doc = "Lockscreen > Swipe down to open quick settings")
+ LOCKSCREEN_QUICK_SETTINGS_OPEN(541),
+
+ @UiEvent(doc = "Swipe down to open quick settings when unlocked")
+ LOCKSCREEN_UNLOCKED_QUICK_SETTINGS_OPEN(542),
+
+ @UiEvent(doc = "Lockscreen > Tap on lock, shows hint")
+ LOCKSCREEN_LOCK_SHOW_HINT(543),
+
+ @UiEvent(doc = "Notification shade > Tap to open quick settings")
+ LOCKSCREEN_NOTIFICATION_SHADE_QUICK_SETTINGS_OPEN(544),
+
+ @UiEvent(doc = "Lockscreen > Dialer")
+ LOCKSCREEN_DIALER(545),
+
+ @UiEvent(doc = "Lockscreen > Camera")
+ LOCKSCREEN_CAMERA(546),
+
+ @UiEvent(doc = "Lockscreen > Unlock gesture")
+ LOCKSCREEN_UNLOCK(547),
+
+ @UiEvent(doc = "Lockscreen > Tap on notification, false touch rejection")
+ LOCKSCREEN_NOTIFICATION_FALSE_TOUCH(548),
+
+ @UiEvent(doc = "Expand the notification panel while unlocked")
+ LOCKSCREEN_UNLOCKED_NOTIFICATION_PANEL_EXPAND(549);
+
+ private final int mId;
+
+ LockscreenUiEvent(int id) {
+ mId = id;
+ }
+
+ @Override
+ public int getId() {
+ return mId;
+ }
+ }
+
private ArrayMap<Integer, Integer> mLegacyMap;
private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
@@ -55,6 +108,13 @@ public class LockscreenGestureLogger {
}
/**
+ * Logs {@link LockscreenUiEvent}.
+ */
+ public void log(LockscreenUiEvent lockscreenUiEvent) {
+ new UiEventLoggerImpl().log(lockscreenUiEvent);
+ }
+
+ /**
* Record the location of a swipe gesture, expressed as percentages of the whole screen
* @param category the action
* @param xPercent x-location / width * 100
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
index 8a5c8b0898bc..5d3910b4c415 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
@@ -45,6 +45,7 @@ import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator.WakeUpListener;
+import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
import com.android.systemui.statusbar.policy.AccessibilityController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -436,6 +437,7 @@ public class LockscreenLockIconController {
private boolean handleLongClick(View view) {
mLockscreenGestureLogger.write(MetricsProto.MetricsEvent.ACTION_LS_LOCK,
0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
+ mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_LOCK_TAP);
mKeyguardIndicationController.showTransientIndication(
R.string.keyguard_indication_trust_disabled);
mKeyguardUpdateMonitor.onLockIconPressed();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 8954d98d0201..11022bbcb7ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -628,6 +628,12 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
resetSecondaryHandle();
} else {
int deltaRotation = deltaRotation(mCurrentRotation, mStartingQuickSwitchRotation);
+ if (mStartingQuickSwitchRotation == -1 || deltaRotation == -1) {
+ // Curious if starting quickswitch can change between the if check and our delta
+ Log.d(TAG, "secondary nav delta rotation: " + deltaRotation
+ + " current: " + mCurrentRotation
+ + " starting: " + mStartingQuickSwitchRotation);
+ }
int height = 0;
int width = 0;
Rect dispSize = mWindowManager.getCurrentWindowMetrics().getBounds();
@@ -691,6 +697,8 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions());
}
+ pw.print(" mStartingQuickSwitchRotation=" + mStartingQuickSwitchRotation);
+ pw.print(" mCurrentRotation=" + mCurrentRotation);
pw.print(" mNavigationBarView=");
if (mNavigationBarView == null) {
pw.println("null");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 8889510cde28..d884bdd47930 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -104,9 +104,9 @@ import com.android.systemui.statusbar.notification.row.ActivatableNotificationVi
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
-import com.android.systemui.statusbar.notification.stack.MediaHeaderView;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
+import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -2474,6 +2474,8 @@ public class NotificationPanelViewController extends PanelViewController {
} else {
mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_HINT,
0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
+ mLockscreenGestureLogger
+ .log(LockscreenUiEvent.LOCKSCREEN_LOCK_SHOW_HINT);
startUnlockHintAnimation();
}
}
@@ -3257,7 +3259,7 @@ public class NotificationPanelViewController extends PanelViewController {
int velocityDp = Math.abs((int) (vel / displayDensity));
if (start) {
mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_DIALER, lengthDp, velocityDp);
-
+ mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_DIALER);
mFalsingManager.onLeftAffordanceOn();
if (mFalsingManager.shouldEnforceBouncer()) {
mStatusBar.executeRunnableDismissingKeyguard(
@@ -3272,6 +3274,7 @@ public class NotificationPanelViewController extends PanelViewController {
mLastCameraLaunchSource)) {
mLockscreenGestureLogger.write(
MetricsEvent.ACTION_LS_CAMERA, lengthDp, velocityDp);
+ mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_CAMERA);
}
mFalsingManager.onCameraOn();
if (mFalsingManager.shouldEnforceBouncer()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 57d36fcafa15..a902e1b0c960 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -47,6 +47,7 @@ import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.io.FileDescriptor;
@@ -319,6 +320,8 @@ public abstract class PanelViewController {
mLockscreenGestureLogger.writeAtFractionalPosition(MetricsEvent.ACTION_PANEL_VIEW_EXPAND,
(int) (event.getX() / width * 100), (int) (event.getY() / height * 100), rot);
+ mLockscreenGestureLogger
+ .log(LockscreenUiEvent.LOCKSCREEN_UNLOCKED_NOTIFICATION_PANEL_EXPAND);
}
protected void maybeVibrateOnOpening() {
@@ -378,6 +381,7 @@ public abstract class PanelViewController {
int heightDp = (int) Math.abs((y - mInitialTouchY) / displayDensity);
int velocityDp = (int) Math.abs(vel / displayDensity);
mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_UNLOCK, heightDp, velocityDp);
+ mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_UNLOCK);
}
fling(vel, expand, isFalseTouch(x, y));
onTrackingStopped(expand);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 1bc42d1a169d..e2714af33247 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1275,7 +1275,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mActivityLaunchAnimator = new ActivityLaunchAnimator(
mNotificationShadeWindowViewController, this, mNotificationPanelViewController,
mNotificationShadeDepthControllerLazy.get(),
- (NotificationListContainer) mStackScroller);
+ (NotificationListContainer) mStackScroller, mContext.getMainExecutor());
// TODO: inject this.
mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanelViewController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 84da35b63d0a..45f0c49a4fd4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -72,6 +72,7 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -374,6 +375,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
mLockscreenGestureLogger.write(
MetricsEvent.ACTION_LS_NOTE,
0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
+ mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_NOTIFICATION_FALSE_TOUCH);
mNotificationPanel.showTransientIndication(R.string.notification_tap_again);
ActivatableNotificationView previousView = mNotificationPanel.getActivatedChild();
if (previousView != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
index 0db76ecf67fb..c929243085d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
@@ -28,6 +28,7 @@ import android.graphics.PixelFormat;
import android.graphics.RecordingCanvas;
import android.graphics.drawable.Drawable;
import android.os.Handler;
+import android.util.Log;
import android.view.RenderNodeAnimator;
import android.view.View;
import android.view.ViewConfiguration;
@@ -220,7 +221,7 @@ public class KeyButtonRipple extends Drawable {
@Override
public void jumpToCurrentState() {
- cancelAnimations();
+ cancelAnimations("jumpToCurrentState");
}
@Override
@@ -234,6 +235,7 @@ public class KeyButtonRipple extends Drawable {
}
public void setPressed(boolean pressed) {
+ Log.d("b/63783866", "KeyButtonRipple.setPressed: pressed=" + pressed);
if (mDark != mLastDark && pressed) {
mRipplePaint = null;
mLastDark = mDark;
@@ -253,7 +255,8 @@ public class KeyButtonRipple extends Drawable {
mHandler.removeCallbacksAndMessages(null);
}
- private void cancelAnimations() {
+ private void cancelAnimations(String reason) {
+ Log.d("b/63783866", "KeyButtonRipple.cancelAnimations: reason=" + reason);
mVisible = false;
mTmpArray.addAll(mRunningAnimations);
int size = mTmpArray.size();
@@ -284,7 +287,7 @@ public class KeyButtonRipple extends Drawable {
}
private void enterSoftware() {
- cancelAnimations();
+ cancelAnimations("enterSoftware");
mVisible = true;
mGlowAlpha = getMaxGlowAlpha();
ObjectAnimator scaleAnimator = ObjectAnimator.ofFloat(this, "glowScale",
@@ -370,7 +373,8 @@ public class KeyButtonRipple extends Drawable {
}
private void enterHardware() {
- cancelAnimations();
+ Log.d("b/63783866", "enterHardware");
+ cancelAnimations("enterHardware");
mVisible = true;
mDrawingHardwareGlow = true;
setExtendStart(CanvasProperty.createFloat(getExtendSize() / 2));
@@ -422,6 +426,7 @@ public class KeyButtonRipple extends Drawable {
}
private void exitHardware() {
+ Log.d("b/63783866", "exitHardware");
mPaintProp = CanvasProperty.createPaint(getRipplePaint());
final RenderNodeAnimator opacityAnim = new RenderNodeAnimator(mPaintProp,
RenderNodeAnimator.PAINT_ALPHA, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 0ca8ef009173..8d7ecd09e760 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -441,6 +441,7 @@ public class KeyButtonView extends ImageView implements ButtonInterface {
@Override
public void abortCurrentGesture() {
+ Log.d("b/63783866", "KeyButtonView.abortCurrentGesture");
setPressed(false);
mRipple.abortDelayedRipple();
mGestureAborted = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 270f248e1a34..ce5bb0508c0a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -367,8 +367,14 @@ public class UserSwitcherController implements Dumpable {
int id;
if (record.isGuest && record.info == null) {
// No guest user. Create one.
- UserInfo guest = mUserManager.createGuest(
- mContext, mContext.getString(com.android.settingslib.R.string.guest_nickname));
+ UserInfo guest;
+ try {
+ guest = mUserManager.createGuest(mContext,
+ mContext.getString(com.android.settingslib.R.string.guest_nickname));
+ } catch (UserManager.UserOperationException e) {
+ Log.e(TAG, "Couldn't create guest user", e);
+ return;
+ }
if (guest == null) {
// Couldn't create guest, most likely because there already exists one, we just
// haven't reloaded the user list yet.
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
index 2973e0aedd43..bc2a55c8d5c4 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
@@ -16,13 +16,19 @@
package com.android.systemui.usb;
+import android.app.Activity;
import android.app.AlertDialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.IntentFilter;
import android.debug.IAdbManager;
+import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.os.IBinder;
import android.os.ServiceManager;
+import android.os.SystemProperties;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -33,13 +39,25 @@ import android.widget.CheckBox;
import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;
import com.android.systemui.R;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+
+import javax.inject.Inject;
public class UsbDebuggingActivity extends AlertActivity
implements DialogInterface.OnClickListener {
private static final String TAG = "UsbDebuggingActivity";
private CheckBox mAlwaysAllow;
+ private UsbDisconnectedReceiver mDisconnectedReceiver;
+ private final BroadcastDispatcher mBroadcastDispatcher;
private String mKey;
+ private boolean mServiceNotified;
+
+ @Inject
+ public UsbDebuggingActivity(BroadcastDispatcher broadcastDispatcher) {
+ super();
+ mBroadcastDispatcher = broadcastDispatcher;
+ }
@Override
public void onCreate(Bundle icicle) {
@@ -50,6 +68,10 @@ public class UsbDebuggingActivity extends AlertActivity
super.onCreate(icicle);
+ if (SystemProperties.getInt("service.adb.tcp.port", 0) == 0) {
+ mDisconnectedReceiver = new UsbDisconnectedReceiver(this);
+ }
+
Intent intent = getIntent();
String fingerprints = intent.getStringExtra("fingerprints");
mKey = intent.getStringExtra("key");
@@ -83,10 +105,77 @@ public class UsbDebuggingActivity extends AlertActivity
super.onWindowAttributesChanged(params);
}
+ private class UsbDisconnectedReceiver extends BroadcastReceiver {
+ private final Activity mActivity;
+ UsbDisconnectedReceiver(Activity activity) {
+ mActivity = activity;
+ }
+
+ @Override
+ public void onReceive(Context content, Intent intent) {
+ String action = intent.getAction();
+ if (!UsbManager.ACTION_USB_STATE.equals(action)) {
+ return;
+ }
+ boolean connected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
+ if (!connected) {
+ notifyService(false);
+ mActivity.finish();
+ }
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ if (mDisconnectedReceiver != null) {
+ IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_STATE);
+ mBroadcastDispatcher.registerReceiver(mDisconnectedReceiver, filter);
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ if (mDisconnectedReceiver != null) {
+ mBroadcastDispatcher.unregisterReceiver(mDisconnectedReceiver);
+ }
+ // If the ADB service has not yet been notified due to this dialog being closed in some
+ // other way then notify the service to deny the connection to ensure system_server sends
+ // a response to adbd.
+ if (!mServiceNotified) {
+ notifyService(false);
+ }
+ super.onStop();
+ }
+
@Override
public void onClick(DialogInterface dialog, int which) {
boolean allow = (which == AlertDialog.BUTTON_POSITIVE);
boolean alwaysAllow = allow && mAlwaysAllow.isChecked();
+ notifyService(allow, alwaysAllow);
+ finish();
+ }
+
+ /**
+ * Notifies the ADB service as to whether the current ADB request should be allowed; if the
+ * request is allowed it is only allowed for this session, and the user should be prompted again
+ * on subsequent requests from this key.
+ *
+ * @param allow whether the connection should be allowed for this session
+ */
+ private void notifyService(boolean allow) {
+ notifyService(allow, false);
+ }
+
+ /**
+ * Notifies the ADB service as to whether the current ADB request should be allowed, and if
+ * subsequent requests from this key should be allowed without user consent.
+ *
+ * @param allow whether the connection should be allowed
+ * @param alwaysAllow whether subsequent requests from this key should be allowed without user
+ * consent
+ */
+ private void notifyService(boolean allow, boolean alwaysAllow) {
try {
IBinder b = ServiceManager.getService(ADB_SERVICE);
IAdbManager service = IAdbManager.Stub.asInterface(b);
@@ -95,9 +184,9 @@ public class UsbDebuggingActivity extends AlertActivity
} else {
service.denyDebugging();
}
+ mServiceNotified = true;
} catch (Exception e) {
Log.e(TAG, "Unable to notify Usb service", e);
}
- finish();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingSecondaryUserActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingSecondaryUserActivity.java
index 421424206370..4850a025b684 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingSecondaryUserActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingSecondaryUserActivity.java
@@ -16,19 +16,47 @@
package com.android.systemui.usb;
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.debug.IAdbManager;
+import android.hardware.usb.UsbManager;
import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.util.Log;
import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;
import com.android.systemui.R;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+
+import javax.inject.Inject;
public class UsbDebuggingSecondaryUserActivity extends AlertActivity
implements DialogInterface.OnClickListener {
+ private static final String TAG = "UsbDebuggingSecondaryUserActivity";
+ private UsbDisconnectedReceiver mDisconnectedReceiver;
+ private final BroadcastDispatcher mBroadcastDispatcher;
+
+ @Inject
+ public UsbDebuggingSecondaryUserActivity(BroadcastDispatcher broadcastDispatcher) {
+ mBroadcastDispatcher = broadcastDispatcher;
+ }
+
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
+ if (SystemProperties.getInt("service.adb.tcp.port", 0) == 0) {
+ mDisconnectedReceiver = new UsbDisconnectedReceiver(this);
+ }
+
final AlertController.AlertParams ap = mAlertParams;
ap.mTitle = getString(R.string.usb_debugging_secondary_user_title);
ap.mMessage = getString(R.string.usb_debugging_secondary_user_message);
@@ -38,6 +66,48 @@ public class UsbDebuggingSecondaryUserActivity extends AlertActivity
setupAlert();
}
+ private class UsbDisconnectedReceiver extends BroadcastReceiver {
+ private final Activity mActivity;
+ UsbDisconnectedReceiver(Activity activity) {
+ mActivity = activity;
+ }
+
+ @Override
+ public void onReceive(Context content, Intent intent) {
+ String action = intent.getAction();
+ if (UsbManager.ACTION_USB_STATE.equals(action)) {
+ boolean connected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
+ if (!connected) {
+ mActivity.finish();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ if (mDisconnectedReceiver != null) {
+ IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_STATE);
+ mBroadcastDispatcher.registerReceiver(mDisconnectedReceiver, filter);
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ if (mDisconnectedReceiver != null) {
+ mBroadcastDispatcher.unregisterReceiver(mDisconnectedReceiver);
+ }
+ try {
+ IBinder b = ServiceManager.getService(ADB_SERVICE);
+ IAdbManager service = IAdbManager.Stub.asInterface(b);
+ service.denyDebugging();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to notify Usb service", e);
+ }
+ super.onStop();
+ }
+
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
diff --git a/packages/SystemUI/src/com/android/systemui/controls/UserAwareController.kt b/packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt
index d2776d27ae62..693c2708b0f7 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/UserAwareController.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.controls
+package com.android.systemui.util
import android.os.UserHandle
@@ -23,6 +23,8 @@ import android.os.UserHandle
* changes.
*/
interface UserAwareController {
+ @JvmDefault
fun changeUser(newUser: UserHandle) {}
+
val currentUserId: Int
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt
index 318a6d727e5c..016f4de724b6 100644
--- a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt
@@ -644,6 +644,12 @@ class PhysicsAnimator<T> private constructor (target: T) {
it.onInternalAnimationEnd(
property, canceled, value, velocity, anim is FlingAnimation)
}
+ if (springAnimations[property] == anim) {
+ springAnimations.remove(property)
+ }
+ if (flingAnimations[property] == anim) {
+ flingAnimations.remove(property)
+ }
}
return anim
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt b/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt
index da7953d27f59..5a2b064c5389 100644
--- a/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt
@@ -32,6 +32,7 @@ import androidx.dynamicanimation.animation.DynamicAnimation
import androidx.dynamicanimation.animation.FloatPropertyCompat
import androidx.dynamicanimation.animation.SpringForce
import com.android.systemui.util.animation.PhysicsAnimator
+import kotlin.math.abs
import kotlin.math.hypot
/**
@@ -231,11 +232,11 @@ abstract class MagnetizedObject<T : Any>(
var flingUnstuckFromTargetMinVelocity = 1000f
/**
- * Sets the maximum velocity above which the object will not stick to the target. Even if the
+ * Sets the maximum X velocity above which the object will not stick to the target. Even if the
* object is dragged through the magnetic field, it will not stick to the target until the
- * velocity is below this value.
+ * horizontal velocity is below this value.
*/
- var stickToTargetMaxVelocity = 2000f
+ var stickToTargetMaxXVelocity = 2000f
/**
* Enable or disable haptic vibration effects when the object interacts with the magnetic field.
@@ -363,7 +364,7 @@ abstract class MagnetizedObject<T : Any>(
// If the object is moving too quickly within the magnetic field, do not stick it. This
// only applies to objects newly stuck to a target. If the object is moved into a new
// target, it wasn't moving at all (since it was stuck to the previous one).
- if (objectNewlyStuckToTarget && hypot(velX, velY) > stickToTargetMaxVelocity) {
+ if (objectNewlyStuckToTarget && abs(velX) > stickToTargetMaxXVelocity) {
return false
}
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
index 32e3a7fc032e..7b114525adcd 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
@@ -19,11 +19,13 @@ package com.android.systemui.wm;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Handler;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.util.Slog;
import android.util.SparseArray;
import android.view.IDisplayWindowInsetsController;
@@ -36,6 +38,7 @@ import android.view.WindowInsets;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
+import com.android.internal.view.IInputMethodManager;
import com.android.systemui.TransactionPool;
import com.android.systemui.dagger.qualifiers.Main;
@@ -352,6 +355,16 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
dispatchEndPositioning(mDisplayId, mCancelled, t);
if (mAnimationDirection == DIRECTION_HIDE && !mCancelled) {
t.hide(mImeSourceControl.getLeash());
+ final IInputMethodManager imms = getImms();
+ if (imms != null) {
+ try {
+ // Remove the IME surface to make the insets invisible for
+ // non-client controlled insets.
+ imms.removeImeSurface();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to remove IME surface.", e);
+ }
+ }
}
t.apply();
mTransactionPool.release(t);
@@ -382,9 +395,9 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
* Called when the IME position is starting to animate.
*
* @param hiddenTop The y position of the top of the IME surface when it is hidden.
- * @param shownTop The y position of the top of the IME surface when it is shown.
- * @param showing {@code true} when we are animating from hidden to shown, {@code false}
- * when animating from shown to hidden.
+ * @param shownTop The y position of the top of the IME surface when it is shown.
+ * @param showing {@code true} when we are animating from hidden to shown, {@code false}
+ * when animating from shown to hidden.
*/
default void onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
boolean showing, SurfaceControl.Transaction t) {}
@@ -406,4 +419,9 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
default void onImeEndPositioning(int displayId, boolean cancel,
SurfaceControl.Transaction t) {}
}
+
+ public IInputMethodManager getImms() {
+ return IInputMethodManager.Stub.asInterface(
+ ServiceManager.getService(Context.INPUT_METHOD_SERVICE));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 7bc453ac9aa1..023879926563 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -452,12 +452,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
@Test
- public void requiresAuthentication_whenEncryptedKeyguard_andBypass() {
- testStrongAuthExceptOnBouncer(
- KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT);
- }
-
- @Test
public void requiresAuthentication_whenTimeoutKeyguard_andBypass() {
testStrongAuthExceptOnBouncer(
KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT);
@@ -513,10 +507,20 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testIgnoresAuth_whenLockdown() {
+ testIgnoresAuth(
+ KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+ }
+
+ @Test
+ public void testIgnoresAuth_whenEncrypted() {
+ testIgnoresAuth(
+ KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT);
+ }
+
+ private void testIgnoresAuth(int strongAuth) {
mKeyguardUpdateMonitor.dispatchStartedWakingUp();
mTestableLooper.processAllMessages();
- when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(
- KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+ when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(strongAuth);
mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
verify(mFaceManager, never()).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index b2c35867e789..713aef9e8a91 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -35,6 +35,7 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.broadcast.FakeBroadcastDispatcher;
+import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.FalsingManager;
@@ -72,8 +73,8 @@ public abstract class SysuiTestCase {
public void SysuiSetup() throws Exception {
SystemUIFactory.createFromConfig(mContext);
mDependency = new TestableDependency(mContext);
- mFakeBroadcastDispatcher = new FakeBroadcastDispatcher(mContext, mock(Handler.class),
- mock(Looper.class), mock(DumpManager.class));
+ mFakeBroadcastDispatcher = new FakeBroadcastDispatcher(mContext, mock(Looper.class),
+ mock(DumpManager.class), mock(BroadcastDispatcherLogger.class));
mRealInstrumentation = InstrumentationRegistry.getInstrumentation();
Instrumentation inst = spy(mRealInstrumentation);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
index 3357c5863d46..4ed284ede634 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
@@ -18,6 +18,7 @@ package com.android.systemui.broadcast
import android.content.BroadcastReceiver
import android.content.Context
+import android.content.Intent
import android.content.IntentFilter
import android.os.Handler
import android.os.Looper
@@ -27,6 +28,7 @@ import android.test.suitebuilder.annotation.SmallTest
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
import com.android.systemui.dump.DumpManager
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
@@ -76,6 +78,8 @@ class BroadcastDispatcherTest : SysuiTestCase() {
private lateinit var intentFilterOther: IntentFilter
@Mock
private lateinit var mockHandler: Handler
+ @Mock
+ private lateinit var logger: BroadcastDispatcherLogger
private lateinit var executor: Executor
@@ -93,9 +97,9 @@ class BroadcastDispatcherTest : SysuiTestCase() {
broadcastDispatcher = TestBroadcastDispatcher(
mockContext,
- Handler(testableLooper.looper),
testableLooper.looper,
mock(DumpManager::class.java),
+ logger,
mapOf(0 to mockUBRUser0, 1 to mockUBRUser1))
// These should be valid filters
@@ -173,7 +177,12 @@ class BroadcastDispatcherTest : SysuiTestCase() {
@Test
fun testRegisterCurrentAsActualUser() {
- setUserMock(mockContext, user1)
+ val intent = Intent(Intent.ACTION_USER_SWITCHED).apply {
+ putExtra(Intent.EXTRA_USER_HANDLE, user1.identifier)
+ }
+ broadcastDispatcher.onReceive(mockContext, intent)
+ testableLooper.processAllMessages()
+
broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter,
mockHandler, UserHandle.CURRENT)
@@ -236,11 +245,11 @@ class BroadcastDispatcherTest : SysuiTestCase() {
private class TestBroadcastDispatcher(
context: Context,
- mainHandler: Handler,
bgLooper: Looper,
dumpManager: DumpManager,
+ logger: BroadcastDispatcherLogger,
var mockUBRMap: Map<Int, UserBroadcastDispatcher>
- ) : BroadcastDispatcher(context, mainHandler, bgLooper, dumpManager) {
+ ) : BroadcastDispatcher(context, bgLooper, dumpManager, logger) {
override fun createUBRForUser(userId: Int): UserBroadcastDispatcher {
return mockUBRMap.getOrDefault(userId, mock(UserBroadcastDispatcher::class.java))
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
index 9a5773a7a3b4..09a091689a23 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
@@ -24,15 +24,16 @@ import android.os.UserHandle
import android.util.ArraySet
import android.util.Log
import com.android.systemui.SysuiTestableContext
+import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
import com.android.systemui.dump.DumpManager
import java.util.concurrent.Executor
class FakeBroadcastDispatcher(
context: SysuiTestableContext,
- handler: Handler,
looper: Looper,
- dumpManager: DumpManager
-) : BroadcastDispatcher(context, handler, looper, dumpManager) {
+ dumpManager: DumpManager,
+ logger: BroadcastDispatcherLogger
+) : BroadcastDispatcher(context, looper, dumpManager, logger) {
private val registeredReceivers = ArraySet<BroadcastReceiver>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt
index 847e442f1a49..443357694f4d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt
@@ -26,6 +26,7 @@ import android.test.suitebuilder.annotation.SmallTest
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import junit.framework.Assert.assertEquals
@@ -40,6 +41,7 @@ import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.eq
import org.mockito.Captor
import org.mockito.Mock
+import org.mockito.Mockito
import org.mockito.Mockito.anyString
import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.never
@@ -62,6 +64,8 @@ class UserBroadcastDispatcherTest : SysuiTestCase() {
private val USER_HANDLE = UserHandle.of(USER_ID)
fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
+ fun <T> any(): T = Mockito.any()
+ fun <T> eq(v: T) = Mockito.eq(v) ?: v
}
@Mock
@@ -72,6 +76,8 @@ class UserBroadcastDispatcherTest : SysuiTestCase() {
private lateinit var mockContext: Context
@Mock
private lateinit var mPendingResult: BroadcastReceiver.PendingResult
+ @Mock
+ private lateinit var logger: BroadcastDispatcherLogger
@Captor
private lateinit var argumentCaptor: ArgumentCaptor<IntentFilter>
@@ -91,7 +97,7 @@ class UserBroadcastDispatcherTest : SysuiTestCase() {
fakeExecutor = FakeExecutor(FakeSystemClock())
userBroadcastDispatcher = UserBroadcastDispatcher(
- mockContext, USER_ID, testableLooper.looper)
+ mockContext, USER_ID, testableLooper.looper, logger)
userBroadcastDispatcher.pendingResult = mPendingResult
}
@@ -106,6 +112,13 @@ class UserBroadcastDispatcherTest : SysuiTestCase() {
}
@Test
+ fun testNotRegisteredOnStart_logging() {
+ testableLooper.processAllMessages()
+
+ verify(logger, never()).logContextReceiverRegistered(anyInt(), any())
+ }
+
+ @Test
fun testSingleReceiverRegistered() {
intentFilter = IntentFilter(ACTION_1)
@@ -126,6 +139,18 @@ class UserBroadcastDispatcherTest : SysuiTestCase() {
}
@Test
+ fun testSingleReceiverRegistered_logging() {
+ intentFilter = IntentFilter(ACTION_1)
+
+ userBroadcastDispatcher.registerReceiver(
+ ReceiverData(broadcastReceiver, intentFilter, fakeExecutor, USER_HANDLE))
+ testableLooper.processAllMessages()
+
+ verify(logger).logReceiverRegistered(USER_HANDLE.identifier, broadcastReceiver)
+ verify(logger).logContextReceiverRegistered(eq(USER_HANDLE.identifier), any())
+ }
+
+ @Test
fun testSingleReceiverUnregistered() {
intentFilter = IntentFilter(ACTION_1)
@@ -145,6 +170,21 @@ class UserBroadcastDispatcherTest : SysuiTestCase() {
}
@Test
+ fun testSingleReceiverUnregistered_logger() {
+ intentFilter = IntentFilter(ACTION_1)
+
+ userBroadcastDispatcher.registerReceiver(
+ ReceiverData(broadcastReceiver, intentFilter, fakeExecutor, USER_HANDLE))
+ testableLooper.processAllMessages()
+
+ userBroadcastDispatcher.unregisterReceiver(broadcastReceiver)
+ testableLooper.processAllMessages()
+
+ verify(logger).logReceiverUnregistered(USER_HANDLE.identifier, broadcastReceiver)
+ verify(logger).logContextReceiverUnregistered(USER_HANDLE.identifier)
+ }
+
+ @Test
fun testFilterHasAllActionsAndCategories_twoReceivers() {
intentFilter = IntentFilter(ACTION_1)
intentFilterOther = IntentFilter(ACTION_2).apply {
@@ -196,6 +236,30 @@ class UserBroadcastDispatcherTest : SysuiTestCase() {
}
@Test
+ fun testDispatch_logger() {
+ intentFilter = IntentFilter(ACTION_1)
+ intentFilterOther = IntentFilter(ACTION_2)
+
+ userBroadcastDispatcher.registerReceiver(
+ ReceiverData(broadcastReceiver, intentFilter, fakeExecutor, USER_HANDLE))
+ userBroadcastDispatcher.registerReceiver(
+ ReceiverData(broadcastReceiverOther, intentFilterOther, fakeExecutor, USER_HANDLE))
+
+ val intent = Intent(ACTION_2)
+
+ userBroadcastDispatcher.onReceive(mockContext, intent)
+ testableLooper.processAllMessages()
+ fakeExecutor.runAllReady()
+
+ val captor = ArgumentCaptor.forClass(Int::class.java)
+ verify(logger)
+ .logBroadcastReceived(captor.capture(), eq(USER_HANDLE.identifier), eq(intent))
+ verify(logger).logBroadcastDispatched(captor.value, ACTION_2, broadcastReceiverOther)
+ verify(logger, never())
+ .logBroadcastDispatched(eq(captor.value), any(), eq(broadcastReceiver))
+ }
+
+ @Test
fun testDispatchToCorrectReceiver_differentFiltersSameReceiver() {
intentFilter = IntentFilter(ACTION_1)
intentFilterOther = IntentFilter(ACTION_2)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 472b121f7b95..1e48b990b19d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -425,7 +425,8 @@ public class BubbleControllerTest extends SysuiTestCase {
BubbleStackView stackView = mBubbleController.getStackView();
mBubbleData.setExpanded(true);
assertTrue(mBubbleController.isStackExpanded());
- verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey());
+ verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged(
+ true, mRow2.getEntry().getKey());
assertTrue(mSysUiStateBubblesExpanded);
@@ -443,9 +444,11 @@ public class BubbleControllerTest extends SysuiTestCase {
mRow.getEntry()));
// collapse for previous bubble
- verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey());
+ verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged(
+ false, mRow2.getEntry().getKey());
// expand for selected bubble
- verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
+ verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged(
+ true, mRow.getEntry().getKey());
// Collapse
mBubbleController.collapseStack();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
index 17022da3ecde..1ca2f02db1bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
@@ -537,7 +537,7 @@ public class BubbleDataTest extends SysuiTestCase {
// Verify the selection was cleared.
verifyUpdateReceived();
assertThat(mBubbleData.isExpanded()).isFalse();
- assertSelectionCleared();
+ assertThat(mBubbleData.getSelectedBubble()).isNull();
}
// EXPANDED / ADD / UPDATE
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index 47cd9bca2861..0be24729dff9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -382,7 +382,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
BubbleStackView stackView = mBubbleController.getStackView();
mBubbleData.setExpanded(true);
assertTrue(mBubbleController.isStackExpanded());
- verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey());
+ verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged(
+ true, mRow.getEntry().getKey());
// Last added is the one that is expanded
assertEquals(mRow2.getEntry(), mBubbleData.getSelectedBubble().getEntry());
@@ -397,9 +398,11 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
mRow.getEntry()));
// collapse for previous bubble
- verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey());
+ verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged(
+ false, mRow2.getEntry().getKey());
// expand for selected bubble
- verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
+ verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged(
+ true, mRow.getEntry().getKey());
// Collapse
mBubbleController.collapseStack();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
index 3a3140f2ff53..6fcf6e37572b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
@@ -51,6 +51,7 @@ import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
private const val KEY = "TEST_KEY"
+private const val KEY_OLD = "TEST_KEY_OLD"
private const val PACKAGE = "PKG"
private const val SESSION_KEY = "SESSION_KEY"
private const val SESSION_ARTIST = "SESSION_ARTIST"
@@ -69,7 +70,6 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
@Mock private lateinit var lmmFactory: LocalMediaManagerFactory
@Mock private lateinit var lmm: LocalMediaManager
@Mock private lateinit var mr2: MediaRouter2Manager
- @Mock private lateinit var featureFlag: MediaFeatureFlag
private lateinit var fakeExecutor: FakeExecutor
@Mock private lateinit var listener: MediaDeviceManager.Listener
@Mock private lateinit var device: MediaDevice
@@ -85,8 +85,7 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
@Before
fun setUp() {
fakeExecutor = FakeExecutor(FakeSystemClock())
- manager = MediaDeviceManager(context, lmmFactory, mr2, featureFlag, fakeExecutor,
- mediaDataManager)
+ manager = MediaDeviceManager(context, lmmFactory, mr2, fakeExecutor, mediaDataManager)
manager.addListener(listener)
// Configure mocks.
@@ -95,7 +94,6 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
whenever(lmmFactory.create(PACKAGE)).thenReturn(lmm)
whenever(lmm.getCurrentConnectedDevice()).thenReturn(device)
whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(route)
- whenever(featureFlag.enabled).thenReturn(true)
// Create a media sesssion and notification for testing.
metadataBuilder = MediaMetadata.Builder().apply {
@@ -132,23 +130,74 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
}
@Test
- fun addNotification() {
+ fun loadMediaData() {
manager.onMediaDataLoaded(KEY, null, mediaData)
verify(lmmFactory).create(PACKAGE)
}
@Test
- fun featureDisabled() {
- whenever(featureFlag.enabled).thenReturn(false)
+ fun loadAndRemoveMediaData() {
manager.onMediaDataLoaded(KEY, null, mediaData)
- verify(lmmFactory, never()).create(PACKAGE)
+ manager.onMediaDataRemoved(KEY)
+ verify(lmm).unregisterCallback(any())
}
@Test
- fun addAndRemoveNotification() {
- manager.onMediaDataLoaded(KEY, null, mediaData)
- manager.onMediaDataRemoved(KEY)
+ fun loadMediaDataWithNullToken() {
+ manager.onMediaDataLoaded(KEY, null, mediaData.copy(token = null))
+ fakeExecutor.runAllReady()
+ val data = captureDeviceData(KEY)
+ assertThat(data.enabled).isTrue()
+ assertThat(data.name).isEqualTo(DEVICE_NAME)
+ }
+
+ @Test
+ fun loadWithNewKey() {
+ // GIVEN that media data has been loaded with an old key
+ manager.onMediaDataLoaded(KEY_OLD, null, mediaData)
+ reset(listener)
+ // WHEN data is loaded with a new key
+ manager.onMediaDataLoaded(KEY, KEY_OLD, mediaData)
+ // THEN the listener for the old key should removed.
verify(lmm).unregisterCallback(any())
+ // AND a new device event emitted
+ val data = captureDeviceData(KEY)
+ assertThat(data.enabled).isTrue()
+ assertThat(data.name).isEqualTo(DEVICE_NAME)
+ }
+
+ @Test
+ fun newKeySameAsOldKey() {
+ // GIVEN that media data has been loaded
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ reset(listener)
+ // WHEN the new key is the same as the old key
+ manager.onMediaDataLoaded(KEY, KEY, mediaData)
+ // THEN no event should be emitted
+ verify(listener, never()).onMediaDeviceChanged(eq(KEY), any())
+ }
+
+ @Test
+ fun unknownOldKey() {
+ manager.onMediaDataLoaded(KEY, "unknown", mediaData)
+ verify(listener).onMediaDeviceChanged(eq(KEY), any())
+ }
+
+ @Test
+ fun updateToSessionTokenWithNullRoute() {
+ // GIVEN that media data has been loaded with a null token
+ manager.onMediaDataLoaded(KEY, null, mediaData.copy(token = null))
+ // WHEN media data is loaded with a different token
+ // AND that token results in a null route
+ reset(listener)
+ whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ // THEN the device should be disabled
+ fakeExecutor.runAllReady()
+ val data = captureDeviceData(KEY)
+ assertThat(data.enabled).isFalse()
+ assertThat(data.name).isNull()
+ assertThat(data.icon).isNull()
}
@Test
@@ -164,6 +213,15 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
}
@Test
+ fun removeListener() {
+ // WHEN a listener is removed
+ manager.removeListener(listener)
+ // THEN it doesn't receive device events
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ verify(listener, never()).onMediaDeviceChanged(eq(KEY), any())
+ }
+
+ @Test
fun deviceListUpdate() {
manager.onMediaDataLoaded(KEY, null, mediaData)
val deviceCallback = captureCallback()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
index 24e9bd837d5d..c8ef9fbf06e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
@@ -40,6 +40,7 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.any
+import org.mockito.Mockito.eq
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
@@ -204,7 +205,7 @@ public class SeekBarViewModelTest : SysuiTestCase() {
@Test
fun updateElapsedTime() {
- // GIVEN that the PlaybackState contins the current position
+ // GIVEN that the PlaybackState contains the current position
val position = 200L
val state = PlaybackState.Builder().run {
setState(PlaybackState.STATE_PLAYING, position, 1f)
@@ -246,7 +247,7 @@ public class SeekBarViewModelTest : SysuiTestCase() {
}
@Test
- fun handleSeek() {
+ fun onSeek() {
whenever(mockController.getTransportControls()).thenReturn(mockTransport)
viewModel.updateController(mockController)
// WHEN user input is dispatched
@@ -258,19 +259,73 @@ public class SeekBarViewModelTest : SysuiTestCase() {
}
@Test
- fun handleProgressChangedUser() {
+ fun onSeekWithFalse() {
whenever(mockController.getTransportControls()).thenReturn(mockTransport)
viewModel.updateController(mockController)
+ // WHEN a false is received during the seek gesture
+ val pos = 42L
+ with(viewModel) {
+ onSeekStarting()
+ onSeekFalse()
+ onSeek(pos)
+ }
+ fakeExecutor.runAllReady()
+ // THEN the seek is rejected and the transport never receives seekTo
+ verify(mockTransport, never()).seekTo(pos)
+ }
+
+ @Test
+ fun onSeekProgress() {
+ val pos = 42L
+ with(viewModel) {
+ onSeekStarting()
+ onSeekProgress(pos)
+ }
+ fakeExecutor.runAllReady()
+ // THEN then elapsed time should be updated
+ assertThat(viewModel.progress.value!!.elapsedTime).isEqualTo(pos)
+ }
+
+ @Test
+ fun onSeekProgressWithSeekStarting() {
+ val pos = 42L
+ with(viewModel) {
+ onSeekProgress(pos)
+ }
+ fakeExecutor.runAllReady()
+ // THEN then elapsed time should not be updated
+ assertThat(viewModel.progress.value!!.elapsedTime).isNull()
+ }
+
+ @Test
+ fun onProgressChangedFromUser() {
// WHEN user starts dragging the seek bar
val pos = 42
- viewModel.seekBarListener.onProgressChanged(SeekBar(context), pos, true)
+ val bar = SeekBar(context)
+ with(viewModel.seekBarListener) {
+ onStartTrackingTouch(bar)
+ onProgressChanged(bar, pos, true)
+ }
fakeExecutor.runAllReady()
- // THEN transport controls should be used
- verify(mockTransport).seekTo(pos.toLong())
+ // THEN then elapsed time should be updated
+ assertThat(viewModel.progress.value!!.elapsedTime).isEqualTo(pos)
+ }
+
+ @Test
+ fun onProgressChangedFromUserWithoutStartTrackingTouch() {
+ // WHEN user starts dragging the seek bar
+ val pos = 42
+ val bar = SeekBar(context)
+ with(viewModel.seekBarListener) {
+ onProgressChanged(bar, pos, true)
+ }
+ fakeExecutor.runAllReady()
+ // THEN then elapsed time should not be updated
+ assertThat(viewModel.progress.value!!.elapsedTime).isNull()
}
@Test
- fun handleProgressChangedOther() {
+ fun onProgressChangedNotFromUser() {
whenever(mockController.getTransportControls()).thenReturn(mockTransport)
viewModel.updateController(mockController)
// WHEN user starts dragging the seek bar
@@ -282,7 +337,7 @@ public class SeekBarViewModelTest : SysuiTestCase() {
}
@Test
- fun handleStartTrackingTouch() {
+ fun onStartTrackingTouch() {
whenever(mockController.getTransportControls()).thenReturn(mockTransport)
viewModel.updateController(mockController)
// WHEN user starts dragging the seek bar
@@ -297,7 +352,7 @@ public class SeekBarViewModelTest : SysuiTestCase() {
}
@Test
- fun handleStopTrackingTouch() {
+ fun onStopTrackingTouch() {
whenever(mockController.getTransportControls()).thenReturn(mockTransport)
viewModel.updateController(mockController)
// WHEN user ends drag
@@ -312,6 +367,26 @@ public class SeekBarViewModelTest : SysuiTestCase() {
}
@Test
+ fun onStopTrackingTouchAfterProgress() {
+ whenever(mockController.getTransportControls()).thenReturn(mockTransport)
+ viewModel.updateController(mockController)
+ // WHEN user starts dragging the seek bar
+ val pos = 42
+ val progPos = 84
+ val bar = SeekBar(context).apply {
+ progress = pos
+ }
+ with(viewModel.seekBarListener) {
+ onStartTrackingTouch(bar)
+ onProgressChanged(bar, progPos, true)
+ onStopTrackingTouch(bar)
+ }
+ fakeExecutor.runAllReady()
+ // THEN then elapsed time should be updated
+ verify(mockTransport).seekTo(eq(pos.toLong()))
+ }
+
+ @Test
fun queuePollTaskWhenPlaying() {
// GIVEN that the track is playing
val state = PlaybackState.Builder().run {
@@ -369,7 +444,7 @@ public class SeekBarViewModelTest : SysuiTestCase() {
}
// AND the playback state is playing
val state = PlaybackState.Builder().run {
- setState(PlaybackState.STATE_STOPPED, 200L, 1f)
+ setState(PlaybackState.STATE_PLAYING, 200L, 1f)
build()
}
whenever(mockController.getPlaybackState()).thenReturn(state)
@@ -398,6 +473,90 @@ public class SeekBarViewModelTest : SysuiTestCase() {
}
@Test
+ fun noQueuePollTaskWhenSeeking() {
+ // GIVEN listening
+ viewModel.listening = true
+ // AND the playback state is playing
+ val state = PlaybackState.Builder().run {
+ setState(PlaybackState.STATE_PLAYING, 200L, 1f)
+ build()
+ }
+ whenever(mockController.getPlaybackState()).thenReturn(state)
+ viewModel.updateController(mockController)
+ with(fakeExecutor) {
+ advanceClockToNext()
+ runAllReady()
+ }
+ // WHEN seek starts
+ viewModel.onSeekStarting()
+ with(fakeExecutor) {
+ advanceClockToNext()
+ runAllReady()
+ }
+ // THEN an update task is not queued because we don't want it fighting with the user when
+ // they are trying to move the thumb.
+ assertThat(fakeExecutor.numPending()).isEqualTo(0)
+ }
+
+ @Test
+ fun queuePollTaskWhenDoneSeekingWithFalse() {
+ // GIVEN listening
+ viewModel.listening = true
+ // AND the playback state is playing
+ val state = PlaybackState.Builder().run {
+ setState(PlaybackState.STATE_PLAYING, 200L, 1f)
+ build()
+ }
+ whenever(mockController.getPlaybackState()).thenReturn(state)
+ viewModel.updateController(mockController)
+ with(fakeExecutor) {
+ advanceClockToNext()
+ runAllReady()
+ }
+ // WHEN seek finishes after a false
+ with(viewModel) {
+ onSeekStarting()
+ onSeekFalse()
+ onSeek(42L)
+ }
+ with(fakeExecutor) {
+ advanceClockToNext()
+ runAllReady()
+ }
+ // THEN an update task is queued because the gesture was ignored and progress was restored.
+ assertThat(fakeExecutor.numPending()).isEqualTo(1)
+ }
+
+ @Test
+ fun noQueuePollTaskWhenDoneSeeking() {
+ // GIVEN listening
+ viewModel.listening = true
+ // AND the playback state is playing
+ val state = PlaybackState.Builder().run {
+ setState(PlaybackState.STATE_PLAYING, 200L, 1f)
+ build()
+ }
+ whenever(mockController.getPlaybackState()).thenReturn(state)
+ viewModel.updateController(mockController)
+ with(fakeExecutor) {
+ advanceClockToNext()
+ runAllReady()
+ }
+ // WHEN seek finishes after a false
+ with(viewModel) {
+ onSeekStarting()
+ onSeek(42L)
+ }
+ with(fakeExecutor) {
+ advanceClockToNext()
+ runAllReady()
+ }
+ // THEN no update task is queued because we are waiting for an updated playback state to be
+ // returned in response to the seek.
+ assertThat(fakeExecutor.numPending()).isEqualTo(0)
+ }
+
+ @Test
fun startListeningQueuesPollTask() {
// GIVEN not listening
viewModel.listening = false
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
index 0ae9461d38b1..61f5a7bdd3b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
@@ -21,6 +21,7 @@ import static com.android.systemui.statusbar.phone.AutoTileManager.WORK;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import android.os.UserHandle;
import android.provider.Settings.Secure;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
@@ -40,6 +41,8 @@ import org.junit.runner.RunWith;
@SmallTest
public class AutoAddTrackerTest extends SysuiTestCase {
+ private static final int USER = 0;
+
private AutoAddTracker mAutoTracker;
@Before
@@ -51,7 +54,7 @@ public class AutoAddTrackerTest extends SysuiTestCase {
public void testMigration() {
Prefs.putBoolean(mContext, Key.QS_DATA_SAVER_ADDED, true);
Prefs.putBoolean(mContext, Key.QS_WORK_ADDED, true);
- mAutoTracker = new AutoAddTracker(mContext);
+ mAutoTracker = new AutoAddTracker(mContext, USER);
assertTrue(mAutoTracker.isAdded(SAVER));
assertTrue(mAutoTracker.isAdded(WORK));
@@ -68,7 +71,7 @@ public class AutoAddTrackerTest extends SysuiTestCase {
@Test
public void testChangeFromBackup() {
- mAutoTracker = new AutoAddTracker(mContext);
+ mAutoTracker = new AutoAddTracker(mContext, USER);
assertFalse(mAutoTracker.isAdded(SAVER));
@@ -82,7 +85,7 @@ public class AutoAddTrackerTest extends SysuiTestCase {
@Test
public void testSetAdded() {
- mAutoTracker = new AutoAddTracker(mContext);
+ mAutoTracker = new AutoAddTracker(mContext, USER);
assertFalse(mAutoTracker.isAdded(SAVER));
mAutoTracker.setTileAdded(SAVER);
@@ -94,16 +97,35 @@ public class AutoAddTrackerTest extends SysuiTestCase {
@Test
public void testPersist() {
- mAutoTracker = new AutoAddTracker(mContext);
+ mAutoTracker = new AutoAddTracker(mContext, USER);
assertFalse(mAutoTracker.isAdded(SAVER));
mAutoTracker.setTileAdded(SAVER);
mAutoTracker.destroy();
- mAutoTracker = new AutoAddTracker(mContext);
+ mAutoTracker = new AutoAddTracker(mContext, USER);
assertTrue(mAutoTracker.isAdded(SAVER));
mAutoTracker.destroy();
}
+
+ @Test
+ public void testIndependentUsers() {
+ mAutoTracker = new AutoAddTracker(mContext, USER);
+ mAutoTracker.setTileAdded(SAVER);
+
+ mAutoTracker = new AutoAddTracker(mContext, USER + 1);
+ assertFalse(mAutoTracker.isAdded(SAVER));
+ }
+
+ @Test
+ public void testChangeUser() {
+ mAutoTracker = new AutoAddTracker(mContext, USER);
+ mAutoTracker.setTileAdded(SAVER);
+
+ mAutoTracker = new AutoAddTracker(mContext, USER + 1);
+ mAutoTracker.changeUser(UserHandle.of(USER));
+ assertTrue(mAutoTracker.isAdded(SAVER));
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
index cdef49d6c94d..2fa6cf02d8b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
@@ -36,6 +36,8 @@ import com.android.systemui.statusbar.notification.stack.NotificationListContain
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Assert;
import org.junit.Before;
@@ -51,6 +53,7 @@ import org.mockito.junit.MockitoRule;
@TestableLooper.RunWithLooper
public class ActivityLaunchAnimatorTest extends SysuiTestCase {
+ private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
private ActivityLaunchAnimator mLaunchAnimator;
@Mock
private ActivityLaunchAnimator.Callback mCallback;
@@ -80,8 +83,8 @@ public class ActivityLaunchAnimatorTest extends SysuiTestCase {
mCallback,
mNotificationPanelViewController,
mNotificationShadeDepthController,
- mNotificationContainer);
-
+ mNotificationContainer,
+ mExecutor);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index ca9cc299b36d..363fe95aae18 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -80,6 +80,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionLogger;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -131,6 +132,7 @@ public class NotifCollectionTest extends SysuiTestCase {
private InOrder mListenerInOrder;
private NoManSimulator mNoMan;
+ private FakeSystemClock mClock = new FakeSystemClock();
@Before
public void setUp() {
@@ -146,10 +148,11 @@ public class NotifCollectionTest extends SysuiTestCase {
mCollection = new NotifCollection(
mStatusBarService,
- mock(DumpManager.class),
+ mClock,
mFeatureFlags,
mLogger,
- mEulogizer);
+ mEulogizer,
+ mock(DumpManager.class));
mCollection.attach(mGroupCoalescer);
mCollection.addCollectionListener(mCollectionListener);
mCollection.setBuildListener(mBuildListener);
@@ -161,6 +164,8 @@ public class NotifCollectionTest extends SysuiTestCase {
mNoMan = new NoManSimulator();
mNoMan.addListener(mNotifHandler);
+
+ mNotifHandler.onNotificationsInitialized();
}
@Test
@@ -1268,6 +1273,42 @@ public class NotifCollectionTest extends SysuiTestCase {
verify(mInterceptor3, never()).shouldInterceptDismissal(clearable);
}
+ @Test(expected = IllegalStateException.class)
+ public void testClearNotificationThrowsIfMissing() {
+ // GIVEN that enough time has passed that we're beyond the forgiveness window
+ mClock.advanceTime(5001);
+
+ // WHEN we get a remove event for a notification we don't know about
+ final NotificationEntry container = new NotificationEntryBuilder()
+ .setPkg(TEST_PACKAGE)
+ .setId(47)
+ .build();
+ mNotifHandler.onNotificationRemoved(
+ container.getSbn(),
+ new RankingMap(new Ranking[]{ container.getRanking() }));
+
+ // THEN an exception is thrown
+ }
+
+ @Test
+ public void testClearNotificationDoesntThrowIfInForgivenessWindow() {
+ // GIVEN that some time has passed but we're still within the initialization forgiveness
+ // window
+ mClock.advanceTime(4999);
+
+ // WHEN we get a remove event for a notification we don't know about
+ final NotificationEntry container = new NotificationEntryBuilder()
+ .setPkg(TEST_PACKAGE)
+ .setId(47)
+ .build();
+ mNotifHandler.onNotificationRemoved(
+ container.getSbn(),
+ new RankingMap(new Ranking[]{ container.getRanking() }));
+
+ // THEN no exception is thrown, but no event is fired
+ verify(mCollectionListener, never()).onEntryRemoved(any(NotificationEntry.class), anyInt());
+ }
+
private static NotificationEntryBuilder buildNotif(String pkg, int id, String tag) {
return new NotificationEntryBuilder()
.setPkg(pkg)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index 05cdd802167a..31779cdf9e71 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -16,18 +16,34 @@
package com.android.systemui.statusbar.phone;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+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.isNotNull;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.ContextWrapper;
import android.hardware.display.ColorDisplayManager;
import android.hardware.display.NightDisplayListener;
import android.os.Handler;
+import android.os.UserHandle;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableContentResolver;
+import android.testing.TestableContext;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
@@ -38,14 +54,18 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.qs.AutoAddTracker;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.SecureSetting;
+import com.android.systemui.statusbar.phone.AutoTileManagerTest.MyContextWrapper;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;
import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.HotspotController;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -64,9 +84,18 @@ public class AutoTileManagerTest extends SysuiTestCase {
private static final String TEST_CUSTOM_SPEC = "custom(" + TEST_COMPONENT + ")";
private static final String SEPARATOR = AutoTileManager.SETTING_SEPARATOR;
+ private static final int USER = 0;
+
@Mock private QSTileHost mQsTileHost;
@Mock private AutoAddTracker mAutoAddTracker;
@Mock private CastController mCastController;
+ @Mock private HotspotController mHotspotController;
+ @Mock private DataSaverController mDataSaverController;
+ @Mock private ManagedProfileController mManagedProfileController;
+ @Mock private NightDisplayListener mNightDisplayListener;
+ @Mock(answer = Answers.RETURNS_SELF)
+ private AutoAddTracker.Builder mAutoAddTrackerBuilder;
+ @Mock private Context mUserContext;
private AutoTileManager mAutoTileManager;
@@ -82,20 +111,110 @@ public class AutoTileManagerTest extends SysuiTestCase {
}
);
- mAutoTileManager = createAutoTileManager();
+ when(mAutoAddTrackerBuilder.build()).thenReturn(mAutoAddTracker);
+ when(mQsTileHost.getUserContext()).thenReturn(mUserContext);
+ when(mUserContext.getUser()).thenReturn(UserHandle.of(USER));
+
+ mAutoTileManager = createAutoTileManager(new
+ MyContextWrapper(mContext));
}
- private AutoTileManager createAutoTileManager() {
- return new AutoTileManager(mContext, mAutoAddTracker, mQsTileHost,
+ @After
+ public void tearDown() {
+ mAutoTileManager.destroy();
+ }
+
+ private AutoTileManager createAutoTileManager(Context context) {
+ return new AutoTileManager(context, mAutoAddTrackerBuilder, mQsTileHost,
Handler.createAsync(TestableLooper.get(this).getLooper()),
- mock(HotspotController.class),
- mock(DataSaverController.class),
- mock(ManagedProfileController.class),
- mock(NightDisplayListener.class),
+ mHotspotController,
+ mDataSaverController,
+ mManagedProfileController,
+ mNightDisplayListener,
mCastController);
}
@Test
+ public void testChangeUserCallbacksStoppedAndStarted() throws Exception {
+ TestableLooper.get(this).runWithLooper(() ->
+ mAutoTileManager.changeUser(UserHandle.of(USER + 1))
+ );
+
+ InOrder inOrderHotspot = inOrder(mHotspotController);
+ inOrderHotspot.verify(mHotspotController).removeCallback(any());
+ inOrderHotspot.verify(mHotspotController).addCallback(any());
+
+ InOrder inOrderDataSaver = inOrder(mDataSaverController);
+ inOrderDataSaver.verify(mDataSaverController).removeCallback(any());
+ inOrderDataSaver.verify(mDataSaverController).addCallback(any());
+
+ InOrder inOrderManagedProfile = inOrder(mManagedProfileController);
+ inOrderManagedProfile.verify(mManagedProfileController).removeCallback(any());
+ inOrderManagedProfile.verify(mManagedProfileController).addCallback(any());
+
+ InOrder inOrderNightDisplay = inOrder(mNightDisplayListener);
+ inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNull());
+ inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNotNull());
+
+ InOrder inOrderCast = inOrder(mCastController);
+ inOrderCast.verify(mCastController).removeCallback(any());
+ inOrderCast.verify(mCastController).addCallback(any());
+
+ SecureSetting setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING);
+ assertEquals(USER + 1, setting.getCurrentUser());
+ assertTrue(setting.isListening());
+ }
+
+ @Test
+ public void testChangeUserSomeCallbacksNotAdded() throws Exception {
+ when(mAutoAddTracker.isAdded("hotspot")).thenReturn(true);
+ when(mAutoAddTracker.isAdded("work")).thenReturn(true);
+ when(mAutoAddTracker.isAdded("cast")).thenReturn(true);
+ when(mAutoAddTracker.isAdded(TEST_SPEC)).thenReturn(true);
+
+ TestableLooper.get(this).runWithLooper(() ->
+ mAutoTileManager.changeUser(UserHandle.of(USER + 1))
+ );
+
+ verify(mAutoAddTracker).changeUser(UserHandle.of(USER + 1));
+
+ InOrder inOrderHotspot = inOrder(mHotspotController);
+ inOrderHotspot.verify(mHotspotController).removeCallback(any());
+ inOrderHotspot.verify(mHotspotController, never()).addCallback(any());
+
+ InOrder inOrderDataSaver = inOrder(mDataSaverController);
+ inOrderDataSaver.verify(mDataSaverController).removeCallback(any());
+ inOrderDataSaver.verify(mDataSaverController).addCallback(any());
+
+ InOrder inOrderManagedProfile = inOrder(mManagedProfileController);
+ inOrderManagedProfile.verify(mManagedProfileController).removeCallback(any());
+ inOrderManagedProfile.verify(mManagedProfileController, never()).addCallback(any());
+
+ InOrder inOrderNightDisplay = inOrder(mNightDisplayListener);
+ inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNull());
+ inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNotNull());
+
+ InOrder inOrderCast = inOrder(mCastController);
+ inOrderCast.verify(mCastController).removeCallback(any());
+ inOrderCast.verify(mCastController, never()).addCallback(any());
+
+ SecureSetting setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING);
+ assertEquals(USER + 1, setting.getCurrentUser());
+ assertFalse(setting.isListening());
+ }
+
+ @Test
+ public void testGetCurrentUserId() throws Exception {
+ assertEquals(USER, mAutoTileManager.getCurrentUserId());
+
+ TestableLooper.get(this).runWithLooper(() ->
+ mAutoTileManager.changeUser(UserHandle.of(USER + 100))
+ );
+
+ assertEquals(USER + 100, mAutoTileManager.getCurrentUserId());
+ }
+
+ @Test
public void nightTileAdded_whenActivated() {
if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) {
return;
@@ -213,14 +332,14 @@ public class AutoTileManagerTest extends SysuiTestCase {
public void testEmptyArray_doesNotCrash() {
mContext.getOrCreateTestableResources().addOverride(
R.array.config_quickSettingsAutoAdd, new String[0]);
- createAutoTileManager();
+ createAutoTileManager(mContext).destroy();
}
@Test
public void testMissingConfig_doesNotCrash() {
mContext.getOrCreateTestableResources().addOverride(
R.array.config_quickSettingsAutoAdd, null);
- createAutoTileManager();
+ createAutoTileManager(mContext).destroy();
}
// Will only notify if it's listening
@@ -231,4 +350,22 @@ public class AutoTileManagerTest extends SysuiTestCase {
s.onChange(false);
}
}
+
+ class MyContextWrapper extends ContextWrapper {
+
+ private TestableContentResolver mSpiedTCR;
+
+ MyContextWrapper(TestableContext context) {
+ super(context);
+ mSpiedTCR = spy(context.getContentResolver());
+ doNothing().when(mSpiedTCR).registerContentObserver(any(), anyBoolean(), any(),
+ anyInt());
+ doNothing().when(mSpiedTCR).unregisterContentObserver(any());
+ }
+
+ @Override
+ public ContentResolver getContentResolver() {
+ return mSpiedTCR;
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/animation/PhysicsAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/animation/PhysicsAnimatorTest.kt
index 661fad2d6d5c..bd596800e86d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/animation/PhysicsAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/animation/PhysicsAnimatorTest.kt
@@ -38,6 +38,7 @@ import org.mockito.MockitoAnnotations
@TestableLooper.RunWithLooper
@RunWith(AndroidTestingRunner::class)
@SmallTest
+@Ignore("Blocking presubmits - investigating in b/158697054")
class PhysicsAnimatorTest : SysuiTestCase() {
private lateinit var viewGroup: ViewGroup
private lateinit var testView: View
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
index d07a70c1af9b..32e2b04e8e4f 100644
--- a/packages/Tethering/Android.bp
+++ b/packages/Tethering/Android.bp
@@ -25,7 +25,7 @@ java_defaults {
],
static_libs: [
"androidx.annotation_annotation",
- "netd_aidl_interface-V3-java",
+ "netd_aidl_interface-java",
"netlink-client",
"networkstack-aidl-interfaces-java",
"android.hardware.tetheroffload.config-V1.0-java",
diff --git a/packages/Tethering/TEST_MAPPING b/packages/Tethering/TEST_MAPPING
index 73254cdc79a9..5617b0c13c1c 100644
--- a/packages/Tethering/TEST_MAPPING
+++ b/packages/Tethering/TEST_MAPPING
@@ -1,7 +1,12 @@
{
- "postsubmit": [
+ "presubmit": [
{
"name": "TetheringTests"
}
+ ],
+ "postsubmit": [
+ {
+ "name": "TetheringIntegrationTests"
+ }
]
}
diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
index 3fd9ee9a330b..1671dda4bd57 100644
--- a/packages/Tethering/src/android/net/ip/IpServer.java
+++ b/packages/Tethering/src/android/net/ip/IpServer.java
@@ -33,7 +33,6 @@ import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.MacAddress;
import android.net.RouteInfo;
-import android.net.TetherOffloadRuleParcel;
import android.net.TetheredClient;
import android.net.TetheringManager;
import android.net.TetheringRequestParcel;
@@ -65,6 +64,8 @@ import androidx.annotation.Nullable;
import com.android.internal.util.MessageUtils;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
+import com.android.networkstack.tethering.BpfCoordinator;
+import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
import com.android.networkstack.tethering.PrivateAddressCoordinator;
import java.io.IOException;
@@ -76,7 +77,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
-import java.util.LinkedHashMap;
import java.util.List;
import java.util.Objects;
import java.util.Random;
@@ -225,6 +225,8 @@ public class IpServer extends StateMachine {
private final SharedLog mLog;
private final INetd mNetd;
+ @NonNull
+ private final BpfCoordinator mBpfCoordinator;
private final Callback mCallback;
private final InterfaceController mInterfaceCtrl;
private final PrivateAddressCoordinator mPrivateAddressCoordinator;
@@ -269,43 +271,6 @@ public class IpServer extends StateMachine {
}
}
- static class Ipv6ForwardingRule {
- public final int upstreamIfindex;
- public final int downstreamIfindex;
- public final Inet6Address address;
- public final MacAddress srcMac;
- public final MacAddress dstMac;
-
- Ipv6ForwardingRule(int upstreamIfindex, int downstreamIfIndex, Inet6Address address,
- MacAddress srcMac, MacAddress dstMac) {
- this.upstreamIfindex = upstreamIfindex;
- this.downstreamIfindex = downstreamIfIndex;
- this.address = address;
- this.srcMac = srcMac;
- this.dstMac = dstMac;
- }
-
- public Ipv6ForwardingRule onNewUpstream(int newUpstreamIfindex) {
- return new Ipv6ForwardingRule(newUpstreamIfindex, downstreamIfindex, address, srcMac,
- dstMac);
- }
-
- // Don't manipulate TetherOffloadRuleParcel directly because implementing onNewUpstream()
- // would be error-prone due to generated stable AIDL classes not having a copy constructor.
- public TetherOffloadRuleParcel toTetherOffloadRuleParcel() {
- final TetherOffloadRuleParcel parcel = new TetherOffloadRuleParcel();
- parcel.inputInterfaceIndex = upstreamIfindex;
- parcel.outputInterfaceIndex = downstreamIfindex;
- parcel.destination = address.getAddress();
- parcel.prefixLength = 128;
- parcel.srcL2Address = srcMac.toByteArray();
- parcel.dstL2Address = dstMac.toByteArray();
- return parcel;
- }
- }
- private final LinkedHashMap<Inet6Address, Ipv6ForwardingRule> mIpv6ForwardingRules =
- new LinkedHashMap<>();
-
private final IpNeighborMonitor mIpNeighborMonitor;
private LinkAddress mIpv4Address;
@@ -314,11 +279,13 @@ public class IpServer extends StateMachine {
// object. It helps to reduce the arguments of the constructor.
public IpServer(
String ifaceName, Looper looper, int interfaceType, SharedLog log,
- INetd netd, Callback callback, boolean usingLegacyDhcp, boolean usingBpfOffload,
+ INetd netd, @NonNull BpfCoordinator coordinator, Callback callback,
+ boolean usingLegacyDhcp, boolean usingBpfOffload,
PrivateAddressCoordinator addressCoordinator, Dependencies deps) {
super(ifaceName, looper);
mLog = log.forSubComponent(ifaceName);
mNetd = netd;
+ mBpfCoordinator = coordinator;
mCallback = callback;
mInterfaceCtrl = new InterfaceController(ifaceName, mNetd, mLog);
mIfaceName = ifaceName;
@@ -749,6 +716,14 @@ public class IpServer extends StateMachine {
}
upstreamIfindex = mDeps.getIfindex(upstreamIface);
+
+ // Add upstream index to name mapping for the tether stats usage in the coordinator.
+ // Although this mapping could be added by both class Tethering and IpServer, adding
+ // mapping from IpServer guarantees that the mapping is added before the adding
+ // forwarding rules. That is because there are different state machines in both
+ // classes. It is hard to guarantee the link property update order between multiple
+ // state machines.
+ mBpfCoordinator.addUpstreamNameToLookupTable(upstreamIfindex, upstreamIface);
}
// If v6only is null, we pass in null to setRaParams(), which handles
@@ -864,43 +839,29 @@ public class IpServer extends StateMachine {
// TODO: Perhaps remove this protection check.
if (!mUsingBpfOffload) return;
- try {
- mNetd.tetherOffloadRuleAdd(rule.toTetherOffloadRuleParcel());
- mIpv6ForwardingRules.put(rule.address, rule);
- } catch (RemoteException | ServiceSpecificException e) {
- mLog.e("Could not add IPv6 downstream rule: ", e);
- }
+ mBpfCoordinator.tetherOffloadRuleAdd(this, rule);
}
- private void removeIpv6ForwardingRule(Ipv6ForwardingRule rule, boolean removeFromMap) {
- // Theoretically, we don't need this check because IP neighbor monitor doesn't start if BPF
- // offload is disabled. Add this check just in case.
+ private void removeIpv6ForwardingRule(Ipv6ForwardingRule rule) {
// TODO: Perhaps remove this protection check.
+ // See the related comment in #addIpv6ForwardingRule.
if (!mUsingBpfOffload) return;
- try {
- mNetd.tetherOffloadRuleRemove(rule.toTetherOffloadRuleParcel());
- if (removeFromMap) {
- mIpv6ForwardingRules.remove(rule.address);
- }
- } catch (RemoteException | ServiceSpecificException e) {
- mLog.e("Could not remove IPv6 downstream rule: ", e);
- }
+ mBpfCoordinator.tetherOffloadRuleRemove(this, rule);
}
private void clearIpv6ForwardingRules() {
- for (Ipv6ForwardingRule rule : mIpv6ForwardingRules.values()) {
- removeIpv6ForwardingRule(rule, false /*removeFromMap*/);
- }
- mIpv6ForwardingRules.clear();
+ if (!mUsingBpfOffload) return;
+
+ mBpfCoordinator.tetherOffloadRuleClear(this);
}
- // Convenience method to replace a rule with the same rule on a new upstream interface.
- // Allows replacing the rules in one iteration pass without ConcurrentModificationExceptions.
- // Relies on the fact that rules are in a map indexed by IP address.
- private void updateIpv6ForwardingRule(Ipv6ForwardingRule rule, int newIfindex) {
- addIpv6ForwardingRule(rule.onNewUpstream(newIfindex));
- removeIpv6ForwardingRule(rule, false /*removeFromMap*/);
+ private void updateIpv6ForwardingRule(int newIfindex) {
+ // TODO: Perhaps remove this protection check.
+ // See the related comment in #addIpv6ForwardingRule.
+ if (!mUsingBpfOffload) return;
+
+ mBpfCoordinator.tetherOffloadRuleUpdate(this, newIfindex);
}
// Handles all updates to IPv6 forwarding rules. These can currently change only if the upstream
@@ -916,9 +877,7 @@ public class IpServer extends StateMachine {
// If the upstream interface has changed, remove all rules and re-add them with the new
// upstream interface.
if (prevUpstreamIfindex != upstreamIfindex) {
- for (Ipv6ForwardingRule rule : mIpv6ForwardingRules.values()) {
- updateIpv6ForwardingRule(rule, upstreamIfindex);
- }
+ updateIpv6ForwardingRule(upstreamIfindex);
}
// If we're here to process a NeighborEvent, do so now.
@@ -938,7 +897,7 @@ public class IpServer extends StateMachine {
if (e.isValid()) {
addIpv6ForwardingRule(rule);
} else {
- removeIpv6ForwardingRule(rule, true /*removeFromMap*/);
+ removeIpv6ForwardingRule(rule);
}
}
diff --git a/packages/Tethering/src/android/net/util/TetheringUtils.java b/packages/Tethering/src/android/net/util/TetheringUtils.java
index dd67dddae1cd..b17b4ba77cfb 100644
--- a/packages/Tethering/src/android/net/util/TetheringUtils.java
+++ b/packages/Tethering/src/android/net/util/TetheringUtils.java
@@ -15,19 +15,94 @@
*/
package android.net.util;
+import android.net.TetherStatsParcel;
import android.net.TetheringRequestParcel;
+import androidx.annotation.NonNull;
+
import java.io.FileDescriptor;
import java.net.SocketException;
import java.util.Objects;
/**
- * Native methods for tethering utilization.
+ * The classes and the methods for tethering utilization.
*
* {@hide}
*/
public class TetheringUtils {
/**
+ * The object which records offload Tx/Rx forwarded bytes/packets.
+ * TODO: Replace the inner class ForwardedStats of class OffloadHardwareInterface with
+ * this class as well.
+ */
+ public static class ForwardedStats {
+ public final long rxBytes;
+ public final long rxPackets;
+ public final long txBytes;
+ public final long txPackets;
+
+ public ForwardedStats() {
+ rxBytes = 0;
+ rxPackets = 0;
+ txBytes = 0;
+ txPackets = 0;
+ }
+
+ public ForwardedStats(long rxBytes, long txBytes) {
+ this.rxBytes = rxBytes;
+ this.rxPackets = 0;
+ this.txBytes = txBytes;
+ this.txPackets = 0;
+ }
+
+ public ForwardedStats(long rxBytes, long rxPackets, long txBytes, long txPackets) {
+ this.rxBytes = rxBytes;
+ this.rxPackets = rxPackets;
+ this.txBytes = txBytes;
+ this.txPackets = txPackets;
+ }
+
+ public ForwardedStats(@NonNull TetherStatsParcel tetherStats) {
+ rxBytes = tetherStats.rxBytes;
+ rxPackets = tetherStats.rxPackets;
+ txBytes = tetherStats.txBytes;
+ txPackets = tetherStats.txPackets;
+ }
+
+ public ForwardedStats(@NonNull ForwardedStats other) {
+ rxBytes = other.rxBytes;
+ rxPackets = other.rxPackets;
+ txBytes = other.txBytes;
+ txPackets = other.txPackets;
+ }
+
+ /** Add Tx/Rx bytes/packets and return the result as a new object. */
+ @NonNull
+ public ForwardedStats add(@NonNull ForwardedStats other) {
+ return new ForwardedStats(rxBytes + other.rxBytes, rxPackets + other.rxPackets,
+ txBytes + other.txBytes, txPackets + other.txPackets);
+ }
+
+ /** Subtract Tx/Rx bytes/packets and return the result as a new object. */
+ @NonNull
+ public ForwardedStats subtract(@NonNull ForwardedStats other) {
+ // TODO: Perhaps throw an exception if any negative difference value just in case.
+ final long rxBytesDiff = Math.max(rxBytes - other.rxBytes, 0);
+ final long rxPacketsDiff = Math.max(rxPackets - other.rxPackets, 0);
+ final long txBytesDiff = Math.max(txBytes - other.txBytes, 0);
+ final long txPacketsDiff = Math.max(txPackets - other.txPackets, 0);
+ return new ForwardedStats(rxBytesDiff, rxPacketsDiff, txBytesDiff, txPacketsDiff);
+ }
+
+ /** Returns the string representation of this object. */
+ @NonNull
+ public String toString() {
+ return String.format("ForwardedStats(rxb: %d, rxp: %d, txb: %d, txp: %d)", rxBytes,
+ rxPackets, txBytes, txPackets);
+ }
+ }
+
+ /**
* Configures a socket for receiving ICMPv6 router solicitations and sending advertisements.
* @param fd the socket's {@link FileDescriptor}.
* @param ifIndex the interface index.
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/packages/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
new file mode 100644
index 000000000000..fc27b6add052
--- /dev/null
+++ b/packages/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
@@ -0,0 +1,637 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.networkstack.tethering;
+
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.METERED_NO;
+import static android.net.NetworkStats.ROAMING_NO;
+import static android.net.NetworkStats.SET_DEFAULT;
+import static android.net.NetworkStats.TAG_NONE;
+import static android.net.NetworkStats.UID_ALL;
+import static android.net.NetworkStats.UID_TETHERING;
+import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
+
+import android.app.usage.NetworkStatsManager;
+import android.net.INetd;
+import android.net.MacAddress;
+import android.net.NetworkStats;
+import android.net.NetworkStats.Entry;
+import android.net.TetherOffloadRuleParcel;
+import android.net.TetherStatsParcel;
+import android.net.ip.IpServer;
+import android.net.netstats.provider.NetworkStatsProvider;
+import android.net.util.SharedLog;
+import android.net.util.TetheringUtils.ForwardedStats;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.SparseArray;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.net.Inet6Address;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Objects;
+
+/**
+ * This coordinator is responsible for providing BPF offload relevant functionality.
+ * - Get tethering stats.
+ * - Set data limit.
+ * - Set global alert.
+ * - Add/remove forwarding rules.
+ *
+ * @hide
+ */
+public class BpfCoordinator {
+ private static final String TAG = BpfCoordinator.class.getSimpleName();
+ @VisibleForTesting
+ static final int DEFAULT_PERFORM_POLL_INTERVAL_MS = 5000; // TODO: Make it customizable.
+
+ @VisibleForTesting
+ enum StatsType {
+ STATS_PER_IFACE,
+ STATS_PER_UID,
+ }
+
+ @NonNull
+ private final Handler mHandler;
+ @NonNull
+ private final INetd mNetd;
+ @NonNull
+ private final SharedLog mLog;
+ @NonNull
+ private final Dependencies mDeps;
+ @Nullable
+ private final BpfTetherStatsProvider mStatsProvider;
+
+ // Tracks whether BPF tethering is started or not. This is set by tethering before it
+ // starts the first IpServer and is cleared by tethering shortly before the last IpServer
+ // is stopped. Note that rule updates (especially deletions, but sometimes additions as
+ // well) may arrive when this is false. If they do, they must be communicated to netd.
+ // Changes in data limits may also arrive when this is false, and if they do, they must
+ // also be communicated to netd.
+ private boolean mPollingStarted = false;
+
+ // Tracking remaining alert quota. Unlike limit quota is subject to interface, the alert
+ // quota is interface independent and global for tether offload.
+ private long mRemainingAlertQuota = QUOTA_UNLIMITED;
+
+ // Maps upstream interface index to offloaded traffic statistics.
+ // Always contains the latest total bytes/packets, since each upstream was started, received
+ // from the BPF maps for each interface.
+ private final SparseArray<ForwardedStats> mStats = new SparseArray<>();
+
+ // Maps upstream interface names to interface quotas.
+ // Always contains the latest value received from the framework for each interface, regardless
+ // of whether offload is currently running (or is even supported) on that interface. Only
+ // includes interfaces that have a quota set. Note that this map is used for storing the quota
+ // which is set from the service. Because the service uses the interface name to present the
+ // interface, this map uses the interface name to be the mapping index.
+ private final HashMap<String, Long> mInterfaceQuotas = new HashMap<>();
+
+ // Maps upstream interface index to interface names.
+ // Store all interface name since boot. Used for lookup what interface name it is from the
+ // tether stats got from netd because netd reports interface index to present an interface.
+ // TODO: Remove the unused interface name.
+ private final SparseArray<String> mInterfaceNames = new SparseArray<>();
+
+ // Map of downstream rule maps. Each of these maps represents the IPv6 forwarding rules for a
+ // given downstream. Each map:
+ // - Is owned by the IpServer that is responsible for that downstream.
+ // - Must only be modified by that IpServer.
+ // - Is created when the IpServer adds its first rule, and deleted when the IpServer deletes
+ // its last rule (or clears its rules).
+ // TODO: Perhaps seal the map and rule operations which communicates with netd into a class.
+ // TODO: Does this need to be a LinkedHashMap or can it just be a HashMap? Also, could it be
+ // a ConcurrentHashMap, in order to avoid the copies in tetherOffloadRuleClear
+ // and tetherOffloadRuleUpdate?
+ // TODO: Perhaps use one-dimensional map and access specific downstream rules via downstream
+ // index. For doing that, IpServer must guarantee that it always has a valid IPv6 downstream
+ // interface index while calling function to clear all rules. IpServer may be calling clear
+ // rules function without a valid IPv6 downstream interface index even if it may have one
+ // before. IpServer would need to call getInterfaceParams() in the constructor instead of when
+ // startIpv6() is called, and make mInterfaceParams final.
+ private final HashMap<IpServer, LinkedHashMap<Inet6Address, Ipv6ForwardingRule>>
+ mIpv6ForwardingRules = new LinkedHashMap<>();
+
+ // Runnable that used by scheduling next polling of stats.
+ private final Runnable mScheduledPollingTask = () -> {
+ updateForwardedStatsFromNetd();
+ maybeSchedulePollingStats();
+ };
+
+ @VisibleForTesting
+ public static class Dependencies {
+ int getPerformPollInterval() {
+ // TODO: Consider make this configurable.
+ return DEFAULT_PERFORM_POLL_INTERVAL_MS;
+ }
+ }
+
+ @VisibleForTesting
+ public BpfCoordinator(@NonNull Handler handler, @NonNull INetd netd,
+ @NonNull NetworkStatsManager nsm, @NonNull SharedLog log, @NonNull Dependencies deps) {
+ mHandler = handler;
+ mNetd = netd;
+ mLog = log.forSubComponent(TAG);
+ BpfTetherStatsProvider provider = new BpfTetherStatsProvider();
+ try {
+ nsm.registerNetworkStatsProvider(getClass().getSimpleName(), provider);
+ } catch (RuntimeException e) {
+ // TODO: Perhaps not allow to use BPF offload because the reregistration failure
+ // implied that no data limit could be applies on a metered upstream if any.
+ Log.wtf(TAG, "Cannot register offload stats provider: " + e);
+ provider = null;
+ }
+ mStatsProvider = provider;
+ mDeps = deps;
+ }
+
+ /**
+ * Start BPF tethering offload stats polling when the first upstream is started.
+ * Note that this can be only called on handler thread.
+ * TODO: Perhaps check BPF support before starting.
+ * TODO: Start the stats polling only if there is any client on the downstream.
+ */
+ public void startPolling() {
+ if (mPollingStarted) return;
+
+ mPollingStarted = true;
+ maybeSchedulePollingStats();
+
+ mLog.i("Polling started");
+ }
+
+ /**
+ * Stop BPF tethering offload stats polling.
+ * The data limit cleanup and the tether stats maps cleanup are not implemented here.
+ * These cleanups rely on all IpServers calling #tetherOffloadRuleRemove. After the
+ * last rule is removed from the upstream, #tetherOffloadRuleRemove does the cleanup
+ * functionality.
+ * Note that this can be only called on handler thread.
+ */
+ public void stopPolling() {
+ if (!mPollingStarted) return;
+
+ // Stop scheduled polling tasks and poll the latest stats from BPF maps.
+ if (mHandler.hasCallbacks(mScheduledPollingTask)) {
+ mHandler.removeCallbacks(mScheduledPollingTask);
+ }
+ updateForwardedStatsFromNetd();
+ mPollingStarted = false;
+
+ mLog.i("Polling stopped");
+ }
+
+ /**
+ * Add forwarding rule. After adding the first rule on a given upstream, must add the data
+ * limit on the given upstream.
+ * Note that this can be only called on handler thread.
+ */
+ public void tetherOffloadRuleAdd(
+ @NonNull final IpServer ipServer, @NonNull final Ipv6ForwardingRule rule) {
+ try {
+ // TODO: Perhaps avoid to add a duplicate rule.
+ mNetd.tetherOffloadRuleAdd(rule.toTetherOffloadRuleParcel());
+ } catch (RemoteException | ServiceSpecificException e) {
+ mLog.e("Could not add IPv6 forwarding rule: ", e);
+ return;
+ }
+
+ if (!mIpv6ForwardingRules.containsKey(ipServer)) {
+ mIpv6ForwardingRules.put(ipServer, new LinkedHashMap<Inet6Address,
+ Ipv6ForwardingRule>());
+ }
+ LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = mIpv6ForwardingRules.get(ipServer);
+
+ // Setup the data limit on the given upstream if the first rule is added.
+ final int upstreamIfindex = rule.upstreamIfindex;
+ if (!isAnyRuleOnUpstream(upstreamIfindex)) {
+ // If failed to set a data limit, probably should not use this upstream, because
+ // the upstream may not want to blow through the data limit that was told to apply.
+ // TODO: Perhaps stop the coordinator.
+ boolean success = updateDataLimit(upstreamIfindex);
+ if (!success) {
+ final String iface = mInterfaceNames.get(upstreamIfindex);
+ mLog.e("Setting data limit for " + iface + " failed.");
+ }
+ }
+
+ // Must update the adding rule after calling #isAnyRuleOnUpstream because it needs to
+ // check if it is about adding a first rule for a given upstream.
+ rules.put(rule.address, rule);
+ }
+
+ /**
+ * Remove forwarding rule. After removing the last rule on a given upstream, must clear
+ * data limit, update the last tether stats and remove the tether stats in the BPF maps.
+ * Note that this can be only called on handler thread.
+ */
+ public void tetherOffloadRuleRemove(
+ @NonNull final IpServer ipServer, @NonNull final Ipv6ForwardingRule rule) {
+ try {
+ // TODO: Perhaps avoid to remove a non-existent rule.
+ mNetd.tetherOffloadRuleRemove(rule.toTetherOffloadRuleParcel());
+ } catch (RemoteException | ServiceSpecificException e) {
+ mLog.e("Could not remove IPv6 forwarding rule: ", e);
+ return;
+ }
+
+ LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = mIpv6ForwardingRules.get(ipServer);
+ if (rules == null) return;
+
+ // Must remove rules before calling #isAnyRuleOnUpstream because it needs to check if
+ // the last rule is removed for a given upstream. If no rule is removed, return early.
+ // Avoid unnecessary work on a non-existent rule which may have never been added or
+ // removed already.
+ if (rules.remove(rule.address) == null) return;
+
+ // Remove the downstream entry if it has no more rule.
+ if (rules.isEmpty()) {
+ mIpv6ForwardingRules.remove(ipServer);
+ }
+
+ // Do cleanup functionality if there is no more rule on the given upstream.
+ final int upstreamIfindex = rule.upstreamIfindex;
+ if (!isAnyRuleOnUpstream(upstreamIfindex)) {
+ try {
+ final TetherStatsParcel stats =
+ mNetd.tetherOffloadGetAndClearStats(upstreamIfindex);
+ // Update the last stats delta and delete the local cache for a given upstream.
+ updateQuotaAndStatsFromSnapshot(new TetherStatsParcel[] {stats});
+ mStats.remove(upstreamIfindex);
+ } catch (RemoteException | ServiceSpecificException e) {
+ Log.wtf(TAG, "Exception when cleanup tether stats for upstream index "
+ + upstreamIfindex + ": ", e);
+ }
+ }
+ }
+
+ /**
+ * Clear all forwarding rules for a given downstream.
+ * Note that this can be only called on handler thread.
+ */
+ public void tetherOffloadRuleClear(@NonNull final IpServer ipServer) {
+ final LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = mIpv6ForwardingRules.get(
+ ipServer);
+ if (rules == null) return;
+
+ // Need to build a rule list because the rule map may be changed in the iteration.
+ for (final Ipv6ForwardingRule rule : new ArrayList<Ipv6ForwardingRule>(rules.values())) {
+ tetherOffloadRuleRemove(ipServer, rule);
+ }
+ }
+
+ /**
+ * Update existing forwarding rules to new upstream for a given downstream.
+ * Note that this can be only called on handler thread.
+ */
+ public void tetherOffloadRuleUpdate(@NonNull final IpServer ipServer, int newUpstreamIfindex) {
+ final LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules = mIpv6ForwardingRules.get(
+ ipServer);
+ if (rules == null) return;
+
+ // Need to build a rule list because the rule map may be changed in the iteration.
+ for (final Ipv6ForwardingRule rule : new ArrayList<Ipv6ForwardingRule>(rules.values())) {
+ // Remove the old rule before adding the new one because the map uses the same key for
+ // both rules. Reversing the processing order causes that the new rule is removed as
+ // unexpected.
+ // TODO: Add new rule first to reduce the latency which has no rule.
+ tetherOffloadRuleRemove(ipServer, rule);
+ tetherOffloadRuleAdd(ipServer, rule.onNewUpstream(newUpstreamIfindex));
+ }
+ }
+
+ /**
+ * Add upstream name to lookup table. The lookup table is used for tether stats interface name
+ * lookup because the netd only reports interface index in BPF tether stats but the service
+ * expects the interface name in NetworkStats object.
+ * Note that this can be only called on handler thread.
+ */
+ public void addUpstreamNameToLookupTable(int upstreamIfindex, @NonNull String upstreamIface) {
+ if (upstreamIfindex == 0 || TextUtils.isEmpty(upstreamIface)) return;
+
+ // The same interface index to name mapping may be added by different IpServer objects or
+ // re-added by reconnection on the same upstream interface. Ignore the duplicate one.
+ final String iface = mInterfaceNames.get(upstreamIfindex);
+ if (iface == null) {
+ mInterfaceNames.put(upstreamIfindex, upstreamIface);
+ } else if (!TextUtils.equals(iface, upstreamIface)) {
+ Log.wtf(TAG, "The upstream interface name " + upstreamIface
+ + " is different from the existing interface name "
+ + iface + " for index " + upstreamIfindex);
+ }
+ }
+
+ /** IPv6 forwarding rule class. */
+ public static class Ipv6ForwardingRule {
+ public final int upstreamIfindex;
+ public final int downstreamIfindex;
+
+ @NonNull
+ public final Inet6Address address;
+ @NonNull
+ public final MacAddress srcMac;
+ @NonNull
+ public final MacAddress dstMac;
+
+ public Ipv6ForwardingRule(int upstreamIfindex, int downstreamIfIndex,
+ @NonNull Inet6Address address, @NonNull MacAddress srcMac,
+ @NonNull MacAddress dstMac) {
+ this.upstreamIfindex = upstreamIfindex;
+ this.downstreamIfindex = downstreamIfIndex;
+ this.address = address;
+ this.srcMac = srcMac;
+ this.dstMac = dstMac;
+ }
+
+ /** Return a new rule object which updates with new upstream index. */
+ @NonNull
+ public Ipv6ForwardingRule onNewUpstream(int newUpstreamIfindex) {
+ return new Ipv6ForwardingRule(newUpstreamIfindex, downstreamIfindex, address, srcMac,
+ dstMac);
+ }
+
+ /**
+ * Don't manipulate TetherOffloadRuleParcel directly because implementing onNewUpstream()
+ * would be error-prone due to generated stable AIDL classes not having a copy constructor.
+ */
+ @NonNull
+ public TetherOffloadRuleParcel toTetherOffloadRuleParcel() {
+ final TetherOffloadRuleParcel parcel = new TetherOffloadRuleParcel();
+ parcel.inputInterfaceIndex = upstreamIfindex;
+ parcel.outputInterfaceIndex = downstreamIfindex;
+ parcel.destination = address.getAddress();
+ parcel.prefixLength = 128;
+ parcel.srcL2Address = srcMac.toByteArray();
+ parcel.dstL2Address = dstMac.toByteArray();
+ return parcel;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Ipv6ForwardingRule)) return false;
+ Ipv6ForwardingRule that = (Ipv6ForwardingRule) o;
+ return this.upstreamIfindex == that.upstreamIfindex
+ && this.downstreamIfindex == that.downstreamIfindex
+ && Objects.equals(this.address, that.address)
+ && Objects.equals(this.srcMac, that.srcMac)
+ && Objects.equals(this.dstMac, that.dstMac);
+ }
+
+ @Override
+ public int hashCode() {
+ // TODO: if this is ever used in production code, don't pass ifindices
+ // to Objects.hash() to avoid autoboxing overhead.
+ return Objects.hash(upstreamIfindex, downstreamIfindex, address, srcMac, dstMac);
+ }
+ }
+
+ /**
+ * A BPF tethering stats provider to provide network statistics to the system.
+ * Note that this class' data may only be accessed on the handler thread.
+ */
+ @VisibleForTesting
+ class BpfTetherStatsProvider extends NetworkStatsProvider {
+ // The offloaded traffic statistics per interface that has not been reported since the
+ // last call to pushTetherStats. Only the interfaces that were ever tethering upstreams
+ // and has pending tether stats delta are included in this NetworkStats object.
+ private NetworkStats mIfaceStats = new NetworkStats(0L, 0);
+
+ // The same stats as above, but counts network stats per uid.
+ private NetworkStats mUidStats = new NetworkStats(0L, 0);
+
+ @Override
+ public void onRequestStatsUpdate(int token) {
+ mHandler.post(() -> pushTetherStats());
+ }
+
+ @Override
+ public void onSetAlert(long quotaBytes) {
+ mHandler.post(() -> updateAlertQuota(quotaBytes));
+ }
+
+ @Override
+ public void onSetLimit(@NonNull String iface, long quotaBytes) {
+ if (quotaBytes < QUOTA_UNLIMITED) {
+ throw new IllegalArgumentException("invalid quota value " + quotaBytes);
+ }
+
+ mHandler.post(() -> {
+ final Long curIfaceQuota = mInterfaceQuotas.get(iface);
+
+ if (null == curIfaceQuota && QUOTA_UNLIMITED == quotaBytes) return;
+
+ if (quotaBytes == QUOTA_UNLIMITED) {
+ mInterfaceQuotas.remove(iface);
+ } else {
+ mInterfaceQuotas.put(iface, quotaBytes);
+ }
+ maybeUpdateDataLimit(iface);
+ });
+ }
+
+ @VisibleForTesting
+ void pushTetherStats() {
+ try {
+ // The token is not used for now. See b/153606961.
+ notifyStatsUpdated(0 /* token */, mIfaceStats, mUidStats);
+
+ // Clear the accumulated tether stats delta after reported. Note that create a new
+ // empty object because NetworkStats#clear is @hide.
+ mIfaceStats = new NetworkStats(0L, 0);
+ mUidStats = new NetworkStats(0L, 0);
+ } catch (RuntimeException e) {
+ mLog.e("Cannot report network stats: ", e);
+ }
+ }
+
+ private void accumulateDiff(@NonNull NetworkStats ifaceDiff,
+ @NonNull NetworkStats uidDiff) {
+ mIfaceStats = mIfaceStats.add(ifaceDiff);
+ mUidStats = mUidStats.add(uidDiff);
+ }
+ }
+
+ private int getInterfaceIndexFromRules(@NonNull String ifName) {
+ for (LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules : mIpv6ForwardingRules
+ .values()) {
+ for (Ipv6ForwardingRule rule : rules.values()) {
+ final int upstreamIfindex = rule.upstreamIfindex;
+ if (TextUtils.equals(ifName, mInterfaceNames.get(upstreamIfindex))) {
+ return upstreamIfindex;
+ }
+ }
+ }
+ return 0;
+ }
+
+ private long getQuotaBytes(@NonNull String iface) {
+ final Long limit = mInterfaceQuotas.get(iface);
+ final long quotaBytes = (limit != null) ? limit : QUOTA_UNLIMITED;
+
+ return quotaBytes;
+ }
+
+ private boolean sendDataLimitToNetd(int ifIndex, long quotaBytes) {
+ if (ifIndex == 0) {
+ Log.wtf(TAG, "Invalid interface index.");
+ return false;
+ }
+
+ try {
+ mNetd.tetherOffloadSetInterfaceQuota(ifIndex, quotaBytes);
+ } catch (RemoteException | ServiceSpecificException e) {
+ mLog.e("Exception when updating quota " + quotaBytes + ": ", e);
+ return false;
+ }
+
+ return true;
+ }
+
+ // Handle the data limit update from the service which is the stats provider registered for.
+ private void maybeUpdateDataLimit(@NonNull String iface) {
+ // Set data limit only on a given upstream which has at least one rule. If we can't get
+ // an interface index for a given interface name, it means either there is no rule for
+ // a given upstream or the interface name is not an upstream which is monitored by the
+ // coordinator.
+ final int ifIndex = getInterfaceIndexFromRules(iface);
+ if (ifIndex == 0) return;
+
+ final long quotaBytes = getQuotaBytes(iface);
+ sendDataLimitToNetd(ifIndex, quotaBytes);
+ }
+
+ // Handle the data limit update while adding forwarding rules.
+ private boolean updateDataLimit(int ifIndex) {
+ final String iface = mInterfaceNames.get(ifIndex);
+ if (iface == null) {
+ mLog.e("Fail to get the interface name for index " + ifIndex);
+ return false;
+ }
+ final long quotaBytes = getQuotaBytes(iface);
+ return sendDataLimitToNetd(ifIndex, quotaBytes);
+ }
+
+ private boolean isAnyRuleOnUpstream(int upstreamIfindex) {
+ for (LinkedHashMap<Inet6Address, Ipv6ForwardingRule> rules : mIpv6ForwardingRules
+ .values()) {
+ for (Ipv6ForwardingRule rule : rules.values()) {
+ if (upstreamIfindex == rule.upstreamIfindex) return true;
+ }
+ }
+ return false;
+ }
+
+ @NonNull
+ private NetworkStats buildNetworkStats(@NonNull StatsType type, int ifIndex,
+ @NonNull final ForwardedStats diff) {
+ NetworkStats stats = new NetworkStats(0L, 0);
+ final String iface = mInterfaceNames.get(ifIndex);
+ if (iface == null) {
+ // TODO: Use Log.wtf once the coordinator owns full control of tether stats from netd.
+ // For now, netd may add the empty stats for the upstream which is not monitored by
+ // the coordinator. Silently ignore it.
+ return stats;
+ }
+ final int uid = (type == StatsType.STATS_PER_UID) ? UID_TETHERING : UID_ALL;
+ // Note that the argument 'metered', 'roaming' and 'defaultNetwork' are not recorded for
+ // network stats snapshot. See NetworkStatsRecorder#recordSnapshotLocked.
+ return stats.addEntry(new Entry(iface, uid, SET_DEFAULT, TAG_NONE, METERED_NO,
+ ROAMING_NO, DEFAULT_NETWORK_NO, diff.rxBytes, diff.rxPackets,
+ diff.txBytes, diff.txPackets, 0L /* operations */));
+ }
+
+ private void updateAlertQuota(long newQuota) {
+ if (newQuota < QUOTA_UNLIMITED) {
+ throw new IllegalArgumentException("invalid quota value " + newQuota);
+ }
+ if (mRemainingAlertQuota == newQuota) return;
+
+ mRemainingAlertQuota = newQuota;
+ if (mRemainingAlertQuota == 0) {
+ mLog.i("onAlertReached");
+ if (mStatsProvider != null) mStatsProvider.notifyAlertReached();
+ }
+ }
+
+ private void updateQuotaAndStatsFromSnapshot(
+ @NonNull final TetherStatsParcel[] tetherStatsList) {
+ long usedAlertQuota = 0;
+ for (TetherStatsParcel tetherStats : tetherStatsList) {
+ final Integer ifIndex = tetherStats.ifIndex;
+ final ForwardedStats curr = new ForwardedStats(tetherStats);
+ final ForwardedStats base = mStats.get(ifIndex);
+ final ForwardedStats diff = (base != null) ? curr.subtract(base) : curr;
+ usedAlertQuota += diff.rxBytes + diff.txBytes;
+
+ // Update the local cache for counting tether stats delta.
+ mStats.put(ifIndex, curr);
+
+ // Update the accumulated tether stats delta to the stats provider for the service
+ // querying.
+ if (mStatsProvider != null) {
+ try {
+ mStatsProvider.accumulateDiff(
+ buildNetworkStats(StatsType.STATS_PER_IFACE, ifIndex, diff),
+ buildNetworkStats(StatsType.STATS_PER_UID, ifIndex, diff));
+ } catch (ArrayIndexOutOfBoundsException e) {
+ Log.wtf(TAG, "Fail to update the accumulated stats delta for interface index "
+ + ifIndex + " : ", e);
+ }
+ }
+ }
+
+ if (mRemainingAlertQuota > 0 && usedAlertQuota > 0) {
+ // Trim to zero if overshoot.
+ final long newQuota = Math.max(mRemainingAlertQuota - usedAlertQuota, 0);
+ updateAlertQuota(newQuota);
+ }
+
+ // TODO: Count the used limit quota for notifying data limit reached.
+ }
+
+ private void updateForwardedStatsFromNetd() {
+ final TetherStatsParcel[] tetherStatsList;
+ try {
+ // The reported tether stats are total data usage for all currently-active upstream
+ // interfaces since tethering start.
+ tetherStatsList = mNetd.tetherOffloadGetStats();
+ } catch (RemoteException | ServiceSpecificException e) {
+ mLog.e("Problem fetching tethering stats: ", e);
+ return;
+ }
+ updateQuotaAndStatsFromSnapshot(tetherStatsList);
+ }
+
+ private void maybeSchedulePollingStats() {
+ if (!mPollingStarted) return;
+
+ if (mHandler.hasCallbacks(mScheduledPollingTask)) {
+ mHandler.removeCallbacks(mScheduledPollingTask);
+ }
+
+ mHandler.postDelayed(mScheduledPollingTask, mDeps.getPerformPollInterval());
+ }
+}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 69eec8df9864..df6745855067 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -232,6 +232,7 @@ public class Tethering {
private final TetheringThreadExecutor mExecutor;
private final TetheringNotificationUpdater mNotificationUpdater;
private final UserManager mUserManager;
+ private final BpfCoordinator mBpfCoordinator;
private final PrivateAddressCoordinator mPrivateAddressCoordinator;
private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID;
// All the usage of mTetheringEventCallback should run in the same thread.
@@ -284,6 +285,8 @@ public class Tethering {
mUpstreamNetworkMonitor = mDeps.getUpstreamNetworkMonitor(mContext, mTetherMasterSM, mLog,
TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
mForwardedDownstreams = new LinkedHashSet<>();
+ mBpfCoordinator = mDeps.getBpfCoordinator(
+ mHandler, mNetd, mLog, new BpfCoordinator.Dependencies());
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_CARRIER_CONFIG_CHANGED);
@@ -1704,6 +1707,9 @@ public class Tethering {
chooseUpstreamType(true);
mTryCell = false;
}
+
+ // TODO: Check the upstream interface if it is managed by BPF offload.
+ mBpfCoordinator.startPolling();
}
@Override
@@ -1716,6 +1722,7 @@ public class Tethering {
mTetherUpstream = null;
reportUpstreamChanged(null);
}
+ mBpfCoordinator.stopPolling();
}
private boolean updateUpstreamWanted() {
@@ -2341,7 +2348,7 @@ public class Tethering {
mLog.log("adding TetheringInterfaceStateMachine for: " + iface);
final TetherState tetherState = new TetherState(
- new IpServer(iface, mLooper, interfaceType, mLog, mNetd,
+ new IpServer(iface, mLooper, interfaceType, mLog, mNetd, mBpfCoordinator,
makeControlCallback(), mConfig.enableLegacyDhcpServer,
mConfig.enableBpfOffload, mPrivateAddressCoordinator,
mDeps.getIpServerDependencies()));
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
index ce546c701a61..d637c8646b4a 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
@@ -41,6 +41,17 @@ import java.util.ArrayList;
*/
public abstract class TetheringDependencies {
/**
+ * Get a reference to the BpfCoordinator to be used by tethering.
+ */
+ public @NonNull BpfCoordinator getBpfCoordinator(
+ @NonNull Handler handler, @NonNull INetd netd, @NonNull SharedLog log,
+ @NonNull BpfCoordinator.Dependencies deps) {
+ final NetworkStatsManager statsManager =
+ (NetworkStatsManager) getContext().getSystemService(Context.NETWORK_STATS_SERVICE);
+ return new BpfCoordinator(handler, netd, statsManager, log, deps);
+ }
+
+ /**
* Get a reference to the offload hardware interface to be used by tethering.
*/
public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) {
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java
index d03deda37fdf..593d04a06b93 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java
@@ -24,8 +24,10 @@ import android.app.Notification.Action;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.net.NetworkCapabilities;
@@ -253,6 +255,14 @@ public class TetheringNotificationUpdater {
}
@VisibleForTesting
+ static String getSettingsPackageName(@NonNull final PackageManager pm) {
+ final Intent settingsIntent = new Intent(Settings.ACTION_SETTINGS);
+ final ComponentName settingsComponent = settingsIntent.resolveActivity(pm);
+ return settingsComponent != null
+ ? settingsComponent.getPackageName() : "com.android.settings";
+ }
+
+ @VisibleForTesting
void notifyTetheringDisabledByRestriction() {
final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
final String title = res.getString(R.string.disable_tether_notification_title);
@@ -262,8 +272,9 @@ public class TetheringNotificationUpdater {
final PendingIntent pi = PendingIntent.getActivity(
mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
0 /* requestCode */,
- new Intent(Settings.ACTION_TETHER_SETTINGS),
- Intent.FLAG_ACTIVITY_NEW_TASK,
+ new Intent(Settings.ACTION_TETHER_SETTINGS)
+ .setPackage(getSettingsPackageName(mContext.getPackageManager())),
+ Intent.FLAG_ACTIVITY_NEW_TASK | PendingIntent.FLAG_IMMUTABLE,
null /* options */);
showNotification(R.drawable.stat_sys_tether_general, title, message,
@@ -284,7 +295,7 @@ public class TetheringNotificationUpdater {
mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
0 /* requestCode */,
intent,
- 0 /* flags */);
+ PendingIntent.FLAG_IMMUTABLE);
final Action action = new Action.Builder(NO_ICON_ID, disableButton, pi).build();
showNotification(R.drawable.stat_sys_tether_general, title, message,
@@ -305,8 +316,9 @@ public class TetheringNotificationUpdater {
final PendingIntent pi = PendingIntent.getActivity(
mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
0 /* requestCode */,
- new Intent(Settings.ACTION_TETHER_SETTINGS),
- Intent.FLAG_ACTIVITY_NEW_TASK,
+ new Intent(Settings.ACTION_TETHER_SETTINGS)
+ .setPackage(getSettingsPackageName(mContext.getPackageManager())),
+ Intent.FLAG_ACTIVITY_NEW_TASK | PendingIntent.FLAG_IMMUTABLE,
null /* options */);
showNotification(R.drawable.stat_sys_tether_general, title, message,
diff --git a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index 0cda29a32f59..c3bc915a232d 100644
--- a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -54,12 +54,14 @@ import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import android.app.usage.NetworkStatsManager;
import android.net.INetd;
import android.net.InetAddresses;
import android.net.InterfaceConfigurationParcel;
@@ -69,6 +71,7 @@ import android.net.LinkProperties;
import android.net.MacAddress;
import android.net.RouteInfo;
import android.net.TetherOffloadRuleParcel;
+import android.net.TetherStatsParcel;
import android.net.dhcp.DhcpServingParamsParcel;
import android.net.dhcp.IDhcpEventCallbacks;
import android.net.dhcp.IDhcpServer;
@@ -80,13 +83,17 @@ import android.net.util.InterfaceParams;
import android.net.util.InterfaceSet;
import android.net.util.PrefixUtils;
import android.net.util.SharedLog;
+import android.os.Handler;
import android.os.RemoteException;
import android.os.test.TestLooper;
import android.text.TextUtils;
+import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.networkstack.tethering.BpfCoordinator;
+import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
import com.android.networkstack.tethering.PrivateAddressCoordinator;
import org.junit.Before;
@@ -100,6 +107,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.Arrays;
import java.util.List;
@@ -133,6 +141,7 @@ public class IpServerTest {
@Mock private IpNeighborMonitor mIpNeighborMonitor;
@Mock private IpServer.Dependencies mDependencies;
@Mock private PrivateAddressCoordinator mAddressCoordinator;
+ @Mock private NetworkStatsManager mStatsManager;
@Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor;
@@ -142,6 +151,7 @@ public class IpServerTest {
private IpServer mIpServer;
private InterfaceConfigurationParcel mInterfaceConfiguration;
private NeighborEventConsumer mNeighborEventConsumer;
+ private BpfCoordinator mBpfCoordinator;
private void initStateMachine(int interfaceType) throws Exception {
initStateMachine(interfaceType, false /* usingLegacyDhcp */, DEFAULT_USING_BPF_OFFLOAD);
@@ -179,7 +189,7 @@ public class IpServerTest {
neighborCaptor.capture());
mIpServer = new IpServer(
- IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd,
+ IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd, mBpfCoordinator,
mCallback, usingLegacyDhcp, usingBpfOffload, mAddressCoordinator, mDependencies);
mIpServer.start();
mNeighborEventConsumer = neighborCaptor.getValue();
@@ -215,6 +225,10 @@ public class IpServerTest {
MockitoAnnotations.initMocks(this);
when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog);
when(mAddressCoordinator.requestDownstreamAddress(any())).thenReturn(mTestAddress);
+
+ BpfCoordinator bc = new BpfCoordinator(new Handler(mLooper.getLooper()), mNetd,
+ mStatsManager, mSharedLog, new BpfCoordinator.Dependencies());
+ mBpfCoordinator = spy(bc);
}
@Test
@@ -222,8 +236,8 @@ public class IpServerTest {
when(mDependencies.getIpNeighborMonitor(any(), any(), any()))
.thenReturn(mIpNeighborMonitor);
mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog,
- mNetd, mCallback, false /* usingLegacyDhcp */, DEFAULT_USING_BPF_OFFLOAD,
- mAddressCoordinator, mDependencies);
+ mNetd, mBpfCoordinator, mCallback, false /* usingLegacyDhcp */,
+ DEFAULT_USING_BPF_OFFLOAD, mAddressCoordinator, mDependencies);
mIpServer.start();
mLooper.dispatchAll();
verify(mCallback).updateInterfaceState(
@@ -619,6 +633,10 @@ public class IpServerTest {
* (actual: "android.net.TetherOffloadRuleParcel@8c827b0" or some such), but at least it does
* work.
*
+ * TODO: consider making the error message more readable by adding a method that catching the
+ * AssertionFailedError and throwing a new assertion with more details. See
+ * NetworkMonitorTest#verifyNetworkTested.
+ *
* See ConnectivityServiceTest#assertRoutesAdded for an alternative approach which solves the
* TooManyActualInvocations problem described above by forcing the caller of the custom assert
* method to specify all expected invocations in one call. This is useful when the stable
@@ -658,6 +676,27 @@ public class IpServerTest {
return argThat(new TetherOffloadRuleParcelMatcher(upstreamIfindex, dst, dstMac));
}
+ private static Ipv6ForwardingRule makeForwardingRule(
+ int upstreamIfindex, @NonNull InetAddress dst, @NonNull MacAddress dstMac) {
+ return new Ipv6ForwardingRule(upstreamIfindex, TEST_IFACE_PARAMS.index,
+ (Inet6Address) dst, TEST_IFACE_PARAMS.macAddr, dstMac);
+ }
+
+ private TetherStatsParcel buildEmptyTetherStatsParcel(int ifIndex) {
+ TetherStatsParcel parcel = new TetherStatsParcel();
+ parcel.ifIndex = ifIndex;
+ return parcel;
+ }
+
+ private void resetNetdAndBpfCoordinator() throws Exception {
+ reset(mNetd, mBpfCoordinator);
+ when(mNetd.tetherOffloadGetStats()).thenReturn(new TetherStatsParcel[0]);
+ when(mNetd.tetherOffloadGetAndClearStats(UPSTREAM_IFINDEX))
+ .thenReturn(buildEmptyTetherStatsParcel(UPSTREAM_IFINDEX));
+ when(mNetd.tetherOffloadGetAndClearStats(UPSTREAM_IFINDEX2))
+ .thenReturn(buildEmptyTetherStatsParcel(UPSTREAM_IFINDEX2));
+ }
+
@Test
public void addRemoveipv6ForwardingRules() throws Exception {
initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */,
@@ -675,75 +714,100 @@ public class IpServerTest {
final MacAddress macA = MacAddress.fromString("00:00:00:00:00:0a");
final MacAddress macB = MacAddress.fromString("11:22:33:00:00:0b");
- reset(mNetd);
+ resetNetdAndBpfCoordinator();
+ verifyNoMoreInteractions(mBpfCoordinator, mNetd);
+
+ // TODO: Perhaps verify the interaction of tetherOffloadSetInterfaceQuota and
+ // tetherOffloadGetAndClearStats in netd while the rules are changed.
// Events on other interfaces are ignored.
recvNewNeigh(notMyIfindex, neighA, NUD_REACHABLE, macA);
- verifyNoMoreInteractions(mNetd);
+ verifyNoMoreInteractions(mBpfCoordinator, mNetd);
// Events on this interface are received and sent to netd.
recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
+ verify(mBpfCoordinator).tetherOffloadRuleAdd(
+ mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighA, macA));
verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA));
- reset(mNetd);
+ resetNetdAndBpfCoordinator();
recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
+ verify(mBpfCoordinator).tetherOffloadRuleAdd(
+ mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighB, macB));
verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB));
- reset(mNetd);
+ resetNetdAndBpfCoordinator();
// Link-local and multicast neighbors are ignored.
recvNewNeigh(myIfindex, neighLL, NUD_REACHABLE, macA);
- verifyNoMoreInteractions(mNetd);
+ verifyNoMoreInteractions(mBpfCoordinator, mNetd);
recvNewNeigh(myIfindex, neighMC, NUD_REACHABLE, macA);
- verifyNoMoreInteractions(mNetd);
+ verifyNoMoreInteractions(mBpfCoordinator, mNetd);
// A neighbor that is no longer valid causes the rule to be removed.
// NUD_FAILED events do not have a MAC address.
recvNewNeigh(myIfindex, neighA, NUD_FAILED, null);
+ verify(mBpfCoordinator).tetherOffloadRuleRemove(
+ mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighA, macNull));
verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macNull));
- reset(mNetd);
+ resetNetdAndBpfCoordinator();
// A neighbor that is deleted causes the rule to be removed.
recvDelNeigh(myIfindex, neighB, NUD_STALE, macB);
+ verify(mBpfCoordinator).tetherOffloadRuleRemove(
+ mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighB, macNull));
verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macNull));
- reset(mNetd);
+ resetNetdAndBpfCoordinator();
- // Upstream changes result in deleting and re-adding the rules.
+ // Upstream changes result in updating the rules.
recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
- reset(mNetd);
+ resetNetdAndBpfCoordinator();
InOrder inOrder = inOrder(mNetd);
LinkProperties lp = new LinkProperties();
lp.setInterfaceName(UPSTREAM_IFACE2);
dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp, -1);
- inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighA, macA));
+ verify(mBpfCoordinator).tetherOffloadRuleUpdate(mIpServer, UPSTREAM_IFINDEX2);
inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA));
- inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighB, macB));
+ inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighA, macA));
inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB));
- reset(mNetd);
+ inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighB, macB));
+ resetNetdAndBpfCoordinator();
// When the upstream is lost, rules are removed.
dispatchTetherConnectionChanged(null, null, 0);
+ // Clear function is called two times by:
+ // - processMessage CMD_TETHER_CONNECTION_CHANGED for the upstream is lost.
+ // - processMessage CMD_IPV6_TETHER_UPDATE for the IPv6 upstream is lost.
+ // See dispatchTetherConnectionChanged.
+ verify(mBpfCoordinator, times(2)).tetherOffloadRuleClear(mIpServer);
verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX2, neighA, macA));
verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX2, neighB, macB));
- reset(mNetd);
+ resetNetdAndBpfCoordinator();
// If the upstream is IPv4-only, no rules are added.
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
- reset(mNetd);
+ resetNetdAndBpfCoordinator();
recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
- verifyNoMoreInteractions(mNetd);
+ // Clear function is called by #updateIpv6ForwardingRules for the IPv6 upstream is lost.
+ verify(mBpfCoordinator).tetherOffloadRuleClear(mIpServer);
+ verifyNoMoreInteractions(mBpfCoordinator, mNetd);
// Rules can be added again once upstream IPv6 connectivity is available.
lp.setInterfaceName(UPSTREAM_IFACE);
dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1);
recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
+ verify(mBpfCoordinator).tetherOffloadRuleAdd(
+ mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighB, macB));
verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB));
+ verify(mBpfCoordinator, never()).tetherOffloadRuleAdd(
+ mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighA, macA));
verify(mNetd, never()).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA));
// If upstream IPv6 connectivity is lost, rules are removed.
- reset(mNetd);
+ resetNetdAndBpfCoordinator();
dispatchTetherConnectionChanged(UPSTREAM_IFACE, null, 0);
+ verify(mBpfCoordinator).tetherOffloadRuleClear(mIpServer);
verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB));
// When the interface goes down, rules are removed.
@@ -751,15 +815,20 @@ public class IpServerTest {
dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1);
recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
+ verify(mBpfCoordinator).tetherOffloadRuleAdd(
+ mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighA, macA));
verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA));
+ verify(mBpfCoordinator).tetherOffloadRuleAdd(
+ mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neighB, macB));
verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB));
- reset(mNetd);
+ resetNetdAndBpfCoordinator();
mIpServer.stop();
mLooper.dispatchAll();
+ verify(mBpfCoordinator).tetherOffloadRuleClear(mIpServer);
verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA));
verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB));
- reset(mNetd);
+ resetNetdAndBpfCoordinator();
}
@Test
@@ -769,35 +838,46 @@ public class IpServerTest {
final MacAddress macA = MacAddress.fromString("00:00:00:00:00:0a");
final MacAddress macNull = MacAddress.fromString("00:00:00:00:00:00");
- reset(mNetd);
-
// Expect that rules can be only added/removed when the BPF offload config is enabled.
- // Note that the usingBpfOffload false case is not a realistic test case. Because IP
+ // Note that the BPF offload disabled case is not a realistic test case. Because IP
// neighbor monitor doesn't start if BPF offload is disabled, there should have no
// neighbor event listening. This is used for testing the protection check just in case.
- // TODO: Perhaps remove this test once we don't need this check anymore.
- for (boolean usingBpfOffload : new boolean[]{true, false}) {
- initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */,
- usingBpfOffload);
-
- // A neighbor is added.
- recvNewNeigh(myIfindex, neigh, NUD_REACHABLE, macA);
- if (usingBpfOffload) {
- verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neigh, macA));
- } else {
- verify(mNetd, never()).tetherOffloadRuleAdd(any());
- }
- reset(mNetd);
-
- // A neighbor is deleted.
- recvDelNeigh(myIfindex, neigh, NUD_STALE, macA);
- if (usingBpfOffload) {
- verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neigh, macNull));
- } else {
- verify(mNetd, never()).tetherOffloadRuleRemove(any());
- }
- reset(mNetd);
- }
+ // TODO: Perhaps remove the BPF offload disabled case test once this check isn't needed
+ // anymore.
+
+ // [1] Enable BPF offload.
+ // A neighbor that is added or deleted causes the rule to be added or removed.
+ initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */,
+ true /* usingBpfOffload */);
+ resetNetdAndBpfCoordinator();
+
+ recvNewNeigh(myIfindex, neigh, NUD_REACHABLE, macA);
+ verify(mBpfCoordinator).tetherOffloadRuleAdd(
+ mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neigh, macA));
+ verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neigh, macA));
+ resetNetdAndBpfCoordinator();
+
+ recvDelNeigh(myIfindex, neigh, NUD_STALE, macA);
+ verify(mBpfCoordinator).tetherOffloadRuleRemove(
+ mIpServer, makeForwardingRule(UPSTREAM_IFINDEX, neigh, macNull));
+ verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neigh, macNull));
+ resetNetdAndBpfCoordinator();
+
+ // [2] Disable BPF offload.
+ // A neighbor that is added or deleted doesn’t cause the rule to be added or removed.
+ initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */,
+ false /* usingBpfOffload */);
+ resetNetdAndBpfCoordinator();
+
+ recvNewNeigh(myIfindex, neigh, NUD_REACHABLE, macA);
+ verify(mBpfCoordinator, never()).tetherOffloadRuleAdd(any(), any());
+ verify(mNetd, never()).tetherOffloadRuleAdd(any());
+ resetNetdAndBpfCoordinator();
+
+ recvDelNeigh(myIfindex, neigh, NUD_STALE, macA);
+ verify(mBpfCoordinator, never()).tetherOffloadRuleRemove(any(), any());
+ verify(mNetd, never()).tetherOffloadRuleRemove(any());
+ resetNetdAndBpfCoordinator();
}
@Test
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
new file mode 100644
index 000000000000..e2d7aab4e33f
--- /dev/null
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.networkstack.tethering;
+
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.METERED_NO;
+import static android.net.NetworkStats.ROAMING_NO;
+import static android.net.NetworkStats.SET_DEFAULT;
+import static android.net.NetworkStats.TAG_NONE;
+import static android.net.NetworkStats.UID_ALL;
+import static android.net.NetworkStats.UID_TETHERING;
+import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
+
+import static com.android.networkstack.tethering.BpfCoordinator
+ .DEFAULT_PERFORM_POLL_INTERVAL_MS;
+import static com.android.networkstack.tethering.BpfCoordinator.StatsType;
+import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_IFACE;
+import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_UID;
+
+import static junit.framework.Assert.assertNotNull;
+
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.app.usage.NetworkStatsManager;
+import android.net.INetd;
+import android.net.NetworkStats;
+import android.net.TetherStatsParcel;
+import android.net.util.SharedLog;
+import android.os.Handler;
+import android.os.test.TestLooper;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.testutils.TestableNetworkStatsProviderCbBinder;
+
+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;
+
+import java.util.ArrayList;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class BpfCoordinatorTest {
+ @Mock private NetworkStatsManager mStatsManager;
+ @Mock private INetd mNetd;
+ // Late init since methods must be called by the thread that created this object.
+ private TestableNetworkStatsProviderCbBinder mTetherStatsProviderCb;
+ private BpfCoordinator.BpfTetherStatsProvider mTetherStatsProvider;
+ private final ArgumentCaptor<ArrayList> mStringArrayCaptor =
+ ArgumentCaptor.forClass(ArrayList.class);
+ private final TestLooper mTestLooper = new TestLooper();
+ private BpfCoordinator.Dependencies mDeps =
+ new BpfCoordinator.Dependencies() {
+ @Override
+ int getPerformPollInterval() {
+ return DEFAULT_PERFORM_POLL_INTERVAL_MS;
+ }
+ };
+
+ @Before public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ private void waitForIdle() {
+ mTestLooper.dispatchAll();
+ }
+
+ private void setupFunctioningNetdInterface() throws Exception {
+ when(mNetd.tetherOffloadGetStats()).thenReturn(new TetherStatsParcel[0]);
+ }
+
+ @NonNull
+ private BpfCoordinator makeBpfCoordinator() throws Exception {
+ BpfCoordinator coordinator = new BpfCoordinator(
+ new Handler(mTestLooper.getLooper()), mNetd, mStatsManager, new SharedLog("test"),
+ mDeps);
+ final ArgumentCaptor<BpfCoordinator.BpfTetherStatsProvider>
+ tetherStatsProviderCaptor =
+ ArgumentCaptor.forClass(BpfCoordinator.BpfTetherStatsProvider.class);
+ verify(mStatsManager).registerNetworkStatsProvider(anyString(),
+ tetherStatsProviderCaptor.capture());
+ mTetherStatsProvider = tetherStatsProviderCaptor.getValue();
+ assertNotNull(mTetherStatsProvider);
+ mTetherStatsProviderCb = new TestableNetworkStatsProviderCbBinder();
+ mTetherStatsProvider.setProviderCallbackBinder(mTetherStatsProviderCb);
+ return coordinator;
+ }
+
+ @NonNull
+ private static NetworkStats.Entry buildTestEntry(@NonNull StatsType how,
+ @NonNull String iface, long rxBytes, long rxPackets, long txBytes, long txPackets) {
+ return new NetworkStats.Entry(iface, how == STATS_PER_IFACE ? UID_ALL : UID_TETHERING,
+ SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, rxBytes,
+ rxPackets, txBytes, txPackets, 0L);
+ }
+
+ @NonNull
+ private static TetherStatsParcel buildTestTetherStatsParcel(@NonNull Integer ifIndex,
+ long rxBytes, long rxPackets, long txBytes, long txPackets) {
+ final TetherStatsParcel parcel = new TetherStatsParcel();
+ parcel.ifIndex = ifIndex;
+ parcel.rxBytes = rxBytes;
+ parcel.rxPackets = rxPackets;
+ parcel.txBytes = txBytes;
+ parcel.txPackets = txPackets;
+ return parcel;
+ }
+
+ private void setTetherOffloadStatsList(TetherStatsParcel[] tetherStatsList) throws Exception {
+ when(mNetd.tetherOffloadGetStats()).thenReturn(tetherStatsList);
+ mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS);
+ waitForIdle();
+ }
+
+ @Test
+ public void testGetForwardedStats() throws Exception {
+ setupFunctioningNetdInterface();
+
+ final BpfCoordinator coordinator = makeBpfCoordinator();
+ coordinator.startPolling();
+
+ final String wlanIface = "wlan0";
+ final Integer wlanIfIndex = 100;
+ final String mobileIface = "rmnet_data0";
+ final Integer mobileIfIndex = 101;
+
+ // Add interface name to lookup table. In realistic case, the upstream interface name will
+ // be added by IpServer when IpServer has received with a new IPv6 upstream update event.
+ coordinator.addUpstreamNameToLookupTable(wlanIfIndex, wlanIface);
+ coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface);
+
+ // [1] Both interface stats are changed.
+ // Setup the tether stats of wlan and mobile interface. Note that move forward the time of
+ // the looper to make sure the new tether stats has been updated by polling update thread.
+ setTetherOffloadStatsList(new TetherStatsParcel[] {
+ buildTestTetherStatsParcel(wlanIfIndex, 1000, 100, 2000, 200),
+ buildTestTetherStatsParcel(mobileIfIndex, 3000, 300, 4000, 400)});
+
+ final NetworkStats expectedIfaceStats = new NetworkStats(0L, 2)
+ .addEntry(buildTestEntry(STATS_PER_IFACE, wlanIface, 1000, 100, 2000, 200))
+ .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 3000, 300, 4000, 400));
+
+ final NetworkStats expectedUidStats = new NetworkStats(0L, 2)
+ .addEntry(buildTestEntry(STATS_PER_UID, wlanIface, 1000, 100, 2000, 200))
+ .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 3000, 300, 4000, 400));
+
+ // Force pushing stats update to verify the stats reported.
+ // TODO: Perhaps make #expectNotifyStatsUpdated to use test TetherStatsParcel object for
+ // verifying the notification.
+ mTetherStatsProvider.pushTetherStats();
+ mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStats, expectedUidStats);
+
+ // [2] Only one interface stats is changed.
+ // The tether stats of mobile interface is accumulated and The tether stats of wlan
+ // interface is the same.
+ setTetherOffloadStatsList(new TetherStatsParcel[] {
+ buildTestTetherStatsParcel(wlanIfIndex, 1000, 100, 2000, 200),
+ buildTestTetherStatsParcel(mobileIfIndex, 3010, 320, 4030, 440)});
+
+ final NetworkStats expectedIfaceStatsDiff = new NetworkStats(0L, 2)
+ .addEntry(buildTestEntry(STATS_PER_IFACE, wlanIface, 0, 0, 0, 0))
+ .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 10, 20, 30, 40));
+
+ final NetworkStats expectedUidStatsDiff = new NetworkStats(0L, 2)
+ .addEntry(buildTestEntry(STATS_PER_UID, wlanIface, 0, 0, 0, 0))
+ .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 10, 20, 30, 40));
+
+ // Force pushing stats update to verify that only diff of stats is reported.
+ mTetherStatsProvider.pushTetherStats();
+ mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStatsDiff,
+ expectedUidStatsDiff);
+
+ // [3] Stop coordinator.
+ // Shutdown the coordinator and clear the invocation history, especially the
+ // tetherOffloadGetStats() calls.
+ coordinator.stopPolling();
+ clearInvocations(mNetd);
+
+ // Verify the polling update thread stopped.
+ mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS);
+ waitForIdle();
+ verify(mNetd, never()).tetherOffloadGetStats();
+ }
+
+ @Test
+ public void testOnSetAlert() throws Exception {
+ setupFunctioningNetdInterface();
+
+ final BpfCoordinator coordinator = makeBpfCoordinator();
+ coordinator.startPolling();
+
+ final String mobileIface = "rmnet_data0";
+ final Integer mobileIfIndex = 100;
+ coordinator.addUpstreamNameToLookupTable(mobileIfIndex, mobileIface);
+
+ // Verify that set quota to 0 will immediately triggers a callback.
+ mTetherStatsProvider.onSetAlert(0);
+ waitForIdle();
+ mTetherStatsProviderCb.expectNotifyAlertReached();
+
+ // Verify that notifyAlertReached never fired if quota is not yet reached.
+ when(mNetd.tetherOffloadGetStats()).thenReturn(
+ new TetherStatsParcel[] {buildTestTetherStatsParcel(mobileIfIndex, 0, 0, 0, 0)});
+ mTetherStatsProvider.onSetAlert(100);
+ mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS);
+ waitForIdle();
+ mTetherStatsProviderCb.assertNoCallback();
+
+ // Verify that notifyAlertReached fired when quota is reached.
+ when(mNetd.tetherOffloadGetStats()).thenReturn(
+ new TetherStatsParcel[] {buildTestTetherStatsParcel(mobileIfIndex, 50, 0, 50, 0)});
+ mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS);
+ waitForIdle();
+ mTetherStatsProviderCb.expectNotifyAlertReached();
+
+ // Verify that set quota with UNLIMITED won't trigger any callback.
+ mTetherStatsProvider.onSetAlert(QUOTA_UNLIMITED);
+ mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS);
+ waitForIdle();
+ mTetherStatsProviderCb.assertNoCallback();
+ }
+}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
index 7d5471f7703d..4b6bbac051e0 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
@@ -19,6 +19,10 @@ package com.android.networkstack.tethering
import android.app.Notification
import android.app.NotificationManager
import android.content.Context
+import android.content.pm.ActivityInfo
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
import android.content.res.Resources
import android.net.ConnectivityManager.TETHERING_WIFI
import android.os.Handler
@@ -51,6 +55,7 @@ import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.times
@@ -351,4 +356,26 @@ class TetheringNotificationUpdaterTest {
notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES)
verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID))
}
+
+ @Test
+ fun testGetSettingsPackageName() {
+ val defaultSettingsPackageName = "com.android.settings"
+ val testSettingsPackageName = "com.android.test.settings"
+ val pm = mock(PackageManager::class.java)
+ doReturn(null).`when`(pm).resolveActivity(any(), anyInt())
+ assertEquals(defaultSettingsPackageName,
+ TetheringNotificationUpdater.getSettingsPackageName(pm))
+
+ val resolveInfo = ResolveInfo().apply {
+ activityInfo = ActivityInfo().apply {
+ name = "test"
+ applicationInfo = ApplicationInfo().apply {
+ packageName = testSettingsPackageName
+ }
+ }
+ }
+ doReturn(resolveInfo).`when`(pm).resolveActivity(any(), anyInt())
+ assertEquals(testSettingsPackageName,
+ TetheringNotificationUpdater.getSettingsPackageName(pm))
+ }
}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index bb65b18edb8a..8146a58dddcb 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -203,6 +203,7 @@ public class TetheringTest {
@Mock private ConnectivityManager mCm;
@Mock private EthernetManager mEm;
@Mock private TetheringNotificationUpdater mNotificationUpdater;
+ @Mock private BpfCoordinator mBpfCoordinator;
private final MockIpServerDependencies mIpServerDependencies =
spy(new MockIpServerDependencies());
@@ -337,6 +338,12 @@ public class TetheringTest {
}
@Override
+ public BpfCoordinator getBpfCoordinator(Handler handler, INetd netd,
+ SharedLog log, BpfCoordinator.Dependencies deps) {
+ return mBpfCoordinator;
+ }
+
+ @Override
public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) {
return mOffloadHardwareInterface;
}
diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
index 26e85beba58b..103151dcdda5 100644
--- a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
+++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
@@ -51,7 +51,7 @@ import java.util.function.Consumer;
*/
public class AppPredictionPerUserService extends
AbstractPerUserSystemService<AppPredictionPerUserService, AppPredictionManagerService>
- implements RemoteAppPredictionService.RemoteAppPredictionServiceCallbacks {
+ implements RemoteAppPredictionService.RemoteAppPredictionServiceCallbacks {
private static final String TAG = AppPredictionPerUserService.class.getSimpleName();
private static final String PREDICT_USING_PEOPLE_SERVICE_PREFIX =
@@ -114,11 +114,8 @@ public class AppPredictionPerUserService extends
public void onCreatePredictionSessionLocked(@NonNull AppPredictionContext context,
@NonNull AppPredictionSessionId sessionId) {
if (!mSessionInfos.containsKey(sessionId)) {
- // TODO(b/157500121): remove below forceUsingPeopleService logic after testing
- // PeopleService for 2 weeks on Droidfood.
- final boolean forceUsingPeopleService = context.getUiSurface().equals("share");
mSessionInfos.put(sessionId, new AppPredictionSessionInfo(sessionId, context,
- forceUsingPeopleService || DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI,
+ DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI,
PREDICT_USING_PEOPLE_SERVICE_PREFIX + context.getUiSurface(), false),
this::removeAppPredictionSessionInfo));
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
index e7a43b75f9d5..48895ad42e99 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
@@ -93,7 +93,7 @@ final class AutofillInlineSuggestionsRequestSession {
@Nullable
private InlineFillUi mInlineFillUi;
@GuardedBy("mLock")
- private boolean mPreviousResponseIsNotEmpty;
+ private Boolean mPreviousResponseIsNotEmpty = null;
@GuardedBy("mLock")
private boolean mDestroyed = false;
@@ -213,7 +213,7 @@ final class AutofillInlineSuggestionsRequestSession {
// if IME is visible, and response is not null, send the response
InlineSuggestionsResponse response = mInlineFillUi.getInlineSuggestionsResponse();
boolean isEmptyResponse = response.getInlineSuggestions().isEmpty();
- if (isEmptyResponse && !mPreviousResponseIsNotEmpty) {
+ if (isEmptyResponse && Boolean.FALSE.equals(mPreviousResponseIsNotEmpty)) {
// No-op if both the previous response and current response are empty.
return;
}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index 851e4cc0bfd1..a7d0061cc043 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -245,6 +245,11 @@ final class RemoteAugmentedAutofillService
if (inlineSuggestionsData == null || inlineSuggestionsData.isEmpty()
|| inlineSuggestionsCallback == null || request == null
|| remoteRenderService == null) {
+ // If it was an inline request and the response doesn't have any inline suggestions,
+ // we will send an empty response to IME.
+ if (inlineSuggestionsCallback != null && request != null) {
+ inlineSuggestionsCallback.apply(InlineFillUi.emptyUi(focusedId));
+ }
return;
}
mCallbacks.setLastResponse(sessionId);
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 9e8a8727dc36..65e98ac8e684 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -100,7 +100,7 @@ java_library_static {
"android.net.ipsec.ike.stubs.module_lib",
"app-compat-annotations",
"framework-tethering.stubs.module_lib",
- "service-permission-stubs",
+ "service-permission.stubs.system_server",
],
required: [
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 81de29c4ee4d..b241bd16d3ee 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -988,4 +988,14 @@ public abstract class PackageManagerInternal {
* Unblocks uninstall for all packages for the user.
*/
public abstract void clearBlockUninstallForUser(@UserIdInt int userId);
+
+ /**
+ * Unsuspends all packages suspended by the given package for the user.
+ */
+ public abstract void unsuspendForSuspendingPackage(String suspendingPackage, int userId);
+
+ /**
+ * Returns {@code true} if the package is suspending any packages for the user.
+ */
+ public abstract boolean isSuspendingAnyPackages(String suspendingPackage, int userId);
}
diff --git a/services/core/java/com/android/server/AppStateTracker.java b/services/core/java/com/android/server/AppStateTracker.java
index b765d81d42b7..74f79e0d40f5 100644
--- a/services/core/java/com/android/server/AppStateTracker.java
+++ b/services/core/java/com/android/server/AppStateTracker.java
@@ -1120,7 +1120,8 @@ public class AppStateTracker {
return false;
}
final int userId = UserHandle.getUserId(uid);
- if (mExemptedPackages.contains(userId, packageName)) {
+ if (mAppStandbyInternal.isAppIdleEnabled() && !mAppStandbyInternal.isInParole()
+ && mExemptedPackages.contains(userId, packageName)) {
return false;
}
return mForceAllAppsStandby;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 0ab571854c72..2958fd2ae63a 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -6426,7 +6426,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
final boolean shouldFilter = requiresVpnIsolation(nai, newNc, nai.linkProperties);
final String iface = nai.linkProperties.getInterfaceName();
// For VPN uid interface filtering, old ranges need to be removed before new ranges can
- // be added, due to the range being expanded and stored as invidiual UIDs. For example
+ // be added, due to the range being expanded and stored as individual UIDs. For example
// the UIDs might be updated from [0, 99999] to ([0, 10012], [10014, 99999]) which means
// prevRanges = [0, 99999] while newRanges = [0, 10012], [10014, 99999]. If prevRanges
// were added first and then newRanges got removed later, there would be only one uid
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 9080bdb44eaf..a3c164d63605 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1606,7 +1606,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatchWithoutDefaultPhoneCheck(r.subId, subId)) {
try {
r.callback.onDisplayInfoChanged(telephonyDisplayInfo);
} catch (RemoteException ex) {
@@ -2726,6 +2726,24 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
Rlog.e(TAG, s);
}
+ /**
+ * If the registrant specified a subId, then we should only notify it if subIds match.
+ * If the registrant registered with DEFAULT subId, we should notify only when the related subId
+ * is default subId (which could be INVALID if there's no default subId).
+ *
+ * This should be the correct way to check record ID match. in idMatch the record's phoneId is
+ * speculated based on subId passed by the registrant so it's not a good reference.
+ * But to avoid triggering potential regression only replace idMatch with it when an issue with
+ * idMatch is reported. Eventually this should replace all instances of idMatch.
+ */
+ private boolean idMatchWithoutDefaultPhoneCheck(int subIdInRecord, int subIdToNotify) {
+ if (subIdInRecord == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
+ return (subIdToNotify == mDefaultSubId);
+ } else {
+ return (subIdInRecord == subIdToNotify);
+ }
+ }
+
boolean idMatch(int rSubId, int subId, int phoneId) {
if(subId < 0) {
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index 27ea4716e12d..df3b6880fdfb 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -337,6 +337,7 @@ public class AdbDebuggingManager {
class PortListenerImpl implements AdbConnectionPortListener {
public void onPortReceived(int port) {
+ if (DEBUG) Slog.d(TAG, "Received tls port=" + port);
Message msg = mHandler.obtainMessage(port > 0
? AdbDebuggingHandler.MSG_SERVER_CONNECTED
: AdbDebuggingHandler.MSG_SERVER_DISCONNECTED);
@@ -392,6 +393,7 @@ public class AdbDebuggingManager {
mOutputStream = mSocket.getOutputStream();
mInputStream = mSocket.getInputStream();
+ mHandler.sendEmptyMessage(AdbDebuggingHandler.MSG_ADBD_SOCKET_CONNECTED);
} catch (IOException ioe) {
Slog.e(TAG, "Caught an exception opening the socket: " + ioe);
closeSocketLocked();
@@ -504,6 +506,7 @@ public class AdbDebuggingManager {
} catch (IOException ex) {
Slog.e(TAG, "Failed closing socket: " + ex);
}
+ mHandler.sendEmptyMessage(AdbDebuggingHandler.MSG_ADBD_SOCKET_DISCONNECTED);
}
/** Call to stop listening on the socket and exit the thread. */
@@ -729,6 +732,10 @@ public class AdbDebuggingManager {
static final int MSG_SERVER_CONNECTED = 24;
// Notifies us the TLS server is disconnected
static final int MSG_SERVER_DISCONNECTED = 25;
+ // Notification when adbd socket successfully connects.
+ static final int MSG_ADBD_SOCKET_CONNECTED = 26;
+ // Notification when adbd socket is disconnected.
+ static final int MSG_ADBD_SOCKET_DISCONNECTED = 27;
// === Messages we can send to adbd ===========
static final String MSG_DISCONNECT_DEVICE = "DD";
@@ -1170,6 +1177,28 @@ public class AdbDebuggingManager {
}
break;
}
+ case MSG_ADBD_SOCKET_CONNECTED: {
+ if (DEBUG) Slog.d(TAG, "adbd socket connected");
+ if (mAdbWifiEnabled) {
+ // In scenarios where adbd is restarted, the tls port may change.
+ mConnectionPortPoller =
+ new AdbDebuggingManager.AdbConnectionPortPoller(mPortListener);
+ mConnectionPortPoller.start();
+ }
+ break;
+ }
+ case MSG_ADBD_SOCKET_DISCONNECTED: {
+ if (DEBUG) Slog.d(TAG, "adbd socket disconnected");
+ if (mConnectionPortPoller != null) {
+ mConnectionPortPoller.cancelAndWait();
+ mConnectionPortPoller = null;
+ }
+ if (mAdbWifiEnabled) {
+ // In scenarios where adbd is restarted, the tls port may change.
+ onAdbdWifiServerDisconnected(-1);
+ }
+ break;
+ }
}
}
diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java
index e1f9a7a150d8..ef81d7159c42 100644
--- a/services/core/java/com/android/server/adb/AdbService.java
+++ b/services/core/java/com/android/server/adb/AdbService.java
@@ -241,12 +241,7 @@ public class AdbService extends IAdbManager.Stub {
private AdbService(Context context) {
mContext = context;
mContentResolver = context.getContentResolver();
-
- boolean secureAdbEnabled = AdbProperties.secure().orElse(false);
- boolean dataEncrypted = "1".equals(SystemProperties.get("vold.decrypt"));
- if (secureAdbEnabled && !dataEncrypted) {
- mDebuggingManager = new AdbDebuggingManager(context);
- }
+ mDebuggingManager = new AdbDebuggingManager(context);
initAdbState();
LocalServices.addService(AdbManagerInternal.class, new AdbManagerInternalImpl());
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 026f147e955c..079512275797 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1671,6 +1671,12 @@ public class ActivityManagerService extends IActivityManager.Stub
*/
@Nullable ContentCaptureManagerInternal mContentCaptureService;
+ /**
+ * Set of {@link ProcessRecord} that have either {@link ProcessRecord#hasTopUi()} or
+ * {@link ProcessRecord#runningRemoteAnimation} set to {@code true}.
+ */
+ final ArraySet<ProcessRecord> mTopUiOrRunningRemoteAnimApps = new ArraySet<>();
+
final class UiHandler extends Handler {
public UiHandler() {
super(com.android.server.UiThread.get().getLooper(), null, true);
@@ -14702,6 +14708,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mProcessesToGc.remove(app);
mPendingPssProcesses.remove(app);
+ mTopUiOrRunningRemoteAnimApps.remove(app);
ProcessList.abortNextPssTime(app.procStateMemTracker);
// Dismiss any open dialogs.
@@ -18490,6 +18497,22 @@ public class ActivityManagerService extends IActivityManager.Stub
return proc;
}
+ /**
+ * @return {@code true} if {@link #mTopUiOrRunningRemoteAnimApps} set contains {@code app} or when there are no apps
+ * in this list, an false otherwise.
+ */
+ boolean containsTopUiOrRunningRemoteAnimOrEmptyLocked(ProcessRecord app) {
+ return mTopUiOrRunningRemoteAnimApps.isEmpty() || mTopUiOrRunningRemoteAnimApps.contains(app);
+ }
+
+ void addTopUiOrRunningRemoteAnim(ProcessRecord app) {
+ mTopUiOrRunningRemoteAnimApps.add(app);
+ }
+
+ void removeTopUiOrRunningRemoteAnim(ProcessRecord app) {
+ mTopUiOrRunningRemoteAnimApps.remove(app);
+ }
+
@Override
public boolean dumpHeap(String process, int userId, boolean managed, boolean mallocInfo,
boolean runGc, String path, ParcelFileDescriptor fd, RemoteCallback finishCallback) {
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index da5f48962130..58b0a157e2c2 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1151,8 +1151,17 @@ public final class OomAdjuster {
// is currently showing UI.
app.systemNoUi = true;
if (app == topApp) {
+ // If specific system app has set ProcessRecord.mHasTopUi or is running a remote
+ // animation (ProcessRecord.runningRemoteAnimation), this will prevent topApp
+ // to use SCHED_GROUP_TOP_APP to ensure process with mHasTopUi will have exclusive
+ // access to configured cores.
+ if (mService.containsTopUiOrRunningRemoteAnimOrEmptyLocked(app)) {
+ app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
+ } else {
+ app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
+ }
app.systemNoUi = false;
- app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
+
app.adjType = "pers-top-activity";
} else if (app.hasTopUi()) {
// sched group/proc state adjustment is below
@@ -1193,10 +1202,20 @@ public final class OomAdjuster {
boolean foregroundActivities = false;
if (PROCESS_STATE_CUR_TOP == PROCESS_STATE_TOP && app == topApp) {
- // The last app on the list is the foreground app.
- adj = ProcessList.FOREGROUND_APP_ADJ;
- schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
- app.adjType = "top-activity";
+
+ // If specific system app has set ProcessRecord.mHasTopUi or is running a remote
+ // animation (ProcessRecord.runningRemoteAnimation), this will prevent topApp
+ // to use SCHED_GROUP_TOP_APP to ensure process with mHasTopUi will have exclusive
+ // access to configured cores.
+ if (mService.containsTopUiOrRunningRemoteAnimOrEmptyLocked(app)) {
+ adj = ProcessList.FOREGROUND_APP_ADJ;
+ schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
+ app.adjType = "top-activity";
+ } else {
+ adj = ProcessList.FOREGROUND_APP_ADJ;
+ schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+ app.adjType = "top-activity-behind-topui";
+ }
foregroundActivities = true;
procState = PROCESS_STATE_CUR_TOP;
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index c5152c081e70..4c75ab21d6f2 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1268,6 +1268,7 @@ class ProcessRecord implements WindowProcessListener {
void setHasTopUi(boolean hasTopUi) {
mHasTopUi = hasTopUi;
mWindowProcessController.setHasTopUi(hasTopUi);
+ updateTopUiOrRunningRemoteAnim();
}
boolean hasTopUi() {
@@ -1518,10 +1519,19 @@ class ProcessRecord implements WindowProcessListener {
Slog.i(TAG, "Setting runningRemoteAnimation=" + runningRemoteAnimation
+ " for pid=" + pid);
}
+ updateTopUiOrRunningRemoteAnim();
mService.updateOomAdjLocked(this, true, OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY);
}
}
+ void updateTopUiOrRunningRemoteAnim() {
+ if (runningRemoteAnimation || hasTopUi()) {
+ mService.addTopUiOrRunningRemoteAnim(this);
+ } else {
+ mService.removeTopUiOrRunningRemoteAnim(this);
+ }
+ }
+
public long getInputDispatchingTimeout() {
return mWindowProcessController.getInputDispatchingTimeout();
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index ea7059894f38..f2c4e4428af2 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -2515,7 +2515,7 @@ class UserController implements Handler.Callback {
showUserSwitchDialog(fromToUserPair);
break;
case CLEAR_USER_JOURNEY_SESSION_MSG:
- clearSessionId(msg.arg1);
+ logAndClearSessionId(msg.arg1);
break;
}
return false;
@@ -2630,6 +2630,21 @@ class UserController implements Handler.Callback {
}
/**
+ * Log a final event of the {@link UserJourneySession} and clear it.
+ */
+ private void logAndClearSessionId(@UserIdInt int userId) {
+ synchronized (mUserIdToUserJourneyMap) {
+ final UserJourneySession userJourneySession = mUserIdToUserJourneyMap.get(userId);
+ if (userJourneySession != null) {
+ FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED,
+ userJourneySession.mSessionId, userId, USER_LIFECYCLE_EVENT_UNKNOWN,
+ USER_LIFECYCLE_EVENT_STATE_NONE);
+ }
+ clearSessionId(userId);
+ }
+ }
+
+ /**
* Helper class to store user journey and session id.
*
* <p> User journey tracks a chain of user lifecycle events occurring during different user
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 6d45abaf0234..36272278e0e4 100755
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -5322,6 +5322,15 @@ public class AudioService extends IAudioService.Stub
}
private void setVolumeIndexInt(int index, int device, int flags) {
+ // Reflect mute state of corresponding stream by forcing index to 0 if muted
+ // Only set audio policy BT SCO stream volume to 0 when the stream is actually muted.
+ // This allows RX path muting by the audio HAL only when explicitly muted but not when
+ // index is just set to 0 to repect BT requirements
+ if (mStreamStates[mPublicStreamType].isFullyMuted()) {
+ index = 0;
+ } else if (mPublicStreamType == AudioSystem.STREAM_BLUETOOTH_SCO && index == 0) {
+ index = 1;
+ }
// Set the volume index
AudioSystem.setVolumeIndexForAttributes(mAudioAttributes, index, device);
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index a498e3867c05..b647a1ab9873 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -207,6 +207,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
static final int MSG_HIDE_CURRENT_INPUT_METHOD = 1035;
static final int MSG_INITIALIZE_IME = 1040;
static final int MSG_CREATE_SESSION = 1050;
+ static final int MSG_REMOVE_IME_SURFACE = 1060;
static final int MSG_START_INPUT = 2000;
@@ -2352,6 +2353,16 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mImeTargetWindowMap.put(startInputToken, mCurFocusedWindow);
mStartInputHistory.addEntry(info);
+ // Seems that PackageManagerInternal#grantImplicitAccess() doesn't handle cross-user
+ // implicit visibility (e.g. IME[user=10] -> App[user=0]) thus we do this only for the
+ // same-user scenarios.
+ // That said ignoring cross-user scenario will never affect IMEs that do not have
+ // INTERACT_ACROSS_USERS(_FULL) permissions, which is actually almost always the case.
+ if (mSettings.getCurrentUserId() == UserHandle.getUserId(mCurClient.uid)) {
+ mPackageManagerInternal.grantImplicitAccess(mSettings.getCurrentUserId(),
+ null /* intent */, UserHandle.getAppId(mCurMethodUid), mCurClient.uid, true);
+ }
+
final SessionState session = mCurClient.curSession;
executeOrSendMessage(session.method, mCaller.obtainMessageIIOOOO(
MSG_START_INPUT, mCurInputContextMissingMethods, initial ? 0 : 1 /* restarting */,
@@ -3936,6 +3947,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
+ @Override
+ public void removeImeSurface() {
+ mContext.enforceCallingPermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW, null);
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE));
+ }
+
@BinderThread
private void notifyUserAction(@NonNull IBinder token) {
if (DEBUG) {
@@ -4206,6 +4223,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
args.recycle();
return true;
}
+ case MSG_REMOVE_IME_SURFACE: {
+ try {
+ if (mEnabledSession != null && mEnabledSession.session != null) {
+ mEnabledSession.session.removeImeSurface();
+ }
+ } catch (RemoteException e) {
+ }
+ return true;
+ }
// ---------------------------------------------------------
case MSG_START_INPUT: {
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 2129e9bd34f3..d025b0f4ece5 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -1462,6 +1462,12 @@ public final class MultiClientInputMethodManagerService {
@BinderThread
@Override
+ public void removeImeSurface() {
+ reportNotSupported();
+ }
+
+ @BinderThread
+ @Override
public boolean showSoftInput(
IInputMethodClient client, IBinder token, int flags,
ResultReceiver resultReceiver) {
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 067bdcb111fb..bfcbe465a271 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -231,7 +231,7 @@ public class LocationManagerService extends ILocationManager.Stub {
private final AppForegroundHelper mAppForegroundHelper;
private final LocationUsageLogger mLocationUsageLogger;
- @Nullable private GnssManagerService mGnssManagerService = null;
+ @Nullable private volatile GnssManagerService mGnssManagerService = null;
private final PassiveLocationProviderManager mPassiveManager;
@@ -381,6 +381,10 @@ public class LocationManagerService extends ILocationManager.Stub {
// prepare providers
initializeProvidersLocked();
}
+
+ // initialize gnss last because it has no awareness of boot phases and blindly assumes that
+ // all other location providers are loaded at initialization
+ initializeGnss();
}
private void onAppOpChanged(String packageName) {
@@ -602,16 +606,19 @@ public class LocationManagerService extends ILocationManager.Stub {
}
manager.setMockProvider(new MockProvider(properties));
}
+ }
- // initialize gnss last because it has no awareness of boot phases and blindly assumes that
- // all other location providers are loaded at initialization
+ private void initializeGnss() {
+ // Do not hold mLock when calling GnssManagerService#isGnssSupported() which calls into HAL.
if (GnssManagerService.isGnssSupported()) {
mGnssManagerService = new GnssManagerService(mContext, mAppOpsHelper, mSettingsHelper,
mAppForegroundHelper, mLocationUsageLogger);
mGnssManagerService.onSystemReady();
LocationProviderManager gnssManager = new LocationProviderManager(GPS_PROVIDER);
- mProviderManagers.add(gnssManager);
+ synchronized (mLock) {
+ mProviderManagers.add(gnssManager);
+ }
gnssManager.setRealProvider(mGnssManagerService.getGnssLocationProvider());
// bind to geofence proxy
diff --git a/services/core/java/com/android/server/location/SettingsHelper.java b/services/core/java/com/android/server/location/SettingsHelper.java
index cbb06b86a291..8a35302d6fd5 100644
--- a/services/core/java/com/android/server/location/SettingsHelper.java
+++ b/services/core/java/com/android/server/location/SettingsHelper.java
@@ -168,7 +168,7 @@ public class SettingsHelper {
* Remove a listener for changes to the location enabled setting.
*/
public void removeOnLocationEnabledChangedListener(UserSettingChangedListener listener) {
- mLocationMode.addListener(listener);
+ mLocationMode.removeListener(listener);
}
/**
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index 8d4efed8604b..5787f7c48138 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -26,6 +26,7 @@ import android.content.pm.UserInfo;
import android.hardware.rebootescrow.IRebootEscrow;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
import android.os.SystemClock;
import android.os.UserManager;
import android.provider.Settings;
@@ -244,6 +245,9 @@ class RebootEscrowManager {
} catch (RemoteException e) {
Slog.w(TAG, "Could not retrieve escrow data");
return null;
+ } catch (ServiceSpecificException e) {
+ Slog.w(TAG, "Got service-specific exception: " + e.errorCode);
+ return null;
}
}
@@ -335,7 +339,7 @@ class RebootEscrowManager {
try {
rebootEscrow.storeKey(new byte[32]);
- } catch (RemoteException e) {
+ } catch (RemoteException | ServiceSpecificException e) {
Slog.w(TAG, "Could not call RebootEscrow HAL to shred key");
}
@@ -373,7 +377,7 @@ class RebootEscrowManager {
rebootEscrow.storeKey(escrowKey.getKeyBytes());
armedRebootEscrow = true;
Slog.i(TAG, "Reboot escrow key stored with RebootEscrow HAL");
- } catch (RemoteException e) {
+ } catch (RemoteException | ServiceSpecificException e) {
Slog.e(TAG, "Failed escrow secret to RebootEscrow HAL", e);
}
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
index 2461b0ce93a5..c7575d4fc8a4 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -48,6 +48,7 @@ import java.util.Objects;
class BluetoothRouteProvider {
private static final String TAG = "BTRouteProvider";
+ private static final String HEARING_AID_ROUTE_ID_PREFIX = "HEARING_AID_";
private static BluetoothRouteProvider sInstance;
@SuppressWarnings("WeakerAccess") /* synthetic access */
@@ -179,9 +180,16 @@ class BluetoothRouteProvider {
@NonNull
List<MediaRoute2Info> getAllBluetoothRoutes() {
- ArrayList<MediaRoute2Info> routes = new ArrayList<>();
+ List<MediaRoute2Info> routes = new ArrayList<>();
+ List<String> routeIds = new ArrayList<>();
+
for (BluetoothRouteInfo btRoute : mBluetoothRoutes.values()) {
+ // A pair of hearing aid devices or the same hardware address
+ if (routeIds.contains(btRoute.route.getId())) {
+ continue;
+ }
routes.add(btRoute.route);
+ routeIds.add(btRoute.route.getId());
}
return routes;
}
@@ -222,8 +230,8 @@ class BluetoothRouteProvider {
private BluetoothRouteInfo createBluetoothRoute(BluetoothDevice device) {
BluetoothRouteInfo newBtRoute = new BluetoothRouteInfo();
newBtRoute.btDevice = device;
- // Current volume will be set when connected.
- // TODO: Is there any BT device which has fixed volume?
+
+ String routeId = device.getAddress();
String deviceName = device.getName();
if (TextUtils.isEmpty(deviceName)) {
deviceName = mContext.getResources().getText(R.string.unknownName).toString();
@@ -236,10 +244,13 @@ class BluetoothRouteProvider {
if (mHearingAidProfile != null
&& mHearingAidProfile.getConnectedDevices().contains(device)) {
newBtRoute.connectedProfiles.put(BluetoothProfile.HEARING_AID, true);
+ // Intentionally assign the same ID for a pair of devices to publish only one of them.
+ routeId = HEARING_AID_ROUTE_ID_PREFIX + mHearingAidProfile.getHiSyncId(device);
type = MediaRoute2Info.TYPE_HEARING_AID;
}
- newBtRoute.route = new MediaRoute2Info.Builder(device.getAddress(), deviceName)
+ // Current volume will be set when connected.
+ newBtRoute.route = new MediaRoute2Info.Builder(routeId, deviceName)
.addFeature(MediaRoute2Info.FEATURE_LIVE_AUDIO)
.setConnectionState(MediaRoute2Info.CONNECTION_STATE_DISCONNECTED)
.setDescription(mContext.getResources().getText(
@@ -247,6 +258,7 @@ class BluetoothRouteProvider {
.setType(type)
.setVolumeHandling(MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
.setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
+ .setAddress(device.getAddress())
.build();
return newBtRoute;
}
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index c12f89c25a9e..9a2aee3d8df6 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -567,7 +567,8 @@ class MediaRouter2ServiceImpl {
boolean hasModifyAudioRoutingPermission) {
final IBinder binder = router.asBinder();
if (mAllRouterRecords.get(binder) != null) {
- Slog.w(TAG, "Same router already exists. packageName=" + packageName);
+ Slog.w(TAG, "registerRouter2Locked: Same router already exists. packageName="
+ + packageName);
return;
}
@@ -665,8 +666,7 @@ class MediaRouter2ServiceImpl {
final RouterRecord routerRecord = mAllRouterRecords.get(binder);
if (routerRecord == null) {
- Slog.w(TAG, "requestCreateSessionWithRouter2ByManagerRequestLocked: "
- + "Ignoring unknown router.");
+ Slog.w(TAG, "notifySessionHintsForCreatingSessionLocked: Ignoring unknown router.");
return;
}
@@ -789,7 +789,8 @@ class MediaRouter2ServiceImpl {
ManagerRecord managerRecord = mAllManagerRecords.get(binder);
if (managerRecord != null) {
- Slog.w(TAG, "Same manager already exists. packageName=" + packageName);
+ Slog.w(TAG, "registerManagerLocked: Same manager already exists. packageName="
+ + packageName);
return;
}
@@ -854,7 +855,8 @@ class MediaRouter2ServiceImpl {
RouterRecord routerRecord = managerRecord.mUserRecord.findRouterRecordLocked(packageName);
if (routerRecord == null) {
- Slog.w(TAG, "Ignoring session creation for unknown router.");
+ Slog.w(TAG, "requestCreateSessionWithManagerLocked: Ignoring session creation for "
+ + "unknown router.");
return;
}
@@ -1231,7 +1233,8 @@ class MediaRouter2ServiceImpl {
for (MediaRoute2Info route : currentRoutes) {
if (!route.isValid()) {
- Slog.w(TAG, "Ignoring invalid route : " + route);
+ Slog.w(TAG, "onProviderStateChangedOnHandler: Ignoring invalid route : "
+ + route);
continue;
}
MediaRoute2Info prevRoute = prevInfo.getRoute(route.getOriginalId());
@@ -1309,8 +1312,8 @@ class MediaRouter2ServiceImpl {
try {
routerRecord.mRouter.getSessionHintsForCreatingSession(uniqueRequestId, route);
} catch (RemoteException ex) {
- Slog.w(TAG, "requestGetSessionHintsOnHandler: "
- + "Failed to request. Router probably died.");
+ Slog.w(TAG, "getSessionHintsForCreatingSessionOnHandler: "
+ + "Failed to request. Router probably died.", ex);
mSessionCreationRequests.remove(request);
notifyRequestFailedToManager(managerRecord.mManager,
toOriginalRequestId(uniqueRequestId), REASON_UNKNOWN_ERROR);
@@ -1323,8 +1326,8 @@ class MediaRouter2ServiceImpl {
final MediaRoute2Provider provider = findProvider(route.getProviderId());
if (provider == null) {
- Slog.w(TAG, "Ignoring session creation request since no provider found for"
- + " given route=" + route);
+ Slog.w(TAG, "requestCreateSessionWithRouter2OnHandler: Ignoring session "
+ + "creation request since no provider found for given route=" + route);
notifySessionCreationFailedToRouter(routerRecord,
toOriginalRequestId(uniqueRequestId));
return;
@@ -1349,22 +1352,21 @@ class MediaRouter2ServiceImpl {
}
}
if (matchingRequest == null) {
- Slog.w(TAG, "requestCreateSessionWithKnownRequestOnHandler: "
- + "Ignoring an unknown request.");
+ Slog.w(TAG, "requestCreateSessionWithManagerOnHandler: "
+ + "Ignoring an unknown session creation request.");
return;
}
if (!TextUtils.equals(matchingRequest.mRoute.getId(), route.getId())) {
- Slog.w(TAG, "requestCreateSessionWithKnownRequestOnHandler: "
+ Slog.w(TAG, "requestCreateSessionWithManagerOnHandler: "
+ "The given route is different from the requested route.");
return;
}
final MediaRoute2Provider provider = findProvider(route.getProviderId());
if (provider == null) {
- Slog.w(TAG, "Ignoring session creation request since no provider found for"
- + " given route=" + route);
-
+ Slog.w(TAG, "requestCreateSessionWithManagerOnHandler: Ignoring session "
+ + "creation request since no provider found for given route=" + route);
mSessionCreationRequests.remove(matchingRequest);
notifyRequestFailedToManager(matchingRequest.mRequestedManagerRecord.mManager,
toOriginalRequestId(uniqueRequestId), REASON_ROUTE_NOT_AVAILABLE);
@@ -1468,7 +1470,7 @@ class MediaRouter2ServiceImpl {
int volume) {
final MediaRoute2Provider provider = findProvider(route.getProviderId());
if (provider == null) {
- Slog.w(TAG, "setRouteVolume: couldn't find provider for route=" + route);
+ Slog.w(TAG, "setRouteVolumeOnHandler: Couldn't find provider for route=" + route);
return;
}
provider.setRouteVolume(uniqueRequestId, route.getOriginalId(), volume);
@@ -1478,8 +1480,8 @@ class MediaRouter2ServiceImpl {
@NonNull String uniqueSessionId, int volume) {
final MediaRoute2Provider provider = findProvider(getProviderId(uniqueSessionId));
if (provider == null) {
- Slog.w(TAG, "setSessionVolume: couldn't find provider for session "
- + "id=" + uniqueSessionId);
+ Slog.w(TAG, "setSessionVolumeOnHandler: Couldn't find provider for session id="
+ + uniqueSessionId);
return;
}
provider.setSessionVolume(uniqueRequestId, getOriginalId(uniqueSessionId), volume);
@@ -1599,7 +1601,8 @@ class MediaRouter2ServiceImpl {
RouterRecord routerRecord = mSessionToRouterMap.get(sessionInfo.getId());
if (routerRecord == null) {
- Slog.w(TAG, "No matching router found for session=" + sessionInfo);
+ Slog.w(TAG, "onSessionInfoChangedOnHandler: No matching router found for session="
+ + sessionInfo);
return;
}
notifySessionInfoChangedToRouter(routerRecord, sessionInfo);
@@ -1612,7 +1615,8 @@ class MediaRouter2ServiceImpl {
RouterRecord routerRecord = mSessionToRouterMap.get(sessionInfo.getId());
if (routerRecord == null) {
- Slog.w(TAG, "No matching router found for session=" + sessionInfo);
+ Slog.w(TAG, "onSessionReleasedOnHandler: No matching router found for session="
+ + sessionInfo);
return;
}
notifySessionReleasedToRouter(routerRecord, sessionInfo);
@@ -1789,7 +1793,7 @@ class MediaRouter2ServiceImpl {
currentRoutes.addAll(systemProviderInfo.getRoutes());
} else {
// This shouldn't happen.
- Slog.w(TAG, "notifyRoutesToRouter: System route provider not found.");
+ Slog.wtf(TAG, "System route provider not found.");
}
currentSystemSessionInfo = mSystemProvider.getSessionInfos().get(0);
} else {
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index bf2cc5e68fac..3337b480d6a8 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -891,8 +891,24 @@ public final class MediaRouterService extends IMediaRouterService.Stub
if (intent.getAction().equals(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED)) {
BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
synchronized (mLock) {
+ boolean wasA2dpOn = mGlobalBluetoothA2dpOn;
mActiveBluetoothDevice = btDevice;
mGlobalBluetoothA2dpOn = btDevice != null;
+ if (wasA2dpOn != mGlobalBluetoothA2dpOn) {
+ UserRecord userRecord = mUserRecords.get(mCurrentUserId);
+ if (userRecord != null) {
+ for (ClientRecord cr : userRecord.mClientRecords) {
+ // mSelectedRouteId will be null for BT and phone speaker.
+ if (cr.mSelectedRouteId == null) {
+ try {
+ cr.mClient.onGlobalA2dpChanged(mGlobalBluetoothA2dpOn);
+ } catch (RemoteException e) {
+ // Ignore exception
+ }
+ }
+ }
+ }
+ }
}
}
}
diff --git a/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
index 0bdf3f22ee9a..a0ab5eae7315 100644
--- a/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
+++ b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
@@ -28,6 +28,7 @@ import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.CollectionUtils;
import java.util.ArrayList;
@@ -38,8 +39,6 @@ import java.util.concurrent.Executor;
/**
* Helper class that watches for events that are triggered per subscription.
*/
-// TODO (b/152176562): Write tests to verify subscription changes generate corresponding
-// register/unregister calls.
public class NetworkStatsSubscriptionsMonitor extends
SubscriptionManager.OnSubscriptionsChangedListener {
@@ -207,5 +206,10 @@ public class NetworkStatsSubscriptionsMonitor extends
mLastCollapsedRatType = collapsedRatType;
mMonitor.mDelegate.onCollapsedRatTypeChanged(mSubscriberId, mLastCollapsedRatType);
}
+
+ @VisibleForTesting
+ public int getSubId() {
+ return mSubId;
+ }
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 0a8155a819b3..b64e99168445 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -92,6 +92,7 @@ import static android.service.notification.NotificationListenerService.TRIM_FULL
import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
+import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES;
@@ -1906,7 +1907,8 @@ public class NotificationManagerService extends SystemService {
mMetricsLogger = new MetricsLogger();
mRankingHandler = rankingHandler;
mConditionProviders = conditionProviders;
- mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders);
+ mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders,
+ new SysUiStatsEvent.BuilderFactory());
mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
@Override
public void onConfigChanged() {
@@ -2046,12 +2048,15 @@ public class NotificationManagerService extends SystemService {
mStripRemoteViewsSizeBytes = getContext().getResources().getInteger(
com.android.internal.R.integer.config_notificationStripRemoteViewSizeBytes);
- mMsgPkgsAllowedAsConvos = Set.of(
- getContext().getResources().getStringArray(
- com.android.internal.R.array.config_notificationMsgPkgsAllowedAsConvos));
+ mMsgPkgsAllowedAsConvos = Set.of(getStringArrayResource(
+ com.android.internal.R.array.config_notificationMsgPkgsAllowedAsConvos));
mStatsManager = statsManager;
}
+ protected String[] getStringArrayResource(int key) {
+ return getContext().getResources().getStringArray(key);
+ }
+
@Override
public void onStart() {
SnoozeHelper snoozeHelper = new SnoozeHelper(getContext(), (userId, r, muteOnReturn) -> {
@@ -2189,6 +2194,12 @@ public class NotificationManagerService extends SystemService {
ConcurrentUtils.DIRECT_EXECUTOR,
mPullAtomCallback
);
+ mStatsManager.setPullAtomCallback(
+ DND_MODE_RULE,
+ null, // use default PullAtomMetadata values
+ BackgroundThread.getExecutor(),
+ mPullAtomCallback
+ );
}
private class StatsPullAtomCallbackImpl implements StatsManager.StatsPullAtomCallback {
@@ -2198,6 +2209,7 @@ public class NotificationManagerService extends SystemService {
case PACKAGE_NOTIFICATION_PREFERENCES:
case PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES:
case PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES:
+ case DND_MODE_RULE:
return pullNotificationStates(atomTag, data);
default:
throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
@@ -2216,6 +2228,9 @@ public class NotificationManagerService extends SystemService {
case PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES:
mPreferencesHelper.pullPackageChannelGroupPreferencesStats(data);
break;
+ case DND_MODE_RULE:
+ mZenModeHelper.pullRules(data);
+ break;
}
return StatsManager.PULL_SUCCESS;
}
@@ -2729,10 +2744,7 @@ public class NotificationManagerService extends SystemService {
}
protected void maybeRegisterMessageSent(NotificationRecord r) {
- Context appContext = r.getSbn().getPackageContext(getContext());
- Notification.Builder nb =
- Notification.Builder.recoverBuilder(appContext, r.getNotification());
- if (nb.getStyle() instanceof Notification.MessagingStyle) {
+ if (r.isConversation()) {
if (r.getShortcutInfo() != null) {
if (mPreferencesHelper.setValidMessageSent(
r.getSbn().getPackageName(), r.getUid())) {
diff --git a/services/core/java/com/android/server/notification/SysUiStatsEvent.java b/services/core/java/com/android/server/notification/SysUiStatsEvent.java
index 9bc2346d4e96..90737bdeae4a 100644
--- a/services/core/java/com/android/server/notification/SysUiStatsEvent.java
+++ b/services/core/java/com/android/server/notification/SysUiStatsEvent.java
@@ -58,6 +58,11 @@ public class SysUiStatsEvent {
mBuilder.writeBoolean(value);
return this;
}
+
+ public Builder writeByteArray(byte[] value) {
+ mBuilder.writeByteArray(value);
+ return this;
+ }
}
static class BuilderFactory {
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index a490b9c99bb4..4931d3f3e95c 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -20,6 +20,10 @@ import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_DISABLED;
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ENABLED;
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_REMOVED;
import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
+import static android.service.notification.DNDModeProto.ROOT_CONFIG;
+
+import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
+import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
import android.app.AppOpsManager;
import android.app.AutomaticZenRule;
@@ -67,6 +71,7 @@ import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.StatsEvent;
import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
@@ -103,6 +108,7 @@ public class ZenModeHelper {
private final SettingsObserver mSettingsObserver;
private final AppOpsManager mAppOps;
@VisibleForTesting protected final NotificationManager mNotificationManager;
+ private final SysUiStatsEvent.BuilderFactory mStatsEventBuilderFactory;
@VisibleForTesting protected ZenModeConfig mDefaultConfig;
private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
private final ZenModeFiltering mFiltering;
@@ -130,7 +136,8 @@ public class ZenModeHelper {
private String[] mPriorityOnlyDndExemptPackages;
- public ZenModeHelper(Context context, Looper looper, ConditionProviders conditionProviders) {
+ public ZenModeHelper(Context context, Looper looper, ConditionProviders conditionProviders,
+ SysUiStatsEvent.BuilderFactory statsEventBuilderFactory) {
mContext = context;
mHandler = new H(looper);
addCallback(mMetrics);
@@ -148,6 +155,7 @@ public class ZenModeHelper {
mFiltering = new ZenModeFiltering(mContext);
mConditions = new ZenModeConditions(this, conditionProviders);
mServiceConfig = conditionProviders.getConfig();
+ mStatsEventBuilderFactory = statsEventBuilderFactory;
}
public Looper getLooper() {
@@ -1170,6 +1178,72 @@ public class ZenModeHelper {
}
}
+ /**
+ * Generate pulled atoms about do not disturb configurations.
+ */
+ public void pullRules(List<StatsEvent> events) {
+ synchronized (mConfig) {
+ final int numConfigs = mConfigs.size();
+ int id = 0;
+ for (int i = 0; i < numConfigs; i++) {
+ final int user = mConfigs.keyAt(i);
+ final ZenModeConfig config = mConfigs.valueAt(i);
+ SysUiStatsEvent.Builder data = mStatsEventBuilderFactory.newBuilder()
+ .setAtomId(DND_MODE_RULE)
+ .writeInt(user)
+ .writeBoolean(config.manualRule != null) // enabled
+ .writeBoolean(config.areChannelsBypassingDnd)
+ .writeInt(ROOT_CONFIG)
+ .writeString("") // name, empty for root config
+ .writeInt(Process.SYSTEM_UID) // system owns root config
+ .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
+ .writeByteArray(config.toZenPolicy().toProto());
+ events.add(data.build());
+ if (config.manualRule != null && config.manualRule.enabler != null) {
+ ruleToProto(user, config.manualRule, events);
+ }
+ for (ZenRule rule : config.automaticRules.values()) {
+ ruleToProto(user, rule, events);
+ }
+ }
+ }
+ }
+
+ private void ruleToProto(int user, ZenRule rule, List<StatsEvent> events) {
+ // Make the ID safe.
+ String id = rule.id == null ? "" : rule.id;
+ if (!ZenModeConfig.DEFAULT_RULE_IDS.contains(id)) {
+ id = "";
+ }
+
+ // Look for packages and enablers, enablers get priority.
+ String pkg = rule.pkg == null ? "" : rule.pkg;
+ if (rule.enabler != null) {
+ pkg = rule.enabler;
+ id = ZenModeConfig.MANUAL_RULE_ID;
+ }
+
+ // TODO: fetch the uid from the package manager
+ int uid = "android".equals(pkg) ? Process.SYSTEM_UID : 0;
+
+ SysUiStatsEvent.Builder data;
+ data = mStatsEventBuilderFactory.newBuilder()
+ .setAtomId(DND_MODE_RULE)
+ .writeInt(user)
+ .writeBoolean(rule.enabled)
+ .writeBoolean(false) // channels_bypassing unused for rules
+ .writeInt(rule.zenMode)
+ .writeString(id)
+ .writeInt(uid)
+ .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
+ byte[] policyProto = new byte[]{};
+ if (rule.zenPolicy != null) {
+ policyProto = rule.zenPolicy.toProto();
+ }
+ data.writeByteArray(policyProto);
+ events.add(data.build());
+ }
+
@VisibleForTesting
protected final class RingerModeDelegate implements AudioManagerInternal.RingerModeDelegate {
@Override
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index ba60f179e661..4b6ee71803a7 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -19,8 +19,6 @@ package com.android.server.pm;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;
-import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
-
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -111,25 +109,12 @@ public class AppsFilter {
private final boolean mSystemAppsQueryable;
private final FeatureConfig mFeatureConfig;
- private final OverlayReferenceMapper mOverlayReferenceMapper;
- private final StateProvider mStateProvider;
+ private final OverlayReferenceMapper mOverlayReferenceMapper;
private PackageParser.SigningDetails mSystemSigningDetails;
private Set<String> mProtectedBroadcasts = new ArraySet<>();
- /**
- * This structure maps uid -> uid and indicates whether access from the first should be
- * filtered to the second. It's essentially a cache of the
- * {@link #shouldFilterApplicationInternal(int, SettingBase, PackageSetting, int)} call.
- * NOTE: It can only be relied upon after the system is ready to avoid unnecessary update on
- * initial scan and is null until {@link #onSystemReady()} is called.
- */
- private volatile SparseArray<SparseBooleanArray> mShouldFilterCache;
-
- @VisibleForTesting(visibility = PRIVATE)
- AppsFilter(StateProvider stateProvider,
- FeatureConfig featureConfig,
- String[] forceQueryableWhitelist,
+ AppsFilter(FeatureConfig featureConfig, String[] forceQueryableWhitelist,
boolean systemAppsQueryable,
@Nullable OverlayReferenceMapper.Provider overlayProvider) {
mFeatureConfig = featureConfig;
@@ -137,23 +122,8 @@ public class AppsFilter {
mSystemAppsQueryable = systemAppsQueryable;
mOverlayReferenceMapper = new OverlayReferenceMapper(true /*deferRebuild*/,
overlayProvider);
- mStateProvider = stateProvider;
- }
-
- /**
- * Provides system state to AppsFilter via {@link CurrentStateCallback} after properly guarding
- * the data with the package lock.
- */
- @VisibleForTesting(visibility = PRIVATE)
- public interface StateProvider {
- void runWithState(CurrentStateCallback callback);
-
- interface CurrentStateCallback {
- void currentState(ArrayMap<String, PackageSetting> settings, int[] users);
- }
}
- @VisibleForTesting(visibility = PRIVATE)
public interface FeatureConfig {
/** Called when the system is ready and components can be queried. */
@@ -170,7 +140,6 @@ public class AppsFilter {
/**
* Turns on logging for the given appId
- *
* @param enable true if logging should be enabled, false if disabled.
*/
void enableLogging(int appId, boolean enable);
@@ -178,7 +147,6 @@ public class AppsFilter {
/**
* Initializes the package enablement state for the given package. This gives opportunity
* to do any expensive operations ahead of the actual checks.
- *
* @param removed true if adding, false if removing
*/
void updatePackageState(PackageSetting setting, boolean removed);
@@ -194,7 +162,6 @@ public class AppsFilter {
@Nullable
private SparseBooleanArray mLoggingEnabled = null;
- private AppsFilter mAppsFilter;
private FeatureConfigImpl(
PackageManagerInternal pmInternal, PackageManagerService.Injector injector) {
@@ -202,10 +169,6 @@ public class AppsFilter {
mInjector = injector;
}
- public void setAppsFilter(AppsFilter filter) {
- mAppsFilter = filter;
- }
-
@Override
public void onSystemReady() {
mFeatureEnabled = DeviceConfig.getBoolean(
@@ -272,28 +235,27 @@ public class AppsFilter {
@Override
public void onCompatChange(String packageName) {
+ updateEnabledState(mPmInternal.getPackage(packageName));
+ }
+
+ private void updateEnabledState(AndroidPackage pkg) {
final long token = Binder.clearCallingIdentity();
try {
- updateEnabledState(mPmInternal.getPackage(packageName));
- mAppsFilter.updateShouldFilterCacheForPackage(packageName);
+ // TODO(b/135203078): Do not use toAppInfo
+ final boolean enabled =
+ mInjector.getCompatibility().isChangeEnabled(
+ PackageManager.FILTER_APPLICATION_QUERY,
+ pkg.toAppInfoWithoutState());
+ if (enabled) {
+ mDisabledPackages.remove(pkg.getPackageName());
+ } else {
+ mDisabledPackages.add(pkg.getPackageName());
+ }
} finally {
Binder.restoreCallingIdentity(token);
}
}
- private void updateEnabledState(AndroidPackage pkg) {
- // TODO(b/135203078): Do not use toAppInfo
- final boolean enabled =
- mInjector.getCompatibility().isChangeEnabled(
- PackageManager.FILTER_APPLICATION_QUERY,
- pkg.toAppInfoWithoutState());
- if (enabled) {
- mDisabledPackages.remove(pkg.getPackageName());
- } else {
- mDisabledPackages.add(pkg.getPackageName());
- }
- }
-
@Override
public void updatePackageState(PackageSetting setting, boolean removed) {
final boolean enableLogging =
@@ -313,7 +275,7 @@ public class AppsFilter {
final boolean forceSystemAppsQueryable =
injector.getContext().getResources()
.getBoolean(R.bool.config_forceSystemPackagesQueryable);
- final FeatureConfigImpl featureConfig = new FeatureConfigImpl(pms, injector);
+ final FeatureConfig featureConfig = new FeatureConfigImpl(pms, injector);
final String[] forcedQueryablePackageNames;
if (forceSystemAppsQueryable) {
// all system apps already queryable, no need to read and parse individual exceptions
@@ -326,16 +288,8 @@ public class AppsFilter {
forcedQueryablePackageNames[i] = forcedQueryablePackageNames[i].intern();
}
}
- final StateProvider stateProvider = command -> {
- synchronized (injector.getLock()) {
- command.currentState(injector.getSettings().mPackages,
- injector.getUserManagerInternal().getUserIds());
- }
- };
- AppsFilter appsFilter = new AppsFilter(stateProvider, featureConfig,
- forcedQueryablePackageNames, forceSystemAppsQueryable, null);
- featureConfig.setAppsFilter(appsFilter);
- return appsFilter;
+ return new AppsFilter(featureConfig, forcedQueryablePackageNames,
+ forceSystemAppsQueryable, null);
}
public FeatureConfig getFeatureConfig() {
@@ -458,54 +412,28 @@ public class AppsFilter {
* visibility of the caller from the target.
*
* @param recipientUid the uid gaining visibility of the {@code visibleUid}.
- * @param visibleUid the uid becoming visible to the {@recipientUid}
+ * @param visibleUid the uid becoming visible to the {@recipientUid}
*/
public void grantImplicitAccess(int recipientUid, int visibleUid) {
- if (recipientUid != visibleUid) {
- if (mImplicitlyQueryable.add(recipientUid, visibleUid) && DEBUG_LOGGING) {
- Slog.i(TAG, "implicit access granted: " + recipientUid + " -> " + visibleUid);
- }
- if (mShouldFilterCache != null) {
- // update the cache in a one-off manner since we've got all the information we need.
- SparseBooleanArray visibleUids = mShouldFilterCache.get(recipientUid);
- if (visibleUids == null) {
- visibleUids = new SparseBooleanArray();
- mShouldFilterCache.put(recipientUid, visibleUids);
- }
- visibleUids.put(visibleUid, false);
- }
+ if (recipientUid != visibleUid
+ && mImplicitlyQueryable.add(recipientUid, visibleUid) && DEBUG_LOGGING) {
+ Slog.i(TAG, "implicit access granted: " + recipientUid + " -> " + visibleUid);
}
}
public void onSystemReady() {
- mShouldFilterCache = new SparseArray<>();
mFeatureConfig.onSystemReady();
mOverlayReferenceMapper.rebuildIfDeferred();
- updateEntireShouldFilterCache();
}
/**
* Adds a package that should be considered when filtering visibility between apps.
*
- * @param newPkgSetting the new setting being added
+ * @param newPkgSetting the new setting being added
+ * @param existingSettings all other settings currently on the device.
*/
- public void addPackage(PackageSetting newPkgSetting) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "filter.addPackage");
- try {
- mStateProvider.runWithState((settings, users) -> {
- addPackageInternal(newPkgSetting, settings, users);
- if (mShouldFilterCache != null) {
- updateShouldFilterCacheForPackage(
- null, newPkgSetting, settings, users, settings.size());
- } // else, rebuild entire cache when system is ready
- });
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- }
-
- private void addPackageInternal(PackageSetting newPkgSetting,
- ArrayMap<String, PackageSetting> existingSettings, int[] allUsers) {
+ public void addPackage(PackageSetting newPkgSetting,
+ ArrayMap<String, PackageSetting> existingSettings) {
if (Objects.equals("android", newPkgSetting.name)) {
// let's set aside the framework signatures
mSystemSigningDetails = newPkgSetting.signatures.mSigningDetails;
@@ -518,151 +446,79 @@ public class AppsFilter {
}
}
- final AndroidPackage newPkg = newPkgSetting.pkg;
- if (newPkg == null) {
- // nothing to add
- return;
- }
-
- if (!newPkg.getProtectedBroadcasts().isEmpty()) {
- mProtectedBroadcasts.addAll(newPkg.getProtectedBroadcasts());
- recomputeComponentVisibility(existingSettings, newPkg.getPackageName());
- }
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "filter.addPackage");
+ try {
+ final AndroidPackage newPkg = newPkgSetting.pkg;
+ if (newPkg == null) {
+ // nothing to add
+ return;
+ }
- final boolean newIsForceQueryable =
- mForceQueryable.contains(newPkgSetting.appId)
- /* shared user that is already force queryable */
- || newPkg.isForceQueryable()
- || newPkgSetting.forceQueryableOverride
- || (newPkgSetting.isSystem() && (mSystemAppsQueryable
- || ArrayUtils.contains(mForceQueryableByDevicePackageNames,
- newPkg.getPackageName())));
- if (newIsForceQueryable
- || (mSystemSigningDetails != null
- && isSystemSigned(mSystemSigningDetails, newPkgSetting))) {
- mForceQueryable.add(newPkgSetting.appId);
- }
+ if (!newPkg.getProtectedBroadcasts().isEmpty()) {
+ mProtectedBroadcasts.addAll(newPkg.getProtectedBroadcasts());
+ recomputeComponentVisibility(existingSettings, newPkg.getPackageName());
+ }
- for (int i = existingSettings.size() - 1; i >= 0; i--) {
- final PackageSetting existingSetting = existingSettings.valueAt(i);
- if (existingSetting.appId == newPkgSetting.appId || existingSetting.pkg == null) {
- continue;
+ final boolean newIsForceQueryable =
+ mForceQueryable.contains(newPkgSetting.appId)
+ /* shared user that is already force queryable */
+ || newPkg.isForceQueryable()
+ || newPkgSetting.forceQueryableOverride
+ || (newPkgSetting.isSystem() && (mSystemAppsQueryable
+ || ArrayUtils.contains(mForceQueryableByDevicePackageNames,
+ newPkg.getPackageName())));
+ if (newIsForceQueryable
+ || (mSystemSigningDetails != null
+ && isSystemSigned(mSystemSigningDetails, newPkgSetting))) {
+ mForceQueryable.add(newPkgSetting.appId);
}
- final AndroidPackage existingPkg = existingSetting.pkg;
- // let's evaluate the ability of already added packages to see this new package
- if (!newIsForceQueryable) {
- if (canQueryViaComponents(existingPkg, newPkg, mProtectedBroadcasts)) {
- mQueriesViaComponent.add(existingSetting.appId, newPkgSetting.appId);
+
+ for (int i = existingSettings.size() - 1; i >= 0; i--) {
+ final PackageSetting existingSetting = existingSettings.valueAt(i);
+ if (existingSetting.appId == newPkgSetting.appId || existingSetting.pkg == null) {
+ continue;
}
- if (canQueryViaPackage(existingPkg, newPkg)
- || canQueryAsInstaller(existingSetting, newPkg)) {
- mQueriesViaPackage.add(existingSetting.appId, newPkgSetting.appId);
+ final AndroidPackage existingPkg = existingSetting.pkg;
+ // let's evaluate the ability of already added packages to see this new package
+ if (!newIsForceQueryable) {
+ if (canQueryViaComponents(existingPkg, newPkg, mProtectedBroadcasts)) {
+ mQueriesViaComponent.add(existingSetting.appId, newPkgSetting.appId);
+ }
+ if (canQueryViaPackage(existingPkg, newPkg)
+ || canQueryAsInstaller(existingSetting, newPkg)) {
+ mQueriesViaPackage.add(existingSetting.appId, newPkgSetting.appId);
+ }
}
- }
- // now we'll evaluate our new package's ability to see existing packages
- if (!mForceQueryable.contains(existingSetting.appId)) {
- if (canQueryViaComponents(newPkg, existingPkg, mProtectedBroadcasts)) {
- mQueriesViaComponent.add(newPkgSetting.appId, existingSetting.appId);
+ // now we'll evaluate our new package's ability to see existing packages
+ if (!mForceQueryable.contains(existingSetting.appId)) {
+ if (canQueryViaComponents(newPkg, existingPkg, mProtectedBroadcasts)) {
+ mQueriesViaComponent.add(newPkgSetting.appId, existingSetting.appId);
+ }
+ if (canQueryViaPackage(newPkg, existingPkg)
+ || canQueryAsInstaller(newPkgSetting, existingPkg)) {
+ mQueriesViaPackage.add(newPkgSetting.appId, existingSetting.appId);
+ }
}
- if (canQueryViaPackage(newPkg, existingPkg)
- || canQueryAsInstaller(newPkgSetting, existingPkg)) {
+ // if either package instruments the other, mark both as visible to one another
+ if (pkgInstruments(newPkgSetting, existingSetting)
+ || pkgInstruments(existingSetting, newPkgSetting)) {
mQueriesViaPackage.add(newPkgSetting.appId, existingSetting.appId);
+ mQueriesViaPackage.add(existingSetting.appId, newPkgSetting.appId);
}
}
- // if either package instruments the other, mark both as visible to one another
- if (pkgInstruments(newPkgSetting, existingSetting)
- || pkgInstruments(existingSetting, newPkgSetting)) {
- mQueriesViaPackage.add(newPkgSetting.appId, existingSetting.appId);
- mQueriesViaPackage.add(existingSetting.appId, newPkgSetting.appId);
- }
- }
-
- int existingSize = existingSettings.size();
- ArrayMap<String, AndroidPackage> existingPkgs = new ArrayMap<>(existingSize);
- for (int index = 0; index < existingSize; index++) {
- PackageSetting pkgSetting = existingSettings.valueAt(index);
- if (pkgSetting.pkg != null) {
- existingPkgs.put(pkgSetting.name, pkgSetting.pkg);
- }
- }
- mOverlayReferenceMapper.addPkg(newPkgSetting.pkg, existingPkgs);
- mFeatureConfig.updatePackageState(newPkgSetting, false /*removed*/);
- }
-
- private void removeAppIdFromVisibilityCache(int appId) {
- if (mShouldFilterCache == null) {
- return;
- }
- for (int i = mShouldFilterCache.size() - 1; i >= 0; i--) {
- if (UserHandle.getAppId(mShouldFilterCache.keyAt(i)) == appId) {
- mShouldFilterCache.removeAt(i);
- continue;
- }
- SparseBooleanArray targetSparseArray = mShouldFilterCache.valueAt(i);
- for (int j = targetSparseArray.size() - 1; j >= 0; j--) {
- if (UserHandle.getAppId(targetSparseArray.keyAt(j)) == appId) {
- targetSparseArray.removeAt(j);
- }
- }
- }
- }
-
- private void updateEntireShouldFilterCache() {
- mStateProvider.runWithState((settings, users) -> {
- mShouldFilterCache.clear();
- for (int i = settings.size() - 1; i >= 0; i--) {
- updateShouldFilterCacheForPackage(
- null /*skipPackage*/, settings.valueAt(i), settings, users, i);
- }
- });
- }
-
- public void onUsersChanged() {
- if (mShouldFilterCache != null) {
- updateEntireShouldFilterCache();
- }
- }
- private void updateShouldFilterCacheForPackage(String packageName) {
- mStateProvider.runWithState((settings, users) -> {
- updateShouldFilterCacheForPackage(null /* skipPackage */, settings.get(packageName),
- settings, users, settings.size() /*maxIndex*/);
- });
-
- }
-
- private void updateShouldFilterCacheForPackage(@Nullable String skipPackageName,
- PackageSetting subjectSetting, ArrayMap<String, PackageSetting> allSettings,
- int[] allUsers, int maxIndex) {
- for (int i = Math.min(maxIndex, allSettings.size() - 1); i >= 0; i--) {
- PackageSetting otherSetting = allSettings.valueAt(i);
- if (subjectSetting.appId == otherSetting.appId) {
- continue;
- }
- //noinspection StringEquality
- if (subjectSetting.name == skipPackageName || otherSetting.name == skipPackageName) {
- continue;
- }
- for (int su = 0; su < allUsers.length; su++) {
- int subjectUser = allUsers[su];
- for (int ou = su; ou < allUsers.length; ou++) {
- int otherUser = allUsers[ou];
- int subjectUid = UserHandle.getUid(subjectUser, subjectSetting.appId);
- if (!mShouldFilterCache.contains(subjectUid)) {
- mShouldFilterCache.put(subjectUid, new SparseBooleanArray());
- }
- int otherUid = UserHandle.getUid(otherUser, otherSetting.appId);
- if (!mShouldFilterCache.contains(otherUid)) {
- mShouldFilterCache.put(otherUid, new SparseBooleanArray());
- }
- mShouldFilterCache.get(subjectUid).put(otherUid,
- shouldFilterApplicationInternal(
- subjectUid, subjectSetting, otherSetting, otherUser));
- mShouldFilterCache.get(otherUid).put(subjectUid,
- shouldFilterApplicationInternal(
- otherUid, otherSetting, subjectSetting, subjectUser));
+ int existingSize = existingSettings.size();
+ ArrayMap<String, AndroidPackage> existingPkgs = new ArrayMap<>(existingSize);
+ for (int index = 0; index < existingSize; index++) {
+ PackageSetting pkgSetting = existingSettings.valueAt(index);
+ if (pkgSetting.pkg != null) {
+ existingPkgs.put(pkgSetting.name, pkgSetting.pkg);
}
}
+ mOverlayReferenceMapper.addPkg(newPkgSetting.pkg, existingPkgs);
+ mFeatureConfig.updatePackageState(newPkgSetting, false /*removed*/);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
@@ -713,7 +569,6 @@ public class AppsFilter {
}
}
}
-
/**
* Fetches all app Ids that a given setting is currently visible to, per provided user. This
* only includes UIDs >= {@link Process#FIRST_APPLICATION_UID} as all other UIDs can already see
@@ -722,11 +577,11 @@ public class AppsFilter {
* If the setting is visible to all UIDs, null is returned. If an app is not visible to any
* applications, the int array will be empty.
*
- * @param users the set of users that should be evaluated for this calculation
+ * @param users the set of users that should be evaluated for this calculation
* @param existingSettings the set of all package settings that currently exist on device
* @return a SparseArray mapping userIds to a sorted int array of appIds that may view the
- * provided setting or null if the app is visible to all and no whitelist should be
- * applied.
+ * provided setting or null if the app is visible to all and no whitelist should be
+ * applied.
*/
@Nullable
public SparseArray<int[]> getVisibilityWhitelist(PackageSetting setting, int[] users,
@@ -771,60 +626,52 @@ public class AppsFilter {
/**
* Removes a package for consideration when filtering visibility between apps.
*
- * @param setting the setting of the package being removed.
+ * @param setting the setting of the package being removed.
+ * @param allUsers array of all current users on device.
*/
- public void removePackage(PackageSetting setting) {
- removeAppIdFromVisibilityCache(setting.appId);
- mStateProvider.runWithState((settings, users) -> {
- for (int u = 0; u < users.length; u++) {
- final int userId = users[u];
- final int removingUid = UserHandle.getUid(userId, setting.appId);
- mImplicitlyQueryable.remove(removingUid);
- for (int i = mImplicitlyQueryable.size() - 1; i >= 0; i--) {
- mImplicitlyQueryable.remove(mImplicitlyQueryable.keyAt(i), removingUid);
- }
- }
-
- mQueriesViaComponent.remove(setting.appId);
- for (int i = mQueriesViaComponent.size() - 1; i >= 0; i--) {
- mQueriesViaComponent.remove(mQueriesViaComponent.keyAt(i), setting.appId);
- }
- mQueriesViaPackage.remove(setting.appId);
- for (int i = mQueriesViaPackage.size() - 1; i >= 0; i--) {
- mQueriesViaPackage.remove(mQueriesViaPackage.keyAt(i), setting.appId);
- }
-
- // re-add other shared user members to re-establish visibility between them and other
- // packages
- if (setting.sharedUser != null) {
- for (int i = setting.sharedUser.packages.size() - 1; i >= 0; i--) {
- if (setting.sharedUser.packages.valueAt(i) == setting) {
- continue;
- }
- addPackageInternal(
- setting.sharedUser.packages.valueAt(i), settings, users);
- }
- }
+ public void removePackage(PackageSetting setting, int[] allUsers,
+ ArrayMap<String, PackageSetting> existingSettings) {
+ mForceQueryable.remove(setting.appId);
- if (!setting.pkg.getProtectedBroadcasts().isEmpty()) {
- final String removingPackageName = setting.pkg.getPackageName();
- mProtectedBroadcasts.clear();
- mProtectedBroadcasts.addAll(
- collectProtectedBroadcasts(settings, removingPackageName));
- recomputeComponentVisibility(settings, removingPackageName);
+ for (int u = 0; u < allUsers.length; u++) {
+ final int userId = allUsers[u];
+ final int removingUid = UserHandle.getUid(userId, setting.appId);
+ mImplicitlyQueryable.remove(removingUid);
+ for (int i = mImplicitlyQueryable.size() - 1; i >= 0; i--) {
+ mImplicitlyQueryable.remove(mImplicitlyQueryable.keyAt(i), removingUid);
}
+ }
- mOverlayReferenceMapper.removePkg(setting.name);
- mFeatureConfig.updatePackageState(setting, true /*removed*/);
+ mQueriesViaComponent.remove(setting.appId);
+ for (int i = mQueriesViaComponent.size() - 1; i >= 0; i--) {
+ mQueriesViaComponent.remove(mQueriesViaComponent.keyAt(i), setting.appId);
+ }
+ mQueriesViaPackage.remove(setting.appId);
+ for (int i = mQueriesViaPackage.size() - 1; i >= 0; i--) {
+ mQueriesViaPackage.remove(mQueriesViaPackage.keyAt(i), setting.appId);
+ }
- if (mShouldFilterCache != null) {
- updateShouldFilterCacheForPackage(
- setting.name, setting, settings, users, settings.size());
+ // re-add other shared user members to re-establish visibility between them and other
+ // packages
+ if (setting.sharedUser != null) {
+ for (int i = setting.sharedUser.packages.size() - 1; i >= 0; i--) {
+ if (setting.sharedUser.packages.valueAt(i) == setting) {
+ continue;
+ }
+ addPackage(setting.sharedUser.packages.valueAt(i), existingSettings);
}
- });
- mForceQueryable.remove(setting.appId);
+ }
+ if (!setting.pkg.getProtectedBroadcasts().isEmpty()) {
+ final String removingPackageName = setting.pkg.getPackageName();
+ mProtectedBroadcasts.clear();
+ mProtectedBroadcasts.addAll(
+ collectProtectedBroadcasts(existingSettings, removingPackageName));
+ recomputeComponentVisibility(existingSettings, removingPackageName);
+ }
+ mOverlayReferenceMapper.removePkg(setting.name);
+ mFeatureConfig.updatePackageState(setting, true /*removed*/);
}
/**
@@ -841,32 +688,11 @@ public class AppsFilter {
PackageSetting targetPkgSetting, int userId) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "shouldFilterApplication");
try {
- if (callingUid < Process.FIRST_APPLICATION_UID
- || UserHandle.getAppId(callingUid) == targetPkgSetting.appId) {
+
+ if (!shouldFilterApplicationInternal(
+ callingUid, callingSetting, targetPkgSetting, userId)) {
return false;
}
- if (mShouldFilterCache != null) { // use cache
- SparseBooleanArray shouldFilterTargets = mShouldFilterCache.get(callingUid);
- final int targetUid = UserHandle.getUid(userId, targetPkgSetting.appId);
- if (shouldFilterTargets == null) {
- Slog.wtf(TAG, "Encountered calling uid with no cached rules: " + callingUid);
- return true;
- }
- int indexOfTargetUid = shouldFilterTargets.indexOfKey(targetUid);
- if (indexOfTargetUid < 0) {
- Slog.w(TAG, "Encountered calling -> target with no cached rules: "
- + callingUid + " -> " + targetUid);
- return true;
- }
- if (!shouldFilterTargets.valueAt(indexOfTargetUid)) {
- return false;
- }
- } else {
- if (!shouldFilterApplicationInternal(
- callingUid, callingSetting, targetPkgSetting, userId)) {
- return false;
- }
- }
if (DEBUG_LOGGING || mFeatureConfig.isLoggingEnabled(UserHandle.getAppId(callingUid))) {
log(callingSetting, targetPkgSetting, "BLOCKED");
}
@@ -877,7 +703,7 @@ public class AppsFilter {
}
private boolean shouldFilterApplicationInternal(int callingUid, SettingBase callingSetting,
- PackageSetting targetPkgSetting, int targetUserId) {
+ PackageSetting targetPkgSetting, int userId) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "shouldFilterApplicationInternal");
try {
final boolean featureEnabled = mFeatureConfig.isGloballyEnabled();
@@ -887,6 +713,12 @@ public class AppsFilter {
}
return false;
}
+ if (callingUid < Process.FIRST_APPLICATION_UID) {
+ if (DEBUG_LOGGING) {
+ Slog.d(TAG, "filtering skipped; " + callingUid + " is system");
+ }
+ return false;
+ }
if (callingSetting == null) {
Slog.wtf(TAG, "No setting found for non system uid " + callingUid);
return true;
@@ -895,14 +727,8 @@ public class AppsFilter {
final ArraySet<PackageSetting> callingSharedPkgSettings;
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "callingSetting instanceof");
if (callingSetting instanceof PackageSetting) {
- if (((PackageSetting) callingSetting).sharedUser == null) {
- callingPkgSetting = (PackageSetting) callingSetting;
- callingSharedPkgSettings = null;
- } else {
- callingPkgSetting = null;
- callingSharedPkgSettings =
- ((PackageSetting) callingSetting).sharedUser.packages;
- }
+ callingPkgSetting = (PackageSetting) callingSetting;
+ callingSharedPkgSettings = null;
} else {
callingPkgSetting = null;
callingSharedPkgSettings = ((SharedUserSetting) callingSetting).packages;
@@ -960,17 +786,13 @@ public class AppsFilter {
}
try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "requestsQueryAllPackages");
- if (callingPkgSetting != null) {
- if (requestsQueryAllPackages(callingPkgSetting)) {
- return false;
- }
- } else {
- for (int i = callingSharedPkgSettings.size() - 1; i >= 0; i--) {
- if (requestsQueryAllPackages(callingSharedPkgSettings.valueAt(i))) {
- return false;
- }
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "hasPermission");
+ if (callingSetting.getPermissionsState().hasPermission(
+ Manifest.permission.QUERY_ALL_PACKAGES, UserHandle.getUserId(callingUid))) {
+ if (DEBUG_LOGGING) {
+ log(callingSetting, targetPkgSetting, "has query-all permission");
}
+ return false;
}
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -1011,7 +833,7 @@ public class AppsFilter {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mImplicitlyQueryable");
- final int targetUid = UserHandle.getUid(targetUserId, targetAppId);
+ final int targetUid = UserHandle.getUid(userId, targetAppId);
if (mImplicitlyQueryable.contains(callingUid, targetUid)) {
if (DEBUG_LOGGING) {
log(callingSetting, targetPkgSetting, "implicitly queryable for user");
@@ -1049,20 +871,13 @@ public class AppsFilter {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
+
return true;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
-
- private static boolean requestsQueryAllPackages(PackageSetting pkgSetting) {
- // we're not guaranteed to have permissions yet analyzed at package add, so we inspect the
- // package directly
- return pkgSetting.pkg.getRequestedPermissions().contains(
- Manifest.permission.QUERY_ALL_PACKAGES);
- }
-
/** Returns {@code true} if the source package instruments the target package. */
private static boolean pkgInstruments(PackageSetting source, PackageSetting target) {
try {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index fd37f041450e..ae8b3a0e9acc 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -11067,7 +11067,7 @@ public class PackageManagerService extends IPackageManager.Stub
} else {
pkgSetting = result.pkgSetting;
if (originalPkgSetting != null) {
- mSettings.addRenamedPackageLPw(parsedPackage.getPackageName(),
+ mSettings.addRenamedPackageLPw(parsedPackage.getRealPackage(),
originalPkgSetting.name);
mTransferredPackages.add(originalPkgSetting.name);
}
@@ -11176,7 +11176,7 @@ public class PackageManagerService extends IPackageManager.Stub
@GuardedBy("mLock")
private @Nullable PackageSetting getOriginalPackageLocked(@NonNull AndroidPackage pkg,
@Nullable String renamedPkgName) {
- if (!isPackageRenamed(pkg, renamedPkgName)) {
+ if (isPackageRenamed(pkg, renamedPkgName)) {
return null;
}
for (int i = ArrayUtils.size(pkg.getOriginalPackages()) - 1; i >= 0; --i) {
@@ -12362,7 +12362,7 @@ public class PackageManagerService extends IPackageManager.Stub
ksms.addScannedPackageLPw(pkg);
mComponentResolver.addAllComponents(pkg, chatty);
- mAppsFilter.addPackage(pkgSetting);
+ mAppsFilter.addPackage(pkgSetting, mSettings.mPackages);
// Don't allow ephemeral applications to define new permissions groups.
if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
@@ -12536,6 +12536,8 @@ public class PackageManagerService extends IPackageManager.Stub
void cleanPackageDataStructuresLILPw(AndroidPackage pkg, boolean chatty) {
mComponentResolver.removeAllComponents(pkg, chatty);
+ mAppsFilter.removePackage(getPackageSetting(pkg.getPackageName()),
+ mInjector.getUserManagerInternal().getUserIds(), mSettings.mPackages);
mPermissionManager.removeAllPermissions(pkg, chatty);
final int instrumentationSize = ArrayUtils.size(pkg.getInstrumentations());
@@ -13516,6 +13518,17 @@ public class PackageManagerService extends IPackageManager.Stub
removeSuspensionsBySuspendingPackage(allPackages, suspendingPackage::equals, userId);
}
+ boolean isSuspendingAnyPackages(String suspendingPackage, int userId) {
+ synchronized (mLock) {
+ for (final PackageSetting ps : mSettings.mPackages.values()) {
+ if (ps.isSuspendedBy(suspendingPackage, userId)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
/**
* Removes any suspensions on given packages that were added by packages that pass the given
* predicate.
@@ -14251,7 +14264,7 @@ public class PackageManagerService extends IPackageManager.Stub
// Okay!
targetPackageSetting.setInstallerPackageName(installerPackageName);
mSettings.addInstallerPackageNames(targetPackageSetting.installSource);
- mAppsFilter.addPackage(targetPackageSetting);
+ mAppsFilter.addPackage(targetPackageSetting, mSettings.mPackages);
scheduleWriteSettingsLocked();
}
}
@@ -18704,7 +18717,6 @@ public class PackageManagerService extends IPackageManager.Stub
clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL, true);
clearDefaultBrowserIfNeeded(packageName);
mSettings.mKeySetManagerService.removeAppKeySetDataLPw(packageName);
- mAppsFilter.removePackage(getPackageSetting(packageName));
removedAppId = mSettings.removePackageLPw(packageName);
if (outInfo != null) {
outInfo.removedAppId = removedAppId;
@@ -23462,7 +23474,6 @@ public class PackageManagerService extends IPackageManager.Stub
scheduleWritePackageRestrictionsLocked(userId);
scheduleWritePackageListLocked(userId);
primeDomainVerificationsLPw(userId);
- mAppsFilter.onUsersChanged();
}
}
@@ -23913,7 +23924,6 @@ public class PackageManagerService extends IPackageManager.Stub
callingUid);
}
-
@Override
public boolean isPlatformSigned(String packageName) {
PackageSetting packageSetting = mSettings.mPackages.get(packageName);
@@ -25018,6 +25028,16 @@ public class PackageManagerService extends IPackageManager.Stub
mSettings.writePackageRestrictionsLPr(userId);
}
}
+
+ @Override
+ public void unsuspendForSuspendingPackage(final String packageName, int affectedUser) {
+ PackageManagerService.this.unsuspendForSuspendingPackage(packageName, affectedUser);
+ }
+
+ @Override
+ public boolean isSuspendingAnyPackages(String suspendingPackage, int userId) {
+ return PackageManagerService.this.isSuspendingAnyPackages(suspendingPackage, userId);
+ }
}
@GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 00a5fe766593..834303cc14c6 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -422,6 +422,11 @@ public abstract class PackageSettingBase extends SettingBase {
return readUserState(userId).suspended;
}
+ boolean isSuspendedBy(String suspendingPackage, int userId) {
+ final PackageUserState state = readUserState(userId);
+ return state.suspendParams != null && state.suspendParams.containsKey(suspendingPackage);
+ }
+
void addOrUpdateSuspension(String suspendingPackage, SuspendDialogInfo dialogInfo,
PersistableBundle appExtras, PersistableBundle launcherExtras, int userId) {
final PackageUserState existingUserState = modifyUserState(userId);
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 2614076e9b6c..eb51cc3cd25c 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -69,6 +69,9 @@
"exclude-annotation": "androidx.test.filters.Suppress"
}
]
+ },
+ {
+ "name": "PackageManagerServiceHostTests"
}
],
"postsubmit": [
diff --git a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
index 58732b4c778f..492b84a0a84b 100644
--- a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
+++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
@@ -96,6 +96,8 @@ import java.util.Set;
class UserSystemPackageInstaller {
private static final String TAG = "UserManagerService";
+ private static final boolean DEBUG = false;
+
/**
* System Property whether to only install system packages on a user if they're whitelisted for
* that user type. These are flags and can be freely combined.
@@ -328,12 +330,15 @@ class UserSystemPackageInstaller {
// Check whether all whitelisted packages are indeed on the system.
final String notPresentFmt = "%s is whitelisted but not present.";
final String notSystemFmt = "%s is whitelisted and present but not a system package.";
+ final String overlayPackageFmt = "%s is whitelisted but it's auto-generated RRO package.";
for (String pkgName : allWhitelistedPackages) {
final AndroidPackage pkg = pmInt.getPackage(pkgName);
if (pkg == null) {
warnings.add(String.format(notPresentFmt, pkgName));
} else if (!pkg.isSystem()) {
warnings.add(String.format(notSystemFmt, pkgName));
+ } else if (isAutoGeneratedRRO(pkg)) {
+ warnings.add(String.format(overlayPackageFmt, pkgName));
}
}
return warnings;
@@ -358,7 +363,8 @@ class UserSystemPackageInstaller {
pmInt.forEachPackage(pkg -> {
if (!pkg.isSystem()) return;
final String pkgName = pkg.getManifestPackageName();
- if (!allWhitelistedPackages.contains(pkgName)) {
+ if (!allWhitelistedPackages.contains(pkgName)
+ && !isAutoGeneratedRRO(pmInt.getPackage(pkgName))) {
errors.add(String.format(logMessageFmt, pkgName));
}
});
@@ -407,6 +413,23 @@ class UserSystemPackageInstaller {
return isImplicitWhitelistSystemMode(getWhitelistMode());
}
+ /**
+ * Whether package name has auto-generated RRO package name suffix.
+ */
+ @VisibleForTesting
+ static boolean hasAutoGeneratedRROSuffix(String name) {
+ return name.endsWith(".auto_generated_rro_product__")
+ || name.endsWith(".auto_generated_rro_vendor__");
+ }
+
+ /**
+ * Whether the package is auto-generated RRO package.
+ */
+ private static boolean isAutoGeneratedRRO(AndroidPackage pkg) {
+ return pkg.isOverlay()
+ && (hasAutoGeneratedRROSuffix(pkg.getManifestPackageName()));
+ }
+
/** See {@link #isEnforceMode()}. */
private static boolean isEnforceMode(int whitelistMode) {
return (whitelistMode & USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE) != 0;
@@ -518,7 +541,18 @@ class UserSystemPackageInstaller {
static boolean shouldInstallPackage(AndroidPackage sysPkg,
@NonNull ArrayMap<String, Long> userTypeWhitelist,
@NonNull Set<String> userWhitelist, boolean implicitlyWhitelist) {
- final String pkgName = sysPkg.getManifestPackageName();
+ final String pkgName;
+ if (isAutoGeneratedRRO(sysPkg)) {
+ pkgName = sysPkg.getOverlayTarget();
+ if (DEBUG) {
+ Slog.i(TAG, "shouldInstallPackage(): " + sysPkg.getManifestPackageName()
+ + " is auto-generated RRO package, will look for overlay system package: "
+ + pkgName);
+ }
+ } else {
+ pkgName = sysPkg.getManifestPackageName();
+ }
+
return (implicitlyWhitelist && !userTypeWhitelist.containsKey(pkgName))
|| userWhitelist.contains(pkgName);
}
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 0b95be15f157..8f0fd607f928 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -616,6 +616,7 @@ public class Notifier {
} catch (RemoteException ex) {
// Ignore
}
+ FrameworkStatsLog.write(FrameworkStatsLog.DISPLAY_WAKE_REPORTED, reason);
}
/**
diff --git a/services/core/java/com/android/server/slice/SliceClientPermissions.java b/services/core/java/com/android/server/slice/SliceClientPermissions.java
index ab94a59c4d9c..e241205cfb99 100644
--- a/services/core/java/com/android/server/slice/SliceClientPermissions.java
+++ b/services/core/java/com/android/server/slice/SliceClientPermissions.java
@@ -160,6 +160,9 @@ public class SliceClientPermissions implements DirtyTracker, Persistable {
// Get to the beginning of the provider.
while (parser.getEventType() != XmlPullParser.START_TAG
|| !TAG_CLIENT.equals(parser.getName())) {
+ if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
+ throw new XmlPullParserException("Can't find client tag in xml");
+ }
parser.next();
}
int depth = parser.getDepth();
@@ -173,6 +176,9 @@ public class SliceClientPermissions implements DirtyTracker, Persistable {
parser.next();
while (parser.getDepth() > depth) {
+ if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
+ return provider;
+ }
if (parser.getEventType() == XmlPullParser.START_TAG
&& TAG_AUTHORITY.equals(parser.getName())) {
try {
diff --git a/services/core/java/com/android/server/slice/SlicePermissionManager.java b/services/core/java/com/android/server/slice/SlicePermissionManager.java
index 1d1c28f5f9b7..343d2e353abb 100644
--- a/services/core/java/com/android/server/slice/SlicePermissionManager.java
+++ b/services/core/java/com/android/server/slice/SlicePermissionManager.java
@@ -130,7 +130,7 @@ public class SlicePermissionManager implements DirtyTracker {
}
SliceClientPermissions client = getClient(pkgUser);
client.clear();
- mHandler.obtainMessage(H.MSG_REMOVE, pkgUser);
+ mHandler.obtainMessage(H.MSG_REMOVE, pkgUser).sendToTarget();
}
public String[] getAllPackagesGranted(String pkg) {
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
index 2f963b7e6b35..522e5e189232 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
@@ -20,8 +20,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback;
import android.hardware.soundtrigger.V2_2.ISoundTriggerHw;
-import android.media.audio.common.AudioConfig;
-import android.media.audio.common.AudioOffloadInfo;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
import android.media.soundtrigger_middleware.ModelParameterRange;
@@ -30,6 +28,7 @@ import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
import android.media.soundtrigger_middleware.PhraseSoundModel;
import android.media.soundtrigger_middleware.RecognitionConfig;
import android.media.soundtrigger_middleware.RecognitionEvent;
+import android.media.soundtrigger_middleware.RecognitionStatus;
import android.media.soundtrigger_middleware.SoundModel;
import android.media.soundtrigger_middleware.SoundModelType;
import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
@@ -579,7 +578,7 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
@NonNull ISoundTriggerHwCallback.RecognitionEvent recognitionEvent,
int cookie) {
synchronized (SoundTriggerModule.this) {
- android.media.soundtrigger_middleware.RecognitionEvent aidlEvent =
+ RecognitionEvent aidlEvent =
ConversionUtil.hidl2aidlRecognitionEvent(recognitionEvent);
aidlEvent.captureSession = mSession.mSessionHandle;
try {
@@ -589,8 +588,7 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
// In any case, client callbacks are considered best effort.
Log.e(TAG, "Client callback execption.", e);
}
- if (aidlEvent.status
- != android.media.soundtrigger_middleware.RecognitionStatus.FORCED) {
+ if (aidlEvent.status != RecognitionStatus.FORCED) {
setState(ModelState.LOADED);
}
}
@@ -601,7 +599,7 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
@NonNull ISoundTriggerHwCallback.PhraseRecognitionEvent phraseRecognitionEvent,
int cookie) {
synchronized (SoundTriggerModule.this) {
- android.media.soundtrigger_middleware.PhraseRecognitionEvent aidlEvent =
+ PhraseRecognitionEvent aidlEvent =
ConversionUtil.hidl2aidlPhraseRecognitionEvent(phraseRecognitionEvent);
aidlEvent.common.captureSession = mSession.mSessionHandle;
try {
@@ -611,8 +609,7 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
// In any case, client callbacks are considered best effort.
Log.e(TAG, "Client callback execption.", e);
}
- if (aidlEvent.common.status
- != android.media.soundtrigger_middleware.RecognitionStatus.FORCED) {
+ if (aidlEvent.common.status != RecognitionStatus.FORCED) {
setState(ModelState.LOADED);
}
}
@@ -623,15 +620,13 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
/**
* Creates a default-initialized recognition event.
*
- * Object fields are default constructed.
- * Array fields are initialized to 0 length.
+ * Non-nullable object fields are default constructed.
+ * Non-nullable array fields are initialized to 0 length.
*
* @return The event.
*/
private static RecognitionEvent newEmptyRecognitionEvent() {
RecognitionEvent result = new RecognitionEvent();
- result.audioConfig = new AudioConfig();
- result.audioConfig.offloadInfo = new AudioOffloadInfo();
result.data = new byte[0];
return result;
}
@@ -639,8 +634,8 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
/**
* Creates a default-initialized phrase recognition event.
*
- * Object fields are default constructed.
- * Array fields are initialized to 0 length.
+ * Non-nullable object fields are default constructed.
+ * Non-nullable array fields are initialized to 0 length.
*
* @return The event.
*/
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index c2bae1a8962b..7fe21e32cbaf 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -209,14 +209,6 @@ public class StatsPullAtomService extends SystemService {
// Random seed stable for StatsPullAtomService life cycle - can be used for stable sampling
private static final int RANDOM_SEED = new Random().nextInt();
- /**
- * Lowest available uid for apps.
- *
- * <p>Used to quickly discard memory snapshots of the zygote forks from native process
- * measurements.
- */
- private static final int MIN_APP_UID = 10_000;
-
private static final int DIMENSION_KEY_SIZE_HARD_LIMIT = 800;
private static final int DIMENSION_KEY_SIZE_SOFT_LIMIT = 500;
private static final long APP_OPS_SAMPLING_INITIALIZATION_DELAY_MILLIS = 45000;
@@ -1819,10 +1811,6 @@ public class StatsPullAtomService extends SystemService {
return StatsManager.PULL_SUCCESS;
}
- private static boolean isAppUid(int uid) {
- return uid >= MIN_APP_UID;
- }
-
private void registerProcessMemoryHighWaterMark() {
int tagId = FrameworkStatsLog.PROCESS_MEMORY_HIGH_WATER_MARK;
mStatsManager.setPullAtomCallback(
@@ -1859,7 +1847,7 @@ public class StatsPullAtomService extends SystemService {
int size = processCmdlines.size();
for (int i = 0; i < size; ++i) {
final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(processCmdlines.keyAt(i));
- if (snapshot == null || isAppUid(snapshot.uid)) {
+ if (snapshot == null) {
continue;
}
StatsEvent e = StatsEvent.newBuilder()
@@ -1920,7 +1908,7 @@ public class StatsPullAtomService extends SystemService {
for (int i = 0; i < size; ++i) {
int pid = processCmdlines.keyAt(i);
final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(pid);
- if (snapshot == null || isAppUid(snapshot.uid)) {
+ if (snapshot == null) {
continue;
}
StatsEvent e = StatsEvent.newBuilder()
@@ -3577,7 +3565,16 @@ public class StatsPullAtomService extends SystemService {
private void processHistoricalOp(AppOpsManager.HistoricalOp op,
List<AppOpEntry> opsList, int uid, int samplingRatio, String packageName,
@Nullable String attributionTag) {
- AppOpEntry entry = new AppOpEntry(packageName, attributionTag, op, uid);
+ int firstChar = 0;
+ if (attributionTag != null && attributionTag.startsWith(packageName)) {
+ firstChar = packageName.length();
+ if (firstChar < attributionTag.length() && attributionTag.charAt(firstChar) == '.') {
+ firstChar++;
+ }
+ }
+ AppOpEntry entry = new AppOpEntry(packageName,
+ attributionTag == null ? null : attributionTag.substring(firstChar), op,
+ uid);
if (entry.mHash < samplingRatio) {
opsList.add(entry);
}
diff --git a/services/core/java/com/android/server/textclassifier/FixedSizeQueue.java b/services/core/java/com/android/server/textclassifier/FixedSizeQueue.java
new file mode 100644
index 000000000000..edb258db88c8
--- /dev/null
+++ b/services/core/java/com/android/server/textclassifier/FixedSizeQueue.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.textclassifier;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayDeque;
+import java.util.Objects;
+import java.util.Queue;
+
+/**
+ * A fixed-size queue which automatically evicts the oldest element from the queue when it is full.
+ *
+ * <p>This class does not accept null element.
+ *
+ * @param <E> the type of elements held in this queue
+ */
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public final class FixedSizeQueue<E> {
+
+ private final Queue<E> mDelegate;
+
+ @Nullable
+ private final OnEntryEvictedListener<E> mOnEntryEvictedListener;
+
+ private final int mMaxSize;
+
+ public FixedSizeQueue(int maxSize, @Nullable OnEntryEvictedListener<E> onEntryEvictedListener) {
+ Preconditions.checkArgument(maxSize > 0, "maxSize (%s) must > 0", maxSize);
+ mDelegate = new ArrayDeque<>(maxSize);
+ mMaxSize = maxSize;
+ mOnEntryEvictedListener = onEntryEvictedListener;
+ }
+
+ /** Returns the number of items in the queue. */
+ public int size() {
+ return mDelegate.size();
+ }
+
+ /** Adds an element to the queue, evicts the oldest element if it reaches its max capacity. */
+ public boolean add(@NonNull E element) {
+ Objects.requireNonNull(element);
+ if (size() == mMaxSize) {
+ E removed = mDelegate.remove();
+ if (mOnEntryEvictedListener != null) {
+ mOnEntryEvictedListener.onEntryEvicted(removed);
+ }
+ }
+ mDelegate.add(element);
+ return true;
+ }
+
+ /**
+ * Returns and removes the head of the queue, or returns null if this queue is empty.
+ */
+ @Nullable
+ public E poll() {
+ return mDelegate.poll();
+ }
+
+ /**
+ * Removes an element from the queue, returns a boolean to indicate if an element is removed.
+ */
+ public boolean remove(@NonNull E element) {
+ Objects.requireNonNull(element);
+ return mDelegate.remove(element);
+ }
+
+ /** Returns whether the queue is empty. */
+ public boolean isEmpty() {
+ return mDelegate.isEmpty();
+ }
+
+ /**
+ * A listener to get notified when an element is evicted.
+ *
+ * @param <E> the type of element
+ */
+ public interface OnEntryEvictedListener<E> {
+ /**
+ * Notifies that an element is evicted because the queue is reaching its max capacity.
+ */
+ void onEntryEvicted(@NonNull E element);
+ }
+}
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 1c96a2e8c5c2..1707d9542813 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -41,6 +41,7 @@ import android.service.textclassifier.TextClassifierService;
import android.service.textclassifier.TextClassifierService.ConnectionState;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.LruCache;
import android.util.Slog;
import android.util.SparseArray;
import android.view.textclassifier.ConversationAction;
@@ -68,12 +69,10 @@ import com.android.server.SystemService;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.Queue;
/**
* A manager for TextClassifier services.
@@ -308,13 +307,15 @@ public final class TextClassificationManagerService extends ITextClassifierServi
Objects.requireNonNull(classificationContext);
Objects.requireNonNull(classificationContext.getSystemTextClassifierMetadata());
+ synchronized (mLock) {
+ mSessionCache.put(sessionId, classificationContext);
+ }
handleRequest(
classificationContext.getSystemTextClassifierMetadata(),
/* verifyCallingPackage= */ true,
/* attemptToBind= */ false,
service -> {
service.onCreateTextClassificationSession(classificationContext, sessionId);
- mSessionCache.put(sessionId, classificationContext);
},
"onCreateTextClassificationSession",
NO_OP_CALLBACK);
@@ -588,12 +589,14 @@ public final class TextClassificationManagerService extends ITextClassifierServi
* are cleaned up automatically when the client process is dead.
*/
static final class SessionCache {
+ private static final int MAX_CACHE_SIZE = 100;
+
@NonNull
private final Object mLock;
@NonNull
@GuardedBy("mLock")
- private final Map<TextClassificationSessionId, StrippedTextClassificationContext> mCache =
- new ArrayMap<>();
+ private final LruCache<TextClassificationSessionId, StrippedTextClassificationContext>
+ mCache = new LruCache<>(MAX_CACHE_SIZE);
@NonNull
@GuardedBy("mLock")
private final Map<TextClassificationSessionId, DeathRecipient> mDeathRecipients =
@@ -775,6 +778,8 @@ public final class TextClassificationManagerService extends ITextClassifierServi
}
private final class ServiceState {
+ private static final int MAX_PENDING_REQUESTS = 20;
+
@UserIdInt
final int mUserId;
@NonNull
@@ -786,7 +791,15 @@ public final class TextClassificationManagerService extends ITextClassifierServi
final int mBindServiceFlags;
@NonNull
@GuardedBy("mLock")
- final Queue<PendingRequest> mPendingRequests = new ArrayDeque<>();
+ final FixedSizeQueue<PendingRequest> mPendingRequests =
+ new FixedSizeQueue<>(MAX_PENDING_REQUESTS,
+ request -> {
+ Slog.w(LOG_TAG,
+ String.format("Pending request[%s] is dropped", request.mName));
+ if (request.mOnServiceFailure != null) {
+ request.mOnServiceFailure.run();
+ }
+ });
@Nullable
@GuardedBy("mLock")
ITextClassifierService mService;
@@ -910,7 +923,7 @@ public final class TextClassificationManagerService extends ITextClassifierServi
pw.printPair("bindServiceFlags", mBindServiceFlags);
pw.printPair("boundServiceUid", mBoundServiceUid);
pw.printPair("binding", mBinding);
- pw.printPair("numberRequests", mPendingRequests.size());
+ pw.printPair("numOfPendingRequests", mPendingRequests.size());
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index f38a506dd460..01eb9c5cb3d9 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -311,7 +311,6 @@ import com.android.server.uri.UriPermissionOwner;
import com.android.server.wm.ActivityMetricsLogger.TransitionInfoSnapshot;
import com.android.server.wm.ActivityStack.ActivityState;
import com.android.server.wm.SurfaceAnimator.AnimationType;
-import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import com.android.server.wm.WindowManagerService.H;
import com.android.server.wm.utils.InsetUtils;
@@ -4166,14 +4165,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
@Override
- boolean applyAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
- boolean isVoiceInteraction,
- @Nullable OnAnimationFinishedCallback animationFinishedCallback) {
+ boolean applyAnimation(LayoutParams lp, int transit, boolean enter,
+ boolean isVoiceInteraction, @Nullable ArrayList<WindowContainer> sources) {
if (mUseTransferredAnimation) {
return false;
}
- return super.applyAnimation(lp, transit, enter, isVoiceInteraction,
- animationFinishedCallback);
+ return super.applyAnimation(lp, transit, enter, isVoiceInteraction, sources);
}
/**
@@ -7540,7 +7537,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
public String toString() {
if (stringName != null) {
return stringName + " t" + (task == null ? INVALID_TASK_ID : task.mTaskId) +
- (finishing ? " f}" : "") + (mIsExiting ? " mIsExiting=" : "") + "}";
+ (finishing ? " f}" : "") + (mIsExiting ? " isExiting" : "") + "}";
}
StringBuilder sb = new StringBuilder(128);
sb.append("ActivityRecord{");
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index c99980911cef..4bede4c3623f 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -3232,22 +3232,22 @@ class ActivityStack extends Task {
@Override
void dump(PrintWriter pw, String prefix, boolean dumpAll) {
- pw.println(prefix + "mStackId=" + getRootTaskId());
- pw.println(prefix + "mDeferRemoval=" + mDeferRemoval);
- pw.println(prefix + "mBounds=" + getRawBounds().toShortString());
- for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) {
- mChildren.get(taskNdx).dump(pw, prefix + " ", dumpAll);
+ if (mDeferRemoval) {
+ pw.println(prefix + "mDeferRemoval=true");
}
+ super.dump(pw, prefix, dumpAll);
if (!mExitingActivities.isEmpty()) {
pw.println();
- pw.println(" Exiting application tokens:");
+ pw.println(prefix + "Exiting application tokens:");
+ final String doublePrefix = prefix + " ";
for (int i = mExitingActivities.size() - 1; i >= 0; i--) {
WindowToken token = mExitingActivities.get(i);
- pw.print(" Exiting App #"); pw.print(i);
+ pw.print(doublePrefix + "Exiting App #" + i);
pw.print(' '); pw.print(token);
pw.println(':');
- token.dump(pw, " ", dumpAll);
+ token.dump(pw, doublePrefix, dumpAll);
}
+ pw.println();
}
mAnimatingActivityRegistry.dump(pw, "AnimatingApps:", prefix);
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index b7ca1a9aeab8..1f9e8609c2ad 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -1886,7 +1886,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
public void dump(PrintWriter pw, String prefix) {
pw.println();
pw.println("ActivityStackSupervisor state:");
- mRootWindowContainer.dump(pw, prefix);
+ mRootWindowContainer.dump(pw, prefix, true /* dumpAll */);
getKeyguardController().dump(pw, prefix);
mService.getLockTaskController().dump(pw, prefix);
pw.print(prefix);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 7c935d0ea546..6dd1ea934497 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1685,6 +1685,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final NeededUriGrants resultGrants = collectGrants(resultData, r.resultTo);
synchronized (mGlobalLock) {
+ // Sanity check in case activity was removed before entering global lock.
+ if (!r.isInHistory()) {
+ return true;
+ }
+
// Keep track of the root activity of the task before we finish it
final Task tr = r.getTask();
final ActivityRecord rootR = tr.getRootActivity();
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 67fe9685fd2a..d60d098071cc 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -49,7 +49,7 @@ import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_W
import static com.android.server.wm.AppTransition.isKeyguardGoingAwayTransit;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
-import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -75,7 +75,6 @@ import java.util.ArrayList;
import java.util.LinkedList;
import java.util.function.Predicate;
-
/**
* Checks for app transition readiness, resolves animation attributes and performs visibility
* change for apps that animate as part of an app transition.
@@ -375,18 +374,14 @@ public class AppTransitionController {
// triggers WC#onAnimationFinished only on the promoted target. So we need to take care
// of triggering AR#onAnimationFinished on each ActivityRecord which is a part of the
// app transition.
- final ArrayList<ActivityRecord> transitioningDecendants = new ArrayList<>();
+ final ArrayList<ActivityRecord> transitioningDescendants = new ArrayList<>();
for (int j = 0; j < apps.size(); ++j) {
final ActivityRecord app = apps.valueAt(j);
if (app.isDescendantOf(wc)) {
- transitioningDecendants.add(app);
+ transitioningDescendants.add(app);
}
}
- wc.applyAnimation(animLp, transit, visible, voiceInteraction, (type, anim) -> {
- for (int j = 0; j < transitioningDecendants.size(); ++j) {
- transitioningDecendants.get(j).onAnimationFinished(type, anim);
- }
- });
+ wc.applyAnimation(animLp, transit, visible, voiceInteraction, transitioningDescendants);
}
}
@@ -540,7 +535,14 @@ public class AppTransitionController {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now opening app %s", app);
app.commitVisibility(true /* visible */, false /* performLayout */);
- if (!app.isAnimating(PARENTS | CHILDREN)) {
+
+ // In case a trampoline activity is used, it can happen that a new ActivityRecord is
+ // added and a new app transition starts before the previous app transition animation
+ // ends. So we cannot simply use app.isAnimating(PARENTS) to determine if the app must
+ // to be added to the list of tokens to be notified of app transition complete.
+ final WindowContainer wc = app.getAnimatingContainer(PARENTS,
+ ANIMATION_TYPE_APP_TRANSITION);
+ if (wc == null || !wc.getAnimationSources().contains(app)) {
// This token isn't going to be animating. Add it to the list of tokens to
// be notified of app transition complete since the notification will not be
// sent be the app window animator.
@@ -599,8 +601,7 @@ public class AppTransitionController {
for (int i = 0; i < appsCount; i++) {
WindowContainer wc = apps.valueAt(i);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now changing app %s", wc);
- wc.applyAnimation(null, transit, true, false,
- null /* animationFinishedCallback */);
+ wc.applyAnimation(null, transit, true, false, null /* sources */);
}
}
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index fdcc3f4d1510..d43a7b87ee35 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -22,7 +22,6 @@ import static com.android.server.wm.AlphaAnimationSpecProto.TO;
import static com.android.server.wm.AnimationSpecProto.ALPHA;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_DIMMER;
-import android.annotation.Nullable;
import android.graphics.Rect;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
@@ -31,7 +30,6 @@ import android.view.SurfaceControl;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wm.SurfaceAnimator.AnimationType;
-import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import java.io.PrintWriter;
@@ -160,8 +158,7 @@ class Dimmer {
@VisibleForTesting
interface SurfaceAnimatorStarter {
void startAnimation(SurfaceAnimator surfaceAnimator, SurfaceControl.Transaction t,
- AnimationAdapter anim, boolean hidden, @AnimationType int type,
- @Nullable OnAnimationFinishedCallback animationFinishedCallback);
+ AnimationAdapter anim, boolean hidden, @AnimationType int type);
}
Dimmer(WindowContainer host) {
@@ -348,7 +345,7 @@ class Dimmer {
mSurfaceAnimatorStarter.startAnimation(animator, t, new LocalAnimationAdapter(
new AlphaAnimationSpec(startAlpha, endAlpha, getDimDuration(container)),
mHost.mWmService.mSurfaceAnimationRunner), false /* hidden */,
- ANIMATION_TYPE_DIMMER, null /* animationFinishedCallback */);
+ ANIMATION_TYPE_DIMMER);
}
private long getDimDuration(WindowContainer container) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 807cd9c0d412..4e19a5224bb4 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2977,12 +2977,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
pw.println();
mWallpaperController.dump(pw, " ");
- pw.println();
- pw.print("mSystemGestureExclusion=");
if (mSystemGestureExclusionListeners.getRegisteredCallbackCount() > 0) {
+ pw.println();
+ pw.print(" mSystemGestureExclusion=");
pw.println(mSystemGestureExclusion);
- } else {
- pw.println("<no lstnrs>");
}
pw.println();
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 6a4975950f1a..c8d9fe0f0a65 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -280,7 +280,7 @@ class InsetsSourceProvider {
}
final Transaction t = mDisplayContent.getPendingTransaction();
mWin.startAnimation(t, mAdapter, !mClientVisible /* hidden */,
- ANIMATION_TYPE_INSETS_CONTROL, null /* animationFinishedCallback */);
+ ANIMATION_TYPE_INSETS_CONTROL);
// The leash was just created. We cannot dispatch it until its surface transaction is
// applied. Otherwise, the client's operation to the leash might be overwritten by us.
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 77bc37f0c2d7..bf9a78489167 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -178,6 +178,7 @@ class InsetsStateController {
if (imeSource != null && imeSource.isVisible()) {
imeSource = new InsetsSource(imeSource);
imeSource.setVisible(false);
+ imeSource.setFrame(0, 0, 0, 0);
state = new InsetsState(state);
state.addSource(imeSource);
}
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 1cd94b40f660..24bb7c8d5560 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -1341,6 +1341,17 @@ class RecentTasks {
break;
}
+ // Tasks managed by/associated with an ActivityView should be excluded from recents.
+ // singleTaskInstance is set on the VirtualDisplay managed by ActivityView
+ // TODO(b/126185105): Find a different signal to use besides isSingleTaskInstance
+ final ActivityStack stack = task.getStack();
+ if (stack != null) {
+ DisplayContent display = stack.getDisplay();
+ if (display != null && display.isSingleTaskInstance()) {
+ return false;
+ }
+ }
+
// If we're in lock task mode, ignore the root task
if (task == mService.getLockTaskController().getRootTask()) {
return false;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index ae5adcae5b9b..583663c5455f 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -3570,12 +3570,14 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
}
- public void dump(PrintWriter pw, String prefix) {
+ @Override
+ public void dump(PrintWriter pw, String prefix, boolean dumpAll) {
+ super.dump(pw, prefix, dumpAll);
pw.print(prefix);
pw.println("topDisplayFocusedStack=" + getTopDisplayFocusedStack());
for (int i = getChildCount() - 1; i >= 0; --i) {
final DisplayContent display = getChildAt(i);
- display.dump(pw, prefix, true /* dumpAll */);
+ display.dump(pw, prefix, dumpAll);
}
pw.println();
}
diff --git a/services/core/java/com/android/server/wm/ShellRoot.java b/services/core/java/com/android/server/wm/ShellRoot.java
index 7a38bb65f73b..0ae9ca9b882e 100644
--- a/services/core/java/com/android/server/wm/ShellRoot.java
+++ b/services/core/java/com/android/server/wm/ShellRoot.java
@@ -104,7 +104,7 @@ public class ShellRoot {
0 /* windowCornerRadius */),
mDisplayContent.mWmService.mSurfaceAnimationRunner);
mToken.startAnimation(mToken.getPendingTransaction(), adapter, false /* hidden */,
- ANIMATION_TYPE_WINDOW_ANIMATION, null /* animationFinishedCallback */);
+ ANIMATION_TYPE_WINDOW_ANIMATION);
}
WindowInfo getWindowInfo() {
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 1b77fd2e8782..0e5d7d910084 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -428,16 +428,11 @@ class SurfaceAnimator {
void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("mLeash="); pw.print(mLeash);
- if (mAnimationStartDelayed) {
- pw.print(" mAnimationStartDelayed="); pw.println(mAnimationStartDelayed);
- } else {
- pw.println();
- }
- pw.print(prefix); pw.println("Animation:");
+ pw.print(" mAnimationType=" + mAnimationType);
+ pw.println(mAnimationStartDelayed ? " mAnimationStartDelayed=true" : "");
+ pw.print(prefix); pw.print("Animation: "); pw.println(mAnimation);
if (mAnimation != null) {
mAnimation.dump(pw, prefix + " ");
- } else {
- pw.print(prefix); pw.println("null");
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 48609e17ba40..56ad9643e619 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -85,7 +85,6 @@ import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
-import static com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.TASK;
@@ -1436,15 +1435,6 @@ class Task extends WindowContainer<WindowContainer> {
mAtmService.getTaskChangeNotificationController().notifyTaskStackChanged();
}
- final boolean isRootTask = isRootTask();
- if (isRootTask) {
- final DisplayContent display = getDisplayContent();
- if (display.isSingleTaskInstance()) {
- mAtmService.notifySingleTaskDisplayEmpty(display.mDisplayId);
- }
- display.mDisplayContent.setLayoutNeeded();
- }
-
if (hasChild()) {
updateEffectiveIntent();
@@ -1465,7 +1455,7 @@ class Task extends WindowContainer<WindowContainer> {
} else if (!mReuseTask && !mCreatedByOrganizer) {
// Remove entire task if it doesn't have any activity left and it isn't marked for reuse
// or created by task organizer.
- if (!isRootTask) {
+ if (!isRootTask()) {
getStack().removeChild(this, reason);
}
EventLogTags.writeWmTaskRemoved(mTaskId,
@@ -2817,6 +2807,10 @@ class Task extends WindowContainer<WindowContainer> {
if (DEBUG_STACK) Slog.i(TAG, "removeTask: removing taskId=" + mTaskId);
EventLogTags.writeWmTaskRemoved(mTaskId, "removeTask");
+ if (mDisplayContent != null && mDisplayContent.isSingleTaskInstance()) {
+ mAtmService.notifySingleTaskDisplayEmpty(mDisplayContent.mDisplayId);
+ }
+
// If applicable let the TaskOrganizer know the Task is vanishing.
setTaskOrganizer(null);
@@ -3500,7 +3494,7 @@ class Task extends WindowContainer<WindowContainer> {
@Override
protected void applyAnimationUnchecked(WindowManager.LayoutParams lp, boolean enter,
int transit, boolean isVoiceInteraction,
- @Nullable OnAnimationFinishedCallback finishedCallback) {
+ @Nullable ArrayList<WindowContainer> sources) {
final RecentsAnimationController control = mWmService.getRecentsAnimationController();
if (control != null) {
// We let the transition to be controlled by RecentsAnimation, and callback task's
@@ -3509,30 +3503,31 @@ class Task extends WindowContainer<WindowContainer> {
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
"applyAnimationUnchecked, control: %s, task: %s, transit: %s",
control, asTask(), AppTransition.appTransitionToString(transit));
- control.addTaskToTargets(this, finishedCallback);
+ control.addTaskToTargets(this, (type, anim) -> {
+ for (int i = 0; i < sources.size(); ++i) {
+ sources.get(i).onAnimationFinished(type, anim);
+ }
+ });
}
} else {
- super.applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, finishedCallback);
+ super.applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, sources);
}
}
@Override
void dump(PrintWriter pw, String prefix, boolean dumpAll) {
super.dump(pw, prefix, dumpAll);
+ pw.println(prefix + "bounds=" + getBounds().toShortString());
final String doublePrefix = prefix + " ";
-
- pw.println(prefix + "taskId=" + mTaskId);
- pw.println(doublePrefix + "mBounds=" + getBounds().toShortString());
- pw.println(doublePrefix + "appTokens=" + mChildren);
-
- final String triplePrefix = doublePrefix + " ";
- final String quadruplePrefix = triplePrefix + " ";
-
- int[] index = { 0 };
- forAllActivities((r) -> {
- pw.println(triplePrefix + "Activity #" + index[0]++ + " " + r);
- r.dump(pw, quadruplePrefix, dumpAll);
- });
+ for (int i = mChildren.size() - 1; i >= 0; i--) {
+ final WindowContainer<?> child = mChildren.get(i);
+ pw.println(prefix + "* " + child);
+ // Only dump non-activity because full activity info is already printed by
+ // RootWindowContainer#dumpActivities.
+ if (child.asActivityRecord() == null) {
+ child.dump(pw, doublePrefix, dumpAll);
+ }
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 22054db20bbd..102c2a6364f4 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1772,16 +1772,20 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> {
@Override
void dump(PrintWriter pw, String prefix, boolean dumpAll) {
pw.println(prefix + "TaskDisplayArea " + getName());
+ super.dump(pw, prefix, dumpAll);
if (mPreferredTopFocusableStack != null) {
pw.println(prefix + " mPreferredTopFocusableStack=" + mPreferredTopFocusableStack);
}
if (mLastFocusedStack != null) {
pw.println(prefix + " mLastFocusedStack=" + mLastFocusedStack);
}
- pw.println(prefix + " Application tokens in top down Z order:");
+ final String doublePrefix = prefix + " ";
+ final String triplePrefix = doublePrefix + " ";
+ pw.println(doublePrefix + "Application tokens in top down Z order:");
for (int stackNdx = getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = getChildAt(stackNdx);
- stack.dump(pw, prefix + " ", dumpAll);
+ pw.println(doublePrefix + "* " + stack);
+ stack.dump(pw, triplePrefix, dumpAll);
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 90156fd4f475..6406f0ae51a6 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -64,6 +64,7 @@ import android.graphics.Rect;
import android.os.Debug;
import android.os.IBinder;
import android.os.Trace;
+import android.util.ArraySet;
import android.util.Pair;
import android.util.Pools;
import android.util.Slog;
@@ -181,6 +182,14 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
final SurfaceFreezer mSurfaceFreezer;
protected final WindowManagerService mWmService;
+ /**
+ * Sources which triggered a surface animation on this container. An animation target can be
+ * promoted to higher level, for example, from a set of {@link ActivityRecord}s to
+ * {@link ActivityStack}. In this case, {@link ActivityRecord}s are set on this variable while
+ * the animation is running, and reset after finishing it.
+ */
+ private final ArraySet<WindowContainer> mSurfaceAnimationSources = new ArraySet<>();
+
private final Point mTmpPos = new Point();
protected final Point mLastSurfacePosition = new Point();
@@ -193,8 +202,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
*/
private boolean mCommittedReparentToAnimationLeash;
- private final Configuration mTmpConfig = new Configuration();
-
/** Interface for {@link #isAnimating} to check which cases for the container is animating. */
public interface AnimationFlags {
/**
@@ -872,29 +879,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* @see AnimationFlags#PARENTS
* @see AnimationFlags#CHILDREN
*/
- boolean isAnimating(int flags, int typesToCheck) {
- int animationType = mSurfaceAnimator.getAnimationType();
- if (mSurfaceAnimator.isAnimating() && (animationType & typesToCheck) > 0) {
- return true;
- }
- if ((flags & TRANSITION) != 0 && isWaitingForTransitionStart()) {
- return true;
- }
- if ((flags & PARENTS) != 0) {
- final WindowContainer parent = getParent();
- if (parent != null && parent.isAnimating(flags & ~CHILDREN, typesToCheck)) {
- return true;
- }
- }
- if ((flags & CHILDREN) != 0) {
- for (int i = 0; i < mChildren.size(); ++i) {
- final WindowContainer wc = mChildren.get(i);
- if (wc.isAnimating(flags & ~PARENTS, typesToCheck)) {
- return true;
- }
- }
- }
- return false;
+ final boolean isAnimating(int flags, int typesToCheck) {
+ return getAnimatingContainer(flags, typesToCheck) != null;
}
/**
@@ -904,16 +890,20 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* checking animating status.
* @param typesToExclude The combination of bitmask {@link AnimationType} to exclude when
* checking if animating.
+ *
+ * @deprecated Use {@link #isAnimating(int, int)}
*/
- boolean isAnimatingExcluding(int flags, int typesToExclude) {
+ @Deprecated
+ final boolean isAnimatingExcluding(int flags, int typesToExclude) {
return isAnimating(flags, ANIMATION_TYPE_ALL & ~typesToExclude);
}
/**
- * @see #isAnimating(int, int)
+ * @deprecated Use {@link #isAnimating(int, int)}
* TODO (b/152333373): Migrate calls to use isAnimating with specified animation type
*/
- boolean isAnimating(int flags) {
+ @Deprecated
+ final boolean isAnimating(int flags) {
return isAnimating(flags, ANIMATION_TYPE_ALL);
}
@@ -2107,10 +2097,15 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
void cancelAnimation() {
+ doAnimationFinished(mSurfaceAnimator.getAnimationType(), mSurfaceAnimator.getAnimation());
mSurfaceAnimator.cancelAnimation();
mSurfaceFreezer.unfreeze(getPendingTransaction());
}
+ ArraySet<WindowContainer> getAnimationSources() {
+ return mSurfaceAnimationSources;
+ }
+
@Override
public SurfaceControl getFreezeSnapshotTarget() {
return null;
@@ -2156,6 +2151,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* @param transit The app transition type indicates what kind of transition to be applied.
* @param enter Whether the app transition is entering transition or not.
* @param isVoiceInteraction Whether the container is participating in voice interaction or not.
+ * @param sources {@link ActivityRecord}s which causes this app transition animation.
*
* @return {@code true} when the container applied the app transition, {@code false} if the
* app transition is disabled or skipped.
@@ -2163,7 +2159,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* @see #getAnimationAdapter
*/
boolean applyAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
- boolean isVoiceInteraction, @Nullable OnAnimationFinishedCallback finishedCallback) {
+ boolean isVoiceInteraction, @Nullable ArrayList<WindowContainer> sources) {
if (mWmService.mDisableTransitionAnimation) {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation: transition animation is disabled or skipped. "
@@ -2178,7 +2174,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WC#applyAnimation");
if (okToAnimate()) {
- applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, finishedCallback);
+ applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, sources);
} else {
cancelAnimation();
}
@@ -2276,14 +2272,17 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
protected void applyAnimationUnchecked(WindowManager.LayoutParams lp, boolean enter,
int transit, boolean isVoiceInteraction,
- @Nullable OnAnimationFinishedCallback finishedCallback) {
+ @Nullable ArrayList<WindowContainer> sources) {
final Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp,
transit, enter, isVoiceInteraction);
AnimationAdapter adapter = adapters.first;
AnimationAdapter thumbnailAdapter = adapters.second;
if (adapter != null) {
+ if (sources != null) {
+ mSurfaceAnimationSources.addAll(sources);
+ }
startAnimation(getPendingTransaction(), adapter, !isVisible(),
- ANIMATION_TYPE_APP_TRANSITION, finishedCallback);
+ ANIMATION_TYPE_APP_TRANSITION);
if (adapter.getShowWallpaper()) {
getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
@@ -2411,10 +2410,18 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
updateSurfacePosition(t);
}
+ private void doAnimationFinished(@AnimationType int type, AnimationAdapter anim) {
+ for (int i = 0; i < mSurfaceAnimationSources.size(); ++i) {
+ mSurfaceAnimationSources.valueAt(i).onAnimationFinished(type, anim);
+ }
+ mSurfaceAnimationSources.clear();
+ }
+
/**
* Called when an animation has finished running.
*/
protected void onAnimationFinished(@AnimationType int type, AnimationAdapter anim) {
+ doAnimationFinished(type, anim);
mWmService.onAnimationFinished();
}
@@ -2428,16 +2435,66 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
/**
* @return The {@link WindowContainer} which is running an animation.
*
- * It traverses from the current container to its parents recursively. If nothing is animating,
- * it will return {@code null}.
+ * By default this only checks if this container itself is actually running an animation, but
+ * you can extend the check target over its relatives, or relax the condition so that this can
+ * return {@code WindowContainer} if an animation starts soon by giving a combination
+ * of {@link AnimationFlags}.
+ *
+ * Note that you can give a combination of bitmask flags to specify targets and condition for
+ * checking animating status.
+ * e.g. {@code isAnimating(TRANSITION | PARENT)} returns {@code true} if either this
+ * container itself or one of its parents is running an animation or waiting for an app
+ * transition.
+ *
+ * Note that TRANSITION propagates to parents and children as well.
+ *
+ * @param flags The combination of bitmask flags to specify targets and condition for
+ * checking animating status.
+ * @param typesToCheck The combination of bitmask {@link AnimationType} to compare when
+ * determining if animating.
+ *
+ * @see AnimationFlags#TRANSITION
+ * @see AnimationFlags#PARENTS
+ * @see AnimationFlags#CHILDREN
*/
@Nullable
- WindowContainer getAnimatingContainer() {
- if (isAnimating()) {
+ WindowContainer getAnimatingContainer(int flags, int typesToCheck) {
+ int animationType = mSurfaceAnimator.getAnimationType();
+ if (mSurfaceAnimator.isAnimating() && (animationType & typesToCheck) > 0) {
return this;
}
- final WindowContainer parent = getParent();
- return (parent != null) ? parent.getAnimatingContainer() : null;
+ if ((flags & TRANSITION) != 0 && isWaitingForTransitionStart()) {
+ return this;
+ }
+ if ((flags & PARENTS) != 0) {
+ final WindowContainer parent = getParent();
+ if (parent != null) {
+ final WindowContainer wc = parent.getAnimatingContainer(
+ flags & ~CHILDREN, typesToCheck);
+ if (wc != null) {
+ return wc;
+ }
+ }
+ }
+ if ((flags & CHILDREN) != 0) {
+ for (int i = 0; i < mChildren.size(); ++i) {
+ final WindowContainer wc = mChildren.get(i).getAnimatingContainer(
+ flags & ~PARENTS, typesToCheck);
+ if (wc != null) {
+ return wc;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @deprecated Use {@link #getAnimatingContainer(int, int)} instead.
+ */
+ @Nullable
+ @Deprecated
+ final WindowContainer getAnimatingContainer() {
+ return getAnimatingContainer(PARENTS, ANIMATION_TYPE_ALL);
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowContainerThumbnail.java b/services/core/java/com/android/server/wm/WindowContainerThumbnail.java
index a27a11259772..126154b10350 100644
--- a/services/core/java/com/android/server/wm/WindowContainerThumbnail.java
+++ b/services/core/java/com/android/server/wm/WindowContainerThumbnail.java
@@ -133,15 +133,14 @@ class WindowContainerThumbnail implements Animatable {
mWindowContainer.getDisplayContent().mAppTransition.canSkipFirstFrame(),
mWindowContainer.getDisplayContent().getWindowCornerRadius()),
mWindowContainer.mWmService.mSurfaceAnimationRunner), false /* hidden */,
- ANIMATION_TYPE_RECENTS, null /* animationFinishedCallback */);
+ ANIMATION_TYPE_RECENTS);
}
/**
* Start animation with existing adapter.
*/
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden) {
- mSurfaceAnimator.startAnimation(t, anim, hidden, ANIMATION_TYPE_RECENTS,
- null /* animationFinishedCallback */);
+ mSurfaceAnimator.startAnimation(t, anim, hidden, ANIMATION_TYPE_RECENTS);
}
private void onAnimationFinished(@AnimationType int type, AnimationAdapter anim) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index fe3ee50c34c5..32717d0e1e65 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -5049,16 +5049,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mWindowFrames.updateLastInsetValues();
}
+ @Nullable
@Override
- boolean isAnimating(int flags, int typesToCheck) {
-
- // If we are an inset provider, all our animations are driven by the inset client, so we
- // aren't really animating.
- // TODO: Replace this with a proper animation type system.
+ WindowContainer<WindowState> getAnimatingContainer(int flags, int typesToCheck) {
if (mControllableInsetProvider != null) {
- return false;
+ return null;
}
- return super.isAnimating(flags, typesToCheck);
+ return super.getAnimatingContainer(flags, typesToCheck);
}
void startAnimation(Animation anim) {
@@ -5101,8 +5098,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
private void startAnimation(Transaction t, AnimationAdapter adapter) {
- startAnimation(t, adapter, mWinAnimator.mLastHidden, ANIMATION_TYPE_WINDOW_ANIMATION,
- null /* animationFinishedCallback */);
+ startAnimation(t, adapter, mWinAnimator.mLastHidden, ANIMATION_TYPE_WINDOW_ANIMATION);
}
@Override
diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp
index 098b2ef6439d..4e1a23416330 100644
--- a/services/core/jni/com_android_server_tv_TvInputHal.cpp
+++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp
@@ -301,6 +301,7 @@ private:
JTvInputHal(JNIEnv* env, jobject thiz, sp<ITvInput> tvInput, const sp<Looper>& looper);
Mutex mLock;
+ Mutex mStreamLock;
jweak mThiz;
sp<Looper> mLooper;
@@ -338,6 +339,7 @@ JTvInputHal* JTvInputHal::createInstance(JNIEnv* env, jobject thiz, const sp<Loo
}
int JTvInputHal::addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface) {
+ Mutex::Autolock autoLock(&mStreamLock);
KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
if (connections.indexOfKey(streamId) < 0) {
connections.add(streamId, Connection());
@@ -412,6 +414,7 @@ int JTvInputHal::addOrUpdateStream(int deviceId, int streamId, const sp<Surface>
}
int JTvInputHal::removeStream(int deviceId, int streamId) {
+ Mutex::Autolock autoLock(&mStreamLock);
KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
if (connections.indexOfKey(streamId) < 0) {
return BAD_VALUE;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index df55b3bbd1a4..7b624cae8141 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -631,7 +631,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
/**
* Whether or not device admin feature is supported. If it isn't return defaults for all
- * public methods.
+ * public methods, unless the caller has the appropriate permission for a particular method.
*/
final boolean mHasFeature;
@@ -2755,7 +2755,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Slog.i(LOG_TAG, "Giving the PO additional power...");
markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(poAdminComponent, poUserId);
Slog.i(LOG_TAG, "Migrating DO policies to PO...");
- moveDoPoliciesToProfileParentAdmin(doAdmin, poAdmin.getParentActiveAdmin());
+ moveDoPoliciesToProfileParentAdminLocked(doAdmin, poAdmin.getParentActiveAdmin());
+ migratePersonalAppSuspensionLocked(doUserId, poUserId, poAdmin);
saveSettingsLocked(poUserId);
Slog.i(LOG_TAG, "Clearing the DO...");
final ComponentName doAdminReceiver = doAdmin.info.getComponent();
@@ -2775,6 +2776,25 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
.write();
}
+ @GuardedBy("getLockObject()")
+ private void migratePersonalAppSuspensionLocked(
+ int doUserId, int poUserId, ActiveAdmin poAdmin) {
+ final PackageManagerInternal pmi = mInjector.getPackageManagerInternal();
+ if (!pmi.isSuspendingAnyPackages(PLATFORM_PACKAGE_NAME, doUserId)) {
+ Slog.i(LOG_TAG, "DO is not suspending any apps.");
+ return;
+ }
+
+ if (getTargetSdk(poAdmin.info.getPackageName(), poUserId) >= Build.VERSION_CODES.R) {
+ Slog.i(LOG_TAG, "PO is targeting R+, keeping personal apps suspended.");
+ getUserData(doUserId).mAppsSuspended = true;
+ poAdmin.mSuspendPersonalApps = true;
+ } else {
+ Slog.i(LOG_TAG, "PO isn't targeting R+, unsuspending personal apps.");
+ pmi.unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, doUserId);
+ }
+ }
+
private void uninstallOrDisablePackage(String packageName, int userHandle) {
final ApplicationInfo appInfo;
try {
@@ -2816,7 +2836,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
pi.uninstall(packageName, 0 /* flags */, new IntentSender((IIntentSender) mLocalSender));
}
- private void moveDoPoliciesToProfileParentAdmin(ActiveAdmin doAdmin, ActiveAdmin parentAdmin) {
+ @GuardedBy("getLockObject()")
+ private void moveDoPoliciesToProfileParentAdminLocked(
+ ActiveAdmin doAdmin, ActiveAdmin parentAdmin) {
// The following policies can be already controlled via parent instance, skip if so.
if (parentAdmin.mPasswordPolicy.quality == PASSWORD_QUALITY_UNSPECIFIED) {
parentAdmin.mPasswordPolicy = doAdmin.mPasswordPolicy;
@@ -6010,7 +6032,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void lockNow(int flags, boolean parent) {
- if (!mHasFeature) {
+ if (!mHasFeature && mContext.checkCallingPermission(android.Manifest.permission.LOCK_DEVICE)
+ != PackageManager.PERMISSION_GRANTED) {
return;
}
@@ -16147,25 +16170,34 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Slog.i(LOG_TAG, String.format("%s personal apps for user %d",
suspended ? "Suspending" : "Unsuspending", userId));
+
+ if (suspended) {
+ suspendPersonalAppsInPackageManager(userId);
+ } else {
+ mInjector.getPackageManagerInternal().unsuspendForSuspendingPackage(
+ PLATFORM_PACKAGE_NAME, userId);
+ }
+
+ synchronized (getLockObject()) {
+ getUserData(userId).mAppsSuspended = suspended;
+ saveSettingsLocked(userId);
+ }
+ }
+
+ private void suspendPersonalAppsInPackageManager(int userId) {
mInjector.binderWithCleanCallingIdentity(() -> {
try {
final String[] appsToSuspend = mInjector.getPersonalAppsForSuspension(userId);
- final String[] failedPackages = mIPackageManager.setPackagesSuspendedAsUser(
- appsToSuspend, suspended, null, null, null, PLATFORM_PACKAGE_NAME, userId);
- if (!ArrayUtils.isEmpty(failedPackages)) {
- Slog.wtf(LOG_TAG, String.format("Failed to %s packages: %s",
- suspended ? "suspend" : "unsuspend", String.join(",", failedPackages)));
+ final String[] failedApps = mIPackageManager.setPackagesSuspendedAsUser(
+ appsToSuspend, true, null, null, null, PLATFORM_PACKAGE_NAME, userId);
+ if (!ArrayUtils.isEmpty(failedApps)) {
+ Slog.wtf(LOG_TAG, "Failed to suspend apps: " + String.join(",", failedApps));
}
} catch (RemoteException re) {
// Shouldn't happen.
Slog.e(LOG_TAG, "Failed talking to the package manager", re);
}
});
-
- synchronized (getLockObject()) {
- getUserData(userId).mAppsSuspended = suspended;
- saveSettingsLocked(userId);
- }
}
@GuardedBy("getLockObject()")
diff --git a/services/net/java/android/net/ip/IpClientCallbacks.java b/services/net/java/android/net/ip/IpClientCallbacks.java
index c93e5c5e4759..b172c4be7b0d 100644
--- a/services/net/java/android/net/ip/IpClientCallbacks.java
+++ b/services/net/java/android/net/ip/IpClientCallbacks.java
@@ -16,7 +16,6 @@
package android.net.ip;
-import android.net.DhcpResults;
import android.net.DhcpResultsParcelable;
import android.net.Layer2PacketParcelable;
import android.net.LinkProperties;
@@ -67,19 +66,15 @@ public class IpClientCallbacks {
* <p>DHCPv4 or static IPv4 configuration failure or success can be determined by whether or not
* the passed-in DhcpResults object is null.
*/
- public void onNewDhcpResults(DhcpResults dhcpResults) {}
-
- /**
- * Callback called when new DHCP results are available.
- *
- * <p>This is purely advisory and not an indication of provisioning success or failure. This is
- * only here for callers that want to expose DHCPv4 results to other APIs
- * (e.g., WifiInfo#setInetAddress).
- *
- * <p>DHCPv4 or static IPv4 configuration failure or success can be determined by whether or not
- * the passed-in DhcpResults object is null.
- */
- public void onNewDhcpResults(DhcpResultsParcelable dhcpResults) {}
+ public void onNewDhcpResults(DhcpResultsParcelable dhcpResults) {
+ // In general callbacks would not use a parcelable directly (DhcpResultsParcelable), and
+ // would use a wrapper instead. But there are already two classes in the tree for DHCP
+ // information: DhcpInfo and DhcpResults, and each of them do not expose an appropriate API
+ // (they are bags of mutable fields and can't be changed because they are public API and
+ // @UnsupportedAppUsage). Adding a third class would cost more than the gain considering
+ // that the only client of this callback is WiFi, which will end up converting the results
+ // to DhcpInfo anyway.
+ }
/**
* Indicates that provisioning was successful.
diff --git a/services/net/java/android/net/ip/IpClientUtil.java b/services/net/java/android/net/ip/IpClientUtil.java
index b329aeec4853..426614ec2f53 100644
--- a/services/net/java/android/net/ip/IpClientUtil.java
+++ b/services/net/java/android/net/ip/IpClientUtil.java
@@ -16,8 +16,6 @@
package android.net.ip;
-import static android.net.shared.IpConfigurationParcelableUtil.fromStableParcelable;
-
import android.content.Context;
import android.net.DhcpResultsParcelable;
import android.net.Layer2PacketParcelable;
@@ -118,7 +116,6 @@ public class IpClientUtil {
// null or not.
@Override
public void onNewDhcpResults(DhcpResultsParcelable dhcpResults) {
- mCb.onNewDhcpResults(fromStableParcelable(dhcpResults));
mCb.onNewDhcpResults(dhcpResults);
}
diff --git a/services/net/java/android/net/util/DhcpResultsCompatUtil.java b/services/net/java/android/net/util/DhcpResultsCompatUtil.java
new file mode 100644
index 000000000000..fce0834c116e
--- /dev/null
+++ b/services/net/java/android/net/util/DhcpResultsCompatUtil.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.util;
+
+import static android.net.shared.IpConfigurationParcelableUtil.unparcelAddress;
+
+import android.annotation.Nullable;
+import android.net.DhcpResults;
+import android.net.DhcpResultsParcelable;
+
+import java.net.Inet4Address;
+
+/**
+ * Compatibility utility for code that still uses DhcpResults.
+ *
+ * TODO: remove this class when all usages of DhcpResults (including Wifi in AOSP) are removed.
+ */
+public class DhcpResultsCompatUtil {
+
+ /**
+ * Convert a DhcpResultsParcelable to DhcpResults.
+ *
+ * contract {
+ * returns(null) implies p == null
+ * returnsNotNull() implies p != null
+ * }
+ */
+ @Nullable
+ public static DhcpResults fromStableParcelable(@Nullable DhcpResultsParcelable p) {
+ if (p == null) return null;
+ final DhcpResults results = new DhcpResults(p.baseConfiguration);
+ results.leaseDuration = p.leaseDuration;
+ results.mtu = p.mtu;
+ results.serverAddress = (Inet4Address) unparcelAddress(p.serverAddress);
+ results.vendorInfo = p.vendorInfo;
+ results.serverHostName = p.serverHostName;
+ results.captivePortalApiUrl = p.captivePortalApiUrl;
+ return results;
+ }
+}
diff --git a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
index 2e60f2afcdea..236ac8407faa 100644
--- a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
+++ b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
@@ -16,6 +16,8 @@
package com.android.server.people.prediction;
+import static java.util.Collections.reverseOrder;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -39,6 +41,7 @@ import com.android.server.people.data.PackageData;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
import java.util.function.Consumer;
@@ -85,7 +88,9 @@ class ShareTargetPredictor extends AppTargetPredictor {
List<ShareTarget> shareTargets = getDirectShareTargets();
SharesheetModelScorer.computeScore(shareTargets, getShareEventType(mIntentFilter),
System.currentTimeMillis());
- Collections.sort(shareTargets, (t1, t2) -> -Float.compare(t1.getScore(), t2.getScore()));
+ Collections.sort(shareTargets,
+ Comparator.comparing(ShareTarget::getScore, reverseOrder())
+ .thenComparing(t -> t.getAppTarget().getRank()));
List<AppTarget> res = new ArrayList<>();
for (int i = 0; i < Math.min(getPredictionContext().getPredictedTargetCount(),
shareTargets.size()); i++) {
@@ -135,6 +140,7 @@ class ShareTargetPredictor extends AppTargetPredictor {
new AppTargetId(shortcutInfo.getId()),
shortcutInfo)
.setClassName(shareShortcut.getTargetComponent().getClassName())
+ .setRank(shortcutInfo.getRank())
.build();
String packageName = shortcutInfo.getPackage();
int userId = shortcutInfo.getUserId();
diff --git a/services/tests/PackageManagerServiceTests/host/Android.bp b/services/tests/PackageManagerServiceTests/host/Android.bp
new file mode 100644
index 000000000000..dad001b52b15
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/Android.bp
@@ -0,0 +1,33 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+java_test_host {
+ name: "PackageManagerServiceHostTests",
+ srcs: ["src/**/*.kt"],
+ libs: [
+ "tradefed",
+ "junit",
+ "truth-prebuilt",
+ ],
+ static_libs: [
+ "frameworks-base-hostutils",
+ ],
+ test_suites: ["general-tests"],
+ java_resources: [
+ ":PackageManagerDummyAppVersion1",
+ ":PackageManagerDummyAppVersion2",
+ ":PackageManagerDummyAppVersion3",
+ ":PackageManagerDummyAppOriginalOverride",
+ ]
+}
diff --git a/services/tests/PackageManagerServiceTests/host/AndroidTest.xml b/services/tests/PackageManagerServiceTests/host/AndroidTest.xml
new file mode 100644
index 000000000000..dc8c8113fac2
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<configuration description="Test module config for PackageManagerServiceHostTests">
+ <option name="test-tag" value="PackageManagerServiceHostTests" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+
+ <test class="com.android.tradefed.testtype.HostTest">
+ <option name="jar" value="PackageManagerServiceHostTests.jar" />
+ </test>
+</configuration>
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt
new file mode 100644
index 000000000000..4927c45550b5
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test
+
+import com.android.internal.util.test.SystemPreparer
+import com.android.tradefed.device.ITestDevice
+import java.io.File
+
+internal fun SystemPreparer.pushApk(file: String, partition: Partition) =
+ pushResourceFile(file, HostUtils.makePathForApk(file, partition))
+
+internal fun SystemPreparer.deleteApk(file: String, partition: Partition) =
+ deleteFile(partition.baseFolder.resolve(file.removeSuffix(".apk")).toString())
+
+internal object HostUtils {
+
+ fun getDataDir(device: ITestDevice, pkgName: String) =
+ device.executeShellCommand("dumpsys package $pkgName")
+ .lineSequence()
+ .map(String::trim)
+ .single { it.startsWith("dataDir=") }
+ .removePrefix("dataDir=")
+
+ fun makePathForApk(fileName: String, partition: Partition) =
+ makePathForApk(File(fileName), partition)
+
+ fun makePathForApk(file: File, partition: Partition) =
+ partition.baseFolder
+ .resolve(file.nameWithoutExtension)
+ .resolve(file.name)
+ .toString()
+}
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt
new file mode 100644
index 000000000000..90494c591363
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test
+
+import com.android.internal.util.test.SystemPreparer
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test
+import com.google.common.truth.Truth.assertThat
+import org.junit.ClassRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.RuleChain
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+
+@RunWith(DeviceJUnit4ClassRunner::class)
+class OriginalPackageMigrationTest : BaseHostJUnit4Test() {
+
+ companion object {
+ private const val TEST_PKG_NAME = "com.android.server.pm.test.dummy_app"
+ private const val VERSION_ONE = "PackageManagerDummyAppVersion1.apk"
+ private const val VERSION_TWO = "PackageManagerDummyAppVersion2.apk"
+ private const val VERSION_THREE = "PackageManagerDummyAppVersion3.apk"
+ private const val NEW_PKG = "PackageManagerDummyAppOriginalOverride.apk"
+
+ @get:ClassRule
+ val deviceRebootRule = SystemPreparer.TestRuleDelegate(true)
+ }
+
+ private val tempFolder = TemporaryFolder()
+ private val preparer: SystemPreparer = SystemPreparer(tempFolder,
+ SystemPreparer.RebootStrategy.START_STOP, deviceRebootRule) { this.device }
+
+ @get:Rule
+ val rules = RuleChain.outerRule(tempFolder).around(preparer)!!
+
+ @Test
+ fun lowerVersion() {
+ runForApk(VERSION_ONE)
+ }
+
+ @Test
+ fun sameVersion() {
+ runForApk(VERSION_TWO)
+ }
+
+ @Test
+ fun higherVersion() {
+ runForApk(VERSION_THREE)
+ }
+
+ // A bug was found where renamed the package during parsing was leading to an invalid version
+ // code check at scan time. A lower version package was being dropped after reboot. To test
+ // this, the override APK is defined as versionCode 2 and the original package is given
+ // versionCode 1, 2, and 3 from the other methods.
+ private fun runForApk(apk: String) {
+ preparer.pushApk(apk, Partition.SYSTEM)
+ .reboot()
+
+ device.getAppPackageInfo(TEST_PKG_NAME).run {
+ assertThat(codePath).contains(apk.removeSuffix(".apk"))
+ }
+
+ // Ensure data is preserved by writing to the original dataDir
+ val file = tempFolder.newFile().apply { writeText("Test") }
+ device.pushFile(file, "${HostUtils.getDataDir(device, TEST_PKG_NAME)}/files/test.txt")
+
+ preparer.deleteApk(apk, Partition.SYSTEM)
+ .pushApk(NEW_PKG, Partition.SYSTEM)
+ .reboot()
+
+ device.getAppPackageInfo(TEST_PKG_NAME)
+ .run {
+ assertThat(this.toString()).isNotEmpty()
+ assertThat(codePath)
+ .contains(NEW_PKG.removeSuffix(".apk"))
+ }
+
+ // And then reading the data contents back
+ assertThat(device.pullFileContents(
+ "${HostUtils.getDataDir(device, TEST_PKG_NAME)}/files/test.txt"))
+ .isEqualTo("Test")
+ }
+}
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/Partition.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/Partition.kt
new file mode 100644
index 000000000000..35192a73ceda
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/Partition.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test
+
+import java.nio.file.Path
+import java.nio.file.Paths
+
+// Unfortunately no easy way to access PMS SystemPartitions, so mock them here
+internal enum class Partition(val baseFolder: Path) {
+ SYSTEM("/system/app"),
+ VENDOR("/vendor/app"),
+ PRODUCT("/product/app"),
+ SYSTEM_EXT("/system_ext/app")
+ ;
+
+ constructor(baseFolder: String) : this(Paths.get(baseFolder))
+}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp
new file mode 100644
index 000000000000..9568faa7dfd0
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp
@@ -0,0 +1,33 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test_helper_app {
+ name: "PackageManagerDummyAppVersion1",
+ manifest: "AndroidManifestVersion1.xml"
+}
+
+android_test_helper_app {
+ name: "PackageManagerDummyAppVersion2",
+ manifest: "AndroidManifestVersion2.xml"
+}
+
+android_test_helper_app {
+ name: "PackageManagerDummyAppVersion3",
+ manifest: "AndroidManifestVersion3.xml"
+}
+
+android_test_helper_app {
+ name: "PackageManagerDummyAppOriginalOverride",
+ manifest: "AndroidManifestOriginalOverride.xml"
+}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml
new file mode 100644
index 000000000000..f16e1bc8a927
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.pm.test.dummy_app.override"
+ android:versionCode="2"
+ >
+
+ <original-package android:name="com.android.server.pm.test.dummy_app"/>
+
+</manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml
new file mode 100644
index 000000000000..d772050d7fd0
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.pm.test.dummy_app"
+ android:versionCode="1"
+ />
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml
new file mode 100644
index 000000000000..53f836b222e6
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.pm.test.dummy_app"
+ android:versionCode="2"
+ />
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml
new file mode 100644
index 000000000000..90ca9d0ac02c
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.pm.test.dummy_app"
+ android:versionCode="3"
+ />
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index ff34ebd8aa9d..b4e0f10f14b0 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -21,7 +21,7 @@ android_test {
"services.core",
"services.net",
"service-jobscheduler",
- "service-permission",
+ "service-permission.impl",
"service-blobstore",
"androidx.test.runner",
"androidx.test.ext.truth",
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 fde40aa77a0e..cdafd32cbbb5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -63,6 +63,7 @@ import static com.android.server.am.ProcessList.VISIBLE_APP_ADJ;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.AdditionalAnswers.answer;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyLong;
@@ -170,6 +171,7 @@ public class MockingOomAdjusterTests {
mock(OomAdjProfiler.class));
doReturn(new ActivityManagerService.ProcessChangeItem()).when(sService)
.enqueueProcessChangeItemLocked(anyInt(), anyInt());
+ doReturn(true).when(sService).containsTopUiOrRunningRemoteAnimOrEmptyLocked(any());
sService.mOomAdjuster = new OomAdjuster(sService, sService.mProcessList,
mock(ActiveUids.class));
sService.mOomAdjuster.mAdjSeq = 10000;
@@ -266,6 +268,21 @@ public class MockingOomAdjusterTests {
@SuppressWarnings("GuardedBy")
@Test
+ public void testUpdateOomAdj_DoOne_TopApp_PreemptedByTopUi() {
+ ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+ MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
+ doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
+ doReturn(app).when(sService).getTopAppLocked();
+ doReturn(false).when(sService).containsTopUiOrRunningRemoteAnimOrEmptyLocked(eq(app));
+ sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
+ doReturn(null).when(sService).getTopAppLocked();
+
+ assertProcStates(app, PROCESS_STATE_TOP, FOREGROUND_APP_ADJ, SCHED_GROUP_DEFAULT);
+ }
+
+ @SuppressWarnings("GuardedBy")
+ @Test
public void testUpdateOomAdj_DoOne_RunningInstrumentation() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 40a17061c7f1..979f4e179e95 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -44,7 +44,7 @@ android_test {
"hamcrest-library",
"servicestests-utils",
"service-jobscheduler",
- "service-permission",
+ "service-permission.impl",
// TODO: remove once Android migrates to JUnit 4.12,
// which provides assertThrows
"testng",
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
index a0b9d9d2a875..3167820f0a48 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
@@ -18,17 +18,24 @@ package com.android.server.devicepolicy;
import static android.os.UserHandle.USER_SYSTEM;
import static com.android.server.devicepolicy.DpmTestUtils.writeInputStreamToFile;
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import static org.junit.Assert.assertArrayEquals;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.ComponentName;
+import android.content.Intent;
import android.content.pm.PackageManager;
+import android.os.Build;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
@@ -354,8 +361,7 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase {
prepareAdmin1AsDo();
prepareAdminAnotherPackageAsPo(COPE_PROFILE_USER_ID);
- final DevicePolicyManagerServiceTestable dpms;
- dpms = bootDpmsUp();
+ final DevicePolicyManagerServiceTestable dpms = bootDpmsUp();
// DO should still be DO since no migration should happen.
assertTrue(dpms.mOwners.hasDeviceOwner());
@@ -364,13 +370,12 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase {
@SmallTest
public void testCompMigrationAffiliated() throws Exception {
prepareAdmin1AsDo();
- prepareAdmin1AsPo(COPE_PROFILE_USER_ID);
+ prepareAdmin1AsPo(COPE_PROFILE_USER_ID, Build.VERSION_CODES.R);
// Secure lock screen is needed for password policy APIs to work.
when(getServices().lockPatternUtils.hasSecureLockScreen()).thenReturn(true);
- final DevicePolicyManagerServiceTestable dpms;
- dpms = bootDpmsUp();
+ final DevicePolicyManagerServiceTestable dpms = bootDpmsUp();
// DO should cease to be DO.
assertFalse(dpms.mOwners.hasDeviceOwner());
@@ -408,6 +413,66 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase {
dpms.getProfileOwnerAdminLocked(COPE_PROFILE_USER_ID)
.getEffectiveRestrictions()
.containsKey(UserManager.DISALLOW_CONFIG_DATE_TIME));
+ assertEquals("Personal apps suspension wasn't migrated",
+ DevicePolicyManager.PERSONAL_APPS_NOT_SUSPENDED,
+ dpm.getPersonalAppsSuspendedReasons(admin1));
+ });
+ }
+
+ @SmallTest
+ public void testCompMigration_keepSuspendedAppsWhenDpcIsRPlus() throws Exception {
+ prepareAdmin1AsDo();
+ prepareAdmin1AsPo(COPE_PROFILE_USER_ID, Build.VERSION_CODES.R);
+
+ // Pretend some packages are suspended.
+ when(getServices().packageManagerInternal.isSuspendingAnyPackages(
+ PLATFORM_PACKAGE_NAME, USER_SYSTEM)).thenReturn(true);
+
+ final DevicePolicyManagerServiceTestable dpms = bootDpmsUp();
+
+ verify(getServices().packageManagerInternal, never())
+ .unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, USER_SYSTEM);
+
+ sendBroadcastWithUser(dpms, Intent.ACTION_USER_STARTED, USER_SYSTEM);
+
+ // Verify that actual package suspension state is not modified after user start
+ verify(getServices().packageManagerInternal, never())
+ .unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, USER_SYSTEM);
+ verify(getServices().ipackageManager, never()).setPackagesSuspendedAsUser(
+ any(), anyBoolean(), any(), any(), any(), any(), anyInt());
+
+ final DpmMockContext poContext = new DpmMockContext(getServices(), mRealTestContext);
+ poContext.binder.callingUid = UserHandle.getUid(COPE_PROFILE_USER_ID, COPE_ADMIN1_APP_ID);
+
+ runAsCaller(poContext, dpms, dpm -> {
+ assertEquals("Personal apps suspension wasn't migrated",
+ DevicePolicyManager.PERSONAL_APPS_SUSPENDED_EXPLICITLY,
+ dpm.getPersonalAppsSuspendedReasons(admin1));
+ });
+ }
+
+ @SmallTest
+ public void testCompMigration_unsuspendAppsWhenDpcNotRPlus() throws Exception {
+ prepareAdmin1AsDo();
+ prepareAdmin1AsPo(COPE_PROFILE_USER_ID, Build.VERSION_CODES.Q);
+
+ // Pretend some packages are suspended.
+ when(getServices().packageManagerInternal.isSuspendingAnyPackages(
+ PLATFORM_PACKAGE_NAME, USER_SYSTEM)).thenReturn(true);
+
+ final DevicePolicyManagerServiceTestable dpms = bootDpmsUp();
+
+ // Verify that apps get unsuspended.
+ verify(getServices().packageManagerInternal)
+ .unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, USER_SYSTEM);
+
+ final DpmMockContext poContext = new DpmMockContext(getServices(), mRealTestContext);
+ poContext.binder.callingUid = UserHandle.getUid(COPE_PROFILE_USER_ID, COPE_ADMIN1_APP_ID);
+
+ runAsCaller(poContext, dpms, dpm -> {
+ assertEquals("Personal apps weren't unsuspended",
+ DevicePolicyManager.PERSONAL_APPS_NOT_SUSPENDED,
+ dpm.getPersonalAppsSuspendedReasons(admin1));
});
}
@@ -439,22 +504,23 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase {
.getAbsoluteFile());
}
- private void prepareAdmin1AsPo(int profileUserId) throws Exception {
+ private void prepareAdmin1AsPo(int profileUserId, int targetSdk) throws Exception {
preparePo(profileUserId, admin1, R.raw.comp_profile_owner_same_package,
- R.raw.comp_policies_profile_same_package, COPE_ADMIN1_APP_ID);
+ R.raw.comp_policies_profile_same_package, COPE_ADMIN1_APP_ID, targetSdk);
}
private void prepareAdminAnotherPackageAsPo(int profileUserId) throws Exception {
preparePo(profileUserId, adminAnotherPackage, R.raw.comp_profile_owner_another_package,
- R.raw.comp_policies_profile_another_package, COPE_ANOTHER_ADMIN_APP_ID);
+ R.raw.comp_policies_profile_another_package, COPE_ANOTHER_ADMIN_APP_ID,
+ Build.VERSION.SDK_INT);
}
private void preparePo(int profileUserId, ComponentName admin, int profileOwnerXmlResId,
- int policyXmlResId, int adminAppId) throws Exception {
+ int policyXmlResId, int adminAppId, int targetSdk) throws Exception {
final File profileDir = getServices().addUser(profileUserId, 0,
UserManager.USER_TYPE_PROFILE_MANAGED, USER_SYSTEM /* profile group */);
- setUpPackageManagerForFakeAdmin(
- admin, UserHandle.getUid(profileUserId, adminAppId), admin1);
+ setUpPackageManagerForFakeAdmin(admin, UserHandle.getUid(profileUserId, adminAppId),
+ /* enabledSetting =*/ null, targetSdk, admin1);
writeInputStreamToFile(getRawStream(policyXmlResId),
(new File(profileDir, "device_policies.xml")).getAbsoluteFile());
writeInputStreamToFile(getRawStream(profileOwnerXmlResId),
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 4a774898e1b5..daaabf8141ff 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -254,7 +254,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
@Override
protected void tearDown() throws Exception {
- flushTasks();
+ flushTasks(dpms);
getMockTransferMetadataManager().deleteMetadataFile();
super.tearDown();
}
@@ -4961,7 +4961,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// CertificateMonitor.updateInstalledCertificates is called on the background thread,
// let it finish with system uid, otherwise it will throw and crash.
- flushTasks();
+ flushTasks(dpms);
mContext.binder.restoreCallingIdentity(ident);
}
@@ -5459,7 +5459,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
getServices().injectBroadcast(mServiceContext, new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED)
.putExtra(Intent.EXTRA_USER_HANDLE, callerUser.getIdentifier()),
callerUser.getIdentifier());
- flushTasks();
+ flushTasks(dpms);
final List<String> ownerInstalledCaCerts = new ArrayList<>();
@@ -5486,7 +5486,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
getServices().injectBroadcast(mServiceContext, new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED)
.putExtra(Intent.EXTRA_USER_HANDLE, callerUser.getIdentifier()),
callerUser.getIdentifier());
- flushTasks();
+ flushTasks(dpms);
// Verify that the CA cert is no longer reported as installed by the Device Owner / Profile
// Owner.
@@ -5530,7 +5530,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
getServices().injectBroadcast(mServiceContext, new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED)
.putExtra(Intent.EXTRA_USER_HANDLE, callerUser.getIdentifier()),
callerUser.getIdentifier());
- flushTasks();
+ flushTasks(dpms);
// Removing the Profile Owner should clear the information on which CA certs were installed
runAsCaller(admin1Context, dpms, dpm -> dpm.clearProfileOwner(admin1));
@@ -6311,7 +6311,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
clearInvocations(getServices().alarmManager);
setUserUnlocked(CALLER_USER_HANDLE, false);
- sendBroadcastWithUser(Intent.ACTION_USER_STOPPED, CALLER_USER_HANDLE);
+ sendBroadcastWithUser(dpms, Intent.ACTION_USER_STOPPED, CALLER_USER_HANDLE);
// Verify the alarm was scheduled for time when the warning should be shown.
verify(getServices().alarmManager, times(1))
@@ -6325,7 +6325,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// Pretend the alarm went off.
dpms.mMockInjector.setSystemCurrentTimeMillis(PROFILE_OFF_WARNING_TIME + 10);
- sendBroadcastWithUser(ACTION_PROFILE_OFF_DEADLINE, CALLER_USER_HANDLE);
+ sendBroadcastWithUser(dpms, ACTION_PROFILE_OFF_DEADLINE, CALLER_USER_HANDLE);
// Verify the alarm was scheduled for the actual deadline this time.
verify(getServices().alarmManager, times(1)).set(anyInt(), eq(PROFILE_OFF_DEADLINE), any());
@@ -6340,7 +6340,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// Pretend the alarm went off.
dpms.mMockInjector.setSystemCurrentTimeMillis(PROFILE_OFF_DEADLINE + 10);
- sendBroadcastWithUser(ACTION_PROFILE_OFF_DEADLINE, CALLER_USER_HANDLE);
+ sendBroadcastWithUser(dpms, ACTION_PROFILE_OFF_DEADLINE, CALLER_USER_HANDLE);
// Verify the alarm was not set.
verifyZeroInteractions(getServices().alarmManager);
@@ -6364,10 +6364,10 @@ public class DevicePolicyManagerTest extends DpmTestBase {
mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
setUserUnlocked(CALLER_USER_HANDLE, false);
- sendBroadcastWithUser(Intent.ACTION_USER_STOPPED, CALLER_USER_HANDLE);
+ sendBroadcastWithUser(dpms, Intent.ACTION_USER_STOPPED, CALLER_USER_HANDLE);
clearInvocations(getServices().alarmManager);
setUserUnlocked(CALLER_USER_HANDLE, true);
- sendBroadcastWithUser(Intent.ACTION_USER_UNLOCKED, CALLER_USER_HANDLE);
+ sendBroadcastWithUser(dpms, Intent.ACTION_USER_UNLOCKED, CALLER_USER_HANDLE);
// Verify that the alarm got discharged.
verify(getServices().alarmManager, times(1)).cancel((PendingIntent) null);
@@ -6384,16 +6384,16 @@ public class DevicePolicyManagerTest extends DpmTestBase {
mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
setUserUnlocked(CALLER_USER_HANDLE, false);
- sendBroadcastWithUser(Intent.ACTION_USER_STOPPED, CALLER_USER_HANDLE);
+ sendBroadcastWithUser(dpms, Intent.ACTION_USER_STOPPED, CALLER_USER_HANDLE);
// Pretend the alarm went off.
dpms.mMockInjector.setSystemCurrentTimeMillis(PROFILE_OFF_WARNING_TIME + 10);
- sendBroadcastWithUser(ACTION_PROFILE_OFF_DEADLINE, CALLER_USER_HANDLE);
+ sendBroadcastWithUser(dpms, ACTION_PROFILE_OFF_DEADLINE, CALLER_USER_HANDLE);
clearInvocations(getServices().alarmManager);
clearInvocations(getServices().notificationManager);
setUserUnlocked(CALLER_USER_HANDLE, true);
- sendBroadcastWithUser(Intent.ACTION_USER_UNLOCKED, CALLER_USER_HANDLE);
+ sendBroadcastWithUser(dpms, Intent.ACTION_USER_UNLOCKED, CALLER_USER_HANDLE);
// Verify that the alarm got discharged.
verify(getServices().alarmManager, times(1)).cancel((PendingIntent) null);
@@ -6413,24 +6413,24 @@ public class DevicePolicyManagerTest extends DpmTestBase {
mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
setUserUnlocked(CALLER_USER_HANDLE, false);
- sendBroadcastWithUser(Intent.ACTION_USER_STOPPED, CALLER_USER_HANDLE);
+ sendBroadcastWithUser(dpms, Intent.ACTION_USER_STOPPED, CALLER_USER_HANDLE);
// Pretend the alarm went off after the deadline.
dpms.mMockInjector.setSystemCurrentTimeMillis(PROFILE_OFF_DEADLINE + 10);
- sendBroadcastWithUser(ACTION_PROFILE_OFF_DEADLINE, CALLER_USER_HANDLE);
+ sendBroadcastWithUser(dpms, ACTION_PROFILE_OFF_DEADLINE, CALLER_USER_HANDLE);
clearInvocations(getServices().alarmManager);
clearInvocations(getServices().notificationManager);
clearInvocations(getServices().ipackageManager);
// Pretend the user clicked on the "apps suspended" notification to turn the profile on.
- sendBroadcastWithUser(ACTION_TURN_PROFILE_ON_NOTIFICATION, CALLER_USER_HANDLE);
+ sendBroadcastWithUser(dpms, ACTION_TURN_PROFILE_ON_NOTIFICATION, CALLER_USER_HANDLE);
// Verify that the profile is turned on.
verify(getServices().userManager, times(1))
.requestQuietModeEnabled(eq(false), eq(UserHandle.of(CALLER_USER_HANDLE)));
setUserUnlocked(CALLER_USER_HANDLE, true);
- sendBroadcastWithUser(Intent.ACTION_USER_UNLOCKED, CALLER_USER_HANDLE);
+ sendBroadcastWithUser(dpms, Intent.ACTION_USER_UNLOCKED, CALLER_USER_HANDLE);
// Verify that the notification is removed (at this point DPC should show it).
verify(getServices().notificationManager, times(1))
@@ -6454,13 +6454,6 @@ public class DevicePolicyManagerTest extends DpmTestBase {
.isEqualTo(DevicePolicyManager.PERSONAL_APPS_SUSPENDED_PROFILE_TIMEOUT);
}
- private void sendBroadcastWithUser(String action, int userHandle) throws Exception {
- final Intent intent = new Intent(action);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle);
- getServices().injectBroadcast(mServiceContext, intent, userHandle);
- flushTasks();
- }
-
private void setUserUnlocked(int userHandle, boolean unlocked) {
when(getServices().userManager.isUserUnlocked(eq(userHandle))).thenReturn(unlocked);
}
@@ -6471,10 +6464,6 @@ public class DevicePolicyManagerTest extends DpmTestBase {
when(getServices().userManager.isUserUnlocked()).thenReturn(true);
- // Pretend our admin handles CHECK_POLICY_COMPLIANCE intent.
- final Intent intent = new Intent(ACTION_CHECK_POLICY_COMPLIANCE);
- intent.setPackage(admin1.getPackageName());
-
doReturn(Collections.singletonList(new ResolveInfo()))
.when(getServices().packageManager).queryIntentActivitiesAsUser(
any(Intent.class), anyInt(), eq(CALLER_USER_HANDLE));
@@ -6674,12 +6663,4 @@ public class DevicePolicyManagerTest extends DpmTestBase {
return new StringParceledListSlice(Arrays.asList(s));
}
- private void flushTasks() throws Exception {
- dpms.mHandler.runWithScissors(() -> {}, 0 /*now*/);
- dpms.mBackgroundHandler.runWithScissors(() -> {}, 0 /*now*/);
-
- // We can't let exceptions happen on the background thread. Throw them here if they happen
- // so they still cause the test to fail despite being suppressed.
- getServices().rethrowBackgroundBroadcastExceptions();
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
index 9a1a5fbfd186..41d54e9010d3 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
@@ -83,6 +83,23 @@ public abstract class DpmTestBase extends AndroidTestCase {
return mServices;
}
+ protected void sendBroadcastWithUser(DevicePolicyManagerServiceTestable dpms, String action,
+ int userHandle) throws Exception {
+ final Intent intent = new Intent(action);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle);
+ getServices().injectBroadcast(getContext(), intent, userHandle);
+ flushTasks(dpms);
+ }
+
+ protected void flushTasks(DevicePolicyManagerServiceTestable dpms) throws Exception {
+ dpms.mHandler.runWithScissors(() -> { }, 0 /*now*/);
+ dpms.mBackgroundHandler.runWithScissors(() -> { }, 0 /*now*/);
+
+ // We can't let exceptions happen on the background thread. Throw them here if they happen
+ // so they still cause the test to fail despite being suppressed.
+ getServices().rethrowBackgroundBroadcastExceptions();
+ }
+
protected interface DpmRunnable {
void run(DevicePolicyManager dpm) throws Exception;
}
@@ -180,7 +197,7 @@ public abstract class DpmTestBase extends AndroidTestCase {
* @param copyFromAdmin package information for {@code admin} will be built based on this
* component's information.
*/
- private void setUpPackageManagerForFakeAdmin(ComponentName admin, int packageUid,
+ protected void setUpPackageManagerForFakeAdmin(ComponentName admin, int packageUid,
Integer enabledSetting, Integer appTargetSdk, ComponentName copyFromAdmin)
throws Exception {
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 4127fece17bd..c4d121170624 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -43,6 +43,7 @@ import android.content.ContextWrapper;
import android.content.pm.UserInfo;
import android.hardware.rebootescrow.IRebootEscrow;
import android.os.RemoteException;
+import android.os.ServiceSpecificException;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
@@ -178,6 +179,13 @@ public class RebootEscrowManagerTests {
}
@Test
+ public void clearCredentials_HalFailure_NonFatal() throws Exception {
+ doThrow(ServiceSpecificException.class).when(mRebootEscrow).storeKey(any());
+ mService.clearRebootEscrow();
+ verify(mRebootEscrow).storeKey(eq(new byte[32]));
+ }
+
+ @Test
public void armService_Success() throws Exception {
RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
mService.setRebootEscrowListener(mockListener);
@@ -200,6 +208,24 @@ public class RebootEscrowManagerTests {
}
@Test
+ public void armService_HalFailure_NonFatal() throws Exception {
+ RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+ mService.setRebootEscrowListener(mockListener);
+ mService.prepareRebootEscrow();
+
+ clearInvocations(mRebootEscrow);
+ mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+ verify(mockListener).onPreparedForReboot(eq(true));
+ verify(mRebootEscrow, never()).storeKey(any());
+
+ assertNull(
+ mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_ARMED_KEY, null, USER_SYSTEM));
+ doThrow(ServiceSpecificException.class).when(mRebootEscrow).storeKey(any());
+ assertFalse(mService.armRebootEscrowIfNeeded());
+ verify(mRebootEscrow).storeKey(any());
+ }
+
+ @Test
public void armService_MultipleUsers_Success() throws Exception {
RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
mService.setRebootEscrowListener(mockListener);
diff --git a/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
index 60104d390eb7..b09a3c374e86 100644
--- a/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
@@ -117,10 +117,10 @@ public final class ShareTargetPredictorTest {
@Test
public void testPredictTargets() {
- mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc1"));
- mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc2"));
- mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc3"));
- mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc4"));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc1", 0));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc2", 0));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc3", 0));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc4", 0));
when(mPackageData1.getConversationInfo("sc1")).thenReturn(mock(ConversationInfo.class));
when(mPackageData1.getConversationInfo("sc2")).thenReturn(mock(ConversationInfo.class));
@@ -165,12 +165,12 @@ public final class ShareTargetPredictorTest {
@Test
public void testPredictTargets_reachTargetsLimit() {
- mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc1"));
- mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc2"));
- mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc3"));
- mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc4"));
- mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc5"));
- mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc6"));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc1", 0));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc2", 0));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc3", 0));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc4", 0));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc5", 0));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc6", 0));
when(mPackageData1.getConversationInfo("sc1")).thenReturn(mock(ConversationInfo.class));
when(mPackageData1.getConversationInfo("sc2")).thenReturn(mock(ConversationInfo.class));
@@ -250,6 +250,41 @@ public final class ShareTargetPredictorTest {
}
@Test
+ public void testPredictTargets_noSharingHistoryRankedByShortcutRank() {
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc1", 3));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc2", 2));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc3", 1));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc4", 0));
+
+ when(mPackageData1.getConversationInfo("sc1")).thenReturn(mock(ConversationInfo.class));
+ when(mPackageData1.getConversationInfo("sc2")).thenReturn(mock(ConversationInfo.class));
+ when(mPackageData2.getConversationInfo("sc3")).thenReturn(mock(ConversationInfo.class));
+ // "sc4" does not have a ConversationInfo.
+
+ mPredictor.predictTargets();
+
+ verify(mUpdatePredictionsMethod).accept(mAppTargetCaptor.capture());
+ List<AppTarget> res = mAppTargetCaptor.getValue();
+ assertEquals(4, res.size());
+
+ assertEquals("sc4", res.get(0).getId().getId());
+ assertEquals(CLASS_2, res.get(0).getClassName());
+ assertEquals(PACKAGE_2, res.get(0).getPackageName());
+
+ assertEquals("sc3", res.get(1).getId().getId());
+ assertEquals(CLASS_2, res.get(1).getClassName());
+ assertEquals(PACKAGE_2, res.get(1).getPackageName());
+
+ assertEquals("sc2", res.get(2).getId().getId());
+ assertEquals(CLASS_1, res.get(2).getClassName());
+ assertEquals(PACKAGE_1, res.get(2).getPackageName());
+
+ assertEquals("sc1", res.get(3).getId().getId());
+ assertEquals(CLASS_1, res.get(3).getClassName());
+ assertEquals(PACKAGE_1, res.get(3).getPackageName());
+ }
+
+ @Test
public void testSortTargets() {
AppTarget appTarget1 = new AppTarget.Builder(
new AppTargetId("cls1#pkg1"), PACKAGE_1, UserHandle.of(USER_ID))
@@ -348,19 +383,20 @@ public final class ShareTargetPredictorTest {
}
private static ShareShortcutInfo buildShareShortcut(
- String packageName, String className, String shortcutId) {
- ShortcutInfo shortcutInfo = buildShortcut(packageName, shortcutId);
+ String packageName, String className, String shortcutId, int rank) {
+ ShortcutInfo shortcutInfo = buildShortcut(packageName, shortcutId, rank);
ComponentName componentName = new ComponentName(packageName, className);
return new ShareShortcutInfo(shortcutInfo, componentName);
}
- private static ShortcutInfo buildShortcut(String packageName, String shortcutId) {
+ private static ShortcutInfo buildShortcut(String packageName, String shortcutId, int rank) {
Context mockContext = mock(Context.class);
when(mockContext.getPackageName()).thenReturn(packageName);
when(mockContext.getUserId()).thenReturn(USER_ID);
when(mockContext.getUser()).thenReturn(UserHandle.of(USER_ID));
ShortcutInfo.Builder builder = new ShortcutInfo.Builder(mockContext, shortcutId)
.setShortLabel(shortcutId)
+ .setRank(rank)
.setIntent(new Intent("TestIntent"));
return builder.build();
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index 399a00f0cb5a..f205fde88c0d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -23,7 +23,6 @@ import static org.hamcrest.Matchers.empty;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -58,7 +57,6 @@ import org.junit.runners.JUnit4;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import org.mockito.stubbing.Answer;
import java.security.cert.CertificateException;
import java.util.ArrayList;
@@ -71,20 +69,16 @@ import java.util.Set;
@RunWith(JUnit4.class)
public class AppsFilterTest {
- private static final int DUMMY_CALLING_APPID = 10345;
- private static final int DUMMY_TARGET_APPID = 10556;
- private static final int DUMMY_ACTOR_APPID = 10656;
- private static final int DUMMY_OVERLAY_APPID = 10756;
- private static final int SYSTEM_USER = 0;
- private static final int[] SINGLE_USER_ARRAY = {SYSTEM_USER};
+ private static final int DUMMY_CALLING_UID = 10345;
+ private static final int DUMMY_TARGET_UID = 10556;
+ private static final int DUMMY_ACTOR_UID = 10656;
+ private static final int DUMMY_OVERLAY_UID = 10756;
+ private static final int DUMMY_ACTOR_TWO_UID = 10856;
@Mock
AppsFilter.FeatureConfig mFeatureConfigMock;
- @Mock
- AppsFilter.StateProvider mStateProvider;
private ArrayMap<String, PackageSetting> mExisting = new ArrayMap<>();
- private Object mDummyLock = new Object();
private static ParsingPackage pkg(String packageName) {
return PackageImpl.forTesting(packageName)
@@ -176,24 +170,15 @@ public class AppsFilterTest {
mExisting = new ArrayMap<>();
MockitoAnnotations.initMocks(this);
- doAnswer(invocation -> {
- ((AppsFilter.StateProvider.CurrentStateCallback) invocation.getArgument(0))
- .currentState(mExisting, SINGLE_USER_ARRAY);
- return null;
- }).when(mStateProvider)
- .runWithState(any(AppsFilter.StateProvider.CurrentStateCallback.class));
-
when(mFeatureConfigMock.isGloballyEnabled()).thenReturn(true);
- when(mFeatureConfigMock.packageIsEnabled(any(AndroidPackage.class))).thenAnswer(
- (Answer<Boolean>) invocation ->
- ((AndroidPackage)invocation.getArgument(SYSTEM_USER)).getTargetSdkVersion()
- >= Build.VERSION_CODES.R);
+ when(mFeatureConfigMock.packageIsEnabled(any(AndroidPackage.class)))
+ .thenReturn(true);
}
@Test
public void testSystemReadyPropogates() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
appsFilter.onSystemReady();
verify(mFeatureConfigMock).onSystemReady();
}
@@ -201,23 +186,22 @@ public class AppsFilterTest {
@Test
public void testQueriesAction_FilterMatches() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
- pkg("com.some.package", new IntentFilter("TEST_ACTION")), DUMMY_TARGET_APPID);
+ pkg("com.some.package", new IntentFilter("TEST_ACTION")), DUMMY_TARGET_UID);
PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package", new Intent("TEST_ACTION")), DUMMY_CALLING_APPID);
+ pkg("com.some.other.package", new Intent("TEST_ACTION")), DUMMY_CALLING_UID);
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
- SYSTEM_USER));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
@Test
public void testQueriesProtectedAction_FilterDoesNotMatch() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
final Signature frameworkSignature = Mockito.mock(Signature.class);
final PackageParser.SigningDetails frameworkSigningDetails =
new PackageParser.SigningDetails(new Signature[]{frameworkSignature}, 1);
@@ -227,174 +211,164 @@ public class AppsFilterTest {
b -> b.setSigningDetails(frameworkSigningDetails));
appsFilter.onSystemReady();
- final int activityUid = DUMMY_TARGET_APPID;
+ final int activityUid = DUMMY_TARGET_UID;
PackageSetting targetActivity = simulateAddPackage(appsFilter,
pkg("com.target.activity", new IntentFilter("TEST_ACTION")), activityUid);
- final int receiverUid = DUMMY_TARGET_APPID + 1;
+ final int receiverUid = DUMMY_TARGET_UID + 1;
PackageSetting targetReceiver = simulateAddPackage(appsFilter,
pkgWithReceiver("com.target.receiver", new IntentFilter("TEST_ACTION")),
receiverUid);
- final int callingUid = DUMMY_CALLING_APPID;
+ final int callingUid = DUMMY_CALLING_UID;
PackageSetting calling = simulateAddPackage(appsFilter,
pkg("com.calling.action", new Intent("TEST_ACTION")), callingUid);
- final int wildcardUid = DUMMY_CALLING_APPID + 1;
+ final int wildcardUid = DUMMY_CALLING_UID + 1;
PackageSetting callingWildCard = simulateAddPackage(appsFilter,
pkg("com.calling.wildcard", new Intent("*")), wildcardUid);
- assertFalse(appsFilter.shouldFilterApplication(callingUid, calling, targetActivity,
- SYSTEM_USER));
- assertTrue(appsFilter.shouldFilterApplication(callingUid, calling, targetReceiver,
- SYSTEM_USER));
+ assertFalse(appsFilter.shouldFilterApplication(callingUid, calling, targetActivity, 0));
+ assertTrue(appsFilter.shouldFilterApplication(callingUid, calling, targetReceiver, 0));
assertFalse(appsFilter.shouldFilterApplication(
- wildcardUid, callingWildCard, targetActivity, SYSTEM_USER));
+ wildcardUid, callingWildCard, targetActivity, 0));
assertTrue(appsFilter.shouldFilterApplication(
- wildcardUid, callingWildCard, targetReceiver, SYSTEM_USER));
+ wildcardUid, callingWildCard, targetReceiver, 0));
}
@Test
public void testQueriesProvider_FilterMatches() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
- pkgWithProvider("com.some.package", "com.some.authority"), DUMMY_TARGET_APPID);
+ pkgWithProvider("com.some.package", "com.some.authority"), DUMMY_TARGET_UID);
PackageSetting calling = simulateAddPackage(appsFilter,
pkgQueriesProvider("com.some.other.package", "com.some.authority"),
- DUMMY_CALLING_APPID);
+ DUMMY_CALLING_UID);
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
- SYSTEM_USER));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
@Test
public void testQueriesDifferentProvider_Filters() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
- pkgWithProvider("com.some.package", "com.some.authority"), DUMMY_TARGET_APPID);
+ pkgWithProvider("com.some.package", "com.some.authority"), DUMMY_TARGET_UID);
PackageSetting calling = simulateAddPackage(appsFilter,
pkgQueriesProvider("com.some.other.package", "com.some.other.authority"),
- DUMMY_CALLING_APPID);
+ DUMMY_CALLING_UID);
- assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
- SYSTEM_USER));
+ assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
@Test
public void testQueriesProviderWithSemiColon_FilterMatches() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
pkgWithProvider("com.some.package", "com.some.authority;com.some.other.authority"),
- DUMMY_TARGET_APPID);
+ DUMMY_TARGET_UID);
PackageSetting calling = simulateAddPackage(appsFilter,
pkgQueriesProvider("com.some.other.package", "com.some.authority"),
- DUMMY_CALLING_APPID);
+ DUMMY_CALLING_UID);
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
- SYSTEM_USER));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
@Test
public void testQueriesAction_NoMatchingAction_Filters() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
- pkg("com.some.package"), DUMMY_TARGET_APPID);
+ pkg("com.some.package"), DUMMY_TARGET_UID);
PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package", new Intent("TEST_ACTION")), DUMMY_CALLING_APPID);
+ pkg("com.some.other.package", new Intent("TEST_ACTION")), DUMMY_CALLING_UID);
- assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
- SYSTEM_USER));
+ assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
@Test
public void testQueriesAction_NoMatchingActionFilterLowSdk_DoesntFilter() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
- pkg("com.some.package"), DUMMY_TARGET_APPID);
- ParsingPackage callingPkg = pkg("com.some.other.package",
- new Intent("TEST_ACTION"))
- .setTargetSdkVersion(Build.VERSION_CODES.P);
- PackageSetting calling = simulateAddPackage(appsFilter, callingPkg,
- DUMMY_CALLING_APPID);
+ pkg("com.some.package"), DUMMY_TARGET_UID);
+ PackageSetting calling = simulateAddPackage(appsFilter,
+ pkg("com.some.other.package",
+ new Intent("TEST_ACTION"))
+ .setTargetSdkVersion(Build.VERSION_CODES.P),
+ DUMMY_CALLING_UID);
+ when(mFeatureConfigMock.packageIsEnabled(calling.pkg)).thenReturn(false);
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
- SYSTEM_USER));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
@Test
public void testNoQueries_Filters() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
- pkg("com.some.package"), DUMMY_TARGET_APPID);
+ pkg("com.some.package"), DUMMY_TARGET_UID);
PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package"), DUMMY_CALLING_APPID);
+ pkg("com.some.other.package"), DUMMY_CALLING_UID);
- assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
- SYSTEM_USER));
+ assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
@Test
public void testForceQueryable_DoesntFilter() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
- pkg("com.some.package").setForceQueryable(true), DUMMY_TARGET_APPID);
+ pkg("com.some.package").setForceQueryable(true), DUMMY_TARGET_UID);
PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package"), DUMMY_CALLING_APPID);
+ pkg("com.some.other.package"), DUMMY_CALLING_UID);
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
- SYSTEM_USER));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
@Test
public void testForceQueryableByDevice_SystemCaller_DoesntFilter() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{"com.some.package"},
- false, null);
+ new AppsFilter(mFeatureConfigMock, new String[]{"com.some.package"}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
- pkg("com.some.package"), DUMMY_TARGET_APPID,
+ pkg("com.some.package"), DUMMY_TARGET_UID,
setting -> setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM));
PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package"), DUMMY_CALLING_APPID);
+ pkg("com.some.other.package"), DUMMY_CALLING_UID);
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
- SYSTEM_USER));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
@Test
public void testSystemSignedTarget_DoesntFilter() throws CertificateException {
final AppsFilter appsFilter =
- new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
appsFilter.onSystemReady();
final Signature frameworkSignature = Mockito.mock(Signature.class);
@@ -408,67 +382,62 @@ public class AppsFilterTest {
simulateAddPackage(appsFilter, pkg("android"), 1000,
b -> b.setSigningDetails(frameworkSigningDetails));
PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"),
- DUMMY_TARGET_APPID,
+ DUMMY_TARGET_UID,
b -> b.setSigningDetails(frameworkSigningDetails)
.setPkgFlags(ApplicationInfo.FLAG_SYSTEM));
PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package"), DUMMY_CALLING_APPID,
+ pkg("com.some.other.package"), DUMMY_CALLING_UID,
b -> b.setSigningDetails(otherSigningDetails));
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
- SYSTEM_USER));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
@Test
public void testForceQueryableByDevice_NonSystemCaller_Filters() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{"com.some.package"},
- false, null);
+ new AppsFilter(mFeatureConfigMock, new String[]{"com.some.package"}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
- pkg("com.some.package"), DUMMY_TARGET_APPID);
+ pkg("com.some.package"), DUMMY_TARGET_UID);
PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package"), DUMMY_CALLING_APPID);
+ pkg("com.some.other.package"), DUMMY_CALLING_UID);
- assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
- SYSTEM_USER));
+ assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
@Test
public void testSystemQueryable_DoesntFilter() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{},
+ new AppsFilter(mFeatureConfigMock, new String[]{},
true /* system force queryable */, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
- pkg("com.some.package"), DUMMY_TARGET_APPID,
+ pkg("com.some.package"), DUMMY_TARGET_UID,
setting -> setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM));
PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package"), DUMMY_CALLING_APPID);
+ pkg("com.some.other.package"), DUMMY_CALLING_UID);
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
- SYSTEM_USER));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
@Test
public void testQueriesPackage_DoesntFilter() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
- pkg("com.some.package"), DUMMY_TARGET_APPID);
+ pkg("com.some.package"), DUMMY_TARGET_UID);
PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package", "com.some.package"), DUMMY_CALLING_APPID);
+ pkg("com.some.other.package", "com.some.package"), DUMMY_CALLING_UID);
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
- SYSTEM_USER));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
@Test
@@ -476,67 +445,63 @@ public class AppsFilterTest {
when(mFeatureConfigMock.packageIsEnabled(any(AndroidPackage.class)))
.thenReturn(false);
final AppsFilter appsFilter =
- new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(
- appsFilter, pkg("com.some.package"), DUMMY_TARGET_APPID);
+ appsFilter, pkg("com.some.package"), DUMMY_TARGET_UID);
PackageSetting calling = simulateAddPackage(
- appsFilter, pkg("com.some.other.package"), DUMMY_CALLING_APPID);
+ appsFilter, pkg("com.some.other.package"), DUMMY_CALLING_UID);
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
- SYSTEM_USER));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
@Test
public void testSystemUid_DoesntFilter() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
- pkg("com.some.package"), DUMMY_TARGET_APPID);
+ pkg("com.some.package"), DUMMY_TARGET_UID);
- assertFalse(appsFilter.shouldFilterApplication(SYSTEM_USER, null, target, SYSTEM_USER));
+ assertFalse(appsFilter.shouldFilterApplication(0, null, target, 0));
assertFalse(appsFilter.shouldFilterApplication(Process.FIRST_APPLICATION_UID - 1,
- null, target, SYSTEM_USER));
+ null, target, 0));
}
@Test
public void testNonSystemUid_NoCallingSetting_Filters() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter,
- pkg("com.some.package"), DUMMY_TARGET_APPID);
+ pkg("com.some.package"), DUMMY_TARGET_UID);
- assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, null, target,
- SYSTEM_USER));
+ assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, null, target, 0));
}
@Test
public void testNoTargetPackage_filters() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = new PackageSettingBuilder()
- .setAppId(DUMMY_TARGET_APPID)
.setName("com.some.package")
.setCodePath("/")
.setResourcePath("/")
.setPVersionCode(1L)
.build();
PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package", new Intent("TEST_ACTION")), DUMMY_CALLING_APPID);
+ pkg("com.some.other.package", new Intent("TEST_ACTION")), DUMMY_CALLING_UID);
- assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
- SYSTEM_USER));
+ assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
@Test
@@ -551,11 +516,7 @@ public class AppsFilterTest {
.setOverlayTargetName("overlayableName");
ParsingPackage actor = pkg("com.some.package.actor");
- final AppsFilter appsFilter = new AppsFilter(
- mStateProvider,
- mFeatureConfigMock,
- new String[]{},
- false,
+ final AppsFilter appsFilter = new AppsFilter(mFeatureConfigMock, new String[]{}, false,
new OverlayReferenceMapper.Provider() {
@Nullable
@Override
@@ -583,34 +544,31 @@ public class AppsFilterTest {
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
- PackageSetting targetSetting = simulateAddPackage(appsFilter, target, DUMMY_TARGET_APPID);
- PackageSetting overlaySetting =
- simulateAddPackage(appsFilter, overlay, DUMMY_OVERLAY_APPID);
- PackageSetting actorSetting = simulateAddPackage(appsFilter, actor, DUMMY_ACTOR_APPID);
+ PackageSetting targetSetting = simulateAddPackage(appsFilter, target, DUMMY_TARGET_UID);
+ PackageSetting overlaySetting = simulateAddPackage(appsFilter, overlay, DUMMY_OVERLAY_UID);
+ PackageSetting actorSetting = simulateAddPackage(appsFilter, actor, DUMMY_ACTOR_UID);
// Actor can see both target and overlay
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_APPID, actorSetting,
- targetSetting, SYSTEM_USER));
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_APPID, actorSetting,
- overlaySetting, SYSTEM_USER));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_UID, actorSetting,
+ targetSetting, 0));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_UID, actorSetting,
+ overlaySetting, 0));
// But target/overlay can't see each other
- assertTrue(appsFilter.shouldFilterApplication(DUMMY_TARGET_APPID, targetSetting,
- overlaySetting, SYSTEM_USER));
- assertTrue(appsFilter.shouldFilterApplication(DUMMY_OVERLAY_APPID, overlaySetting,
- targetSetting, SYSTEM_USER));
+ assertTrue(appsFilter.shouldFilterApplication(DUMMY_TARGET_UID, targetSetting,
+ overlaySetting, 0));
+ assertTrue(appsFilter.shouldFilterApplication(DUMMY_OVERLAY_UID, overlaySetting,
+ targetSetting, 0));
// And can't see the actor
- assertTrue(appsFilter.shouldFilterApplication(DUMMY_TARGET_APPID, targetSetting,
- actorSetting, SYSTEM_USER));
- assertTrue(appsFilter.shouldFilterApplication(DUMMY_OVERLAY_APPID, overlaySetting,
- actorSetting, SYSTEM_USER));
+ assertTrue(appsFilter.shouldFilterApplication(DUMMY_TARGET_UID, targetSetting,
+ actorSetting, 0));
+ assertTrue(appsFilter.shouldFilterApplication(DUMMY_OVERLAY_UID, overlaySetting,
+ actorSetting, 0));
}
@Test
public void testActsOnTargetOfOverlayThroughSharedUser() throws Exception {
-// Debug.waitForDebugger();
-
final String actorName = "overlay://test/actorName";
ParsingPackage target = pkg("com.some.package.target")
@@ -622,11 +580,7 @@ public class AppsFilterTest {
ParsingPackage actorOne = pkg("com.some.package.actor.one");
ParsingPackage actorTwo = pkg("com.some.package.actor.two");
- final AppsFilter appsFilter = new AppsFilter(
- mStateProvider,
- mFeatureConfigMock,
- new String[]{},
- false,
+ final AppsFilter appsFilter = new AppsFilter(mFeatureConfigMock, new String[]{}, false,
new OverlayReferenceMapper.Provider() {
@Nullable
@Override
@@ -655,114 +609,108 @@ public class AppsFilterTest {
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
- PackageSetting targetSetting = simulateAddPackage(appsFilter, target, DUMMY_TARGET_APPID);
- SharedUserSetting actorSharedSetting = new SharedUserSetting("actorSharedUser",
- targetSetting.pkgFlags, targetSetting.pkgPrivateFlags);
- PackageSetting overlaySetting =
- simulateAddPackage(appsFilter, overlay, DUMMY_OVERLAY_APPID);
- simulateAddPackage(appsFilter, actorOne, DUMMY_ACTOR_APPID,
- null /*settingBuilder*/, actorSharedSetting);
- simulateAddPackage(appsFilter, actorTwo, DUMMY_ACTOR_APPID,
- null /*settingBuilder*/, actorSharedSetting);
+ PackageSetting targetSetting = simulateAddPackage(appsFilter, target, DUMMY_TARGET_UID);
+ PackageSetting overlaySetting = simulateAddPackage(appsFilter, overlay, DUMMY_OVERLAY_UID);
+ PackageSetting actorOneSetting = simulateAddPackage(appsFilter, actorOne, DUMMY_ACTOR_UID);
+ PackageSetting actorTwoSetting = simulateAddPackage(appsFilter, actorTwo,
+ DUMMY_ACTOR_TWO_UID);
+ SharedUserSetting actorSharedSetting = new SharedUserSetting("actorSharedUser",
+ actorOneSetting.pkgFlags, actorOneSetting.pkgPrivateFlags);
+ actorSharedSetting.addPackage(actorOneSetting);
+ actorSharedSetting.addPackage(actorTwoSetting);
// actorTwo can see both target and overlay
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_APPID, actorSharedSetting,
- targetSetting, SYSTEM_USER));
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_APPID, actorSharedSetting,
- overlaySetting, SYSTEM_USER));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_TWO_UID, actorSharedSetting,
+ targetSetting, 0));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_TWO_UID, actorSharedSetting,
+ overlaySetting, 0));
}
@Test
public void testInitiatingApp_DoesntFilter() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"),
- DUMMY_TARGET_APPID);
+ DUMMY_TARGET_UID);
PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package"),
- DUMMY_CALLING_APPID, withInstallSource(target.name, null, null, false));
+ DUMMY_CALLING_UID, withInstallSource(target.name, null, null, false));
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
- SYSTEM_USER));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
@Test
public void testUninstalledInitiatingApp_Filters() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"),
- DUMMY_TARGET_APPID);
+ DUMMY_TARGET_UID);
PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package"),
- DUMMY_CALLING_APPID, withInstallSource(target.name, null, null, true));
+ DUMMY_CALLING_UID, withInstallSource(target.name, null, null, true));
- assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
- SYSTEM_USER));
+ assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
@Test
public void testOriginatingApp_Filters() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"),
- DUMMY_TARGET_APPID);
+ DUMMY_TARGET_UID);
PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package"),
- DUMMY_CALLING_APPID, withInstallSource(null, target.name, null, false));
+ DUMMY_CALLING_UID, withInstallSource(null, target.name, null, false));
- assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
- SYSTEM_USER));
+ assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
@Test
public void testInstallingApp_DoesntFilter() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"),
- DUMMY_TARGET_APPID);
+ DUMMY_TARGET_UID);
PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package"),
- DUMMY_CALLING_APPID, withInstallSource(null, null, target.name, false));
+ DUMMY_CALLING_UID, withInstallSource(null, null, target.name, false));
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
- SYSTEM_USER));
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
@Test
public void testInstrumentation_DoesntFilter() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"),
- DUMMY_TARGET_APPID);
+ DUMMY_TARGET_UID);
PackageSetting instrumentation = simulateAddPackage(appsFilter,
pkgWithInstrumentation("com.some.other.package", "com.some.package"),
- DUMMY_CALLING_APPID);
+ DUMMY_CALLING_UID);
assertFalse(
- appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, instrumentation, target,
- SYSTEM_USER));
+ appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, instrumentation, target, 0));
assertFalse(
- appsFilter.shouldFilterApplication(DUMMY_TARGET_APPID, target, instrumentation,
- SYSTEM_USER));
+ appsFilter.shouldFilterApplication(DUMMY_TARGET_UID, target, instrumentation, 0));
}
@Test
public void testWhoCanSee() throws Exception {
final AppsFilter appsFilter =
- new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null);
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
@@ -780,25 +728,21 @@ public class AppsFilterTest {
queriesProviderAppId);
final int[] systemFilter =
- appsFilter.getVisibilityWhitelist(system, SINGLE_USER_ARRAY, mExisting)
- .get(SYSTEM_USER);
+ appsFilter.getVisibilityWhitelist(system, new int[]{0}, mExisting).get(0);
assertThat(toList(systemFilter), empty());
final int[] seesNothingFilter =
- appsFilter.getVisibilityWhitelist(seesNothing, SINGLE_USER_ARRAY, mExisting)
- .get(SYSTEM_USER);
+ appsFilter.getVisibilityWhitelist(seesNothing, new int[]{0}, mExisting).get(0);
assertThat(toList(seesNothingFilter),
contains(seesNothingAppId));
final int[] hasProviderFilter =
- appsFilter.getVisibilityWhitelist(hasProvider, SINGLE_USER_ARRAY, mExisting)
- .get(SYSTEM_USER);
+ appsFilter.getVisibilityWhitelist(hasProvider, new int[]{0}, mExisting).get(0);
assertThat(toList(hasProviderFilter),
contains(hasProviderAppId, queriesProviderAppId));
int[] queriesProviderFilter =
- appsFilter.getVisibilityWhitelist(queriesProvider, SINGLE_USER_ARRAY, mExisting)
- .get(SYSTEM_USER);
+ appsFilter.getVisibilityWhitelist(queriesProvider, new int[]{0}, mExisting).get(0);
assertThat(toList(queriesProviderFilter),
contains(queriesProviderAppId));
@@ -807,8 +751,7 @@ public class AppsFilterTest {
// ensure implicit access is included in the filter
queriesProviderFilter =
- appsFilter.getVisibilityWhitelist(queriesProvider, SINGLE_USER_ARRAY, mExisting)
- .get(SYSTEM_USER);
+ appsFilter.getVisibilityWhitelist(queriesProvider, new int[]{0}, mExisting).get(0);
assertThat(toList(queriesProviderFilter),
contains(hasProviderAppId, queriesProviderAppId));
}
@@ -836,17 +779,11 @@ public class AppsFilterTest {
private PackageSetting simulateAddPackage(AppsFilter filter,
ParsingPackage newPkgBuilder, int appId) {
- return simulateAddPackage(filter, newPkgBuilder, appId, null /*settingBuilder*/);
+ return simulateAddPackage(filter, newPkgBuilder, appId, null);
}
private PackageSetting simulateAddPackage(AppsFilter filter,
ParsingPackage newPkgBuilder, int appId, @Nullable WithSettingBuilder action) {
- return simulateAddPackage(filter, newPkgBuilder, appId, action, null /*sharedUserSetting*/);
- }
-
- private PackageSetting simulateAddPackage(AppsFilter filter,
- ParsingPackage newPkgBuilder, int appId, @Nullable WithSettingBuilder action,
- @Nullable SharedUserSetting sharedUserSetting) {
AndroidPackage newPkg = ((ParsedPackage) newPkgBuilder.hideAsParsed()).hideAsFinal();
final PackageSettingBuilder settingBuilder = new PackageSettingBuilder()
@@ -858,12 +795,8 @@ public class AppsFilterTest {
.setPVersionCode(1L);
final PackageSetting setting =
(action == null ? settingBuilder : action.withBuilder(settingBuilder)).build();
+ filter.addPackage(setting, mExisting);
mExisting.put(newPkg.getPackageName(), setting);
- if (sharedUserSetting != null) {
- sharedUserSetting.addPackage(setting);
- setting.sharedUser = sharedUserSetting;
- }
- filter.addPackage(setting);
return setting;
}
@@ -876,3 +809,4 @@ public class AppsFilterTest {
return setting -> setting.setInstallSource(installSource);
}
}
+
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
index c55df512bd1e..87979fb00021 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
@@ -366,9 +366,75 @@ public class UserSystemPackageInstallerTest {
for (PackageInfo p : packageInfos) {
actualPackages.add(p.packageName);
}
+
+ // Add auto-generated RRO package to expectedPackages since they are not (supposed to be)
+ // in the whitelist but they should be installed.
+ for (PackageInfo p : packageInfos) {
+ if (p.isOverlayPackage()
+ && UserSystemPackageInstaller.hasAutoGeneratedRROSuffix(p.packageName)
+ && expectedPackages.contains(p.overlayTarget)) {
+ expectedPackages.add(p.packageName);
+ }
+ }
checkPackageDifferences(expectedPackages, actualPackages);
}
+ @Test
+ public void testAutoGeneratedRROMatchesSuffix() {
+ final List<PackageInfo> packageInfos = mContext.getPackageManager()
+ .getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES);
+
+ Log.v(TAG, "Found total packages: " + packageInfos.size());
+
+ for (PackageInfo p : packageInfos) {
+ if (p.packageName.contains(".auto_generated_rro_")) {
+ assertTrue("Auto-generated RRO package name does not match the suffix: "
+ + p.packageName,
+ UserSystemPackageInstaller.hasAutoGeneratedRROSuffix(p.packageName));
+ }
+ }
+ }
+
+ /**
+ * Test that overlay package not in whitelist should be installed for all user at Explicit mode.
+ */
+ @Test
+ public void testInstallOverlayPackagesExplicitMode() {
+ setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE);
+
+ final String[] userTypes = new String[]{"type"};
+ final long maskOfType = 0b0001L;
+
+ final String packageName1 = "whitelistedPkg";
+ final String packageName2 = "nonWhitelistedPkg";
+ final String overlayName1 = String.format("%s.auto_generated_rro_product__", packageName1);
+ final String overlayName2 = String.format("%s.auto_generated_rro_product__", packageName2);
+
+ final AndroidPackage overlayPackage1 = ((ParsedPackage) PackageImpl.forTesting(overlayName1)
+ .setOverlay(true)
+ .setOverlayTarget(packageName1)
+ .hideAsParsed())
+ .hideAsFinal();
+
+ final AndroidPackage overlayPackage2 = ((ParsedPackage) PackageImpl.forTesting(overlayName2)
+ .setOverlay(true)
+ .setOverlayTarget(packageName2)
+ .hideAsParsed())
+ .hideAsFinal();
+
+ final ArrayMap<String, Long> userTypeWhitelist = new ArrayMap<>();
+ userTypeWhitelist.put(packageName1, maskOfType);
+
+ final Set<String> userWhitelist = new ArraySet<>();
+ userWhitelist.add(packageName1);
+
+ boolean implicit = false;
+ assertTrue("Overlay for package1 should be installed", UserSystemPackageInstaller
+ .shouldInstallPackage(overlayPackage1, userTypeWhitelist, userWhitelist, implicit));
+ assertFalse("Overlay for package2 should not be installed", UserSystemPackageInstaller
+ .shouldInstallPackage(overlayPackage2, userTypeWhitelist, userWhitelist, implicit));
+ }
+
/** Asserts that actual is a subset of expected. */
private void checkPackageDifferences(Set<String> expected, Set<String> actual) {
final Set<String> uniqueToExpected = new ArraySet<>(expected);
diff --git a/services/tests/servicestests/src/com/android/server/textclassifier/FixedSizeQueueTest.java b/services/tests/servicestests/src/com/android/server/textclassifier/FixedSizeQueueTest.java
new file mode 100644
index 000000000000..90527b82c2c3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/textclassifier/FixedSizeQueueTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.textclassifier;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class FixedSizeQueueTest {
+
+ @Test
+ public void add_belowMaxCapacity() {
+ FixedSizeQueue<Integer> queue = new FixedSizeQueue<>(1, /* onEntryEvictedListener= */ null);
+ assertThat(queue.size()).isEqualTo(0);
+
+ queue.add(1);
+
+ assertThat(queue.size()).isEqualTo(1);
+ assertThat(queue.poll()).isEqualTo(1);
+ }
+
+ @Test
+ public void add_exceedMaxCapacity() {
+ FixedSizeQueue<Integer> queue = new FixedSizeQueue<>(2, /* onEntryEvictedListener= */ null);
+
+ queue.add(1);
+ queue.add(2);
+ queue.add(3);
+
+ assertThat(queue.size()).isEqualTo(2);
+ assertThat(queue.poll()).isEqualTo(2);
+ assertThat(queue.poll()).isEqualTo(3);
+ }
+
+ @Test
+ public void poll() {
+ FixedSizeQueue<Integer> queue = new FixedSizeQueue<>(1, /* onEntryEvictedListener= */ null);
+
+ queue.add(1);
+
+ assertThat(queue.poll()).isEqualTo(1);
+ assertThat(queue.poll()).isNull();
+ }
+
+ @Test
+ public void remove() {
+ FixedSizeQueue<Integer> queue = new FixedSizeQueue<>(1, /* onEntryEvictedListener= */ null);
+
+ queue.add(1);
+
+ assertThat(queue.remove(1)).isTrue();
+ assertThat(queue.isEmpty()).isTrue();
+ }
+
+ @Test
+ public void remove_noSuchElement() {
+ FixedSizeQueue<Integer> queue = new FixedSizeQueue<>(1, /* onEntryEvictedListener= */ null);
+
+ queue.add(1);
+
+ assertThat(queue.remove(2)).isFalse();
+ }
+
+ @Test
+ public void isEmpty_true() {
+ FixedSizeQueue<Integer> queue = new FixedSizeQueue<>(1, /* onEntryEvictedListener= */ null);
+
+ assertThat(queue.isEmpty()).isTrue();
+ }
+
+ @Test
+ public void isEmpty_false() {
+ FixedSizeQueue<Integer> queue = new FixedSizeQueue<>(1, /* onEntryEvictedListener= */ null);
+
+ queue.add(1);
+
+ assertThat(queue.isEmpty()).isFalse();
+ }
+
+ @Test
+ public void onEntryEvicted() {
+ List<Integer> onEntryEvictedElements = new ArrayList<>();
+ FixedSizeQueue<Integer> queue =
+ new FixedSizeQueue<>(1, onEntryEvictedElements::add);
+
+ queue.add(1);
+ queue.add(2);
+ queue.add(3);
+
+ assertThat(onEntryEvictedElements).containsExactly(1, 2).inOrder();
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index ae22b2be0c69..ef4d5db2f32f 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -178,6 +178,7 @@ import com.android.server.wm.WindowManagerInternal;
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -347,6 +348,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
super.setNotificationAssistantAccessGrantedForUserInternal(assistant, userId, granted);
}
+ @Override
+ protected String[] getStringArrayResource(int key) {
+ return new String[] {PKG_O};
+ }
+
private void setNotificationAssistantAccessGrantedCallback(
@Nullable NotificationAssistantAccessGrantedCallback callback) {
this.mNotificationAssistantAccessGrantedCallback = callback;
@@ -434,7 +440,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// Setup managed services
mListener = mListeners.new ManagedServiceInfo(
- null, new ComponentName(PKG, "test_class"), mUid, true, null, 0);
+ null, new ComponentName(PKG, "test_class"),
+ UserHandle.getUserId(mUid), true, null, 0);
ComponentName defaultComponent = ComponentName.unflattenFromString("config/device");
ArraySet<ComponentName> components = new ArraySet<>();
components.add(defaultComponent);
@@ -444,7 +451,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
when(mAssistants.getDefaultComponents()).thenReturn(components);
when(mAssistants.queryPackageForServices(
anyString(), anyInt(), anyInt())).thenReturn(components);
- when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
+ when(mListeners.checkServiceTokenLocked(null)).thenReturn(mListener);
ManagedServices.Config listenerConfig = new ManagedServices.Config();
listenerConfig.xmlTag = NotificationListeners.TAG_ENABLED_NOTIFICATION_LISTENERS;
when(mListeners.getConfig()).thenReturn(listenerConfig);
@@ -497,6 +504,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mBinderService.createNotificationChannels(
PKG, new ParceledListSlice(Arrays.asList(mTestNotificationChannel)));
+ mBinderService.createNotificationChannels(
+ PKG_P, new ParceledListSlice(Arrays.asList(mTestNotificationChannel)));
+ mBinderService.createNotificationChannels(
+ PKG_O, new ParceledListSlice(Arrays.asList(mTestNotificationChannel)));
assertNotNull(mBinderService.getNotificationChannel(
PKG, mContext.getUserId(), PKG, TEST_CHANNEL_ID));
clearInvocations(mRankingHandler);
@@ -583,7 +594,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.setGroupSummary(isSummary);
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, id,
"tag" + System.currentTimeMillis(), mUid, 0,
- nb.build(), new UserHandle(mUid), null, 0);
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
return new NotificationRecord(mContext, sbn, channel);
}
@@ -603,7 +614,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
nb.extend(extender);
}
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
- nb.build(), new UserHandle(mUid), null, 0);
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
return new NotificationRecord(mContext, sbn, channel);
}
@@ -635,7 +646,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
Notification.Builder nb = getMessageStyleNotifBuilder(addMetadata, groupKey, isSummary);
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, id,
tag, mUid, 0,
- nb.build(), new UserHandle(mUid), null, 0);
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
return new NotificationRecord(mContext, sbn, channel);
}
@@ -1328,6 +1339,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ @Ignore
public void testPostCancelPostNotifiesListeners() throws Exception {
// WHEN a notification is posted
final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
@@ -1687,7 +1699,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.setSmallIcon(android.R.drawable.sym_def_app_icon)
.build();
StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, null, mUid, 0,
- n, new UserHandle(mUid), null, 0);
+ n, UserHandle.getUserHandleForUid(mUid), null, 0);
sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
sbn.getId(), sbn.getNotification(), sbn.getUserId());
@@ -2191,7 +2203,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void testCreateChannelNotifyListener() throws Exception {
List<String> associations = new ArrayList<>();
associations.add("a");
- when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
+ when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
+ .thenReturn(associations);
mService.setPreferencesHelper(mPreferencesHelper);
when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
eq(mTestNotificationChannel.getId()), anyBoolean()))
@@ -2219,7 +2232,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void testCreateChannelGroupNotifyListener() throws Exception {
List<String> associations = new ArrayList<>();
associations.add("a");
- when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
+ when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
+ .thenReturn(associations);
mService.setPreferencesHelper(mPreferencesHelper);
NotificationChannelGroup group1 = new NotificationChannelGroup("a", "b");
NotificationChannelGroup group2 = new NotificationChannelGroup("n", "m");
@@ -2239,7 +2253,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void testUpdateChannelNotifyListener() throws Exception {
List<String> associations = new ArrayList<>();
associations.add("a");
- when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
+ when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
+ .thenReturn(associations);
mService.setPreferencesHelper(mPreferencesHelper);
mTestNotificationChannel.setLightColor(Color.CYAN);
when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
@@ -2257,7 +2272,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void testDeleteChannelNotifyListener() throws Exception {
List<String> associations = new ArrayList<>();
associations.add("a");
- when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
+ when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
+ .thenReturn(associations);
mService.setPreferencesHelper(mPreferencesHelper);
when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
eq(mTestNotificationChannel.getId()), anyBoolean()))
@@ -2273,7 +2289,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void testDeleteChannelGroupNotifyListener() throws Exception {
List<String> associations = new ArrayList<>();
associations.add("a");
- when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
+ when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
+ .thenReturn(associations);
NotificationChannelGroup ncg = new NotificationChannelGroup("a", "b/c");
mService.setPreferencesHelper(mPreferencesHelper);
when(mPreferencesHelper.getNotificationChannelGroup(eq(ncg.getId()), eq(PKG), anyInt()))
@@ -2290,7 +2307,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.setPreferencesHelper(mPreferencesHelper);
List<String> associations = new ArrayList<>();
associations.add("a");
- when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
+ when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
+ .thenReturn(associations);
when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
eq(mTestNotificationChannel.getId()), anyBoolean()))
.thenReturn(mTestNotificationChannel);
@@ -2310,7 +2328,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void testUpdateNotificationChannelFromPrivilegedListener_noAccess() throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
List<String> associations = new ArrayList<>();
- when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
+ when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
+ .thenReturn(associations);
try {
mBinderService.updateNotificationChannelFromPrivilegedListener(
@@ -2333,7 +2352,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.setPreferencesHelper(mPreferencesHelper);
List<String> associations = new ArrayList<>();
associations.add("a");
- when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
+ when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
+ .thenReturn(associations);
mListener = mock(ManagedServices.ManagedServiceInfo.class);
mListener.component = new ComponentName(PKG, PKG);
when(mListener.enabledAndUserMatches(anyInt())).thenReturn(false);
@@ -2360,7 +2380,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.setPreferencesHelper(mPreferencesHelper);
List<String> associations = new ArrayList<>();
associations.add("a");
- when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
+ when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
+ .thenReturn(associations);
mBinderService.getNotificationChannelsFromPrivilegedListener(
null, PKG, Process.myUserHandle());
@@ -2373,7 +2394,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void testGetNotificationChannelFromPrivilegedListener_cdm_noAccess() throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
List<String> associations = new ArrayList<>();
- when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
+ when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
+ .thenReturn(associations);
try {
mBinderService.getNotificationChannelsFromPrivilegedListener(
@@ -2391,7 +2413,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void testGetNotificationChannelFromPrivilegedListener_assistant_success()
throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
- when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(new ArrayList<>());
+ when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
+ .thenReturn(new ArrayList<>());
when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
mBinderService.getNotificationChannelsFromPrivilegedListener(
@@ -2405,7 +2428,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void testGetNotificationChannelFromPrivilegedListener_assistant_noAccess()
throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
- when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(new ArrayList<>());
+ when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
+ .thenReturn(new ArrayList<>());
when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(false);
try {
@@ -2425,7 +2449,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.setPreferencesHelper(mPreferencesHelper);
List<String> associations = new ArrayList<>();
associations.add("a");
- when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
+ when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
+ .thenReturn(associations);
mListener = mock(ManagedServices.ManagedServiceInfo.class);
when(mListener.enabledAndUserMatches(anyInt())).thenReturn(false);
when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
@@ -2447,7 +2472,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.setPreferencesHelper(mPreferencesHelper);
List<String> associations = new ArrayList<>();
associations.add("a");
- when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
+ when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
+ .thenReturn(associations);
mBinderService.getNotificationChannelGroupsFromPrivilegedListener(
null, PKG, Process.myUserHandle());
@@ -2459,7 +2485,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void testGetNotificationChannelGroupsFromPrivilegedListener_noAccess() throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
List<String> associations = new ArrayList<>();
- when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
+ when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
+ .thenReturn(associations);
try {
mBinderService.getNotificationChannelGroupsFromPrivilegedListener(
@@ -2476,7 +2503,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void testGetNotificationChannelGroupsFromPrivilegedListener_badUser() throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
List<String> associations = new ArrayList<>();
- when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
+ when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
+ .thenReturn(associations);
mListener = mock(ManagedServices.ManagedServiceInfo.class);
when(mListener.enabledAndUserMatches(anyInt())).thenReturn(false);
when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
@@ -3026,8 +3054,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mBinderService.setNotificationAssistantAccessGranted(c, true);
verify(mListeners).migrateToXml();
- verify(mListeners).notifyNotificationChannelChanged(anyString(), any(), any(),
- anyInt());
verify(mConditionProviders).setPackageOrComponentEnabled(
anyString(), anyInt(), anyBoolean(), anyBoolean());
verify(mAssistants).migrateToXml();
@@ -3041,8 +3067,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mBinderService.setNotificationPolicyAccessGranted(c.getPackageName(), true);
verify(mListeners).migrateToXml();
- verify(mListeners).notifyNotificationChannelChanged(anyString(), any(), any(),
- anyInt());
verify(mConditionProviders).setPackageOrComponentEnabled(
anyString(), anyInt(), anyBoolean(), anyBoolean());
verify(mAssistants).migrateToXml();
@@ -3178,7 +3202,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.setSmallIcon(android.R.drawable.sym_def_app_icon);
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
"testNoFakeColorizedPermission", mUid, 0,
- nb.build(), new UserHandle(mUid), null, 0);
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
@@ -3216,7 +3240,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.build();
StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "tag", mUid, 0,
- n, new UserHandle(mUid), null, 0);
+ n, UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord otherPackage =
new NotificationRecord(mContext, sbn, mTestNotificationChannel);
mService.addEnqueuedNotification(otherPackage);
@@ -3224,7 +3248,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// Same notifications are enqueued as posted, everything counts b/c id and tag don't match
// anything that's currently enqueued or posted
- int userId = new UserHandle(mUid).getIdentifier();
+ int userId = UserHandle.getUserId(mUid);
assertEquals(40,
mService.getNotificationCountLocked(PKG, userId, 0, null));
assertEquals(40,
@@ -3441,8 +3465,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
StatusBarNotification sbn = new StatusBarNotification(preOPkg, preOPkg, 9,
"testBumpFGImportance_noChannelChangePreOApp",
- Binder.getCallingUid(), 0, nb.build(), new UserHandle(Binder.getCallingUid()), null,
- 0);
+ Binder.getCallingUid(), 0, nb.build(),
+ UserHandle.getUserHandleForUid(Binder.getCallingUid()), null, 0);
mBinderService.enqueueNotificationWithTag(sbn.getPackageName(), sbn.getOpPkg(),
sbn.getTag(), sbn.getId(), sbn.getNotification(), sbn.getUserId());
@@ -3459,7 +3483,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
sbn = new StatusBarNotification(preOPkg, preOPkg, 9,
"testBumpFGImportance_noChannelChangePreOApp", Binder.getCallingUid(),
- 0, nb.build(), new UserHandle(Binder.getCallingUid()), null, 0);
+ 0, nb.build(), UserHandle.getUserHandleForUid(Binder.getCallingUid()), null, 0);
mBinderService.enqueueNotificationWithTag(preOPkg, preOPkg,
"testBumpFGImportance_noChannelChangePreOApp",
@@ -3813,7 +3837,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.addMessage(message1)
.addMessage(message2));
NotificationRecord recordA = new NotificationRecord(mContext, new StatusBarNotification(
- PKG, PKG, 0, "tag", mUid, 0, nbA.build(), new UserHandle(mUid), null, 0), c);
+ PKG, PKG, 0, "tag", mUid, 0, nbA.build(), UserHandle.getUserHandleForUid(mUid),
+ null, 0), c);
// First post means we grant access to both
reset(mUgm);
@@ -3831,7 +3856,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.setSmallIcon(android.R.drawable.sym_def_app_icon)
.setStyle(new Notification.MessagingStyle("").addMessage(message2));
NotificationRecord recordB = new NotificationRecord(mContext, new StatusBarNotification(PKG,
- PKG, 0, "tag", mUid, 0, nbB.build(), new UserHandle(mUid), null, 0), c);
+ PKG, 0, "tag", mUid, 0, nbB.build(), UserHandle.getUserHandleForUid(mUid), null, 0),
+ c);
// Update means we drop access to first
reset(mUgmInternal);
@@ -3870,7 +3896,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.setStyle(new Notification.MessagingStyle("")
.addMessage(message1));
NotificationRecord recordA = new NotificationRecord(mContext, new StatusBarNotification(
- PKG, PKG, 0, "tag", mUid, 0, nbA.build(), new UserHandle(mUid), null, 0), c);
+ PKG, PKG, 0, "tag", mUid, 0, nbA.build(), UserHandle.getUserHandleForUid(mUid),
+ null, 0), c);
doThrow(new SecurityException("no access")).when(mUgm)
.grantUriPermissionFromOwner(
@@ -4029,7 +4056,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
Notification.Builder nb1 = new Notification.Builder(mContext, "")
.setContentTitle("foo");
StatusBarNotification sbn1 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
- nb1.build(), new UserHandle(mUid), null, 0);
+ nb1.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r1 =
new NotificationRecord(mContext, sbn1, mock(NotificationChannel.class));
@@ -4037,7 +4064,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.setFlag(FLAG_FOREGROUND_SERVICE, true)
.setContentTitle("bar");
StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
- nb2.build(), new UserHandle(mUid), null, 0);
+ nb2.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r2 =
new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
@@ -4049,14 +4076,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
Notification.Builder nb1 = new Notification.Builder(mContext, "")
.setContentTitle("foo");
StatusBarNotification sbn1 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
- nb1.build(), new UserHandle(mUid), null, 0);
+ nb1.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r1 =
new NotificationRecord(mContext, sbn1, mock(NotificationChannel.class));
Notification.Builder nb2 = new Notification.Builder(mContext, "")
.setContentTitle("bar");
StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
- nb2.build(), new UserHandle(mUid), null, 0);
+ nb2.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r2 =
new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
@@ -4069,7 +4096,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.setStyle(new Notification.InboxStyle()
.addLine("line1").addLine("line2"));
StatusBarNotification sbn1 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
- nb1.build(), new UserHandle(mUid), null, 0);
+ nb1.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r1 =
new NotificationRecord(mContext, sbn1, mock(NotificationChannel.class));
@@ -4077,7 +4104,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.setStyle(new Notification.InboxStyle()
.addLine("line1").addLine("line2_changed"));
StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
- nb2.build(), new UserHandle(mUid), null, 0);
+ nb2.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r2 =
new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
@@ -4087,7 +4114,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.setStyle(new Notification.InboxStyle()
.addLine("line1"));
StatusBarNotification sbn3 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
- nb3.build(), new UserHandle(mUid), null, 0);
+ nb3.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r3 =
new NotificationRecord(mContext, sbn3, mock(NotificationChannel.class));
@@ -4097,7 +4124,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.setStyle(new Notification.InboxStyle()
.addLine("line1").addLine("line2").addLine("line3"));
StatusBarNotification sbn4 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
- nb4.build(), new UserHandle(mUid), null, 0);
+ nb4.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r4 =
new NotificationRecord(mContext, sbn4, mock(NotificationChannel.class));
@@ -4106,7 +4133,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
Notification.Builder nb5 = new Notification.Builder(mContext, "")
.setContentText("not an inbox");
StatusBarNotification sbn5 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
- nb5.build(), new UserHandle(mUid), null, 0);
+ nb5.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r5 =
new NotificationRecord(mContext, sbn5, mock(NotificationChannel.class));
@@ -4118,14 +4145,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
Notification.Builder nb1 = new Notification.Builder(mContext, "")
.setContentText("foo");
StatusBarNotification sbn1 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
- nb1.build(), new UserHandle(mUid), null, 0);
+ nb1.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r1 =
new NotificationRecord(mContext, sbn1, mock(NotificationChannel.class));
Notification.Builder nb2 = new Notification.Builder(mContext, "")
.setContentText("bar");
StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
- nb2.build(), new UserHandle(mUid), null, 0);
+ nb2.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r2 =
new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
@@ -4137,14 +4164,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
Notification.Builder nb1 = new Notification.Builder(mContext, "")
.setContentText("foo");
StatusBarNotification sbn1 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
- nb1.build(), new UserHandle(mUid), null, 0);
+ nb1.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r1 =
new NotificationRecord(mContext, sbn1, mock(NotificationChannel.class));
Notification.Builder nb2 = new Notification.Builder(mContext, "")
.setContentText("foo");
StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
- nb2.build(), new UserHandle(mUid), null, 0);
+ nb2.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r2 =
new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
@@ -4156,14 +4183,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
Notification.Builder nb1 = new Notification.Builder(mContext, "")
.setContentText(Html.fromHtml("<b>foo</b>"));
StatusBarNotification sbn1 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
- nb1.build(), new UserHandle(mUid), null, 0);
+ nb1.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r1 =
new NotificationRecord(mContext, sbn1, mock(NotificationChannel.class));
Notification.Builder nb2 = new Notification.Builder(mContext, "")
.setContentText(Html.fromHtml("<b>foo</b>"));
StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
- nb2.build(), new UserHandle(mUid), null, 0);
+ nb2.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r2 =
new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
@@ -4175,14 +4202,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
Notification.Builder nb1 = new Notification.Builder(mContext, "")
.setContentText(Html.fromHtml("<b>foo</b>"));
StatusBarNotification sbn1 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
- nb1.build(), new UserHandle(mUid), null, 0);
+ nb1.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r1 =
new NotificationRecord(mContext, sbn1, mock(NotificationChannel.class));
Notification.Builder nb2 = new Notification.Builder(mContext, "")
.setContentText(Html.fromHtml("<b>bar</b>"));
StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
- nb2.build(), new UserHandle(mUid), null, 0);
+ nb2.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r2 =
new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
@@ -4194,14 +4221,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
Notification.Builder nb1 = new Notification.Builder(mContext, "")
.setProgress(100, 90, false);
StatusBarNotification sbn1 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
- nb1.build(), new UserHandle(mUid), null, 0);
+ nb1.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r1 =
new NotificationRecord(mContext, sbn1, mock(NotificationChannel.class));
Notification.Builder nb2 = new Notification.Builder(mContext, "")
.setProgress(100, 100, false);
StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
- nb2.build(), new UserHandle(mUid), null, 0);
+ nb2.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r2 =
new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
@@ -4213,14 +4240,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
Notification.Builder nb1 = new Notification.Builder(mContext, "")
.setProgress(100, 90, false);
StatusBarNotification sbn1 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
- nb1.build(), new UserHandle(mUid), null, 0);
+ nb1.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r1 =
new NotificationRecord(mContext, sbn1, mock(NotificationChannel.class));
Notification.Builder nb2 = new Notification.Builder(mContext, "")
.setProgress(100, 91, false);
StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
- nb2.build(), new UserHandle(mUid), null, 0);
+ nb2.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r2 =
new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
@@ -4232,14 +4259,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
Notification.Builder nb1 = new Notification.Builder(mContext, "")
.setProgress(100, 100, false);
StatusBarNotification sbn1 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
- nb1.build(), new UserHandle(mUid), null, 0);
+ nb1.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r1 =
new NotificationRecord(mContext, sbn1, mock(NotificationChannel.class));
Notification.Builder nb2 = new Notification.Builder(mContext, "")
.setProgress(100, 100, false);
StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
- nb2.build(), new UserHandle(mUid), null, 0);
+ nb2.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r2 =
new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
@@ -4253,7 +4280,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.setFlag(Notification.FLAG_GROUP_SUMMARY, true)
.setContentText("foo");
StatusBarNotification sbn1 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
- nb1.build(), new UserHandle(mUid), null, 0);
+ nb1.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r1 =
new NotificationRecord(mContext, sbn1, mock(NotificationChannel.class));
@@ -4262,7 +4289,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.setFlag(Notification.FLAG_GROUP_SUMMARY, true)
.setContentText("bar");
StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
- nb2.build(), new UserHandle(mUid), null, 0);
+ nb2.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r2 =
new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
@@ -4276,7 +4303,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.setFlag(Notification.FLAG_GROUP_SUMMARY, true)
.setContentText("bar");
StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
- nb2.build(), new UserHandle(mUid), null, 0);
+ nb2.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r2 =
new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
@@ -4694,7 +4721,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
n.flags |= FLAG_FOREGROUND_SERVICE;
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 9, null, mUid, 0,
- n, new UserHandle(mUid), null, 0);
+ n, UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
mService.addEnqueuedNotification(r);
@@ -4713,7 +4740,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
n.flags |= FLAG_FOREGROUND_SERVICE;
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 9, null, mUid, 0,
- n, new UserHandle(mUid), null, 0);
+ n, UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
mService.addNotification(r);
@@ -5026,7 +5053,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.setSmallIcon(android.R.drawable.sym_def_app_icon);
StatusBarNotification sbn = new StatusBarNotification(PKG, "opPkg", 0, "tag", mUid, 0,
- nb.build(), new UserHandle(mUid), null, 0);
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
mService.reportSeen(r);
@@ -5050,7 +5077,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, r.getSbn().getId(),
r.getSbn().getTag(), mUid, 0,
new Notification.Builder(mContext, mTestNotificationChannel.getId()).build(),
- new UserHandle(mUid), null, 0);
+ UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord update = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
mService.addEnqueuedNotification(update);
assertNull(update.getSbn().getNotification().getSmallIcon());
@@ -5084,7 +5111,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- public void testgetNotificationChannels_crossUser() throws Exception {
+ public void testGetNotificationChannels_crossUser() throws Exception {
// same user no problem
mBinderService.getNotificationChannels("src", "target", mContext.getUserId());
@@ -5273,7 +5300,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.setSmallIcon(android.R.drawable.sym_def_app_icon)
.setBubbleMetadata(getBubbleMetadata());
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, "tag", mUid, 0,
- nb.build(), new UserHandle(mUid), null, 0);
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
// Say we're foreground
@@ -5319,7 +5346,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
nb.setShortcutId(null);
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
null, mUid, 0,
- nb.build(), new UserHandle(mUid), null, 0);
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
sbn.getId(), sbn.getNotification(), sbn.getUserId());
@@ -5363,7 +5390,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
"testFlagBubbleNotifs_noFlag_notBubble", mUid, 0,
- nb.build(), new UserHandle(mUid), null, 0);
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
// Post the notification
@@ -6178,7 +6205,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
nb.setShortcutId(VALID_CONVO_SHORTCUT_ID);
nb.setBubbleMetadata(metadata);
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
- "tag", mUid, 0, nb.build(), new UserHandle(mUid), null, 0);
+ "tag", mUid, 0, nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
// Test: Send the bubble notification
@@ -6203,7 +6230,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// Test: Remove the shortcut
when(mLauncherApps.getShortcuts(any(), any())).thenReturn(null);
launcherAppsCallback.getValue().onShortcutsChanged(PKG, Collections.emptyList(),
- new UserHandle(mUid));
+ UserHandle.getUserHandleForUid(mUid));
waitForIdle();
// Verify:
@@ -6238,7 +6265,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
nb.setShortcutId(shortcutId);
nb.setBubbleMetadata(metadata);
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
- "tag", mUid, 0, nb.build(), new UserHandle(mUid), null, 0);
+ "tag", mUid, 0, nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
// Pretend the shortcut exists
@@ -6686,15 +6713,43 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testRecordMessages_invalidMsg() throws RemoteException {
- NotificationRecord nr =
- generateMessageBubbleNotifRecord(mTestNotificationChannel,
- "testRecordMessages_invalidMsg");
+ Notification.Builder nb = getMessageStyleNotifBuilder(false /* addDefaultMetadata */,
+ null /* groupKey */, false /* isSummary */);
+ nb.setShortcutId(null);
+ StatusBarNotification sbn = new StatusBarNotification(PKG_P, PKG_P, 1,
+ "testRecordMessages_invalidMsg", mUid, 0, nb.build(),
+ UserHandle.getUserHandleForUid(mUid), null, 0);
+ NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
when(mLauncherApps.getShortcuts(any(), any())).thenReturn(null);
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+ mBinderService.enqueueNotificationWithTag(PKG_P, PKG_P, nr.getSbn().getTag(),
nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
- assertTrue(mBinderService.isInInvalidMsgState(PKG, mUid));
+ assertTrue(mBinderService.isInInvalidMsgState(PKG_P, mUid));
+ }
+
+ @Test
+ public void testRecordMessages_invalidMsg_notMessageStyle() throws RemoteException {
+ Notification.Builder nb = new Notification.Builder(mContext,
+ mTestNotificationChannel.getId())
+ .setContentTitle("foo")
+ .setShortcutId(null)
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setCategory(Notification.CATEGORY_MESSAGE);
+ StatusBarNotification sbn = new StatusBarNotification(PKG_O, PKG_O, 1,
+ "testRecordMessages_invalidMsg_notMessageStyle", mUid, 0, nb.build(),
+ UserHandle.getUserHandleForUid(mUid), null, 0);
+ NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ when(mLauncherApps.getShortcuts(any(), any())).thenReturn(null);
+ mBinderService.enqueueNotificationWithTag(PKG_O, PKG_O, nr.getSbn().getTag(),
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
+ waitForIdle();
+
+ // PKG_O is allowed to be in conversation space b/c of override in
+ // TestableNotificationManagerService
+ assertTrue(mBinderService.isInInvalidMsgState(PKG_O, mUid));
}
@Test
@@ -6702,24 +6757,25 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
Notification.Builder nb = getMessageStyleNotifBuilder(false /* addDefaultMetadata */,
null /* groupKey */, false /* isSummary */);
nb.setShortcutId(null);
- StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
- "testRecordMessages_validMsg", mUid, 0, nb.build(), new UserHandle(mUid), null, 0);
+ StatusBarNotification sbn = new StatusBarNotification(PKG_P, PKG_P, 1,
+ "testRecordMessages_validMsg", mUid, 0, nb.build(),
+ UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+ mBinderService.enqueueNotificationWithTag(PKG_P, PKG_P, nr.getSbn().getTag(),
nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
- assertTrue(mBinderService.isInInvalidMsgState(PKG, mUid));
+ assertTrue(mBinderService.isInInvalidMsgState(PKG_P, mUid));
nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
"testRecordMessages_validMsg");
- mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+ mBinderService.enqueueNotificationWithTag(PKG_P, PKG_P, nr.getSbn().getTag(),
nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
- assertFalse(mBinderService.isInInvalidMsgState(PKG, mUid));
+ assertFalse(mBinderService.isInInvalidMsgState(PKG_P, mUid));
}
@Test
@@ -6739,7 +6795,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
nb.setShortcutId(null);
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
"testRecordMessages_invalidMsg_afterValidMsg_2", mUid, 0, nb.build(),
- new UserHandle(mUid), null, 0);
+ UserHandle.getUserHandleForUid(mUid), null, 0);
nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/WrappedSysUiStatsEvent.java b/services/tests/uiservicestests/src/com/android/server/notification/WrappedSysUiStatsEvent.java
index f4f64d779d30..89adc724f600 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/WrappedSysUiStatsEvent.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/WrappedSysUiStatsEvent.java
@@ -88,11 +88,21 @@ public class WrappedSysUiStatsEvent {
return index < mValues.size() ? mValues.get(index) : null;
}
- /** useful to make assertTrue() statemetns more readable. */
+ /** useful to make assertTrue() statements more readable. */
public boolean getBoolean(int index) {
return (Boolean) mValues.get(index);
}
+ /** useful to make assertTrue() statements more readable. */
+ public int getInt(int index) {
+ return (Integer) mValues.get(index);
+ }
+
+ /** useful to make assertTrue() statements more readable. */
+ public String getString(int index) {
+ return (String) mValues.get(index);
+ }
+
private void addValue(Object value) {
mLastIndex = mValues.size();
mValues.add(value);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index f7b435ed937b..013a99433041 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -101,11 +101,39 @@ public class ZenModeConfigTest extends UiServiceTestCase {
Policy expectedPolicy = new Policy(priorityCategories, priorityCallSenders,
priorityMessageSenders, suppressedVisualEffects, 0, priorityConversationsSenders);
-
assertEquals(expectedPolicy, config.toNotificationPolicy(zenPolicy));
}
@Test
+ public void testZenConfigToZenPolicy() {
+ ZenPolicy expected = new ZenPolicy.Builder()
+ .allowAlarms(true)
+ .allowReminders(true)
+ .allowEvents(true)
+ .showLights(false)
+ .showBadges(false)
+ .showInAmbientDisplay(false)
+ .build();
+
+ ZenModeConfig config = getMutedAllConfig();
+ config.allowAlarms = true;
+ config.allowReminders = true;
+ config.allowEvents = true;
+ config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE;
+ config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS;
+ config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_AMBIENT;
+ ZenPolicy actual = config.toZenPolicy();
+
+ assertEquals(expected.getVisualEffectBadge(), actual.getVisualEffectBadge());
+ assertEquals(expected.getPriorityCategoryAlarms(), actual.getPriorityCategoryAlarms());
+ assertEquals(expected.getPriorityCategoryReminders(),
+ actual.getPriorityCategoryReminders());
+ assertEquals(expected.getPriorityCategoryEvents(), actual.getPriorityCategoryEvents());
+ assertEquals(expected.getVisualEffectLights(), actual.getVisualEffectLights());
+ assertEquals(expected.getVisualEffectAmbient(), actual.getVisualEffectAmbient());
+ }
+
+ @Test
public void testPriorityOnlyMutingAll() {
ZenModeConfig config = getMutedAllConfig();
assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 108af892c7dd..3c7206fee9d1 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -27,16 +27,25 @@ import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM;
import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
-import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS;
import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_STARRED;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
+import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+
+import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
+import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
+import static com.android.os.AtomsProto.DNDModeProto.CHANNELS_BYPASSING_FIELD_NUMBER;
+import static com.android.os.AtomsProto.DNDModeProto.ENABLED_FIELD_NUMBER;
+import static com.android.os.AtomsProto.DNDModeProto.ID_FIELD_NUMBER;
+import static com.android.os.AtomsProto.DNDModeProto.UID_FIELD_NUMBER;
+import static com.android.os.AtomsProto.DNDModeProto.ZEN_MODE_FIELD_NUMBER;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.TestCase.assertTrue;
+import static junit.framework.TestCase.fail;
import static org.junit.Assert.assertNotEquals;
import static org.mockito.ArgumentMatchers.any;
@@ -70,10 +79,12 @@ import android.media.AudioManagerInternal;
import android.media.AudioSystem;
import android.media.VolumePolicy;
import android.net.Uri;
+import android.os.Process;
import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.service.notification.Condition;
+import android.service.notification.DNDModeProto;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeConfig.ScheduleInfo;
import android.service.notification.ZenPolicy;
@@ -82,6 +93,7 @@ import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.StatsEvent;
import android.util.Xml;
import com.android.internal.R;
@@ -106,6 +118,9 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Objects;
@SmallTest
@@ -115,6 +130,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
private static final String EVENTS_DEFAULT_RULE_ID = "EVENTS_DEFAULT_RULE";
private static final String SCHEDULE_DEFAULT_RULE_ID = "EVERY_NIGHT_DEFAULT_RULE";
+ private static final int ZEN_MODE_FOR_TESTING = 99;
ConditionProviders mConditionProviders;
@Mock NotificationManager mNotificationManager;
@@ -124,6 +140,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
private Context mContext;
private ContentResolver mContentResolver;
@Mock AppOpsManager mAppOps;
+ private WrappedSysUiStatsEvent.WrappedBuilderFactory mStatsEventBuilderFactory;
@Before
public void setUp() {
@@ -140,6 +157,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
Log.d("ZenModeHelperTest", "Couldn't mock default zen mode config xml file err=" +
e.toString());
}
+ mStatsEventBuilderFactory = new WrappedSysUiStatsEvent.WrappedBuilderFactory();
when(mContext.getSystemService(AppOpsManager.class)).thenReturn(mAppOps);
when(mContext.getSystemService(NotificationManager.class)).thenReturn(mNotificationManager);
@@ -147,7 +165,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
AppGlobals.getPackageManager());
mConditionProviders.addSystemProvider(new CountdownConditionProvider());
mZenModeHelperSpy = spy(new ZenModeHelper(mContext, mTestableLooper.getLooper(),
- mConditionProviders));
+ mConditionProviders, mStatsEventBuilderFactory));
}
private XmlResourceParser getDefaultConfigParser() throws IOException, XmlPullParserException {
@@ -212,6 +230,10 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
private ArrayMap<String, ZenModeConfig.ZenRule> getCustomAutomaticRules() {
+ return getCustomAutomaticRules(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+ }
+
+ private ArrayMap<String, ZenModeConfig.ZenRule> getCustomAutomaticRules(int zenMode) {
ArrayMap<String, ZenModeConfig.ZenRule> automaticRules = new ArrayMap<>();
ZenModeConfig.ZenRule customRule = new ZenModeConfig.ZenRule();
final ScheduleInfo customRuleInfo = new ScheduleInfo();
@@ -219,10 +241,10 @@ public class ZenModeHelperTest extends UiServiceTestCase {
customRule.creationTime = 0;
customRule.id = "customRule";
customRule.name = "Custom Rule";
- customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ customRule.zenMode = zenMode;
customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
customRule.configurationActivity =
- new ComponentName("android", "ScheduleConditionProvider");
+ new ComponentName("not.android", "ScheduleConditionProvider");
customRule.pkg = customRule.configurationActivity.getPackageName();
automaticRules.put("customRule", customRule);
return automaticRules;
@@ -244,7 +266,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
@Test
public void testZenOn_NotificationApplied() {
- mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
// The most permissive policy
mZenModeHelperSpy.mConsolidatedPolicy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS |
PRIORITY_CATEGORY_MEDIA | PRIORITY_CATEGORY_MESSAGES
@@ -267,7 +289,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
@Test
public void testZenOn_StarredCallers_CallTypesBlocked() {
- mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
// The most permissive policy
mZenModeHelperSpy.mConsolidatedPolicy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS |
PRIORITY_CATEGORY_MEDIA | PRIORITY_CATEGORY_MESSAGES
@@ -287,7 +309,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
@Test
public void testZenOn_AllCallers_CallTypesAllowed() {
- mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
// The most permissive policy
mZenModeHelperSpy.mConsolidatedPolicy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS |
PRIORITY_CATEGORY_MEDIA | PRIORITY_CATEGORY_MESSAGES
@@ -307,7 +329,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
@Test
public void testZenOn_AllowAlarmsMedia_NoAlarmMediaMuteApplied() {
- mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
mZenModeHelperSpy.mConsolidatedPolicy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS |
PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0, 0);
@@ -320,7 +342,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
@Test
public void testZenOn_DisallowAlarmsMedia_AlarmMediaMuteApplied() {
- mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0, 0);
mZenModeHelperSpy.applyRestrictions();
verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, true,
@@ -406,7 +428,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
public void testZenAllCannotBypass() {
// Only audio attributes with SUPPRESIBLE_NEVER can bypass
// with special case USAGE_ASSISTANCE_SONIFICATION
- mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0, 0);
mZenModeHelperSpy.applyRestrictions();
@@ -428,7 +450,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
@Test
public void testApplyRestrictions_whitelist_priorityOnlyMode() {
mZenModeHelperSpy.setPriorityOnlyDndExemptPackages(new String[] {PKG_O});
- mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0, 0);
mZenModeHelperSpy.applyRestrictions();
@@ -479,7 +501,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
Settings.Secure.putInt(mContentResolver, Settings.Secure.ZEN_SETTINGS_UPDATED, 0);
mZenModeHelperSpy.mIsBootComplete = true;
mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0, 0);
- mZenModeHelperSpy.setZenModeSetting(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+ mZenModeHelperSpy.setZenModeSetting(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
verify(mZenModeHelperSpy, times(1)).createZenUpgradeNotification();
verify(mNotificationManager, times(1)).notify(eq(ZenModeHelper.TAG),
@@ -494,7 +516,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
Settings.Secure.putInt(mContentResolver, Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, 0);
Settings.Secure.putInt(mContentResolver, Settings.Secure.ZEN_SETTINGS_UPDATED, 0);
mZenModeHelperSpy.mIsBootComplete = true;
- mZenModeHelperSpy.setZenModeSetting(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+ mZenModeHelperSpy.setZenModeSetting(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
verify(mZenModeHelperSpy, never()).createZenUpgradeNotification();
verify(mNotificationManager, never()).notify(eq(ZenModeHelper.TAG),
@@ -507,7 +529,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
Settings.Secure.putInt(mContentResolver, Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, 0);
Settings.Secure.putInt(mContentResolver, Settings.Secure.ZEN_SETTINGS_UPDATED, 1);
mZenModeHelperSpy.mIsBootComplete = true;
- mZenModeHelperSpy.setZenModeSetting(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+ mZenModeHelperSpy.setZenModeSetting(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
verify(mZenModeHelperSpy, never()).createZenUpgradeNotification();
verify(mNotificationManager, never()).notify(eq(ZenModeHelper.TAG),
@@ -524,7 +546,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// 1. Current ringer is normal
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
// Set zen to priority-only with all notification sounds muted (so ringer will be muted)
- mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
mZenModeHelperSpy.mConfig.allowReminders = false;
mZenModeHelperSpy.mConfig.allowCalls = false;
mZenModeHelperSpy.mConfig.allowMessages = false;
@@ -568,7 +590,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// ringtone, notification and system streams are affected by ringer mode
mZenModeHelperSpy.mConfig.allowAlarms = true;
mZenModeHelperSpy.mConfig.allowReminders = true;
- mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
ZenModeHelper.RingerModeDelegate ringerModeDelegateRingerMuted =
mZenModeHelperSpy.new RingerModeDelegate();
@@ -615,7 +637,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// 1. Current ringer is normal
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
- mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
mZenModeHelperSpy.mConfig.allowReminders = true;
// 2. apply priority only zen - verify ringer is normal
@@ -640,7 +662,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// 1. Current ringer is silent
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
- mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
mZenModeHelperSpy.mConfig.allowReminders = true;
// 2. apply priority only zen - verify ringer is silent
@@ -666,7 +688,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// 1. Current ringer is normal
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
// Set zen to priority-only with all notification sounds muted (so ringer will be muted)
- mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
mZenModeHelperSpy.mConfig.allowReminders = true;
// 2. apply priority only zen - verify zen will still be normal
@@ -728,7 +750,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// apply zen off multiple times - verify ringer is not set to normal
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
- mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
for (int i = 0; i < 3; i++) {
// if zen doesn't change, zen should not reapply itself to the ringer
mZenModeHelperSpy.evaluateZenMode("test", true);
@@ -756,7 +778,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// apply zen off multiple times - verify ringer is not set to normal
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
- mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
for (int i = 0; i < 3; i++) {
// if zen doesn't change, zen should not reapply itself to the ringer
mZenModeHelperSpy.evaluateZenMode("test", true);
@@ -768,7 +790,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
@Test
public void testParcelConfig() {
- mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
mZenModeHelperSpy.mConfig.allowAlarms = false;
mZenModeHelperSpy.mConfig.allowMedia = false;
mZenModeHelperSpy.mConfig.allowSystem = false;
@@ -792,7 +814,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
@Test
public void testWriteXml() throws Exception {
- mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
mZenModeHelperSpy.mConfig.allowAlarms = false;
mZenModeHelperSpy.mConfig.allowMedia = false;
mZenModeHelperSpy.mConfig.allowSystem = false;
@@ -806,7 +828,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
mZenModeHelperSpy.mConfig.suppressedVisualEffects = SUPPRESSED_EFFECT_BADGE;
mZenModeHelperSpy.mConfig.manualRule = new ZenModeConfig.ZenRule();
mZenModeHelperSpy.mConfig.manualRule.zenMode =
- Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ ZEN_MODE_IMPORTANT_INTERRUPTIONS;
mZenModeHelperSpy.mConfig.manualRule.component = new ComponentName("a", "a");
mZenModeHelperSpy.mConfig.manualRule.pkg = "a";
mZenModeHelperSpy.mConfig.manualRule.enabled = true;
@@ -822,6 +844,102 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
+ public void testProto() {
+ mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ mZenModeHelperSpy.mConfig.manualRule = new ZenModeConfig.ZenRule();
+
+ int n = mZenModeHelperSpy.mConfig.automaticRules.size();
+ List<String> ids = new ArrayList<>(n);
+ for (ZenModeConfig.ZenRule rule : mZenModeHelperSpy.mConfig.automaticRules.values()) {
+ ids.add(rule.id);
+ }
+ ids.add("");
+
+ List<StatsEvent> events = new LinkedList<>();
+ mZenModeHelperSpy.pullRules(events);
+ assertEquals(n + 1, events.size());
+ for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
+ if (builder.getAtomId() == DND_MODE_RULE) {
+ if (builder.getInt(ZEN_MODE_FIELD_NUMBER) == DNDModeProto.ROOT_CONFIG) {
+ assertTrue(builder.getBoolean(ENABLED_FIELD_NUMBER));
+ assertFalse(builder.getBoolean(CHANNELS_BYPASSING_FIELD_NUMBER));
+ }
+ assertEquals(Process.SYSTEM_UID, builder.getInt(UID_FIELD_NUMBER));
+ assertTrue(builder.getBooleanAnnotation(UID_FIELD_NUMBER, ANNOTATION_ID_IS_UID));
+ String name = (String) builder.getValue(ID_FIELD_NUMBER);
+ assertTrue("unexpected rule id", ids.contains(name));
+ ids.remove(name);
+ } else {
+ fail("unexpected atom id: " + builder.getAtomId());
+ }
+ }
+ assertEquals("extra rule in output", 0, ids.size());
+ }
+
+ @Test
+ public void testProtoWithAutoRule() throws Exception {
+ setupZenConfig();
+ // one enabled automatic rule
+ mZenModeHelperSpy.mConfig.automaticRules = getCustomAutomaticRules(ZEN_MODE_FOR_TESTING);
+
+ List<StatsEvent> events = new LinkedList<>();
+ mZenModeHelperSpy.pullRules(events);
+
+ boolean foundCustomEvent = false;
+ for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
+ if (builder.getAtomId() == DND_MODE_RULE) {
+ if (ZEN_MODE_FOR_TESTING == builder.getInt(ZEN_MODE_FIELD_NUMBER)) {
+ foundCustomEvent = true;
+ assertEquals(0, builder.getInt(UID_FIELD_NUMBER));
+ assertTrue(builder.getBoolean(ENABLED_FIELD_NUMBER));
+ }
+ } else {
+ fail("unexpected atom id: " + builder.getAtomId());
+ }
+ }
+ assertTrue("couldn't find custom rule", foundCustomEvent);
+ }
+
+ @Test
+ public void testProtoRedactsIds() throws Exception {
+ setupZenConfig();
+ // one enabled automatic rule
+ mZenModeHelperSpy.mConfig.automaticRules = getCustomAutomaticRules();
+
+ List<StatsEvent> events = new LinkedList<>();
+ mZenModeHelperSpy.pullRules(events);
+
+ boolean foundCustomEvent = false;
+ for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
+ if (builder.getAtomId() == DND_MODE_RULE
+ && "customRule".equals(builder.getString(ID_FIELD_NUMBER))) {
+ fail("non-default IDs should be redacted");
+ }
+ }
+ }
+
+ @Test
+ public void testProtoWithManualRule() throws Exception {
+ setupZenConfig();
+ mZenModeHelperSpy.mConfig.automaticRules = getCustomAutomaticRules();
+ mZenModeHelperSpy.mConfig.manualRule = new ZenModeConfig.ZenRule();
+ mZenModeHelperSpy.mConfig.manualRule.enabled = true;
+ mZenModeHelperSpy.mConfig.manualRule.enabler = "com.enabler";
+
+ List<StatsEvent> events = new LinkedList<>();
+ mZenModeHelperSpy.pullRules(events);
+
+ boolean foundManualRule = false;
+ for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
+ if (builder.getAtomId() == DND_MODE_RULE
+ && ZenModeConfig.MANUAL_RULE_ID.equals(builder.getString(ID_FIELD_NUMBER))) {
+ assertEquals(0, builder.getInt(UID_FIELD_NUMBER));
+ foundManualRule = true;
+ }
+ }
+ assertTrue("couldn't find manual rule", foundManualRule); }
+
+ @Test
public void testWriteXml_onlyBackupsTargetUser() throws Exception {
// Setup configs for user 10 and 11.
setupZenConfig();
@@ -906,7 +1024,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
customRule.creationTime = 0;
customRule.id = "customRule";
customRule.name = "Custom Rule";
- customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ customRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
customRule.configurationActivity =
new ComponentName("android", "ScheduleConditionProvider");
@@ -951,7 +1069,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
customRule.creationTime = 0;
customRule.id = ruleId;
customRule.name = "Custom Rule";
- customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ customRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
customRule.configurationActivity =
new ComponentName("android", "ScheduleConditionProvider");
@@ -986,7 +1104,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
final ScheduleInfo weeknights = new ScheduleInfo();
customRule.enabled = true;
customRule.name = "Custom Rule";
- customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ customRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
customRule.conditionId = ZenModeConfig.toScheduleConditionId(weeknights);
customRule.component = new ComponentName("android", "ScheduleConditionProvider");
enabledAutoRule.put("customRule", customRule);
@@ -1151,7 +1269,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
final ScheduleInfo weeknights = new ScheduleInfo();
customRule.enabled = false;
customRule.name = "Custom Rule";
- customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ customRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
customRule.conditionId = ZenModeConfig.toScheduleConditionId(weeknights);
customRule.component = new ComponentName("android", "ScheduleConditionProvider");
disabledAutoRule.put("customRule", customRule);
@@ -1187,7 +1305,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
final ScheduleInfo customRuleInfo = new ScheduleInfo();
customRule.enabled = false;
customRule.name = "Custom Rule";
- customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ customRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
customRule.component = new ComponentName("android", "ScheduleConditionProvider");
customRule.zenPolicy = new ZenPolicy.Builder()
@@ -1200,7 +1318,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
final ScheduleInfo defaultScheduleRuleInfo = new ScheduleInfo();
defaultScheduleRule.enabled = false;
defaultScheduleRule.name = "Default Schedule Rule";
- defaultScheduleRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ defaultScheduleRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
defaultScheduleRule.conditionId = ZenModeConfig.toScheduleConditionId(
defaultScheduleRuleInfo);
customRule.component = new ComponentName("android", "ScheduleConditionProvider");
@@ -1238,7 +1356,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
final ScheduleInfo customRuleInfo = new ScheduleInfo();
customRule.enabled = false;
customRule.name = "Custom Rule";
- customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ customRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
customRule.component = new ComponentName("android", "ScheduleConditionProvider");
customRule.zenPolicy = new ZenPolicy.Builder()
@@ -1251,7 +1369,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
final ScheduleInfo defaultScheduleRuleInfo = new ScheduleInfo();
defaultScheduleRule.enabled = false;
defaultScheduleRule.name = "Default Schedule Rule";
- defaultScheduleRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ defaultScheduleRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
defaultScheduleRule.conditionId = ZenModeConfig.toScheduleConditionId(
defaultScheduleRuleInfo);
defaultScheduleRule.id = ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID;
@@ -1265,7 +1383,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
final ScheduleInfo defaultEventRuleInfo = new ScheduleInfo();
defaultEventRule.enabled = false;
defaultEventRule.name = "Default Event Rule";
- defaultEventRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ defaultEventRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
defaultEventRule.conditionId = ZenModeConfig.toScheduleConditionId(
defaultEventRuleInfo);
defaultEventRule.id = ZenModeConfig.EVENTS_DEFAULT_RULE_ID;
@@ -1295,6 +1413,10 @@ public class ZenModeHelperTest extends UiServiceTestCase {
assertTrue(rules.containsKey("customRule"));
setupZenConfigMaintained();
+
+ List<StatsEvent> events = new LinkedList<>();
+ mZenModeHelperSpy.pullRules(events);
+ assertEquals(4, events.size());
}
@Test
@@ -1323,10 +1445,12 @@ public class ZenModeHelperTest extends UiServiceTestCase {
@Test
public void testEmptyDefaultRulesMap() {
+ List<StatsEvent> events = new LinkedList<>();
ZenModeConfig config = new ZenModeConfig();
config.automaticRules = new ArrayMap<>();
mZenModeHelperSpy.mConfig = config;
mZenModeHelperSpy.updateDefaultZenRules(); // shouldn't throw null pointer
+ mZenModeHelperSpy.pullRules(events); // shouldn't throw null pointer
}
@Test
@@ -1342,7 +1466,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
updatedDefaultRule.creationTime = 0;
updatedDefaultRule.id = SCHEDULE_DEFAULT_RULE_ID;
updatedDefaultRule.name = "Schedule Default Rule";
- updatedDefaultRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ updatedDefaultRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
updatedDefaultRule.conditionId = ZenModeConfig.toScheduleConditionId(new ScheduleInfo());
updatedDefaultRule.component = new ComponentName("android", "ScheduleConditionProvider");
@@ -1368,7 +1492,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
updatedDefaultRule.creationTime = 0;
updatedDefaultRule.id = SCHEDULE_DEFAULT_RULE_ID;
updatedDefaultRule.name = "Schedule Default Rule";
- updatedDefaultRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ updatedDefaultRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
updatedDefaultRule.conditionId = ZenModeConfig.toScheduleConditionId(new ScheduleInfo());
updatedDefaultRule.component = new ComponentName("android", "ScheduleConditionProvider");
@@ -1395,7 +1519,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
customDefaultRule.creationTime = 0;
customDefaultRule.id = SCHEDULE_DEFAULT_RULE_ID;
customDefaultRule.name = "Schedule Default Rule";
- customDefaultRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ customDefaultRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
customDefaultRule.conditionId = ZenModeConfig.toScheduleConditionId(new ScheduleInfo());
customDefaultRule.component = new ComponentName("android", "ScheduleConditionProvider");
@@ -1433,7 +1557,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
private void setupZenConfig() {
- mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
mZenModeHelperSpy.mConfig.allowAlarms = false;
mZenModeHelperSpy.mConfig.allowMedia = false;
mZenModeHelperSpy.mConfig.allowSystem = false;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java
index a0ee417c8b47..b16ca8b92848 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java
@@ -17,14 +17,18 @@
package com.android.server.notification;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.fail;
import android.service.notification.ZenPolicy;
+import android.service.notification.nano.DNDPolicyProto;
import android.test.suitebuilder.annotation.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.UiServiceTestCase;
+import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
+
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -190,6 +194,7 @@ public class ZenPolicyTest extends UiServiceTestCase {
ZenPolicy policy = builder.build();
assertEquals(ZenPolicy.STATE_UNSET, policy.getPriorityCategoryMessages());
assertEquals(ZenPolicy.PEOPLE_TYPE_UNSET, policy.getPriorityMessageSenders());
+ assertProtoMatches(policy, policy.toProto());
}
@Test
@@ -201,6 +206,7 @@ public class ZenPolicyTest extends UiServiceTestCase {
ZenPolicy policy = builder.build();
assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryCalls());
assertEquals(ZenPolicy.PEOPLE_TYPE_ANYONE, policy.getPriorityCallSenders());
+ assertProtoMatches(policy, policy.toProto());
}
@Test
@@ -210,6 +216,7 @@ public class ZenPolicyTest extends UiServiceTestCase {
ZenPolicy policy = builder.build();
assertAllPriorityCategoriesUnsetExcept(policy, -1);
assertAllVisualEffectsUnsetExcept(policy, -1);
+ assertProtoMatches(policy, policy.toProto());
}
@Test
@@ -221,12 +228,14 @@ public class ZenPolicyTest extends UiServiceTestCase {
assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_REMINDERS);
assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryReminders());
assertAllVisualEffectsUnsetExcept(policy, -1);
+ assertProtoMatches(policy, policy.toProto());
builder.allowReminders(false);
policy = builder.build();
assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_REMINDERS);
assertEquals(ZenPolicy.STATE_DISALLOW, policy.getPriorityCategoryReminders());
assertAllVisualEffectsUnsetExcept(policy, -1);
+ assertProtoMatches(policy, policy.toProto());
}
@Test
@@ -238,12 +247,14 @@ public class ZenPolicyTest extends UiServiceTestCase {
assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_EVENTS);
assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryEvents());
assertAllVisualEffectsUnsetExcept(policy, -1);
+ assertProtoMatches(policy, policy.toProto());
builder.allowEvents(false);
policy = builder.build();
assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_EVENTS);
assertEquals(ZenPolicy.STATE_DISALLOW, policy.getPriorityCategoryEvents());
assertAllVisualEffectsUnsetExcept(policy, -1);
+ assertProtoMatches(policy, policy.toProto());
}
@Test
@@ -256,6 +267,7 @@ public class ZenPolicyTest extends UiServiceTestCase {
assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryMessages());
assertEquals(ZenPolicy.PEOPLE_TYPE_ANYONE, policy.getPriorityMessageSenders());
assertAllVisualEffectsUnsetExcept(policy, -1);
+ assertProtoMatches(policy, policy.toProto());
builder.allowMessages(ZenPolicy.PEOPLE_TYPE_CONTACTS);
policy = builder.build();
@@ -263,6 +275,7 @@ public class ZenPolicyTest extends UiServiceTestCase {
assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryMessages());
assertEquals(ZenPolicy.PEOPLE_TYPE_CONTACTS, policy.getPriorityMessageSenders());
assertAllVisualEffectsUnsetExcept(policy, -1);
+ assertProtoMatches(policy, policy.toProto());
builder.allowMessages(ZenPolicy.PEOPLE_TYPE_STARRED);
policy = builder.build();
@@ -270,6 +283,7 @@ public class ZenPolicyTest extends UiServiceTestCase {
assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryMessages());
assertEquals(ZenPolicy.PEOPLE_TYPE_STARRED, policy.getPriorityMessageSenders());
assertAllVisualEffectsUnsetExcept(policy, -1);
+ assertProtoMatches(policy, policy.toProto());
builder.allowMessages(ZenPolicy.PEOPLE_TYPE_NONE);
policy = builder.build();
@@ -277,11 +291,13 @@ public class ZenPolicyTest extends UiServiceTestCase {
assertEquals(ZenPolicy.STATE_DISALLOW, policy.getPriorityCategoryMessages());
assertEquals(ZenPolicy.PEOPLE_TYPE_NONE, policy.getPriorityMessageSenders());
assertAllVisualEffectsUnsetExcept(policy, -1);
+ assertProtoMatches(policy, policy.toProto());
builder.allowMessages(ZenPolicy.PEOPLE_TYPE_UNSET);
policy = builder.build();
assertAllPriorityCategoriesUnsetExcept(policy, -1);
assertAllVisualEffectsUnsetExcept(policy, -1);
+ assertProtoMatches(policy, policy.toProto());
}
@Test
@@ -294,6 +310,7 @@ public class ZenPolicyTest extends UiServiceTestCase {
assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryCalls());
assertEquals(ZenPolicy.PEOPLE_TYPE_ANYONE, policy.getPriorityCallSenders());
assertAllVisualEffectsUnsetExcept(policy, -1);
+ assertProtoMatches(policy, policy.toProto());
builder.allowCalls(ZenPolicy.PEOPLE_TYPE_CONTACTS);
policy = builder.build();
@@ -301,6 +318,7 @@ public class ZenPolicyTest extends UiServiceTestCase {
assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryCalls());
assertEquals(ZenPolicy.PEOPLE_TYPE_CONTACTS, policy.getPriorityCallSenders());
assertAllVisualEffectsUnsetExcept(policy, -1);
+ assertProtoMatches(policy, policy.toProto());
builder.allowCalls(ZenPolicy.PEOPLE_TYPE_STARRED);
policy = builder.build();
@@ -308,6 +326,7 @@ public class ZenPolicyTest extends UiServiceTestCase {
assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryCalls());
assertEquals(ZenPolicy.PEOPLE_TYPE_STARRED, policy.getPriorityCallSenders());
assertAllVisualEffectsUnsetExcept(policy, -1);
+ assertProtoMatches(policy, policy.toProto());
builder.allowCalls(ZenPolicy.PEOPLE_TYPE_NONE);
policy = builder.build();
@@ -315,11 +334,13 @@ public class ZenPolicyTest extends UiServiceTestCase {
assertEquals(ZenPolicy.STATE_DISALLOW, policy.getPriorityCategoryCalls());
assertEquals(ZenPolicy.PEOPLE_TYPE_NONE, policy.getPriorityCallSenders());
assertAllVisualEffectsUnsetExcept(policy, -1);
+ assertProtoMatches(policy, policy.toProto());
builder.allowCalls(ZenPolicy.PEOPLE_TYPE_UNSET);
policy = builder.build();
assertAllPriorityCategoriesUnsetExcept(policy, -1);
assertAllVisualEffectsUnsetExcept(policy, -1);
+ assertProtoMatches(policy, policy.toProto());
}
@Test
@@ -331,12 +352,14 @@ public class ZenPolicyTest extends UiServiceTestCase {
assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS);
assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryRepeatCallers());
assertAllVisualEffectsUnsetExcept(policy, -1);
+ assertProtoMatches(policy, policy.toProto());
builder.allowRepeatCallers(false);
policy = builder.build();
assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS);
assertEquals(ZenPolicy.STATE_DISALLOW, policy.getPriorityCategoryRepeatCallers());
assertAllVisualEffectsUnsetExcept(policy, -1);
+ assertProtoMatches(policy, policy.toProto());
}
@Test
@@ -348,12 +371,14 @@ public class ZenPolicyTest extends UiServiceTestCase {
assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_ALARMS);
assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryAlarms());
assertAllVisualEffectsUnsetExcept(policy, -1);
+ assertProtoMatches(policy, policy.toProto());
builder.allowAlarms(false);
policy = builder.build();
assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_ALARMS);
assertEquals(ZenPolicy.STATE_DISALLOW, policy.getPriorityCategoryAlarms());
assertAllVisualEffectsUnsetExcept(policy, -1);
+ assertProtoMatches(policy, policy.toProto());
}
@Test
@@ -365,12 +390,14 @@ public class ZenPolicyTest extends UiServiceTestCase {
assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_MEDIA);
assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryMedia());
assertAllVisualEffectsUnsetExcept(policy, -1);
+ assertProtoMatches(policy, policy.toProto());
builder.allowMedia(false);
policy = builder.build();
assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_MEDIA);
assertEquals(ZenPolicy.STATE_DISALLOW, policy.getPriorityCategoryMedia());
assertAllVisualEffectsUnsetExcept(policy, -1);
+ assertProtoMatches(policy, policy.toProto());
}
@Test
@@ -382,12 +409,119 @@ public class ZenPolicyTest extends UiServiceTestCase {
assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_SYSTEM);
assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategorySystem());
assertAllVisualEffectsUnsetExcept(policy, -1);
+ assertProtoMatches(policy, policy.toProto());
builder.allowSystem(false);
policy = builder.build();
assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_SYSTEM);
assertEquals(ZenPolicy.STATE_DISALLOW, policy.getPriorityCategorySystem());
assertAllVisualEffectsUnsetExcept(policy, -1);
+ assertProtoMatches(policy, policy.toProto());
+ }
+
+ @Test
+ public void tesShowFullScreenIntent() {
+ ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+ builder.showFullScreenIntent(true);
+ ZenPolicy policy = builder.build();
+ assertAllVisualEffectsUnsetExcept(policy, ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT);
+ assertProtoMatches(policy, policy.toProto());
+
+ builder.showFullScreenIntent(false);
+ policy = builder.build();
+ assertAllVisualEffectsUnsetExcept(policy, ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT);
+ assertProtoMatches(policy, policy.toProto());
+ }
+
+ @Test
+ public void tesShowLights() {
+ ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+ builder.showLights(true);
+ ZenPolicy policy = builder.build();
+ assertAllVisualEffectsUnsetExcept(policy, ZenPolicy.VISUAL_EFFECT_LIGHTS);
+ assertProtoMatches(policy, policy.toProto());
+
+ builder.showLights(false);
+ policy = builder.build();
+ assertAllVisualEffectsUnsetExcept(policy, ZenPolicy.VISUAL_EFFECT_LIGHTS);
+ assertProtoMatches(policy, policy.toProto());
+ }
+
+ @Test
+ public void tesShowPeeking() {
+ ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+ builder.showPeeking(true);
+ ZenPolicy policy = builder.build();
+ assertAllVisualEffectsUnsetExcept(policy, ZenPolicy.VISUAL_EFFECT_PEEK);
+ assertProtoMatches(policy, policy.toProto());
+
+ builder.showPeeking(false);
+ policy = builder.build();
+ assertAllVisualEffectsUnsetExcept(policy, ZenPolicy.VISUAL_EFFECT_PEEK);
+ assertProtoMatches(policy, policy.toProto());
+ }
+
+ @Test
+ public void tesShowStatusBarIcons() {
+ ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+ builder.showStatusBarIcons(true);
+ ZenPolicy policy = builder.build();
+ assertAllVisualEffectsUnsetExcept(policy, ZenPolicy.VISUAL_EFFECT_STATUS_BAR);
+ assertProtoMatches(policy, policy.toProto());
+
+ builder.showStatusBarIcons(false);
+ policy = builder.build();
+ assertAllVisualEffectsUnsetExcept(policy, ZenPolicy.VISUAL_EFFECT_STATUS_BAR);
+ assertProtoMatches(policy, policy.toProto());
+ }
+
+ @Test
+ public void tesShowBadges() {
+ ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+ builder.showBadges(true);
+ ZenPolicy policy = builder.build();
+ assertAllVisualEffectsUnsetExcept(policy, ZenPolicy.VISUAL_EFFECT_BADGE);
+ assertProtoMatches(policy, policy.toProto());
+
+ builder.showBadges(false);
+ policy = builder.build();
+ assertAllVisualEffectsUnsetExcept(policy, ZenPolicy.VISUAL_EFFECT_BADGE);
+ assertProtoMatches(policy, policy.toProto());
+ }
+
+ @Test
+ public void tesShowInAmbientDisplay() {
+ ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+ builder.showInAmbientDisplay(true);
+ ZenPolicy policy = builder.build();
+ assertAllVisualEffectsUnsetExcept(policy, ZenPolicy.VISUAL_EFFECT_AMBIENT);
+ assertProtoMatches(policy, policy.toProto());
+
+ builder.showInAmbientDisplay(false);
+ policy = builder.build();
+ assertAllVisualEffectsUnsetExcept(policy, ZenPolicy.VISUAL_EFFECT_AMBIENT);
+ assertProtoMatches(policy, policy.toProto());
+ }
+
+ @Test
+ public void tesShowInNotificationList() {
+ ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+ builder.showInNotificationList(true);
+ ZenPolicy policy = builder.build();
+ assertAllVisualEffectsUnsetExcept(policy, ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST);
+ assertProtoMatches(policy, policy.toProto());
+
+ builder.showInNotificationList(false);
+ policy = builder.build();
+ assertAllVisualEffectsUnsetExcept(policy, ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST);
+ assertProtoMatches(policy, policy.toProto());
}
private void assertAllPriorityCategoriesUnsetExcept(ZenPolicy policy, int except) {
@@ -453,4 +587,35 @@ public class ZenPolicyTest extends UiServiceTestCase {
assertEquals(ZenPolicy.STATE_UNSET, policy.getVisualEffectNotificationList());
}
}
+
+ private void assertProtoMatches(ZenPolicy policy, byte[] bytes) {
+ try {
+ DNDPolicyProto proto = DNDPolicyProto.parseFrom(bytes);
+
+ assertEquals(policy.getPriorityCategoryCalls(), proto.calls);
+ assertEquals(policy.getPriorityCategoryRepeatCallers(), proto.repeatCallers);
+ assertEquals(policy.getPriorityCategoryMessages(), proto.messages);
+ assertEquals(policy.getPriorityCategoryConversations(), proto.conversations);
+ assertEquals(policy.getPriorityCategoryReminders(), proto.reminders);
+ assertEquals(policy.getPriorityCategoryEvents(), proto.events);
+ assertEquals(policy.getPriorityCategoryAlarms(), proto.alarms);
+ assertEquals(policy.getPriorityCategoryMedia(), proto.media);
+ assertEquals(policy.getPriorityCategorySystem(), proto.system);
+
+ assertEquals(policy.getVisualEffectFullScreenIntent(), proto.fullscreen);
+ assertEquals(policy.getVisualEffectLights(), proto.lights);
+ assertEquals(policy.getVisualEffectPeek(), proto.peek);
+ assertEquals(policy.getVisualEffectStatusBar(), proto.statusBar);
+ assertEquals(policy.getVisualEffectBadge(), proto.badge);
+ assertEquals(policy.getVisualEffectAmbient(), proto.ambient);
+ assertEquals(policy.getVisualEffectNotificationList(), proto.notificationList);
+
+ assertEquals(policy.getPriorityCallSenders(), proto.allowCallsFrom);
+ assertEquals(policy.getPriorityMessageSenders(), proto.allowMessagesFrom);
+ assertEquals(policy.getPriorityConversationSenders(), proto.allowConversationsFrom);
+ } catch (InvalidProtocolBufferNanoException e) {
+ fail("could not parse proto bytes");
+ }
+
+ }
}
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index 30df0d4b4ad9..4040fa6a675e 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -54,6 +54,8 @@
<activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityViewTestActivity" />
<activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityInActivityView"
android:resizeableActivity="true" />
+ <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityLaunchesNewActivityInActivityView"
+ android:resizeableActivity="true" />
<activity android:name="com.android.server.wm.TaskStackChangedListenerTest$LandscapeActivity"
android:screenOrientation="sensorLandscape"
android:showWhenLocked="true"
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index 02de408343c5..ef28a450695b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -38,7 +38,6 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
@@ -65,12 +64,12 @@ import android.view.WindowManager;
import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
-import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+
/**
* Tests for the {@link ActivityRecord} class.
*
@@ -406,12 +405,11 @@ public class AppWindowTokenTests extends WindowTestsBase {
assertHasStartingWindow(activity2);
// Assert that bottom activity is allowed to do animation.
+ ArrayList<WindowContainer> sources = new ArrayList<>();
+ sources.add(activity2);
doReturn(true).when(activity2).okToAnimate();
doReturn(true).when(activity2).isAnimating();
- final OnAnimationFinishedCallback onAnimationFinishedCallback =
- mock(OnAnimationFinishedCallback.class);
- assertTrue(activity2.applyAnimation(null, TRANSIT_ACTIVITY_OPEN, true, false,
- onAnimationFinishedCallback));
+ assertTrue(activity2.applyAnimation(null, TRANSIT_ACTIVITY_OPEN, true, false, sources));
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index 77ceeedae1ac..f7beb74688d6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -29,18 +29,14 @@ import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.when;
-import android.annotation.Nullable;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import com.android.server.wm.SurfaceAnimator.AnimationType;
-import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import org.junit.Before;
import org.junit.Test;
@@ -119,12 +115,8 @@ public class DimmerTests extends WindowTestsBase {
private static class SurfaceAnimatorStarterImpl implements Dimmer.SurfaceAnimatorStarter {
@Override
public void startAnimation(SurfaceAnimator surfaceAnimator, SurfaceControl.Transaction t,
- AnimationAdapter anim, boolean hidden, @AnimationType int type,
- @Nullable OnAnimationFinishedCallback animationFinishedCallback) {
+ AnimationAdapter anim, boolean hidden, @AnimationType int type) {
surfaceAnimator.mStaticAnimationFinishedCallback.onAnimationFinished(type, anim);
- if (animationFinishedCallback != null) {
- animationFinishedCallback.onAnimationFinished(type, anim);
- }
}
}
@@ -229,7 +221,7 @@ public class DimmerTests extends WindowTestsBase {
mDimmer.updateDims(mTransaction, new Rect());
verify(mSurfaceAnimatorStarter).startAnimation(any(SurfaceAnimator.class), any(
SurfaceControl.Transaction.class), any(AnimationAdapter.class), anyBoolean(),
- eq(ANIMATION_TYPE_DIMMER), isNull());
+ eq(ANIMATION_TYPE_DIMMER));
verify(mHost.getPendingTransaction()).remove(dimLayer);
}
@@ -287,7 +279,7 @@ public class DimmerTests extends WindowTestsBase {
mDimmer.updateDims(mTransaction, new Rect());
verify(mSurfaceAnimatorStarter, never()).startAnimation(any(SurfaceAnimator.class), any(
SurfaceControl.Transaction.class), any(AnimationAdapter.class), anyBoolean(),
- eq(ANIMATION_TYPE_DIMMER), isNull());
+ eq(ANIMATION_TYPE_DIMMER));
verify(mTransaction).remove(dimLayer);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
index 2c17bbeae498..2bd342420e2c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
@@ -299,6 +299,20 @@ public class TaskStackChangedListenerTest {
waitForCallback(singleTaskDisplayDrawnLatch);
}
+ public static class ActivityLaunchesNewActivityInActivityView extends TestActivity {
+ private boolean mActivityBLaunched = false;
+
+ @Override
+ protected void onPostResume() {
+ super.onPostResume();
+ if (mActivityBLaunched) {
+ return;
+ }
+ mActivityBLaunched = true;
+ startActivity(new Intent(this, ActivityB.class));
+ }
+ }
+
@Test
public void testSingleTaskDisplayEmpty() throws Exception {
final Instrumentation instrumentation = getInstrumentation();
@@ -335,13 +349,20 @@ public class TaskStackChangedListenerTest {
});
waitForCallback(activityViewReadyLatch);
+ // 1. start ActivityLaunchesNewActivityInActivityView in an ActivityView
+ // 2. ActivityLaunchesNewActivityInActivityView launches ActivityB
+ // 3. ActivityB finishes self.
+ // 4. Verify ITaskStackListener#onSingleTaskDisplayEmpty is not called yet.
final Context context = instrumentation.getContext();
- Intent intent = new Intent(context, ActivityInActivityView.class);
+ Intent intent = new Intent(context, ActivityLaunchesNewActivityInActivityView.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
activityView.startActivity(intent);
waitForCallback(singleTaskDisplayDrawnLatch);
+ UiDevice.getInstance(getInstrumentation()).waitForIdle();
assertEquals(1, singleTaskDisplayEmptyLatch.getCount());
+ // 5. Release the container, and ActivityLaunchesNewActivityInActivityView finishes.
+ // 6. Verify ITaskStackListener#onSingleTaskDisplayEmpty is called.
activityView.release();
waitForCallback(activityViewDestroyedLatch);
waitForCallback(singleTaskDisplayEmptyLatch);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
index d65b084ca8c1..f1dbde066125 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
@@ -153,7 +153,8 @@ public class TaskStackTests extends WindowTestsBase {
// Stack removal is deferred if one of its child is animating.
doReturn(true).when(stack).hasWindowsAlive();
- doReturn(true).when(task).isAnimating(eq(TRANSITION | CHILDREN), anyInt());
+ doReturn(stack).when(task).getAnimatingContainer(
+ eq(TRANSITION | CHILDREN), anyInt());
stack.removeIfPossible();
// For the case of deferred removal the task controller will still be connected to the its
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 00439f84702d..87485eac3412 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -66,14 +66,14 @@ import android.view.SurfaceSession;
import androidx.test.filters.SmallTest;
-import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
-
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
+import java.util.ArrayList;
import java.util.Comparator;
+
/**
* Test class for {@link WindowContainer}.
*
@@ -828,17 +828,21 @@ public class WindowContainerTests extends WindowTestsBase {
public void testTaskCanApplyAnimation() {
final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stack, 0 /* userId */);
- final ActivityRecord activity =
+ final ActivityRecord activity2 =
+ WindowTestUtils.createActivityRecordInTask(mDisplayContent, task);
+ final ActivityRecord activity1 =
WindowTestUtils.createActivityRecordInTask(mDisplayContent, task);
- verifyWindowContainerApplyAnimation(task, activity);
+ verifyWindowContainerApplyAnimation(task, activity1, activity2);
}
@Test
public void testStackCanApplyAnimation() {
final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
- final ActivityRecord activity = WindowTestUtils.createActivityRecordInTask(mDisplayContent,
+ final ActivityRecord activity2 = WindowTestUtils.createActivityRecordInTask(mDisplayContent,
createTaskInStack(stack, 0 /* userId */));
- verifyWindowContainerApplyAnimation(stack, activity);
+ final ActivityRecord activity1 = WindowTestUtils.createActivityRecordInTask(mDisplayContent,
+ createTaskInStack(stack, 0 /* userId */));
+ verifyWindowContainerApplyAnimation(stack, activity1, activity2);
}
@Test
@@ -871,7 +875,8 @@ public class WindowContainerTests extends WindowTestsBase {
assertEquals(displayArea, displayArea.getDisplayArea());
}
- private void verifyWindowContainerApplyAnimation(WindowContainer wc, ActivityRecord act) {
+ private void verifyWindowContainerApplyAnimation(WindowContainer wc, ActivityRecord act,
+ ActivityRecord act2) {
// Initial remote animation for app transition.
final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
new IRemoteAnimationRunner.Stub() {
@@ -895,17 +900,23 @@ public class WindowContainerTests extends WindowTestsBase {
wc.getDisplayContent().mAppTransition.overridePendingAppTransitionRemote(adapter);
spyOn(wc);
doReturn(true).when(wc).okToAnimate();
- final OnAnimationFinishedCallback onAnimationFinishedCallback =
- mock(OnAnimationFinishedCallback.class);
// Make sure animating state is as expected after applied animation.
- assertTrue(wc.applyAnimation(null, TRANSIT_TASK_OPEN, true, false,
- onAnimationFinishedCallback));
- assertEquals(wc.getTopMostActivity(), act);
+
+ // Animation target is promoted from act to wc. act2 is a descendant of wc, but not a source
+ // of the animation.
+ ArrayList<WindowContainer<WindowState>> sources = new ArrayList<>();
+ sources.add(act);
+ assertTrue(wc.applyAnimation(null, TRANSIT_TASK_OPEN, true, false, sources));
+
+ assertEquals(act, wc.getTopMostActivity());
assertTrue(wc.isAnimating());
+ assertTrue(wc.isAnimating(0, ANIMATION_TYPE_APP_TRANSITION));
+ assertTrue(wc.getAnimationSources().contains(act));
+ assertFalse(wc.getAnimationSources().contains(act2));
assertTrue(act.isAnimating(PARENTS));
- verify(onAnimationFinishedCallback, times(0)).onAnimationFinished(
- eq(ANIMATION_TYPE_APP_TRANSITION), any());
+ assertTrue(act.isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION));
+ assertEquals(wc, act.getAnimatingContainer(PARENTS, ANIMATION_TYPE_APP_TRANSITION));
// Make sure animation finish callback will be received and reset animating state after
// animation finish.
@@ -914,8 +925,6 @@ public class WindowContainerTests extends WindowTestsBase {
verify(wc).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION), any());
assertFalse(wc.isAnimating());
assertFalse(act.isAnimating(PARENTS));
- verify(onAnimationFinishedCallback, times(1)).onAnimationFinished(
- eq(ANIMATION_TYPE_APP_TRANSITION), any());
}
/* Used so we can gain access to some protected members of the {@link WindowContainer} class */
diff --git a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
index b3d7c0d36763..4606fb4b631c 100644
--- a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
+++ b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
@@ -21,7 +21,6 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.res.Resources;
import android.os.SystemConfigManager;
import android.os.UserHandle;
import android.permission.PermissionManager;
@@ -30,9 +29,7 @@ import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.Log;
-import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.util.ArrayUtils;
import java.util.ArrayList;
import java.util.List;
@@ -162,12 +159,9 @@ public final class CarrierAppUtils {
try {
for (ApplicationInfo ai : candidates) {
String packageName = ai.packageName;
- String[] restrictedCarrierApps = Resources.getSystem().getStringArray(
- R.array.config_restrictedPreinstalledCarrierApps);
boolean hasPrivileges = telephonyManager != null
&& telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName)
- == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS
- && !ArrayUtils.contains(restrictedCarrierApps, packageName);
+ == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
// add hiddenUntilInstalled flag for carrier apps and associated apps
packageManager.setSystemAppState(
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index 1993550d52b8..e6279dc977ee 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -366,7 +366,7 @@ public final class CellIdentityLte extends CellIdentity {
.append(" mPci=").append(mPci)
.append(" mTac=").append(mTac)
.append(" mEarfcn=").append(mEarfcn)
- .append(" mBands=").append(mBands)
+ .append(" mBands=").append(Arrays.toString(mBands))
.append(" mBandwidth=").append(mBandwidth)
.append(" mMcc=").append(mMccStr)
.append(" mMnc=").append(mMncStr)
diff --git a/telephony/java/android/telephony/CellIdentityNr.java b/telephony/java/android/telephony/CellIdentityNr.java
index 8dd7bdd57841..e34bbfcde492 100644
--- a/telephony/java/android/telephony/CellIdentityNr.java
+++ b/telephony/java/android/telephony/CellIdentityNr.java
@@ -242,7 +242,7 @@ public final class CellIdentityNr extends CellIdentity {
.append(" mPci = ").append(mPci)
.append(" mTac = ").append(mTac)
.append(" mNrArfcn = ").append(mNrArfcn)
- .append(" mBands = ").append(mBands)
+ .append(" mBands = ").append(Arrays.toString(mBands))
.append(" mMcc = ").append(mMccStr)
.append(" mMnc = ").append(mMncStr)
.append(" mNci = ").append(mNci)
diff --git a/tests/net/common/java/android/net/NetworkAgentConfigTest.kt b/tests/net/common/java/android/net/NetworkAgentConfigTest.kt
index de65ba24972b..a4d8353d1253 100644
--- a/tests/net/common/java/android/net/NetworkAgentConfigTest.kt
+++ b/tests/net/common/java/android/net/NetworkAgentConfigTest.kt
@@ -44,7 +44,7 @@ class NetworkAgentConfigTest {
setPartialConnectivityAcceptable(false)
setUnvalidatedConnectivityAcceptable(true)
}.build()
- assertParcelSane(config, 9)
+ assertParcelSane(config, 10)
}
@Test @IgnoreUpTo(Build.VERSION_CODES.Q)
diff --git a/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java b/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
new file mode 100644
index 000000000000..058856dcd6fb
--- /dev/null
+++ b/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Looper;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.util.CollectionUtils;
+import com.android.server.net.NetworkStatsSubscriptionsMonitor.Delegate;
+import com.android.server.net.NetworkStatsSubscriptionsMonitor.RatTypeListener;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+@RunWith(JUnit4.class)
+public final class NetworkStatsSubscriptionsMonitorTest {
+ private static final int TEST_SUBID1 = 3;
+ private static final int TEST_SUBID2 = 5;
+ private static final String TEST_IMSI1 = "466921234567890";
+ private static final String TEST_IMSI2 = "466920987654321";
+ private static final String TEST_IMSI3 = "466929999999999";
+
+ @Mock private Context mContext;
+ @Mock private PhoneStateListener mPhoneStateListener;
+ @Mock private SubscriptionManager mSubscriptionManager;
+ @Mock private TelephonyManager mTelephonyManager;
+ @Mock private Delegate mDelegate;
+ private final List<Integer> mTestSubList = new ArrayList<>();
+
+ private final Executor mExecutor = Executors.newSingleThreadExecutor();
+ private NetworkStatsSubscriptionsMonitor mMonitor;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+
+ when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
+
+ when(mContext.getSystemService(eq(Context.TELEPHONY_SUBSCRIPTION_SERVICE)))
+ .thenReturn(mSubscriptionManager);
+ when(mContext.getSystemService(eq(Context.TELEPHONY_SERVICE)))
+ .thenReturn(mTelephonyManager);
+
+ mMonitor = new NetworkStatsSubscriptionsMonitor(mContext, mExecutor, mDelegate);
+ }
+
+ @Test
+ public void testStartStop() {
+ // Verify that addOnSubscriptionsChangedListener() is never called before start().
+ verify(mSubscriptionManager, never())
+ .addOnSubscriptionsChangedListener(mExecutor, mMonitor);
+ mMonitor.start();
+ verify(mSubscriptionManager).addOnSubscriptionsChangedListener(mExecutor, mMonitor);
+
+ // Verify that removeOnSubscriptionsChangedListener() is never called before stop()
+ verify(mSubscriptionManager, never()).removeOnSubscriptionsChangedListener(mMonitor);
+ mMonitor.stop();
+ verify(mSubscriptionManager).removeOnSubscriptionsChangedListener(mMonitor);
+ }
+
+ @NonNull
+ private static int[] convertArrayListToIntArray(@NonNull List<Integer> arrayList) {
+ final int[] list = new int[arrayList.size()];
+ for (int i = 0; i < arrayList.size(); i++) {
+ list[i] = arrayList.get(i);
+ }
+ return list;
+ }
+
+ private void setRatTypeForSub(List<RatTypeListener> listeners,
+ int subId, int type) {
+ final ServiceState serviceState = mock(ServiceState.class);
+ when(serviceState.getDataNetworkType()).thenReturn(type);
+ final RatTypeListener match = CollectionUtils
+ .find(listeners, it -> it.getSubId() == subId);
+ if (match != null) {
+ match.onServiceStateChanged(serviceState);
+ }
+ }
+
+ private void addTestSub(int subId, String subscriberId) {
+ // add SubId to TestSubList.
+ if (!mTestSubList.contains(subId)) {
+ mTestSubList.add(subId);
+ }
+ final int[] subList = convertArrayListToIntArray(mTestSubList);
+ when(mSubscriptionManager.getCompleteActiveSubscriptionIdList()).thenReturn(subList);
+ when(mTelephonyManager.getSubscriberId(subId)).thenReturn(subscriberId);
+ mMonitor.onSubscriptionsChanged();
+ }
+
+ private void removeTestSub(int subId) {
+ // Remove subId from TestSubList.
+ mTestSubList.removeIf(it -> it == subId);
+ final int[] subList = convertArrayListToIntArray(mTestSubList);
+ when(mSubscriptionManager.getCompleteActiveSubscriptionIdList()).thenReturn(subList);
+ mMonitor.onSubscriptionsChanged();
+ }
+
+ private void assertRatTypeChangedForSub(String subscriberId, int ratType) {
+ assertEquals(mMonitor.getRatTypeForSubscriberId(subscriberId), ratType);
+ final ArgumentCaptor<Integer> typeCaptor = ArgumentCaptor.forClass(Integer.class);
+ // Verify callback with the subscriberId and the RAT type should be as expected.
+ // It will fail if get a callback with an unexpected RAT type.
+ verify(mDelegate).onCollapsedRatTypeChanged(eq(subscriberId), typeCaptor.capture());
+ final int type = typeCaptor.getValue();
+ assertEquals(ratType, type);
+ }
+
+ private void assertRatTypeNotChangedForSub(String subscriberId, int ratType) {
+ assertEquals(mMonitor.getRatTypeForSubscriberId(subscriberId), ratType);
+ // Should never get callback with any RAT type.
+ verify(mDelegate, never()).onCollapsedRatTypeChanged(eq(subscriberId), anyInt());
+ }
+
+ @Test
+ public void testSubChangedAndRatTypeChanged() {
+ final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor =
+ ArgumentCaptor.forClass(RatTypeListener.class);
+
+ mMonitor.start();
+ // Insert sim1, verify RAT type is NETWORK_TYPE_UNKNOWN, and never get any callback
+ // before changing RAT type.
+ addTestSub(TEST_SUBID1, TEST_IMSI1);
+ assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+
+ // Insert sim2.
+ addTestSub(TEST_SUBID2, TEST_IMSI2);
+ assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ verify(mTelephonyManager, times(2)).listen(ratTypeListenerCaptor.capture(),
+ eq(PhoneStateListener.LISTEN_SERVICE_STATE));
+ reset(mDelegate);
+
+ // Set RAT type of sim1 to UMTS.
+ // Verify RAT type of sim1 after subscription gets onCollapsedRatTypeChanged() callback
+ // and others remain untouched.
+ setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1,
+ TelephonyManager.NETWORK_TYPE_UMTS);
+ assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS);
+ assertRatTypeNotChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ assertRatTypeNotChangedForSub(TEST_IMSI3, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ reset(mDelegate);
+
+ // Set RAT type of sim2 to LTE.
+ // Verify RAT type of sim2 after subscription gets onCollapsedRatTypeChanged() callback
+ // and others remain untouched.
+ setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID2,
+ TelephonyManager.NETWORK_TYPE_LTE);
+ assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS);
+ assertRatTypeChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_LTE);
+ assertRatTypeNotChangedForSub(TEST_IMSI3, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ reset(mDelegate);
+
+ // Remove sim2 and verify that callbacks are fired and RAT type is correct for sim2.
+ // while the other two remain untouched.
+ removeTestSub(TEST_SUBID2);
+ verify(mTelephonyManager).listen(any(), eq(PhoneStateListener.LISTEN_NONE));
+ assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS);
+ assertRatTypeChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ assertRatTypeNotChangedForSub(TEST_IMSI3, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ reset(mDelegate);
+
+ // Set RAT type of sim1 to UNKNOWN. Then stop monitoring subscription changes
+ // and verify that the listener for sim1 is removed.
+ setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1,
+ TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ reset(mDelegate);
+
+ mMonitor.stop();
+ verify(mTelephonyManager, times(2)).listen(any(), eq(PhoneStateListener.LISTEN_NONE));
+ assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ }
+}
diff --git a/tests/utils/hostutils/Android.bp b/tests/utils/hostutils/Android.bp
new file mode 100644
index 000000000000..c9ad70280aa6
--- /dev/null
+++ b/tests/utils/hostutils/Android.bp
@@ -0,0 +1,28 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+java_library_host {
+ name: "frameworks-base-hostutils",
+
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
+
+ libs: [
+ "tradefed",
+ "junit",
+ ],
+}
diff --git a/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java b/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java
new file mode 100644
index 000000000000..6bd6985f9675
--- /dev/null
+++ b/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util.test;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil;
+
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.rules.ExternalResource;
+import org.junit.rules.TemporaryFolder;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+
+import javax.annotation.Nullable;
+
+/**
+ * Allows pushing files onto the device and various options for rebooting. Useful for installing
+ * APKs/files to system partitions which otherwise wouldn't be easily changed.
+ *
+ * It's strongly recommended to pass in a {@link ClassRule} annotated {@link TestRuleDelegate} to
+ * do a full reboot at the end of a test to ensure the device is in a valid state, assuming the
+ * default {@link RebootStrategy#FULL} isn't used.
+ */
+public class SystemPreparer extends ExternalResource {
+ private static final long OVERLAY_ENABLE_TIMEOUT_MS = 30000;
+
+ // The paths of the files pushed onto the device through this rule.
+ private ArrayList<String> mPushedFiles = new ArrayList<>();
+
+ // The package names of packages installed through this rule.
+ private ArrayList<String> mInstalledPackages = new ArrayList<>();
+
+ private final TemporaryFolder mHostTempFolder;
+ private final DeviceProvider mDeviceProvider;
+ private final RebootStrategy mRebootStrategy;
+ private final TearDownRule mTearDownRule;
+
+ public SystemPreparer(TemporaryFolder hostTempFolder, DeviceProvider deviceProvider) {
+ this(hostTempFolder, RebootStrategy.FULL, null, deviceProvider);
+ }
+
+ public SystemPreparer(TemporaryFolder hostTempFolder, RebootStrategy rebootStrategy,
+ @Nullable TestRuleDelegate testRuleDelegate, DeviceProvider deviceProvider) {
+ mHostTempFolder = hostTempFolder;
+ mDeviceProvider = deviceProvider;
+ mRebootStrategy = rebootStrategy;
+ mTearDownRule = new TearDownRule(mDeviceProvider);
+ if (testRuleDelegate != null) {
+ testRuleDelegate.setDelegate(mTearDownRule);
+ }
+ }
+
+ /** Copies a file within the host test jar to a path on device. */
+ public SystemPreparer pushResourceFile(String filePath, String outputPath)
+ throws DeviceNotAvailableException, IOException {
+ final ITestDevice device = mDeviceProvider.getDevice();
+ remount();
+ assertTrue(device.pushFile(copyResourceToTemp(filePath), outputPath));
+ mPushedFiles.add(outputPath);
+ return this;
+ }
+
+ /** Copies a file directly from the host file system to a path on device. */
+ public SystemPreparer pushFile(File file, String outputPath)
+ throws DeviceNotAvailableException {
+ final ITestDevice device = mDeviceProvider.getDevice();
+ remount();
+ assertTrue(device.pushFile(file, outputPath));
+ mPushedFiles.add(outputPath);
+ return this;
+ }
+
+ /** Deletes the given path from the device */
+ public SystemPreparer deleteFile(String file) throws DeviceNotAvailableException {
+ final ITestDevice device = mDeviceProvider.getDevice();
+ remount();
+ device.deleteFile(file);
+ return this;
+ }
+
+ /** Installs an APK within the host test jar onto the device. */
+ public SystemPreparer installResourceApk(String resourcePath, String packageName)
+ throws DeviceNotAvailableException, IOException {
+ final ITestDevice device = mDeviceProvider.getDevice();
+ final File tmpFile = copyResourceToTemp(resourcePath);
+ final String result = device.installPackage(tmpFile, true /* reinstall */);
+ Assert.assertNull(result);
+ mInstalledPackages.add(packageName);
+ return this;
+ }
+
+ /** Sets the enable state of an overlay package. */
+ public SystemPreparer setOverlayEnabled(String packageName, boolean enabled)
+ throws DeviceNotAvailableException {
+ final ITestDevice device = mDeviceProvider.getDevice();
+ final String enable = enabled ? "enable" : "disable";
+
+ // Wait for the overlay to change its enabled state.
+ final long endMillis = System.currentTimeMillis() + OVERLAY_ENABLE_TIMEOUT_MS;
+ String result;
+ while (System.currentTimeMillis() <= endMillis) {
+ device.executeShellCommand(String.format("cmd overlay %s %s", enable, packageName));
+ result = device.executeShellCommand("cmd overlay dump isenabled "
+ + packageName);
+ if (((enabled) ? "true\n" : "false\n").equals(result)) {
+ return this;
+ }
+
+ try {
+ Thread.sleep(200);
+ } catch (InterruptedException ignore) {
+ }
+ }
+
+ throw new IllegalStateException(String.format("Failed to %s overlay %s:\n%s", enable,
+ packageName, device.executeShellCommand("cmd overlay list")));
+ }
+
+ /** Restarts the device and waits until after boot is completed. */
+ public SystemPreparer reboot() throws DeviceNotAvailableException {
+ ITestDevice device = mDeviceProvider.getDevice();
+ switch (mRebootStrategy) {
+ case FULL:
+ device.reboot();
+ break;
+ case UNTIL_ONLINE:
+ device.rebootUntilOnline();
+ break;
+ case USERSPACE:
+ device.rebootUserspace();
+ break;
+ case USERSPACE_UNTIL_ONLINE:
+ device.rebootUserspaceUntilOnline();
+ break;
+ case START_STOP:
+ device.executeShellCommand("stop");
+ device.executeShellCommand("start");
+ ITestDevice.RecoveryMode cachedRecoveryMode = device.getRecoveryMode();
+ device.setRecoveryMode(ITestDevice.RecoveryMode.ONLINE);
+
+ if (device.isEncryptionSupported()) {
+ if (device.isDeviceEncrypted()) {
+ LogUtil.CLog.e("Device is encrypted after userspace reboot!");
+ device.unlockDevice();
+ }
+ }
+
+ device.setRecoveryMode(cachedRecoveryMode);
+ device.waitForDeviceAvailable();
+ break;
+ }
+ return this;
+ }
+
+ public SystemPreparer remount() throws DeviceNotAvailableException {
+ mTearDownRule.remount();
+ return this;
+ }
+
+ /** Copies a file within the host test jar to a temporary file on the host machine. */
+ private File copyResourceToTemp(String resourcePath) throws IOException {
+ final File tempFile = mHostTempFolder.newFile();
+ final ClassLoader classLoader = getClass().getClassLoader();
+ try (InputStream assetIs = classLoader.getResource(resourcePath).openStream();
+ FileOutputStream assetOs = new FileOutputStream(tempFile)) {
+ if (assetIs == null) {
+ throw new IllegalStateException("Failed to find resource " + resourcePath);
+ }
+
+ int b;
+ while ((b = assetIs.read()) >= 0) {
+ assetOs.write(b);
+ }
+ }
+
+ return tempFile;
+ }
+
+ /** Removes installed packages and files that were pushed to the device. */
+ @Override
+ protected void after() {
+ final ITestDevice device = mDeviceProvider.getDevice();
+ try {
+ remount();
+ for (final String file : mPushedFiles) {
+ device.deleteFile(file);
+ }
+ for (final String packageName : mInstalledPackages) {
+ device.uninstallPackage(packageName);
+ }
+ reboot();
+ } catch (DeviceNotAvailableException e) {
+ Assert.fail(e.toString());
+ }
+ }
+
+ /**
+ * A hacky workaround since {@link org.junit.AfterClass} and {@link ClassRule} require static
+ * members. Will defer assignment of the actual {@link TestRule} to execute until after any
+ * test case has been run.
+ *
+ * In effect, this makes the {@link ITestDevice} to be accessible after all test cases have
+ * been executed, allowing {@link ITestDevice#reboot()} to be used to fully restore the device.
+ */
+ public static class TestRuleDelegate implements TestRule {
+
+ private boolean mThrowOnNull;
+
+ @Nullable
+ private TestRule mTestRule;
+
+ public TestRuleDelegate(boolean throwOnNull) {
+ mThrowOnNull = throwOnNull;
+ }
+
+ public void setDelegate(TestRule testRule) {
+ mTestRule = testRule;
+ }
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ if (mTestRule == null) {
+ if (mThrowOnNull) {
+ throw new IllegalStateException("TestRule delegate was not set");
+ } else {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ base.evaluate();
+ }
+ };
+ }
+ }
+
+ Statement statement = mTestRule.apply(base, description);
+ mTestRule = null;
+ return statement;
+ }
+ }
+
+ /**
+ * Forces a full reboot at the end of the test class to restore any device state.
+ */
+ private static class TearDownRule extends ExternalResource {
+
+ private DeviceProvider mDeviceProvider;
+ private boolean mInitialized;
+ private boolean mWasVerityEnabled;
+ private boolean mWasAdbRoot;
+ private boolean mIsVerityEnabled;
+
+ TearDownRule(DeviceProvider deviceProvider) {
+ mDeviceProvider = deviceProvider;
+ }
+
+ @Override
+ protected void before() {
+ // This method will never be run
+ }
+
+ @Override
+ protected void after() {
+ try {
+ initialize();
+ ITestDevice device = mDeviceProvider.getDevice();
+ if (mWasVerityEnabled != mIsVerityEnabled) {
+ device.executeShellCommand(
+ mWasVerityEnabled ? "enable-verity" : "disable-verity");
+ }
+ device.reboot();
+ if (!mWasAdbRoot) {
+ device.disableAdbRoot();
+ }
+ } catch (DeviceNotAvailableException e) {
+ Assert.fail(e.toString());
+ }
+ }
+
+ /**
+ * Remount is done inside this class so that the verity state can be tracked.
+ */
+ public void remount() throws DeviceNotAvailableException {
+ initialize();
+ ITestDevice device = mDeviceProvider.getDevice();
+ device.enableAdbRoot();
+ if (mIsVerityEnabled) {
+ mIsVerityEnabled = false;
+ device.executeShellCommand("disable-verity");
+ device.reboot();
+ }
+ device.executeShellCommand("remount");
+ device.waitForDeviceAvailable();
+ }
+
+ private void initialize() throws DeviceNotAvailableException {
+ if (mInitialized) {
+ return;
+ }
+ mInitialized = true;
+ ITestDevice device = mDeviceProvider.getDevice();
+ mWasAdbRoot = device.isAdbRoot();
+ device.enableAdbRoot();
+ String veritySystem = device.getProperty("partition.system.verified");
+ String verityVendor = device.getProperty("partition.vendor.verified");
+ mWasVerityEnabled = (veritySystem != null && !veritySystem.isEmpty())
+ || (verityVendor != null && !verityVendor.isEmpty());
+ mIsVerityEnabled = mWasVerityEnabled;
+ }
+ }
+
+ public interface DeviceProvider {
+ ITestDevice getDevice();
+ }
+
+ /**
+ * How to reboot the device. Ordered from slowest to fastest.
+ */
+ public enum RebootStrategy {
+ /** @see ITestDevice#reboot() */
+ FULL,
+
+ /** @see ITestDevice#rebootUntilOnline() () */
+ UNTIL_ONLINE,
+
+ /** @see ITestDevice#rebootUserspace() */
+ USERSPACE,
+
+ /** @see ITestDevice#rebootUserspaceUntilOnline() () */
+ USERSPACE_UNTIL_ONLINE,
+
+ /**
+ * Uses shell stop && start to "reboot" the device. May leave invalid state after each test.
+ * Whether this matters or not depends on what's being tested.
+ */
+ START_STOP
+ }
+}
diff --git a/wifi/Android.bp b/wifi/Android.bp
index 9c5b7b66f2a3..941ff61b3ba5 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -12,6 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+java_defaults {
+ name: "wifi-module-sdk-version-defaults",
+ min_sdk_version: "30",
+ target_sdk_version: "30",
+}
+
filegroup {
name: "framework-wifi-updatable-exported-aidl-sources",
srcs: ["aidl-export/**/*.aidl"],
@@ -73,6 +79,7 @@ test_access_hidden_api_whitelist = [
// classes before they are renamed.
java_library {
name: "framework-wifi-pre-jarjar",
+ defaults: ["wifi-module-sdk-version-defaults"],
sdk_version: "module_current",
static_libs: [
"framework-wifi-util-lib",
@@ -98,7 +105,10 @@ java_library {
// post-jarjar version of framework-wifi
java_sdk_library {
name: "framework-wifi",
- defaults: ["framework-module-defaults"],
+ defaults: [
+ "framework-module-defaults",
+ "wifi-module-sdk-version-defaults",
+ ],
static_libs: [
"framework-wifi-util-lib",
"android.hardware.wifi-V1.0-java-constants",
diff --git a/wifi/java/android/net/wifi/SoftApCapability.java b/wifi/java/android/net/wifi/SoftApCapability.java
index 18b26db1b020..dcb57ecc933f 100644
--- a/wifi/java/android/net/wifi/SoftApCapability.java
+++ b/wifi/java/android/net/wifi/SoftApCapability.java
@@ -102,7 +102,9 @@ public final class SoftApCapability implements Parcelable {
/**
* Returns true when all of the queried features are supported, otherwise false.
*
- * @param features One or combination of the features from {@link @HotspotFeatures}
+ * @param features One or combination of the following features:
+ * {@link #SOFTAP_FEATURE_ACS_OFFLOAD}, {@link #SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT} or
+ * {@link #SOFTAP_FEATURE_WPA3_SAE}.
*/
public boolean areFeaturesSupported(@HotspotFeatures long features) {
return (mSupportedFeatures & features) == features;
@@ -122,7 +124,9 @@ public final class SoftApCapability implements Parcelable {
* Constructor with combination of the feature.
* Zero to no supported feature.
*
- * @param features One or combination of the features from {@link @HotspotFeatures}.
+ * @param features One or combination of the following features:
+ * {@link #SOFTAP_FEATURE_ACS_OFFLOAD}, {@link #SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT} or
+ * {@link #SOFTAP_FEATURE_WPA3_SAE}.
* @hide
*/
public SoftApCapability(@HotspotFeatures long features) {
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index 457e0db9dc54..2bcd4f4241a6 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -165,7 +165,8 @@ public final class SoftApConfiguration implements Parcelable {
/**
* The operating band of the AP.
- * One of the band types from {@link @BandType}.
+ * One or combination of the following band type:
+ * {@link #BAND_2GHZ}, {@link #BAND_5GHZ}, {@link #BAND_6GHZ}.
*/
private final @BandType int mBand;
@@ -181,7 +182,11 @@ public final class SoftApConfiguration implements Parcelable {
/**
* The operating security type of the AP.
- * One of the security types from {@link @SecurityType}
+ * One of the following security types:
+ * {@link #SECURITY_TYPE_OPEN},
+ * {@link #SECURITY_TYPE_WPA2_PSK},
+ * {@link #SECURITY_TYPE_WPA3_SAE_TRANSITION},
+ * {@link #SECURITY_TYPE_WPA3_SAE}
*/
private final @SecurityType int mSecurityType;
@@ -393,8 +398,12 @@ public final class SoftApConfiguration implements Parcelable {
}
/**
- * Returns {@link BandType} set to be the band for the AP.
- * {@link Builder#setBand(@BandType int)}.
+ * Returns band type set to be the band for the AP.
+ *
+ * One or combination of the following band type:
+ * {@link #BAND_2GHZ}, {@link #BAND_5GHZ}, {@link #BAND_6GHZ}.
+ *
+ * {@link Builder#setBand(int)}.
*
* @hide
*/
@@ -679,15 +688,19 @@ public final class SoftApConfiguration implements Parcelable {
/**
* Specifies that this AP should use specific security type with the given ASCII passphrase.
*
- * @param securityType one of the security types from {@link @SecurityType}.
- * @param passphrase The passphrase to use for sepcific {@link @SecurityType} configuration
- * or null with {@link @SecurityType#SECURITY_TYPE_OPEN}.
+ * @param securityType One of the following security types:
+ * {@link #SECURITY_TYPE_OPEN},
+ * {@link #SECURITY_TYPE_WPA2_PSK},
+ * {@link #SECURITY_TYPE_WPA3_SAE_TRANSITION},
+ * {@link #SECURITY_TYPE_WPA3_SAE}.
+ * @param passphrase The passphrase to use for sepcific {@code securityType} configuration
+ * or null with {@link #SECURITY_TYPE_OPEN}.
*
* @return Builder for chaining.
* @throws IllegalArgumentException when the passphrase length is invalid and
- * {@code securityType} is not {@link @SecurityType#SECURITY_TYPE_OPEN}
+ * {@code securityType} is not {@link #SECURITY_TYPE_OPEN}
* or non-null passphrase and {@code securityType} is
- * {@link @SecurityType#SECURITY_TYPE_OPEN}.
+ * {@link #SECURITY_TYPE_OPEN}.
*/
@NonNull
public Builder setPassphrase(@Nullable String passphrase, @SecurityType int securityType) {
@@ -735,9 +748,10 @@ public final class SoftApConfiguration implements Parcelable {
/**
* Specifies the band for the AP.
* <p>
- * <li>If not set, defaults to BAND_2GHZ {@link @BandType}.</li>
+ * <li>If not set, defaults to {@link #BAND_2GHZ}.</li>
*
- * @param band One or combination of the band types from {@link @BandType}.
+ * @param band One or combination of the following band type:
+ * {@link #BAND_2GHZ}, {@link #BAND_5GHZ}, {@link #BAND_6GHZ}.
* @return Builder for chaining.
*/
@NonNull
@@ -758,7 +772,7 @@ public final class SoftApConfiguration implements Parcelable {
* <p>
* The default for the channel is a the special value 0 to have the framework
* auto-select a valid channel from the band configured with
- * {@link #setBand(@BandType int)}.
+ * {@link #setBand(int)}.
*
* The channel auto selection will offload to driver when
* {@link SoftApCapability#areFeaturesSupported(
diff --git a/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java
index 3215246a9c1f..4116234c4c8d 100644
--- a/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java
+++ b/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java
@@ -1039,11 +1039,11 @@ public class WifiNl80211Manager {
* The result depends on the on the country code that has been set.
*
* @param band as specified by one of the WifiScanner.WIFI_BAND_* constants.
- * The following bands are supported {@link @WifiScanner.WifiBandBasic}:
- * WifiScanner.WIFI_BAND_24_GHZ
- * WifiScanner.WIFI_BAND_5_GHZ
- * WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY
- * WifiScanner.WIFI_BAND_6_GHZ
+ * The following bands are supported:
+ * {@link WifiScanner#WIFI_BAND_24_GHZ},
+ * {@link WifiScanner#WIFI_BAND_5_GHZ},
+ * {@link WifiScanner#WIFI_BAND_5_GHZ_DFS_ONLY},
+ * {@link WifiScanner#WIFI_BAND_6_GHZ}
* @return frequencies vector of valid frequencies (MHz), or an empty array for error.
* @throws IllegalArgumentException if band is not recognized.
*/